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