BackTracker.java

Go to the documentation of this file.
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                 // traceWalls3d();
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          * Returns a factor describing the percentage of border-pixels that are
00292          * adjacent to a white border-area A too low percentage is a strong
00293          * indication that this region is not a valid fiber
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                 //int depth = control.getStack().getDepth();
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                 //int depth = control.getStack().getDepth();
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                                         // Decide if this area is a ok lumen.
00386                                         boolean okLumen = true;
00387 
00388                                         //FIXME use values from config instead of hardcoded ones:
00389 
00390                                         okLumen = okLumen && lumenArea < 4000 && lumenArea > 40;
00391 
00392                                         RegionMask2D maskreg = new RegionMask2D(mask, width, height);
00393 
00394                                         // get bounds, use bounds
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                                                 // Ok, let's assume this is a lumen now
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                 // End search if fibre is outside volume (bounds==null indicates no
00474                 // pixels found)
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                 // Create a distance map for use by backtracking, where lumen = distance 1
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                 // copy new lumen back to source
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                 //int depth = control.getStack().getDepth();
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                                                 // no pixels have a white border, so we remove it!
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                 //int depth = control.getStack().getDepth();
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                         // copy marked pixels to output
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                                                 // Find path between the two largest lumen parts
00701                                                 if (liste.size() > 1) {
00702                                                         Collections.sort(liste);
00703                                                         // Get the two largest lumen parts
00704                                                         FiberSlice lumen1 = liste.remove(0);
00705                                                         FiberSlice lumen2 = liste.remove(0);
00706                                                         // find border of lumen 2
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                                                         // find geodesic distance from lumen 1
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                                                         // find path that connects lumen1 with lumen2 and
00721                                                         // goes through non-void part in the image
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; // MUST AVOID FINDING NEW
00728                                                                                                         // PIXELS AS NEW REGION
00729                                                                         newLumenMask[i] = fid;
00730                                                                         pixels[i] = fid;
00731                                                                 }
00732                                                         }
00733                                                 }
00734                                                 // Calculate and store distance from lumen
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                         // Copy lumens into a separate file (only used for visual inspection
00745                         // later)
00746                         short[] lumenOutput = new short[pixels.length];
00747                         for (int i = 0; i < lumenOutput.length; i++) {
00748                                 // copy only non-white pixels to output
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                                 // Join regions inside lumens
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                                 // Fill in watershed borders
00783                                 for (int x = 0; x < pixels.length; x++) {
00784                                         if (currentWall[x] == (short) 255) {
00785                                                 currentWall[x] = 0;
00786                                         }
00787                                 }
00788                                 // dilate to fill borders
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                                 // Obtain the original pixels ( without connecting paths written
00796                                 // to them)
00797                                 pixels = control.getSlice(z);
00798                                 // get median wallsize
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                                 // Mask out erroneously dilated pixels
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 // not
00826                                                                                                                                                         // a
00827                                                                                                                                                         // foreground
00828                                                                                                                                                         // pixel
00829                                                                 || lumen.getDistanceMap()[i] > maxWallSize) { // outside
00830                                                                                                                                                                 // max
00831                                                                                                                                                                 // wallsize
00832                                                         currentWall[i] = 0;
00833                                                 }
00834                                                 // remove lumenmark from data
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; // set type to type of closest lumen
00866                                         }
00867                                 }
00868                         }
00869                         // Copy finished pixels and remove tracked walls from data source
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                 } // end z-loop
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                         // Trace forward from seed
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                         // Trace backward from seed
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                         // We must recalculate length, since the total length may have grown
00968                         // after merges have been applied!
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                                 // Only merge regions of they have an average distance
01024                                 // across all coinciding lumen sections below this threshhold
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                 //int depth = control.getStack().getDepth();
01054                 short[] waterShed = control.getSlice(z).clone();
01055                 // Filling tracked lumen with white
01056                 for (int i = 0; i < waterShed.length; i++) {
01057                         if (waterShed[i] != 0) {
01058                                 waterShed[i] = (short) 0xff;
01059                         }
01060                 }
01061                 // make distancemap
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                 // invert map
01068                 for (int i = 0; i < waterShed.length; i++) {
01069                         waterShed[i] = (short) (255 - waterShed[i]);
01070                 }
01071                 // set pixels originally of value 0 (backgrund) to 0, because we don't
01072                 // want the watershed to grow into the background
01073                 for (int i = 0; i < waterShed.length; i++) {
01074                         if (control.getSlice(z)[i] == 0)
01075                                 waterShed[i] = (short) 0;
01076                 }
01077 
01078                 // Apply watershed
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 }

Generated on Fri Nov 13 08:57:07 2009 for Theba by  doxygen 1.6.1