00001 package theba.core;
00002
00003 import java.awt.Point;
00004 import java.awt.Rectangle;
00005 import java.io.FileOutputStream;
00006 import java.io.IOException;
00007 import java.util.ArrayList;
00008 import java.util.LinkedList;
00009
00010 import javax.swing.JOptionPane;
00011
00012 import theba.core.gui.ThebaGUI;
00013 import theba.core.io.DataConversion;
00014 import theba.core.math.Point3D;
00015 import theba.descriptors.AvgCurvature;
00016 import theba.descriptors.BendingEnergyDescriptor;
00017 import theba.descriptors.CircularityDescriptor;
00018 import theba.descriptors.EccentricityDescriptor;
00019
00020
00029 public class ImageFunctions {
00030 public static short[] cachedMap = null;
00031
00037 public static double bendingEnergy(short[] pixels, int width, int height) {
00038 return (new BendingEnergyDescriptor()).measure(pixels, width, height);
00039 }
00040
00046 public static int floodFill2D(int xp, int yp, int width, int height,
00047 final short[] input, short[] output, short value) {
00048 if (input == null) {
00049 System.err.println("Input was zero in floodFill2DMasked!");
00050 return 0;
00051 }
00052 if (output == null) {
00053 System.err.println("Output was zero in floodFill2DMasked!");
00054 return 0;
00055 }
00056 if (input == output)
00057 throw new RuntimeException("Input equals output!");
00058 if (xp < 0 || yp < 0 || xp >= width || yp >= height) {
00059 System.err.print("Coord out of bounds!");
00060 return 0;
00061 }
00062 LinkedList<Point> queue = new LinkedList<Point> ();
00063 queue.addFirst(new Point(xp, yp));
00064 int count = 0;
00065 short col = input[xp + yp * width];
00066 while (queue.isEmpty() == false) {
00067 Point p = queue.removeFirst();
00068 int x = p.x;
00069 int y = p.y;
00070 int index = x + y * width;
00071 if ((input[index]) == col) {
00072 count++;
00073 output[index] = value;
00074 if (x + 1 < width && output[index + 1] == 0) {
00075 queue.addFirst(new Point(x + 1, y));
00076 }
00077 if (y + 1 < height && output[index + width] == 0) {
00078 queue.addFirst(new Point(x, y + 1));
00079 }
00080 if (y - 1 >= 0 && output[index - width] == 0) {
00081 queue.addFirst(new Point(x, y - 1));
00082 }
00083 if (x - 1 >= 0 && output[index - 1] == 0) {
00084 queue.addFirst(new Point(x - 1, y));
00085 }
00086 }
00087 }
00088 return count;
00089 }
00090
00095 public static ArrayList<Point> borderTrace(short[] pixels, int width, int height) {
00096 Point start = null;
00097
00098 short[][] contour = new short[width + 2][height + 2];
00099 for (int i = 0; i < width; i++) {
00100 for (int j = 0; j < height; j++) {
00101 if (pixels[i + j * width] != 0) {
00102 contour[i + 1][j + 1] = (short) 1;
00103 if (start == null)
00104 start = new Point(i + 1, j + 1);
00105 }
00106 }
00107 }
00108 if (start == null) {
00109 System.err.println("Warning! could not find lumen!");
00110 return null;
00111 }
00112
00113 int i = start.x;
00114 int j = start.y;
00115
00116
00117 final int[][] path = { { 0, 1 }, { -1, 1 }, { -1, 0 }, { -1, -1 },
00118 { 0, -1 }, { 1, -1 }, { 1, 0 }, { 1, 1 } };
00119 int cane = 4;
00120 final int[] next = { 1, 2, 3, 4, 5, 6, 7, 0 };
00121 final int[] invert = { 4, 5, 6, 7, 0, 1, 2, 3 };
00122 int[][] neighbor = new int[path.length][path[0].length];
00123 for (int k = 0; k < neighbor.length; k++) {
00124 neighbor[k][0] = path[k][0] + i;
00125 neighbor[k][1] = path[k][1] + j;
00126 }
00127 ArrayList<Point> points = new ArrayList<Point>() ;
00128 int cini;
00129 boolean finished = false;
00130 boolean continueLooking = true;
00131 while (!finished) {
00132 points.add(new Point(i - 1, j - 1));
00133 cini = cane;
00134 continueLooking = true;
00135 while (continueLooking) {
00136 cane = next[cane];
00137 continueLooking = (contour[neighbor[cane][0]][neighbor[cane][1]] == 0)
00138 && cane != cini;
00139 }
00140 if (contour[neighbor[cane][0]][neighbor[cane][1]] == 1) {
00141 i = i + path[cane][0];
00142 j = j + path[cane][1];
00143 for (int k = 0; k < neighbor.length; k++) {
00144 neighbor[k][0] = neighbor[k][0] + path[cane][0];
00145 neighbor[k][1] = neighbor[k][1] + path[cane][1];
00146 }
00147 cane = invert[cane];
00148 finished = (j == start.y && i == start.x);
00149 } else {
00150 finished = true;
00151 }
00152 }
00153 return points;
00154 }
00155
00156
00157 public static double circlish(short[] mask, int width, int height) {
00158 return (new CircularityDescriptor()).measure(mask, width, height);
00159 }
00160
00166 public static int countOutlinePixels(final short[] input, int xp, int yp,
00167 int width, int height, short val) {
00168 if (xp < 0 || yp < 0 || xp >= width || yp >= height)
00169 return 0;
00170 LinkedList<Point> queue = new LinkedList<Point>();
00171 queue.addFirst(new Point(xp, yp));
00172 int count = 0;
00173 short[] mark = new short[input.length];
00174 while (queue.size() > 0) {
00175 Point p = queue.removeLast();
00176 int x = p.x;
00177 int y = p.y;
00178 int index = x + y * width;
00179 if (input[index] != 0 && mark[index] != 2) {
00180 mark[index] = 2;
00181 if (x + 1 < width && mark[index + 1] == 0) {
00182 queue.addFirst(new Point(x + 1, y));
00183 if (input[index + 1] == 0)
00184 count++;
00185 mark[index + 1] = (short) 1;
00186 }
00187 if (y + 1 < height && mark[index + width] == 0) {
00188 queue.addFirst(new Point(x, y + 1));
00189 if (input[index + width] == 0)
00190 count++;
00191 mark[index + width] = (short) 1;
00192 }
00193 if (y - 1 >= 0 && mark[index - width] == 0) {
00194 queue.addFirst(new Point(x, y - 1));
00195 if (input[index - width] == 0)
00196 count++;
00197 mark[index - width] = (short) 1;
00198 }
00199 if (x - 1 >= 0 && mark[index - 1] == 0) {
00200 queue.addFirst(new Point(x - 1, y));
00201 if (input[index - 1] == 0)
00202 count++;
00203 mark[index - 1] = (short) 1;
00204 }
00205 }
00206 }
00207 return count;
00208 }
00209
00215 public static double curvature(short[] pixels, int width, int height) {
00216 return (new AvgCurvature()).measure(pixels, width, height);
00217 }
00218
00225 public static void delete3D(Stack volume, int xp, int yp, int zp) {
00226 if (volume.getVoxel(xp, yp, zp) == 0)
00227 return;
00228 floodFill3D26(volume, xp, yp, zp, (short) 0);
00229 ThebaGUI.getInstance().updateImage();
00230 }
00231
00239 public static short[] dilate(short[] input, int width, int height) {
00240 return dilate(input, width, height, new Rectangle(0, 0, width, height));
00241 }
00242
00252 public static short[] dilate(short[] input, int width, int height,
00253 Rectangle bounds) {
00254 return dilate(input, new short[input.length], width, height, bounds);
00255 }
00256
00266 public static short[] dilate(short[] input, short[] output, int width,
00267 int height, Rectangle bounds) {
00268 if (input == output)
00269 throw new RuntimeException("input should not be equal to output!");
00270 int startX = (int) bounds.getX();
00271 int startY = (int) bounds.getY();
00272 int stopX = startX + (int) bounds.getWidth();
00273 if (stopX > width)
00274 stopX = width;
00275 int stopY = startY + (int) bounds.getHeight();
00276 if (stopY > height)
00277 stopY = height;
00278 for (int x = startX; x < stopX; x++)
00279 for (int y = startY; y < stopY; y++) {
00280 int index = x + y * width;
00281 int max = input[index];
00282 if (x > 0 && (input[index - 1]) > max)
00283 max = input[index - 1];
00284 else if (x < width - 1 && (input[index + 1]) > max)
00285 max = (input[index + 1]);
00286 else if (y > 0 && (input[index - width]) > max)
00287 max = (input[index - width]);
00288 else if (y < height - 1 && (input[index + width]) > max)
00289 max = input[index + width];
00290 output[index] = (short) max;
00291 }
00292 return output;
00293 }
00294
00302 public static short[] dilate(short[] input, short[] output, int width,
00303 int height, Rectangle bounds, short val) {
00304 if (input == output)
00305 throw new RuntimeException("input should not be equal to output!");
00306 int startX = (int) bounds.getX();
00307 int startY = (int) bounds.getY();
00308 int stopX = startX + (int) bounds.getWidth();
00309 if (stopX > width)
00310 stopX = width;
00311 int stopY = startY + (int) bounds.getHeight();
00312 if (stopY > height)
00313 stopY = height;
00314 if (startX < 0 || startY < 0)
00315 throw new RuntimeException("Bounds cannot be negative!");
00316 for (int x = startX; x < stopX; x++)
00317 for (int y = startY; y < stopY; y++) {
00318 int index = x + y * width;
00319 short max = 0;
00320 if (x > 0 && input[index - 1] > max)
00321 max = 1;
00322 else if (x < width - 1 && input[index + 1] > max)
00323 max = 1;
00324 else if (y > 0 && input[index - width] > max)
00325 max = 1;
00326 else if (y < height - 1 && input[index + width] > max)
00327 max = 1;
00328 if (max != 0 && input[index] == 0)
00329 output[index] = val;
00330 }
00331 return output;
00332 }
00333
00344 public static short[] dilateMasked(short[] input, short[] output,
00345 short[] mask, int width, int height, Rectangle bounds, short val) {
00346 if (input == output)
00347 throw new RuntimeException("input should not be equal to output!");
00348 int startX = (int) bounds.getX();
00349 int startY = (int) bounds.getY();
00350 int stopX = startX + (int) bounds.getWidth();
00351 if (stopX > width)
00352 stopX = width;
00353 int stopY = startY + (int) bounds.getHeight();
00354 if (stopY > height)
00355 stopY = height;
00356 if (startX < 0 || startY < 0)
00357 throw new RuntimeException("Bounds cannot be negative!");
00358 int wmax = width - 1;
00359 int hmax = height - 1;
00360 for (int y = startY; y < stopY; y++) {
00361 for (int x = startX; x < stopX; x++) {
00362 int index = x + y * width;
00363 if (mask[index] != 0 && input[index] == 0) {
00364
00365 short max = 0;
00366
00367 if (input[index] > max) {
00368 max = 1;
00369 } else if (x > 0 && input[index - 1] > max)
00370 max = 1;
00371 else if (x < wmax && input[index + 1] > max)
00372 max = 1;
00373 else if (y > 0 && input[index - width] > max)
00374 max = 1;
00375 else if (y < hmax && input[index + width] > max)
00376 max = 1;
00377 if (max != 0 && input[index] == 0)
00378 output[index] = val;
00379 }
00380 }
00381 }
00382 return output;
00383 }
00384
00391 public static void dilate3d(Stack voxels) {
00392 int width = voxels.getWidth();
00393 int height = voxels.getHeight();
00394 int depth = voxels.getDepth();
00395 short[] prevdata = new short[width * height];
00396 short[] data = new short[width * height];
00397 short[] nextdata = new short[width * height];
00398 ThebaGUI control = ThebaGUI.getInstance();
00399 System.arraycopy(voxels.getSlice(1), 0, nextdata, 0, nextdata.length);
00400 System.arraycopy(voxels.getSlice(0), 0, data, 0, data.length);
00401 for (int z = 0; z < depth; z++) {
00402 control.setProgress(z);
00403 if (control.isStopped())
00404 return;
00405 int index = 0;
00406 for (int y = 0; y < height; y++) {
00407 for (int x = 0; x < width; x++) {
00408 short max = data[index];
00409 if (x > 0)
00410 max = max(max, data[index - 1]);
00411 if (x < width - 1)
00412 max = max(max, data[index + 1]);
00413 if (y > 0)
00414 max = max(max, data[index - width]);
00415 if (y < height - 1)
00416 max = max(max, data[index + width]);
00417 if (z > 0)
00418 max = max(max, prevdata[index]);
00419 if (z < depth - 1)
00420 max = max(max, nextdata[index]);
00421 voxels.getSlice(z)[index] = max;
00422 index++;
00423 }
00424 }
00425 if (z > 0)
00426 System.arraycopy(data, 0, prevdata, 0, prevdata.length);
00427 System.arraycopy(nextdata, 0, data, 0, data.length);
00428 if (z < depth - 1)
00429 System.arraycopy(voxels.getSlice(z + 1), 0, nextdata, 0,
00430 nextdata.length);
00431 }
00432 }
00433
00444 public static int dilateAndCount(final short[] input, short[] output,
00445 short[] mask, int width, int height, Rectangle bounds) {
00446 int count = 0;
00447 if (input == output)
00448 throw new RuntimeException("input should not be equal to outpu!");
00449 int startX = (int) bounds.getX();
00450 int startY = (int) bounds.getY();
00451 int stopX = startX + (int) bounds.getWidth();
00452 if (stopX > width)
00453 stopX = width;
00454 int stopY = startY + (int) bounds.getHeight();
00455 if (stopY > height)
00456 stopY = height;
00457 for (int x = startX; x < stopX; x++)
00458 for (int y = startY; y < stopY; y++) {
00459 int index = x + y * width;
00460 int max = input[index];
00461
00462 if (x > 0 && (input[index - 1]) > max) {
00463 max = input[index - 1];
00464 }
00465 if (x < width - 1 && (input[index + 1]) > max) {
00466 max = (input[index + 1]);
00467 }
00468 if (y > 0 && (input[index - width]) > max) {
00469 max = (input[index - width]);
00470 }
00471 if (y < height - 1 && (input[index + width]) > max) {
00472 max = input[index + width];
00473 }
00474 if (mask[index] != 0 && max != 0) {
00475 if (input[index] == 0) {
00476 count++;
00477 output[index] = (short) (max + 1);
00478 } else {
00479 output[index] = input[index];
00480 }
00481 }
00482 }
00483 return count;
00484 }
00485
00493 public static short[] erode(short[] input, int width, int height) {
00494 return erode(input, width, height, new Rectangle(0, 0, width, height));
00495 }
00496
00506 public static short[] erode(short[] input, int width, int height,
00507 Rectangle bounds) {
00508 return erode(input, new short[input.length], width, height, bounds);
00509 }
00510
00520 public static short[] erode(short[] input, short[] output, int width,
00521 int height, Rectangle bounds) {
00522 if (input == output)
00523 throw new RuntimeException("input should not be equal to output!");
00524 int startX = (int) bounds.getX();
00525 int startY = (int) bounds.getY();
00526 int stopX = startX + (int) bounds.getWidth();
00527 if (stopX > width)
00528 stopX = width;
00529 int stopY = startY + (int) bounds.getHeight();
00530 if (stopY > height)
00531 stopY = height;
00532 for (int x = startX; x < stopX; x++)
00533 for (int y = startY; y < stopY; y++) {
00534 int index = x + y * width;
00535 int min = input[index];
00536 if (x > 0 && (input[index - 1]) < min)
00537 min = input[index - 1];
00538 else if (x < width - 1 && (input[index + 1]) < min)
00539 min = (input[index + 1]);
00540 else if (y > 0 && (input[index - width]) < min)
00541 min = (input[index - width]);
00542 else if (y < height - 1 && (input[index + width]) < min)
00543 min = input[index + width];
00544 output[index] = (short) min;
00545 }
00546 return output;
00547 }
00548
00549
00556 public static void erode3d(Stack voxels) {
00557 int width = voxels.getWidth();
00558 int height = voxels.getHeight();
00559 int depth = voxels.getDepth();
00560 short[] prevdata = new short[width * height];
00561 short[] data = new short[width * height];
00562 short[] nextdata = new short[width * height];
00563 ThebaGUI control = ThebaGUI.getInstance();
00564 System.arraycopy(voxels.getSlice(1), 0, nextdata, 0, nextdata.length);
00565 System.arraycopy(voxels.getSlice(0), 0, data, 0, data.length);
00566 for (int z = 0; z < depth; z++) {
00567 control.setProgress(z);
00568 if (control.isStopped())
00569 return;
00570 int index = 0;
00571 for (int y = 0; y < height; y++) {
00572 for (int x = 0; x < width; x++) {
00573 short min = data[index];
00574 if (x > 0)
00575 min = min(min, data[index - 1]);
00576 if (x < width - 1)
00577 min = min(min, data[index + 1]);
00578 if (y > 0)
00579 min = min(min, data[index - width]);
00580 if (y < height - 1)
00581 min = min(min, data[index + width]);
00582 if (z > 0)
00583 min = min(min, prevdata[index]);
00584 if (z < depth - 1)
00585 min = min(min, nextdata[index]);
00586 voxels.getSlice(z)[index] = min;
00587 index++;
00588 }
00589 }
00590 if (z > 0)
00591 System.arraycopy(data, 0, prevdata, 0, prevdata.length);
00592 System.arraycopy(nextdata, 0, data, 0, data.length);
00593 if (z < depth - 1)
00594 System.arraycopy(voxels.getSlice(z + 1), 0, nextdata, 0,
00595 nextdata.length);
00596 }
00597 voxels.flush();
00598 }
00599
00605 public static int floodFill2D(int xp, int yp, int width, int height,
00606 final short[] input, short value) {
00607 short[] mark = new short[input.length];
00608 if (input == null) {
00609 System.err.println("Input was zero in floodFill2DMasked!");
00610 return 0;
00611 }
00612 if (xp < 0 || yp < 0 || xp >= width || yp >= height) {
00613 System.err.print("Coord out of bounds!");
00614 return 0;
00615 }
00616 LinkedList<Point> queue = new LinkedList<Point>();
00617 queue.addFirst(new Point(xp, yp));
00618 int count = 0;
00619 short col = input[xp + yp * width];
00620 while (queue.isEmpty() == false) {
00621 Point p = queue.removeLast();
00622 int x = p.x;
00623 int y = p.y;
00624 int index = x + y * width;
00625 count++;
00626 input[index] = value;
00627 if (x + 1 < width && mark[index + 1] == 0
00628 && input[index + 1] == col) {
00629 mark[index + 1] = 1;
00630 queue.addFirst(new Point(x + 1, y));
00631 }
00632 if (y + 1 < height && mark[index + width] == 0
00633 && input[index + width] == col) {
00634 mark[index + width] = 1;
00635 queue.addFirst(new Point(x, y + 1));
00636 }
00637 if (y - 1 >= 0 && mark[index - width] == 0
00638 && input[index - width] == col) {
00639 mark[index - width] = 1;
00640 queue.addFirst(new Point(x, y - 1));
00641 }
00642 if (x - 1 >= 0 && mark[index - 1] == 0 && input[index - 1] == col) {
00643 mark[index - 1] = 1;
00644 queue.addFirst(new Point(x - 1, y));
00645 }
00646 }
00647 return count;
00648 }
00649
00655 public static int floodFill2D(final short[] input, short[] output, int xp,
00656 int yp, int width, int height, short val) {
00657 if (xp < 0 || yp < 0 || xp >= width || yp >= height)
00658 return 0;
00659 LinkedList<Point> queue = new LinkedList<Point>();
00660 queue.addFirst(new Point(xp, yp));
00661 int startindex = xp + yp * width;
00662 if (input[startindex] != 0)
00663 return 0;
00664 int count = 0;
00665 while (queue.size() > 0) {
00666 Point p = queue.removeLast();
00667 int x = p.x;
00668 int y = p.y;
00669 int index = x + y * width;
00670 if (input[index] == 0 && output[index] == 0) {
00671 count++;
00672 output[index] = val;
00673 if (x + 1 < width && output[index + 1] == 0) {
00674 queue.addFirst(new Point(x + 1, y));
00675 }
00676 if (y + 1 < height && output[index + width] == 0) {
00677 queue.addFirst(new Point(x, y + 1));
00678 }
00679 if (y - 1 >= 0 && output[index - width] == 0) {
00680 queue.addFirst(new Point(x, y - 1));
00681 }
00682 if (x - 1 >= 0 && output[index - 1] == 0) {
00683 queue.addFirst(new Point(x - 1, y));
00684 }
00685 }
00686 }
00687 return count;
00688 }
00689
00696 public static int floodFill2DMasked(int xp, int yp, int width, int height,
00697 short[] input, short[] mask, short[] output, short val) {
00698 if (input == null) {
00699 System.err.println("Input was zero in floodFill2DMasked!");
00700 return 0;
00701 }
00702 if (mask == null) {
00703 System.err.println("Mask was zero in floodFill2DMasked!");
00704 return 0;
00705 }
00706 if (output == null) {
00707 System.err.println("Output was zero in floodFill2DMasked!");
00708 return 0;
00709 }
00710 if (input == output)
00711 throw new RuntimeException("Input equals output!");
00712 LinkedList<Point> queue = new LinkedList<Point>();
00713 queue.addFirst(new Point(xp, yp));
00714 int count = 0;
00715 short col = input[xp + yp * width];
00716 while (queue.isEmpty() == false) {
00717 Point p = queue.removeLast();
00718 int x = p.x;
00719 int y = p.y;
00720 int index = x + y * width;
00721 if ((input[index]) == col && mask[index] != 0) {
00722 count++;
00723 output[index] = val;
00724 if (x + 1 < width && output[index + 1] == 0) {
00725 output[index + 1] = 1;
00726 queue.addFirst(new Point(x + 1, y));
00727 }
00728 if (y + 1 < height && output[index + width] == 0) {
00729 output[index + width] = 1;
00730 queue.addFirst(new Point(x, y + 1));
00731 }
00732 if (y - 1 >= 0 && output[index - width] == 0) {
00733 output[index - width] = 1;
00734 queue.addFirst(new Point(x, y - 1));
00735 }
00736 if (x - 1 >= 0 && output[index - 1] == 0) {
00737 output[index - 1] = 1;
00738 queue.addFirst(new Point(x - 1, y));
00739 }
00740 } else {
00741 output[index] = 0;
00742 }
00743 }
00744 return count;
00745 }
00746
00752 public static long floodFill3D(Stack volume, int xp, int yp, int zp,
00753 short val) {
00754 long count = 0;
00755 int depth = volume.getDepth();
00756 int height = volume.getHeight();
00757 int width = volume.getWidth();
00758 if (xp < 0 || yp < 0 || xp >= width || yp >= height || zp < 0
00759 || zp > depth)
00760 return 0;
00761 int s = volume.getVoxelUnchecked(xp, yp, zp);
00762 if (s == val)
00763 return 0;
00764 LinkedList<Point3D> queue = new LinkedList<Point3D>();
00765 queue.addFirst(new Point3D(xp, yp, zp));
00766 while (queue.isEmpty() == false) {
00767 Point3D p = queue.removeLast();
00768 int x = p.x;
00769 int y = p.y;
00770 int z = p.z;
00771 int pixelIndex = x + y * width;
00772 short[] pixels = volume.getSlice(z);
00773 pixels[pixelIndex] = val;
00774 count++;
00775 if (x < width - 1 && pixels[pixelIndex + 1] == s) {
00776 queue.addFirst(new Point3D(x + 1, y, z));
00777 pixels[pixelIndex + 1] = -1;
00778 }
00779 if (x - 1 >= 0 && pixels[pixelIndex - 1] == s) {
00780 queue.addFirst(new Point3D(x - 1, y, z));
00781 pixels[pixelIndex - 1] = -1;
00782 }
00783 if (y < height - 1 && pixels[pixelIndex + width] == s) {
00784 queue.addFirst(new Point3D(x, y + 1, z));
00785 pixels[pixelIndex + width] = -1;
00786 }
00787 if (y - 1 >= 0 && pixels[pixelIndex - width] == s) {
00788 queue.addFirst(new Point3D(x, y - 1, z));
00789 pixels[pixelIndex - width] = -1;
00790 }
00791 if (z >= 1 && volume.getSlice(z - 1)[pixelIndex] == s) {
00792 queue.addFirst(new Point3D(x, y, z - 1));
00793 volume.getSlice((z - 1))[pixelIndex] = -1;
00794 }
00795 if (z < depth - 1 && volume.getSlice(z + 1)[pixelIndex] == s) {
00796 queue.addFirst(new Point3D(x, y, z + 1));
00797 volume.getSlice((z + 1))[pixelIndex] = -1;
00798 }
00799 }
00800 return count;
00801 }
00802
00814 public static long floodFill3D26(Stack volume, int xp, int yp, int zp,
00815 short newValue) {
00816 long count = 0;
00817 int depth = volume.getDepth();
00818 int height = volume.getHeight();
00819 int width = volume.getWidth();
00820 if (xp < 0 || yp < 0 || xp >= width || yp >= height || zp < 0
00821 || zp >= depth)
00822 return 0;
00823 int oldValue = volume.getVoxelUnchecked(xp, yp, zp);
00824 if (oldValue == newValue)
00825 return 0;
00826 if (oldValue == 255) {
00827 int ans = JOptionPane.showConfirmDialog(null,
00828 "Are you sure you want to fill foreground pixels( label 255) ?",
00829 "Warning", JOptionPane.YES_NO_OPTION);
00830 if (ans == JOptionPane.NO_OPTION)
00831 return 0;
00832 }
00833 LinkedList<Point3D> queue = new LinkedList<Point3D>();
00834 queue.addFirst(new Point3D(xp, yp, zp));
00835 while (queue.isEmpty() == false) {
00836 Point3D p = queue.removeLast();
00837 int x = p.x;
00838 int y = p.y;
00839 int z = p.z;
00840 count++;
00841 volume.setVoxelUnchecked(x, y, z, newValue);
00842 for (int i = -1; i < 2; i++)
00843 for (int j = -1; j < 2; j++)
00844 for (int k = -1; k < 2; k++) {
00845 int xx = i + x;
00846 int yy = j + y;
00847 int zz = k + z;
00848 if (xx < 0 || xx >= width)
00849 continue;
00850 if (yy < 0 || yy >= height)
00851 continue;
00852 if (zz < 0 || zz >= depth)
00853 continue;
00854 short voxel = volume.getVoxel(xx, yy, zz);
00855 if (voxel == oldValue) {
00856 queue.addFirst(new Point3D(xx, yy, zz));
00857 volume.setVoxel(xx, yy, zz, 0);
00858 }
00859 }
00860 }
00861 return count;
00862 }
00863
00867 public static short[] geodist(short[] input, short[] mask, int width,
00868 int height) {
00869 short[] output = input.clone();
00870 for (int i = 0; i < output.length; i++) {
00871 if (input[i] != 0)
00872 output[i] = 1;
00873 }
00874 int count = 10;
00875 int maxdilations = 100;
00876 while (count > 0 && maxdilations > 0) {
00877 maxdilations--;
00878 count = dilateAndCount(output.clone(), output, mask, width, height, new Rectangle(0, 0, width, height));
00879 }
00880 return output;
00881 }
00882
00890 public static double[] getArcLenghts(int[] chaincode) {
00891 if (chaincode == null)
00892 return null;
00893
00894 double arcLenghts[] = new double[chaincode.length];
00895 arcLenghts[0] = 0;
00896 for (int i = 1; i < arcLenghts.length; i++) {
00897
00898 if (chaincode[i] % 2 == 0 && chaincode[i - 1] % 2 == 0) {
00899 arcLenghts[i] = arcLenghts[i - 1] + 0.5 + 0.5;
00900 } else if (chaincode[i] % 2 == 0 || chaincode[i - 1] % 2 == 0) {
00901 arcLenghts[i] = arcLenghts[i - 1] + Math.sqrt(2) / 2 + 0.5;
00902 } else {
00903 arcLenghts[i] = arcLenghts[i - 1] + Math.sqrt(2) / 2 + Math.sqrt(2) / 2;
00904 }
00905 }
00906 return arcLenghts;
00907 }
00908
00914 public static Point getAveragePoint(short[] lumenMask, int width, int height) {
00915 double xp = 0;
00916 double yp = 0;
00917 double count = 0;
00918 for (int x = 0; x < width; x++) {
00919 for (int y = 0; y < height; y++) {
00920 if (lumenMask[x + y * width] != 0) {
00921 xp += x;
00922 yp += y;
00923 count++;
00924 }
00925 }
00926 }
00927 return new Point((int) (xp / count), (int) (yp / count));
00928 }
00929
00935 public static Point getAveragePoint(short[] lumenMask, int width,
00936 int height, short currentId) {
00937 double xp = 0;
00938 double yp = 0;
00939 double count = 0;
00940 for (int x = 0; x < width; x++) {
00941 for (int y = 0; y < height; y++) {
00942 if (lumenMask[x + y * width] == currentId) {
00943 xp += x;
00944 yp += y;
00945 count++;
00946 }
00947 }
00948 }
00949 return new Point((int) (xp / count), (int) (yp / count));
00950 }
00951
00961 public static short[] distance2d(short[] input, int width, int height) {
00962 short[] pixels = input.clone();
00963 for (int x = 0; x < width; x++) {
00964 for (int y = 0; y < height; y++) {
00965 int index = x + y * width;
00966 if (pixels[index] != 0)
00967 pixels[index] = (short) 254;
00968 if (x == 0 || y == 0 || x == width - 1 || y == height - 1)
00969 pixels[index] = 0;
00970 }
00971 }
00972 final int[] wf = new int[] { 1, 1, 1, 1, 0, 0, 0, 0, 0 };
00973 final int[] wb = new int[] { 0, 0, 0, 0, 0, 1, 1, 1, 1 };
00974
00975
00976 for (int x = 1; x < width - 1; x++) {
00977 for (int y = 1; y < height - 1; y++) {
00978 int index = x + y * width;
00979 int[] slask = new int[3 * 3];
00980 int min = 255;
00981 for (int j = -1; j < 2; j++) {
00982 for (int i = -1; i < 2; i++) {
00983 int slaskindex = (i + 1) + (j + 1) * 3;
00984 if (wf[slaskindex] != 0) {
00985 slask[slaskindex] = Math.min(255, wf[slaskindex]
00986 + pixels[index + (i) + j * width]);
00987 if (slask[slaskindex] < min)
00988 min = slask[slaskindex];
00989 }
00990 }
00991 }
00992 if (min < (pixels[index])) {
00993 pixels[index] = (short) min;
00994 }
00995 }
00996 }
00997
00998 for (int x = width - 2; x > 1; x--) {
00999 for (int y = height - 2; y > 1; y--) {
01000 int index = x + y * width;
01001 int[] slask = new int[3 * 3];
01002 int min = 255;
01003 for (int j = -1; j < 2; j++) {
01004 for (int i = -1; i < 2; i++) {
01005 int slaskindex = (i + 1) + (j + 1) * 3;
01006 if (wb[slaskindex] != 0) {
01007 slask[slaskindex] = Math.min(255, wb[slaskindex]
01008 + pixels[index + (i) + j * width]);
01009 if (slask[slaskindex] < min)
01010 min = slask[slaskindex];
01011 }
01012 }
01013 }
01014 if (min < (pixels[index])) {
01015 pixels[index] = (short) min;
01016 }
01017 }
01018 }
01019 return pixels;
01020 }
01021
01028 public static short[] distance2d(short[] input, int width, int height,
01029 Rectangle bounds) {
01030 short[] pixels = input.clone();
01031 int startX = (int) bounds.getX();
01032 int startY = (int) bounds.getY();
01033 if (startX < 1)
01034 startX = 1;
01035 if (startY < 1)
01036 startY = 1;
01037 int stopX = startX + (int) bounds.getWidth();
01038 if (stopX > width - 2)
01039 stopX = width - 2;
01040 int stopY = startY + (int) bounds.getHeight();
01041 if (stopY > height - 2)
01042 stopY = height - 2;
01043 for (int x = 0; x < width; x++) {
01044 for (int y = 0; y < height; y++) {
01045 int index = x + y * width;
01046 if (pixels[index] != 0)
01047 pixels[index] = (short) 254;
01048 if (x == 0 || y == 0 || x == width - 1 || y == height - 1)
01049 pixels[index] = 0;
01050 }
01051 }
01052 final int[] wf = new int[] { 1, 1, 1, 1, 0, 0, 0, 0, 0 };
01053 final int[] wb = new int[] { 0, 0, 0, 0, 0, 1, 1, 1, 1 };
01054
01055
01056 for (int x = startX; x < stopX; x++) {
01057 for (int y = startY; y < stopY; y++) {
01058 int index = x + y * width;
01059 int[] slask = new int[3 * 3];
01060 int min = 255;
01061 for (int j = -1; j < 2; j++) {
01062 for (int i = -1; i < 2; i++) {
01063 int slaskindex = (i + 1) + (j + 1) * 3;
01064 if (wf[slaskindex] != 0) {
01065 slask[slaskindex] = Math.min(255, wf[slaskindex]
01066 + pixels[index + (i) + j * width]);
01067 if (slask[slaskindex] < min)
01068 min = slask[slaskindex];
01069 }
01070 }
01071 }
01072 if (min < (pixels[index])) {
01073 pixels[index] = (short) min;
01074 }
01075 }
01076 }
01077
01078 for (int x = stopX; x >= startX; x--) {
01079 for (int y = stopY; y >= startY; y--) {
01080 int index = x + y * width;
01081 int[] slask = new int[3 * 3];
01082 int min = 255;
01083 for (int j = -1; j < 2; j++) {
01084 for (int i = -1; i < 2; i++) {
01085 int slaskindex = (i + 1) + (j + 1) * 3;
01086 if (wb[slaskindex] != 0) {
01087 slask[slaskindex] = Math.min(255, wb[slaskindex]
01088 + pixels[index + (i) + j * width]);
01089 if (slask[slaskindex] < min)
01090 min = slask[slaskindex];
01091 }
01092 }
01093 }
01094 if (min < (pixels[index])) {
01095 pixels[index] = (short) min;
01096 }
01097 }
01098 }
01099 return pixels;
01100 }
01101
01110 public static void distance3d(Stack voxels) {
01111 int width = voxels.getWidth();
01112 int height = voxels.getHeight();
01113 int depth = voxels.getDepth();
01114 ThebaGUI control = ThebaGUI.getInstance();
01115
01116 for (int z = 0; z < depth; z++) {
01117 for (int x = 0; x < width; x++) {
01118 for (int y = 0; y < height; y++) {
01119 int index = x + y * width;
01120 if (z == 0 || x == 0 || y == 0 || z == depth - 1
01121 || y == height - 1 || x == width - 1)
01122 voxels.getSlice(z)[index] = (short) 0;
01123 else
01124 voxels.getSlice(z)[index] = (short) (0xff - (voxels
01125 .getSlice(z)[index]));
01126 }
01127 }
01128 }
01129
01130 int a = 3, b = 4, c = 5, d = 3, e = 7;
01131 int[] wf = new int[] { e, d, e, d, c, d, e, d, e, b, a, b, a, 255, 255,
01132 255, 255, 255, };
01133 int[] wb = new int[] { 255, 255, 255, 255, 255, a, b, a, b, e, d, e, d,
01134 c, d, e, d, e, };
01135 int[] slask = new int[2 * 3 * 3];
01136
01137 for (int z = 1; z < depth - 1; z++) {
01138 control.setProgress(z / 2);
01139 for (int x = 1; x < width - 1; x++) {
01140 for (int y = 1; y < height - 1; y++) {
01141 int index = x + y * width;
01142 for (int k = -1; k < 1; k++) {
01143 for (int j = -1; j < 2; j++) {
01144 for (int i = -1; i < 2; i++) {
01145 int slaskindex = (i + 1) + (j + 1) * 3
01146 + (k + 1) * 3 * 3;
01147 slask[slaskindex] = (voxels.getSlice(z + k)[index
01148 + (i) + j * width])
01149 + wf[slaskindex];
01150 }
01151 }
01152 }
01153 int minval = slask[0];
01154 for (int i = 1; i < slask.length; i++) {
01155 if ((slask[i]) < minval)
01156 minval = (slask[i]);
01157 }
01158 if ((voxels.getSlice(z)[index]) > minval) {
01159 voxels.getSlice(z)[index] = (short) minval;
01160 }
01161 }
01162 }
01163 }
01164
01165 for (int z = depth - 2; z > 0; z--) {
01166 control.setProgress((depth - z / 2));
01167 for (int x = width - 2; x > 0; x--) {
01168 for (int y = height - 2; y > 0; y--) {
01169 int index = x + y * width;
01170 for (int k = 0; k < 2; k++) {
01171 for (int j = -1; j < 2; j++) {
01172 for (int i = -1; i < 2; i++) {
01173 int slaskindex = (i + 1) + (j + 1) * 3 + (k)
01174 * 3 * 3;
01175 slask[slaskindex] = (voxels.getSlice(z + k)[index
01176 + (i) + j * width])
01177 + wb[slaskindex];
01178 }
01179 }
01180 }
01181 int minval = slask[0];
01182 for (int i = 1; i < slask.length; i++) {
01183 if ((slask[i]) < minval)
01184 minval = (slask[i]);
01185 }
01186 if ((voxels.getSlice(z)[index]) > minval)
01187 voxels.getSlice(z)[index] = (short) minval;
01188 }
01189 }
01190 }
01191 try {
01192 System.out.println("Writing to file dmap.raw");
01193 FileOutputStream fs = new FileOutputStream("dmap.raw");
01194 short[] buffer = new short[width];
01195 for (int z = 0; z < depth; z++)
01196 for (int y = 0; y < height; y++) {
01197 for (int x = 0; x < width; x++) {
01198 buffer[x] = voxels.getSlice(z)[x + y * width];
01199 }
01200 byte[] out = DataConversion.shortToBytes(buffer);
01201 fs.write(out);
01202 }
01203 } catch (IOException e1) {
01204 e1.printStackTrace();
01205 }
01206 control.setProgressComplete();
01207 }
01208
01214 public static double eccentricity(short[] pixels, int width, int height) {
01215 return (new EccentricityDescriptor()).measure(pixels, width, height);
01216 }
01217
01218
01229 public static Rectangle getBounds(short[] image, int width, int height,
01230 int margin) {
01231 int xMax = 0, yMax = 0;
01232 int xMin = width, yMin = height;
01233 for (int x = 0; x < width; x++)
01234 for (int y = 0; y < height; y++) {
01235 if (image[x + y * width] != 0) {
01236 if (x > xMax)
01237 xMax = x;
01238 if (y > yMax)
01239 yMax = y;
01240 if (x < xMin)
01241 xMin = x;
01242 if (y < yMin)
01243 yMin = y;
01244 }
01245 }
01246 if (xMax == 0 || yMax == 0 || xMin == width || yMin == height)
01247 return null;
01248 Rectangle bounds = new Rectangle(xMin, yMin, xMax - xMin, yMax - yMin);
01249 bounds.x = bounds.x - margin;
01250 bounds.y = bounds.y - margin;
01251 bounds.width = bounds.width + 2 * margin;
01252 bounds.height = bounds.height + 2 * margin;
01253 if (bounds.x < 0)
01254 bounds.x = 0;
01255 if (bounds.y < 0)
01256 bounds.y = 0;
01257 if (bounds.width + bounds.x > width)
01258 bounds.width = width - bounds.x;
01259 if (bounds.height + bounds.y > height)
01260 bounds.height = height - bounds.y;
01261 if (bounds.height < 0)
01262 bounds.height = 0;
01263 if (bounds.width < 0)
01264 bounds.width = 0;
01265 return bounds;
01266 }
01267
01274 public static Rectangle getBounds(short[] image, int width, int height,
01275 int margin, short id) {
01276 int xMax = 0, yMax = 0;
01277 int xMin = width, yMin = height;
01278 for (int x = 0; x < width; x++)
01279 for (int y = 0; y < height; y++) {
01280 if (image[x + y * width] == id) {
01281 if (x > xMax)
01282 xMax = x;
01283 if (y > yMax)
01284 yMax = y;
01285 if (x < xMin)
01286 xMin = x;
01287 if (y < yMin)
01288 yMin = y;
01289 }
01290 }
01291 if (xMax == 0 || yMax == 0 || xMin == width || yMin == height)
01292 return null;
01293 Rectangle bounds = new Rectangle(xMin, yMin, xMax - xMin + 1, yMax
01294 - yMin + 1);
01295 bounds.x = bounds.x - margin;
01296 bounds.y = bounds.y - margin;
01297 bounds.width = bounds.width + 2 * margin;
01298 bounds.height = bounds.height + 2 * margin;
01299 if (bounds.x < 0)
01300 bounds.x = 0;
01301 if (bounds.y < 0)
01302 bounds.y = 0;
01303 if (bounds.width + bounds.x > width)
01304 bounds.width = bounds.x + bounds.width - width;
01305 if (bounds.height + bounds.y > height)
01306 bounds.height = bounds.y + bounds.height - height;
01307 if (bounds.height < 0)
01308 bounds.height = 0;
01309 if (bounds.width < 0)
01310 bounds.width = 0;
01311 return bounds;
01312 }
01313
01321 public static int[] getChaincode(ArrayList borderPoints) {
01322 if (borderPoints == null)
01323 return null;
01324
01325
01326 int chaincode[] = new int[borderPoints.size()];
01327 chaincode[0] = 0;
01328 Point prev = (Point) borderPoints.get(0);
01329 Point current = null;
01330 for (int i = 1; i < borderPoints.size(); i++) {
01331 current = (Point) borderPoints.get(i);
01332 if (prev.x == current.x) {
01333 if (prev.y < current.y) {
01334 chaincode[i] = 2;
01335 } else {
01336 chaincode[i] = 6;
01337 }
01338 } else if (prev.x < current.x) {
01339 if (prev.y < current.y) {
01340 chaincode[i] = 1;
01341 } else if (prev.y > current.y) {
01342 chaincode[i] = 7;
01343 } else {
01344 chaincode[i] = 0;
01345 }
01346 } else {
01347 if (prev.y < current.y) {
01348 chaincode[i] = 3;
01349 } else if (prev.y > current.y) {
01350 chaincode[i] = 5;
01351 } else {
01352 chaincode[i] = 4;
01353 }
01354 }
01355 prev = current;
01356 }
01357 return chaincode;
01358 }
01359
01360
01361
01362
01363 public static double getInterpolatedValue(double[] xa, double[] ya, double x) {
01364 if (xa.length != ya.length) {
01365 System.err.println("Wrong dimension in input, xa, ya must be equal in length");
01366 return Double.NaN;
01367 }
01368 int ns = 1;
01369 double y, dy;
01370 double den, dif, dift, ho, hp, w;
01371 int n = xa.length;
01372 double[] c = new double[n];
01373 double[] d = new double[n];
01374
01375 dif = Math.abs(x - xa[0]);
01376 for (int i = 1; i <= n; i++) {
01377 dift = Math.abs(x - xa[i - 1]);
01378 if (dift > dif) {
01379 ns = i;
01380 dif = dift;
01381 }
01382 c[i - 1] = ya[i - 1];
01383 d[i - 1] = ya[i - 1];
01384 }
01385 y = ya[ns - 1];
01386 for (int m = 1; m < n; m++) {
01387 for (int i = 1; i <= n - m; i++) {
01388 ho = xa[i - 1] - x;
01389 hp = xa[i + m - 1] - x;
01390 w = c[i] - d[i - 1];
01391 den = ho - hp;
01392 if (den == 0.0) {
01393
01394
01395 den = 0.0001;
01396 }
01397 den = w / den;
01398 d[i - 1] = hp * den;
01399 c[i - 1] = ho * den;
01400 }
01401 if (2 * ns < (n - m)) {
01402 dy = c[ns];
01403 } else {
01404 dy = d[ns - 1];
01405 ns--;
01406 }
01407 y += dy;
01408 }
01409 return y;
01410 }
01411
01419 public static short[] invertMask(short[] input, boolean in_place) {
01420 short[] output;
01421 if (in_place)
01422 output = input;
01423 else
01424 output = new short[input.length];
01425 for (int i = 0; i < input.length; i++) {
01426 if (input[i] != 0)
01427 output[i] = 0;
01428 else
01429 output[i] = 255;
01430 }
01431 return output;
01432 }
01433
01445 public static short[] mask(short[] input, short[] mask, boolean in_place) {
01446 if (in_place) {
01447 for (int i = 0; i < input.length; i++) {
01448 if (mask[i] == 0)
01449 input[i] = 0;
01450 }
01451 return input;
01452 }
01453 short[] output = new short[input.length];
01454 if ((input.length != mask.length))
01455 throw new RuntimeException("all arrays must be of equal length!");
01456 for (int i = 0; i < input.length; i++) {
01457 if (mask[i] != 0)
01458 output[i] = input[i];
01459 else
01460 output[i] = 0;
01461 }
01462 return output;
01463 }
01464
01476 public static short[] mask(short[] input, short[] mask, boolean in_place,
01477 int width, int height, Rectangle bounds) {
01478 int startX = (int) bounds.getX() + 1;
01479 int startY = (int) bounds.getY() + 1;
01480 int stopX = startX + (int) bounds.getWidth();
01481 if (stopX > width) stopX = width;
01482 int stopY = startY + (int) bounds.getHeight();
01483 if (stopY > height) stopY = height;
01484 if (in_place) {
01485 for (int x = startX; x < stopX; x++) {
01486 for (int y = startY; y < stopY; y++) {
01487 int i = x + y * width;
01488 if (mask[i] == 0) input[i] = 0;
01489 }
01490 }
01491 return input;
01492 }
01493 short[] output = new short[input.length];
01494 if ((input.length != mask.length))
01495 throw new RuntimeException("all arrays must be of equal length!");
01496 for (int x = startX; x < stopX; x++) {
01497 for (int y = startY; y < stopY; y++) {
01498 int i = x + y * width;
01499 if (mask[i] != 0) output[i] = input[i];
01500 else output[i] = 0;
01501 }
01502 }
01503 return output;
01504 }
01505
01511 public static void median3d(Stack voxels) {
01512 int width = voxels.getWidth();
01513 int height = voxels.getHeight();
01514 int depth = voxels.getDepth();
01515 short[] prevdata = new short[width * height];
01516 short[] data = new short[width * height];
01517 short[] nextdata = new short[width * height];
01518 ThebaGUI control = ThebaGUI.getInstance();
01519 System.arraycopy(voxels.getSlice(1), 0, nextdata, 0, nextdata.length);
01520 System.arraycopy(voxels.getSlice(0), 0, data, 0, data.length);
01521 for (int z = 1; z < depth - 1; z++) {
01522 if (control.isStopped())
01523 return;
01524 control.setProgress(z);
01525 for (int x = 1; x < width - 1; x++) {
01526 for (int y = 1; y < height - 1; y++) {
01527 int offset = x + y * width;
01528 int count = 0;
01529 for (int i = -1; i < 2; i++) {
01530 for (int j = -1; j < 2; j++) {
01531 if ((data[offset]) > 0)
01532 count++;
01533 if ((prevdata[offset]) > 0)
01534 count++;
01535 if ((nextdata[offset]) > 0)
01536 count++;
01537 }
01538 }
01539 if (count > 12)
01540 voxels.getSlice(z)[offset] = 0xff;
01541 }
01542 }
01543 if (z > 0)
01544 System.arraycopy(data, 0, prevdata, 0, prevdata.length);
01545 System.arraycopy(nextdata, 0, data, 0, data.length);
01546 if (z < depth - 1)
01547 System.arraycopy(voxels.getSlice(z + 1), 0, nextdata, 0,
01548 nextdata.length);
01549 }
01550 }
01551
01563 public static boolean touchBorder(short[] mask, int w, int h) {
01564 boolean touch = false;
01565
01566 for (int y = 0; y < h; y++) {
01567 if (mask[y * w] != 0) {
01568 touch = true;
01569 break;
01570 }
01571 if (mask[w - 1 + y * w] != 0) {
01572 touch = true;
01573 break;
01574 }
01575 }
01576 if (!touch) {
01577 for (int x = 0; x < w; x++) {
01578
01579 if (mask[x] != 0) {
01580 touch = true;
01581 break;
01582 }
01583 if (mask[x + w * (h - 1)] != 0) {
01584 touch = true;
01585 break;
01586 }
01587 }
01588 }
01589 return touch;
01590 }
01591
01595 private static final short max(short x, short y) {
01596 if (x > y)
01597 return x;
01598 return y;
01599 }
01600
01604 private static final short min(short x, short y) {
01605 if (x < y)
01606 return x;
01607 return y;
01608 }
01609 }