00001 package theba.trackers;
00002
00003 import java.awt.Point;
00004 import java.awt.Rectangle;
00005 import java.io.File;
00006 import java.io.FileNotFoundException;
00007 import java.io.FileOutputStream;
00008 import java.io.PrintStream;
00009 import java.util.ArrayList;
00010 import java.util.Collections;
00011 import java.util.Iterator;
00012 import java.util.LinkedList;
00013
00014 import javax.swing.JOptionPane;
00015 import javax.swing.JToggleButton;
00016
00017 import theba.core.BackTrack;
00018 import theba.core.FiberSlice;
00019 import theba.core.ImageFunctions;
00020 import theba.core.Lumen;
00021 import theba.core.LumenCandidate;
00022 import theba.core.RegionMask2D;
00023 import theba.core.Seed;
00024 import theba.core.Stack;
00025 import theba.core.Tracker;
00026 import theba.core.gui.ThebaGUI;
00027 import theba.core.io.SliceWriter;
00028 import theba.core.math.DistancePath;
00029 import theba.core.math.EDM;
00030 import theba.core.math.Point3D;
00031 import watershed.Watershed_Algorithm;
00032 import theba.descriptors.CircularityDescriptor;
00033 import theba.descriptors.ConvexityDescriptor;
00034 import theba.descriptors.EccentricityDescriptor;
00035 import theba.descriptors.InternalRegionDescriptor;
00036 import theba.descriptors.SolidityDescriptor;
00037
00038
00039 public class BackTracker extends Tracker {
00040 private LinkedList fibers = new LinkedList();
00041
00042 private JToggleButton seedButton;
00043
00044 private LinkedList<Seed> seedQueue;
00045
00046 private LinkedList<LumenCandidate> seeds = new LinkedList<LumenCandidate>();
00047
00048 PrintStream neuralOut;
00049
00050 boolean seedStillValid = true;
00051
00052 private short currentId;
00053
00054 private boolean stopped = false;
00055
00056 private boolean automerge = false;
00057
00058 private short[] map = null;
00059
00060 private short[] map2;
00061
00062 private final class FindAllCandidatesAction implements Runnable {
00063 public void run() {
00064 int depth = control.getStack().getDepth();
00065 String s = JOptionPane.showInputDialog("How many slices to skip?",
00066 "10");
00067 try {
00068 final int num = Integer.parseInt(s);
00069 for (int z = 0; z < depth; z += num) {
00070 control.setProgress(z);
00071 findCandidates(z);
00072 }
00073 } catch (NumberFormatException ee) {
00074 }
00075 }
00076 }
00077
00078 private final class FindCandidatesAction implements Runnable {
00079 public void run() {
00080 findCandidates(control.currentSlice());
00081 }
00082 }
00083
00084 private final class AutosegmentAction implements Runnable {
00085 public void run() {
00086 automerge = (1 == control.getPreferences().getInt("autoMerge", 0));
00087 autoSegment();
00088 }
00089 }
00090
00091 public BackTracker(ThebaGUI f) {
00092 super(f);
00093 }
00094
00095 @Override
00096 public void setup() {
00097 seedButton = new JToggleButton("Mark seed");
00098 control.addToolbarButton(seedButton);
00099 Runnable lumenAction = new Runnable() {
00100 public void run() {
00101 track();
00102 }
00103 };
00104 Runnable wallAction = new Runnable() {
00105 public void run() {
00106 traceWalls();
00107 }
00108 };
00109 Runnable wallAction2 = new Runnable() {
00110 public void run() {
00111 traceWalls3d();
00112 }
00113
00114 };
00115
00116 FindCandidatesAction findCandidates = new FindCandidatesAction();
00117 FindAllCandidatesAction findAllCandidates = new FindAllCandidatesAction();
00118 Runnable clearAction = new Runnable() {
00119 public void run() {
00120 seeds.clear();
00121 }
00122 };
00123 control.addMenuItem("Track lumens", lumenAction);
00124 control.addMenuItem("Track walls", wallAction);
00125 control.addMenuItem("Track walls 3D", wallAction2);
00126 control.addMenuSeparator();
00127 control.addMenuItem("Find seeds in this slice", findCandidates);
00128 control.addMenuItem("Find seeds for every n'th slice",
00129 findAllCandidates);
00130 control.addMenuSeparator();
00131 control.addMenuItem("Autosegment", new AutosegmentAction());
00132 control.addMenuSeparator();
00133 control.addMenuItem("Clear seeds", clearAction);
00134
00135 try {
00136 neuralOut = new PrintStream(new FileOutputStream(new File(
00137 "weights.txt")));
00138 } catch (FileNotFoundException e) {
00139 e.printStackTrace();
00140 }
00141
00142 }
00143
00144 public void autoSegment() {
00145
00146
00147 Stack v = control.getStack();
00148
00149 for (int i = 0; i < v.getDepth(); i += 100) {
00150 findCandidates(i);
00151 }
00152
00153 track();
00154
00155 control.flipVolume();
00156
00157 for (int i = 0; i < v.getDepth(); i += 100) {
00158 findCandidates(i);
00159 }
00160
00161 track();
00162
00163 control.flipVolume();
00164
00165
00166 }
00167
00168 static final short max(short x, short y) {
00169 if ((x) > (y))
00170 return x;
00171 return y;
00172 }
00173
00179 public static void dilate3d_toWhite(Stack voxels) {
00180
00181 int width = voxels.getWidth();
00182 int height = voxels.getHeight();
00183 int depth = voxels.getDepth();
00184
00185 short[] prevdata = new short[width * height];
00186 short[] data = new short[width * height];
00187 short[] nextdata = new short[width * height];
00188
00189 ThebaGUI control = ThebaGUI.getInstance();
00190
00191 System.arraycopy(voxels.getSlice(1), 0, nextdata, 0, nextdata.length);
00192 System.arraycopy(voxels.getSlice(0), 0, data, 0, data.length);
00193
00194 for (int z = 0; z < depth; z++) {
00195
00196 short[] origdata = voxels.getSlice(z);
00197
00198 control.setProgress(z);
00199
00200 if (control.isStopped())
00201 return;
00202
00203 int index = 0;
00204
00205 if (z > 0)
00206 System.arraycopy(data, 0, prevdata, 0, prevdata.length);
00207 System.arraycopy(nextdata, 0, data, 0, data.length);
00208 if (z < depth - 1)
00209 System.arraycopy(voxels.getSlice(z + 1), 0, nextdata, 0,
00210 nextdata.length);
00211
00212 for (int y = 0; y < height; y++) {
00213 for (int x = 0; x < width; x++) {
00214
00215 short max = data[index];
00216
00217 if (max == 255) {
00218
00219 if (x > 0)
00220 max = max(max, data[index - 1]);
00221
00222 if (x < width - 1)
00223 max = max(max, data[index + 1]);
00224
00225 if (y > 0)
00226 max = max(max, data[index - width]);
00227
00228 if (y < height - 1)
00229 max = max(max, data[index + width]);
00230
00231 if (z > 0)
00232 max = max(max, prevdata[index]);
00233
00234 if (z < depth - 1)
00235 max = max(max, nextdata[index]);
00236
00237 if (max > data[index])
00238 origdata[index] = (short) (max | (1 << 12));
00239
00240 }
00241 index++;
00242 }
00243 }
00244 }
00245 }
00246
00247 private void traceWalls3d() {
00248
00249 Stack stack = control.getStack();
00250
00251 int width = stack.getWidth();
00252 int height = stack.getHeight();
00253 int depth = stack.getDepth();
00254
00255 for (int i = 0; i < 10; i++)
00256 dilate3d_toWhite(control.getStack());
00257
00258 ThebaGUI control = ThebaGUI.getInstance();
00259
00260 for (int z = 0; z < depth; z++) {
00261
00262 short[] data = control.getSlice(z);
00263
00264 control.setProgress(z);
00265
00266 if (control.isStopped())
00267 return;
00268
00269 for (int x = 0; x < width; x++) {
00270 for (int y = 0; y < height; y++) {
00271
00272 int index = x + y * width;
00273
00274 if (data[index] == 255)
00275 continue;
00276
00277 if ((data[index] & (1 << 12)) != 0) {
00278 data[index] = (short) (data[index] & ~(1 << 12));
00279 } else {
00280 data[index] = 0;
00281 }
00282 }
00283 }
00284 }
00285
00286 control.updateImage();
00287
00288 }
00289
00290
00291
00292
00293
00294
00295 public float checkReg(int xp, int yp, final short[] input) {
00296 short[] mark = new short[input.length];
00297 int width = control.getStack().getWidth();
00298 int height = control.getStack().getHeight();
00299
00300 LinkedList<Point> queue = new LinkedList<Point>();
00301 queue.addFirst(new Point(xp, yp));
00302 float whiteCount = 0;
00303 float borderCount = 0;
00304 short col = input[xp + yp * width];
00305 while (queue.isEmpty() == false) {
00306 Point p = queue.removeLast();
00307 int x = p.x;
00308 int y = p.y;
00309 int index = x + y * width;
00310 if (x + 1 < width) {
00311 if (input[index + 1] == 255)
00312 whiteCount++;
00313 else if (input[index + 1] == 0)
00314 borderCount++;
00315 if (mark[index + 1] == 0 && input[index + 1] == col) {
00316 mark[index + 1] = 1;
00317 queue.addFirst(new Point(x + 1, y));
00318 }
00319 }
00320 if (y + 1 < height) {
00321 if (input[index + width] == 255)
00322 whiteCount++;
00323 else if (input[index + width] == 0)
00324 borderCount++;
00325 if (mark[index + width] == 0 && input[index + width] == col) {
00326 mark[index + width] = 1;
00327 queue.addFirst(new Point(x, y + 1));
00328 }
00329 }
00330 if (y - 1 >= 0) {
00331 if (input[index - width] == 255)
00332 whiteCount++;
00333 else if (input[index - width] == 0)
00334 borderCount++;
00335 if (mark[index - width] == 0 && input[index - width] == col) {
00336 mark[index - width] = 1;
00337 queue.addFirst(new Point(x, y - 1));
00338 }
00339 }
00340 if (x - 1 >= 0) {
00341 if (input[index - 1] == 255)
00342 whiteCount++;
00343 else if (input[index - 1] == 0)
00344 borderCount++;
00345 if (mark[index - 1] == 0 && input[index - 1] == col) {
00346 mark[index - 1] = 1;
00347 queue.addFirst(new Point(x - 1, y));
00348 }
00349 }
00350 }
00351 borderCount += whiteCount;
00352 return whiteCount / borderCount;
00353 }
00354
00355 public void dispose() {
00356 System.out.println("file closed");
00357 neuralOut.close();
00358 }
00359
00367 public void findCandidates(int slice) {
00368 int width = control.getStack().getWidth();
00369 int height = control.getStack().getHeight();
00370
00371 short[] pixels = control.getSlice(slice);
00372 short[] copy = pixels.clone();
00373 for (int y = 0; y < height; y++) {
00374 for (int x = 0; x < width; x++) {
00375 int index = x + y * width;
00376 if (copy[index] == 0) {
00377 short[] mask = new short[pixels.length];
00378 short color = (short) 0xf0;
00379 int lumenArea = ImageFunctions.floodFill2D(x, y, width,
00380 height, copy, mask, color);
00381 for (int i = 0; i < pixels.length; i++)
00382 if (mask[i] != 0)
00383 copy[i] = (short) 1;
00384
00385
00386 boolean okLumen = true;
00387
00388
00389
00390 okLumen = okLumen && lumenArea < 4000 && lumenArea > 40;
00391
00392 RegionMask2D maskreg = new RegionMask2D(mask, width, height);
00393
00394
00395 if (okLumen) {
00396 double circularity = (new CircularityDescriptor())
00397 .measure(mask, width, height);
00398 okLumen = okLumen && circularity < 2.0
00399 && circularity > 0.5;
00400 }
00401
00402 if (okLumen) {
00403 double convexity = (Double) (new ConvexityDescriptor())
00404 .measure(maskreg);
00405 okLumen = okLumen && convexity > 0.85
00406 && convexity < 1.0;
00407 }
00408 if (okLumen) {
00409 double solidity = (new SolidityDescriptor()).measure(
00410 mask, width, height);
00411 okLumen = okLumen && solidity < 1.1 && solidity > 0.9;
00412 }
00413
00414 if (okLumen) {
00415 double eccentricity = (new EccentricityDescriptor())
00416 .measure(mask, width, height);
00417 okLumen = okLumen && eccentricity < 40
00418 && eccentricity >= 1;
00419 }
00420
00421 if (okLumen) {
00422 int internalRegionCount = (Integer) (new InternalRegionDescriptor())
00423 .measure(maskreg);
00424 okLumen = okLumen && internalRegionCount < 2;
00425 }
00426
00427 if (okLumen) {
00428 okLumen = okLumen
00429 && !ImageFunctions.touchBorder(mask, width,
00430 height);
00431 }
00432
00433 if (okLumen) {
00434
00435 LumenCandidate newSeed = new LumenCandidate(x, y,
00436 slice, (lumenArea));
00437 seeds.add(newSeed);
00438 for (int i = 0; i < pixels.length; i++)
00439 if (mask[i] != 0)
00440 copy[i] = 0xcc;
00441 }
00442 }
00443 }
00444 }
00445 control.showImage(copy);
00446 control.setLabel("Added " + seeds.size() + " seeds");
00447 }
00448
00449 public LinkedList getFibers() {
00450 return fibers;
00451 }
00452
00453 public boolean isReserved(short val) {
00454 if (val == 255 || val == control.INVALID)
00455 return true;
00456 return false;
00457 }
00458
00459 @Override
00460 public void mouseClicked(Point3D p) {
00461 if (seedButton.isSelected())
00462 selectSeed(new Point(p.x, p.y));
00463 }
00464
00465 public boolean removeCrack(int slice, short[] lumen) {
00466 int width = control.getStack().getWidth();
00467 int height = control.getStack().getHeight();
00468 int depth = control.getStack().getDepth();
00469 short[] data = control.getSlice(slice);
00470 short[] invdata = ImageFunctions.invertMask(data, false);
00471 ImageFunctions.invertMask(lumen, false);
00472 Rectangle bounds = ImageFunctions.getBounds(lumen, width, height, 12);
00473
00474
00475 if (bounds == null || bounds.x < 0 || bounds.y < 0
00476 || bounds.width > width || bounds.height > height) {
00477 seedStillValid = false;
00478 slice = depth;
00479 return true;
00480 }
00481
00482 if (map == null || map.length != data.length){
00483 map = new short[data.length];
00484 map2 = new short[data.length];
00485 }
00486
00487
00488 for (int i = 0; i < map.length; i++) {
00489 if (lumen[i] != 0)
00490 map[i] = 1;
00491 else
00492 map[i] = 0;
00493 }
00494
00495 map = ImageFunctions.dilate(lumen, map, width, height, bounds,
00496 (short) 2);
00497
00498 int maxDilations = control.getPreferences().getInt("maxDilations", 12);
00499
00500 for (int i = 3; i < maxDilations; i++) {
00501 System.arraycopy(map, 0, map2, 0, map.length);
00502 map = ImageFunctions.dilateMasked(map2, map, invdata, width,
00503 height, bounds, (short) i);
00504 }
00505
00506 map = ImageFunctions.mask(map, invdata, true);
00507 BackTrack.backTrack(map, 9, width, height, bounds);
00508
00509
00510 for (int i = 0; i < map.length; i++) {
00511 lumen[i] = map[i];
00512 if (lumen[i] != 0)
00513 data[i] = currentId;
00514 }
00515 return false;
00516 }
00517
00518 @SuppressWarnings("unchecked")
00519 public void removeStuff(int slice, short[] lumen) {
00520 int width = control.getStack().getWidth();
00521 int height = control.getStack().getHeight();
00522
00523 int regId = 1;
00524 short[] mark = new short[width * height];
00525 short[] slicedata = control.getSlice(slice);
00526 ArrayList<LumenCandidate> liste = new ArrayList<LumenCandidate>();
00527 for (int x = 0; x < width; x++) {
00528 for (int y = 0; y < height; y++) {
00529 if (mark[x + y * width] == 0 && lumen[x + y * width] != 0) {
00530 regId++;
00531
00532 int size = ImageFunctions.floodFill2D(x, y, width, height,
00533 lumen, mark, (short) regId);
00534
00535 LumenCandidate thisCandidate = new LumenCandidate(x, y, 0,
00536 size);
00537
00538 float borderRatio = checkReg(x, y, slicedata);
00539
00540 if (borderRatio < 0.7) {
00541
00542 log("Removed isolated region");
00543 ImageFunctions.floodFill2D(x, y, width, height,
00544 slicedata, (short) 0);
00545 ImageFunctions.floodFill2D(x, y, width, height, lumen,
00546 (short) 0);
00547 } else {
00548 liste.add(thisCandidate);
00549 }
00550
00551 }
00552 }
00553 }
00554
00555 int numberOfRegionsToKeep = 2;
00556
00557 if (liste.size() > 1) {
00558 Collections.sort(liste);
00559 boolean useMR_rules = true;
00560 if (useMR_rules) {
00561 LumenCandidate A = liste.get(0);
00562 LumenCandidate B = liste.get(1);
00563 if (A.getSize() > 5 * B.getSize()) {
00564 numberOfRegionsToKeep--;
00565 }
00566 }
00567 for (int i = numberOfRegionsToKeep; i < liste.size(); i++) {
00568 LumenCandidate l = liste.remove(i);
00569 ImageFunctions.floodFill2D(l.x, l.y, width, height, slicedata,
00570 (short) 0);
00571 ImageFunctions.floodFill2D(l.x, l.y, width, height, lumen,
00572 (short) 0);
00573 }
00574 }
00575 }
00576
00577 @Override
00578 public void reset() {
00579 stopped = false;
00580
00581 }
00582
00583 public void selectSeed(Point e) {
00584 int width = control.getStack().getWidth();
00585 int height = control.getStack().getHeight();
00586
00587 short[] data = control.getCurrentPixels();
00588 short[] filledRegion = new short[data.length];
00589 if (data[(int) e.getX() + (int) e.getY() * width] == 255)
00590 return;
00591 int count = ImageFunctions.floodFill2D(data, filledRegion, (int) e
00592 .getX(), (int) e.getY(), width, height, (short) 200);
00593
00594 Point p = e.getLocation();
00595
00596 short[] displayedImage = data.clone();
00597 if (p != null) {
00598
00599 for (int i = 0; i < data.length; i++) {
00600 if (filledRegion[i] != 0) {
00601 displayedImage[i] = filledRegion[i];
00602 }
00603 }
00604 control.showImage(displayedImage);
00605
00606 if (count > 10000) {
00607 int ans = JOptionPane
00608 .showConfirmDialog(
00609 null,
00610 "This regions has "
00611 + count
00612 + " pixels \nAre you sure you want to add this region?",
00613 "Confirm seed", JOptionPane.YES_NO_OPTION);
00614 if (ans == JOptionPane.NO_OPTION) {
00615 control.updateImage();
00616 return;
00617 }
00618
00619 }
00620 seeds.add(new LumenCandidate(p.x, p.y, control.currentSlice(),
00621 count));
00622 }
00623 }
00624
00625 public void setFibers(LinkedList fibers) {
00626 this.fibers = fibers;
00627 }
00628
00639 @SuppressWarnings("unchecked")
00640 public void traceWalls() {
00641 int width = control.getStack().getWidth();
00642 int height = control.getStack().getHeight();
00643 int depth = control.getStack().getDepth();
00644 control.clearChannels();
00645 SliceWriter lumenlog = new SliceWriter("lumens.raw", width, height,
00646 depth, 5);
00647 control.addChannel(lumenlog, "Lumens");
00648 SliceWriter walls = new SliceWriter("walls.raw", width, height, depth,
00649 5);
00650 control.addChannel(walls, "Walls");
00651 SliceWriter skeletonLog = new SliceWriter("skeleton.raw", width,
00652 height, depth, 5);
00653 control.addChannel(skeletonLog, "Skeleton");
00654 keepTracking = true;
00655 for (int z = 0; z < depth && keepTracking; z++) {
00656 long time = System.currentTimeMillis();
00657 short[] pixels = control.getSlice(z).clone();
00658 short[] map = ImageFunctions.distance2d(pixels, width, height);
00659 short[] mark = new short[width * height];
00660 short[] sout = new short[pixels.length];
00661 ArrayList<Lumen> lumens = new ArrayList<Lumen>();
00662 for (int x = 0; x < width; x++) {
00663 for (int y = 0; y < height; y++) {
00664 int index = x + y * width;
00665 if (mark[index] == 0 && pixels[index] != 0
00666 && !isReserved(pixels[index])) {
00667 short[] newLumenMask = new short[pixels.length];
00668 short fid = pixels[index];
00669 int count = 0;
00670 for (int i = 0; i < newLumenMask.length; i++) {
00671 if (pixels[i] == fid) {
00672 newLumenMask[i] = fid;
00673 mark[i] = (short) 1;
00674 count++;
00675 }
00676 }
00677 Rectangle bounds = ImageFunctions.getBounds(
00678 newLumenMask, width, height, 12);
00679 int regionID = 1;
00680 short[] mark2 = new short[width * height];
00681 ArrayList<FiberSlice> liste = new ArrayList<FiberSlice>();
00682 for (int xx = 0; xx < width; xx++) {
00683 for (int yy = 0; yy < height; yy++) {
00684 if (mark2[xx + yy * width] == 0
00685 && newLumenMask[xx + yy * width] == fid) {
00686 regionID++;
00687 ImageFunctions.floodFill2D(xx, yy, width,
00688 height, newLumenMask, mark2,
00689 (short) regionID);
00690 short[] lumenPart = new short[newLumenMask.length];
00691 int size = ImageFunctions.floodFill2D(xx,
00692 yy, width, height, newLumenMask,
00693 lumenPart, (short) regionID);
00694 FiberSlice storedLumenPart = new FiberSlice(
00695 lumenPart, size, z, fid);
00696 liste.add(storedLumenPart);
00697 }
00698 }
00699 }
00700
00701 if (liste.size() > 1) {
00702 Collections.sort(liste);
00703
00704 FiberSlice lumen1 = liste.remove(0);
00705 FiberSlice lumen2 = liste.remove(0);
00706
00707 short[] lumenBorder = ImageFunctions.dilate(
00708 lumen2.lumendata.clone(), width, height);
00709 lumenBorder = ImageFunctions.mask(lumenBorder,
00710 ImageFunctions.invertMask(lumen2.lumendata,
00711 true), true);
00712
00713 short[] geodist = new short[lumen1.lumendata.length];
00714 geodist = ImageFunctions.geodist(lumen1.lumendata,
00715 pixels, width, height);
00716 for (int i = 0; i < geodist.length; i++) {
00717 if (geodist[i] == 0)
00718 geodist[i] = 255;
00719 }
00720
00721
00722 DistancePath bestPath = new DistancePath();
00723 short[] path = bestPath.getPath(geodist,
00724 lumenBorder, map, width, height);
00725 for (int i = 0; i < path.length; i++) {
00726 if (path[i] != 0) {
00727 mark[i] = fid;
00728
00729 newLumenMask[i] = fid;
00730 pixels[i] = fid;
00731 }
00732 }
00733 }
00734
00735 short[] dmap = ImageFunctions.distance2d(ImageFunctions
00736 .invertMask(newLumenMask, false), width,
00737 height, bounds);
00738 Lumen newLumen = new Lumen(newLumenMask, dmap, bounds,
00739 count, fid);
00740 lumens.add(newLumen);
00741 }
00742 }
00743 }
00744
00745
00746 short[] lumenOutput = new short[pixels.length];
00747 for (int i = 0; i < lumenOutput.length; i++) {
00748
00749 if (!isReserved((short) i)) {
00750 lumenOutput[i] = pixels[i];
00751 }
00752 }
00753 lumenlog.putSlice(lumenOutput, z);
00754 short[] waterShed = getWatershed(z);
00755 for (int lum = 0; lum < lumens.size(); lum++) {
00756 Lumen lumen = lumens.get(lum);
00757 Rectangle bounds = lumen.getBounds();
00758 int startX = bounds.x;
00759 int stopX = bounds.width + bounds.x;
00760 int startY = bounds.y;
00761 int stopY = bounds.height + bounds.y;
00762 short[] currentWall = waterShed.clone();
00763 short[] searchMask = new short[width * height];
00764 searchMask = ImageFunctions.dilate(lumen.getLumenMask(), width,
00765 height);
00766 for (int q = 0; q < pixels.length; q++) {
00767 if (lumen.getLumenMask()[q] != 0)
00768 searchMask[q] = 0;
00769 }
00770
00771 for (int x = 0; x < width; x++) {
00772 for (int y = 0; y < height; y++) {
00773 int index = x + y * width;
00774 if (currentWall[index] == 0 && searchMask[index] != 0) {
00775 ImageFunctions
00776 .floodFill2D(currentWall.clone(),
00777 currentWall, x, y, width, height,
00778 (short) 1);
00779 }
00780 }
00781 }
00782
00783 for (int x = 0; x < pixels.length; x++) {
00784 if (currentWall[x] == (short) 255) {
00785 currentWall[x] = 0;
00786 }
00787 }
00788
00789 currentWall = ImageFunctions.dilate(currentWall.clone(),
00790 currentWall, width, height, lumen.getBounds());
00791 currentWall = ImageFunctions.dilate(currentWall.clone(),
00792 currentWall, width, height, lumen.getBounds());
00793 currentWall = ImageFunctions.dilate(currentWall.clone(),
00794 currentWall, width, height, lumen.getBounds());
00795
00796
00797 pixels = control.getSlice(z);
00798
00799 short ff = (short) 0xff;
00800 ArrayList<Integer> sizeList = new ArrayList<Integer>();
00801 for (int x = startX; x < stopX; x++) {
00802 for (int y = startY; y < stopY; y++) {
00803 int i = x + y * width;
00804 if (pixels[i] == ff && lumen.getDistanceMap()[i] < 9) {
00805 sizeList.add((int) map[i]);
00806 }
00807 }
00808 }
00809 Collections.sort(sizeList);
00810 int medianWallSize = 0;
00811 if (sizeList.size() == 0) {
00812 System.err.println("warning sizelist empty!");
00813 break;
00814 }
00815 medianWallSize = sizeList.get(sizeList.size() / 2);
00816 medianWallSize = medianWallSize * 2 + 1;
00817 int maxWallSize = (int) (medianWallSize * 1.5);
00818
00819 double avgx = 0;
00820 double avgy = 0;
00821 double count = 0;
00822 for (int x = startX; x < stopX; x++) {
00823 for (int y = startY; y < stopY; y++) {
00824 int i = x + y * width;
00825 if (currentWall[i] != 0 && pixels[i] != (short) 255
00826
00827
00828
00829 || lumen.getDistanceMap()[i] > maxWallSize) {
00830
00831
00832 currentWall[i] = 0;
00833 }
00834
00835 if (lumen.getLumenMask()[i] != 0) {
00836 count++;
00837 avgx += x;
00838 avgy += y;
00839 pixels[i] = 0;
00840 }
00841 if (currentWall[i] != 0) {
00842 lumen.getLumenMask()[i] = (short) 255;
00843 }
00844 }
00845 }
00846 sout[(int) (avgx / count) + (int) (avgy / count) * width] = lumen
00847 .getId();
00848 }
00849 skeletonLog.putSlice(sout, z);
00850 for (int i = 0; i < pixels.length; i++) {
00851 int min = Integer.MAX_VALUE;
00852 short minId = 0;
00853 if (pixels[i] != 0) {
00854 for (int j = 0; j < lumens.size(); j++) {
00855 Lumen candidate = lumens.get(j);
00856 if (candidate.getLumenMask()[i] == (short) 255) {
00857 int val = candidate.getDistanceMap()[i];
00858 if (val < min) {
00859 min = val;
00860 minId = candidate.getId();
00861 }
00862 }
00863 }
00864 if (minId != 0) {
00865 pixels[i] = minId;
00866 }
00867 }
00868 }
00869
00870 short[] wallOutput = new short[pixels.length];
00871 for (int i = 0; i < wallOutput.length; i++) {
00872 if (pixels[i] != control.INVALID && pixels[i] != (short) 255
00873 && pixels[i] != 0) {
00874 wallOutput[i] = pixels[i];
00875 }
00876 }
00877 walls.putSlice(wallOutput, z);
00878 control.updateSlice(z);
00879 System.out.println("Total time for slice " + z + " : "
00880 + (System.currentTimeMillis() - time) / 1000.0);
00881 }
00882 control.updateImage();
00883 walls.flush();
00884 lumenlog.flush();
00885 }
00886
00887 @SuppressWarnings("unchecked")
00888 @Override
00889 public void track() {
00890 int width = control.getStack().getWidth();
00891 int height = control.getStack().getHeight();
00892 int depth = control.getStack().getDepth();
00893 automerge = (1 == control.getPreferences().getInt("autoMerge", 0));
00894
00895 Point[][] skeleton = new Point[control.MAX_FIBERS][depth];
00896
00897 Collections.sort(seeds);
00898 long time = System.currentTimeMillis();
00899 LumenCandidate s = null;
00900 Iterator<LumenCandidate> iter = seeds.iterator();
00901 int count = 0;
00902 keepTracking = true;
00903 while (iter.hasNext() && keepTracking && !stopped) {
00904 while (paused)
00905 Thread.yield();
00906 s = iter.next();
00907 seedQueue = new LinkedList<Seed>();
00908 count++;
00909 System.out.println("\nTracking seed " + count + " of "
00910 + seeds.size() + " starting at " + s);
00911 short[] data = control.getSlice(s.z);
00912 if (data[s.x + s.y * width] != 0) {
00913 continue;
00914 }
00915 currentId = (control.getNewFiberId());
00916 ImageFunctions.delete3D(control.getStack(), s.x, s.y, s.z);
00917 seedQueue.addLast(new Seed(s.x, s.y, s.z, 1, null, null, 0));
00918 log("***************************");
00919 log("Tracking seed with origin " + s);
00920 StringBuffer results = new StringBuffer();
00921 results.append("Tracking seed " + count + " of " + seeds.size()
00922 + "\n from " + s);
00923 control.showResults(results, "Lumen tracking");
00924 short[] lumenMask = new short[data.length];
00925 int slice = s.z;
00926 ImageFunctions.floodFill2D(s.x, s.y, width, height,
00927 data, lumenMask, currentId);
00928 short[] lcopy = lumenMask.clone();
00929
00930 seedStillValid = true;
00931 int length = 0;
00932
00933
00934
00935 while (slice < depth && seedStillValid && keepTracking) {
00936 control.setProgress(slice);
00937 removeCrack(slice, lumenMask);
00938 removeStuff(slice, lumenMask);
00939 if (automerge)
00940 skeleton[currentId][slice] = ImageFunctions
00941 .getAveragePoint(lumenMask, width, height);
00942 if (slice % 2 == 0)
00943 control.showImage(control.getSlice(slice));
00944 slice++;
00945 }
00946
00947 seedStillValid = true;
00948 slice = s.z - 1;
00949 lumenMask = lcopy;
00950
00951 while (slice >= 0 && seedStillValid && keepTracking && !stopped) {
00952 control.setProgress(slice);
00953 removeCrack(slice, lumenMask);
00954 removeStuff(slice, lumenMask);
00955 if (slice % 2 == 0)
00956 control.showImage(control.getSlice(slice));
00957 if (automerge)
00958 skeleton[currentId][slice] = ImageFunctions
00959 .getAveragePoint(lumenMask, width, height);
00960 slice--;
00961 }
00962
00963 control.showImage(control.getSlice(slice + 1));
00964
00965 if (automerge)
00966 checkMerges(skeleton);
00967
00968
00969 length = 0;
00970 for (int z = 0; z < depth; z++) {
00971 if (skeleton[currentId][z] != null)
00972 length++;
00973 }
00974
00975 }
00976 control.setProgressComplete();
00977 control.updateImage();
00978 seeds.clear();
00979 StringBuffer results = new StringBuffer();
00980 results.append("Total time taken:"
00981 + (System.currentTimeMillis() - time) / 1000.0 + " seconds\n\n");
00982 control.showResults(results, "Lumen tracking");
00983 }
00984
00985 private void checkMerges(Point[][] skel) {
00986 int width = control.getStack().getWidth();
00987 int height = control.getStack().getHeight();
00988 int depth = control.getStack().getDepth();
00989 int[] mergeList = new int[control.MAX_FIBERS];
00990 for (int z = 1; z < depth - 1; z++) {
00991 short[] slice = control.getSlice(z);
00992 short[] next = control.getSlice(z + 1);
00993 short[] prev = control.getSlice(z - 1);
00994 for (int x = 1; x < width - 1; x++) {
00995 for (int y = 1; y < height - 1; y++) {
00996 int index = x + y * width;
00997 if (slice[index] == currentId) {
00998 mergeList[next[index]] = 1;
00999 mergeList[prev[index]] = 1;
01000 }
01001 }
01002 }
01003 }
01004 for (short color = 1; color < control.MAX_FIBERS; color++) {
01005 if (isReserved(color))
01006 continue;
01007 if (mergeList[color] != 0 && color != currentId) {
01008 double avgDist = 0;
01009 double count = 0;
01010 for (int i = 0; i < depth; i++) {
01011 if (skel[currentId][i] != null && skel[color][i] != null) {
01012 double a = skel[currentId][i].x - skel[color][i].x;
01013 double b = skel[currentId][i].y - skel[color][i].y;
01014 double dist = Math.sqrt(a * a + b * b);
01015 avgDist += dist;
01016 count++;
01017 }
01018 }
01019 if (count > 0)
01020 avgDist /= count;
01021 System.out.println("Overlaps " + count);
01022 System.out.println("Avgdist " + avgDist);
01023
01024
01025 if (avgDist < 50 || count == 0) {
01026 System.out.println("Merging " + color + " with "
01027 + (currentId));
01028 control.releaseFiberId(color);
01029 for (int z = 0; z < depth; z++) {
01030 short[] slice = control.getSlice(z);
01031 for (int x = 1; x < width - 1; x++) {
01032 for (int y = 1; y < height - 1; y++) {
01033 int index = x + y * width;
01034 if (slice[index] == color) {
01035 slice[index] = currentId;
01036 }
01037 }
01038 }
01039 skel[currentId][z] = ImageFunctions.getAveragePoint(
01040 slice, width, height, currentId);
01041 skel[color][z] = null;
01042 }
01043 } else {
01044 System.out.println("Rejected merge!");
01045 }
01046 }
01047 }
01048 }
01049
01050 private short[] getWatershed(int z) {
01051 int width = control.getStack().getWidth();
01052 int height = control.getStack().getHeight();
01053
01054 short[] waterShed = control.getSlice(z).clone();
01055
01056 for (int i = 0; i < waterShed.length; i++) {
01057 if (waterShed[i] != 0) {
01058 waterShed[i] = (short) 0xff;
01059 }
01060 }
01061
01062 EDM edm = new EDM();
01063 edm.run(waterShed, width, height);
01064 for (int i = 0; i < waterShed.length; i++) {
01065 waterShed[i] = (short) (waterShed[i] >> 2);
01066 }
01067
01068 for (int i = 0; i < waterShed.length; i++) {
01069 waterShed[i] = (short) (255 - waterShed[i]);
01070 }
01071
01072
01073 for (int i = 0; i < waterShed.length; i++) {
01074 if (control.getSlice(z)[i] == 0)
01075 waterShed[i] = (short) 0;
01076 }
01077
01078
01079 Watershed_Algorithm wc = new Watershed_Algorithm();
01080 wc.run(waterShed, width, height);
01081 int regionID = 1;
01082 int maxCount = Integer.MIN_VALUE;
01083 Point p = null;
01084 short[] mark = new short[width * height];
01085 int countregs = 0;
01086 for (int xx = 0; xx < width; xx++) {
01087 for (int yy = 0; yy < height; yy++) {
01088 if (mark[xx + yy * width] == 0
01089 && waterShed[xx + yy * width] == 0) {
01090 countregs++;
01091 regionID++;
01092 int count = ImageFunctions.floodFill2D(xx, yy, width,
01093 height, waterShed, mark, (short) 1);
01094 if (count > maxCount) {
01095 p = new Point(xx, yy);
01096 maxCount = count;
01097 }
01098 }
01099 }
01100 }
01101 ImageFunctions.floodFill2D(p.x, p.y, width, height, waterShed.clone(),
01102 waterShed, (short) 255);
01103 return waterShed;
01104 }
01105
01106 @Override
01107 public void stop() {
01108 stopped = true;
01109 }
01110
01111 }