HHTracker.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.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         // configurable options
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                         // results.append("Tracked lumens =" + (control.MAX_FIBERS -
00103                         // idList.size() - 2) + "\n");
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                         // Trace forward from seed
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                         // Trace backward from seed
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                         // We must recalculate length, since the total length may have grown
00146                         // after
00147                         // merges have been applied!
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(); // Clear seeds marks
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                                                  * Consider only propagating pixels that are not marked
00264                                                  * by a negative value in the counts table, this
00265                                                  * indicates that they are beyond their detected wall
00266                                                  * size and should not be propagated further
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                         // reset all non-negative values
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          * Returns a factor describing the percentage of border-pixels that are
00357          * adjacent to a white border-area A too low percentage is a strong
00358          * indication that this region is not a valid fiber
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                 //int depth = control.getStack().getDepth();
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                 //int depth = control.getStack().getDepth();
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                                         // Decide if this area is a ok lumen.
00445                                         boolean okLumen = true;
00446                                         okLumen = okLumen && lumenArea < 8000 && lumenArea > 50;
00447                                         RegionMask2D maskreg = new RegionMask2D(mask, width, height);
00448                                         // get bounds, use bounds
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; // 4.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; // 4
00481                                         }
00482                                         // if the area is very small and the eccentricity is large,
00483                                         // then we have a small thin region found in
00484                                         // ring-like fiber
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                                         // Ok, let's assume this is a lumen now
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                 // End search if fibre is outside volume (bounds==null indicates no
00540                 // pixels found)
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                 // Create a distancemap for use by backtracking, where lumen= distance 1
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                 // copy new lumen back to source
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                 //int depth = control.getStack().getDepth();
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                                                 // no pixels have a white border, so we remove it!
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                                 // bruk maritrunar-regler
00616                                 if (A.getSize() > 5 * B.getSize()) {
00617                                         // System.out.println("Deleted small lumen-part.");
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                 //int depth = control.getStack().getDepth();
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                         // copy marked pixels to output
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                                                 // Find path between the two largest lumen parts
00745                                                 if (liste.size() > 1) {
00746                                                         Collections.sort(liste);
00747                                                         // Get the two largest lumen parts
00748                                                         FiberSlice lumen1 = liste.remove(0);
00749                                                         FiberSlice lumen2 = liste.remove(0);
00750                                                         // find border of lumen 2
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                                                         // find geodesic distance from lumen 1
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                                                         // find path that connects lumen1 with lumen2 and
00765                                                         // goes through non-void part in the image
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; // MUST AVOID FINDING NEW
00772                                                                         // PIXELS AS NEW REGION
00773                                                                         newLumenMask[i] = fid;
00774                                                                         pixels[i] = fid;
00775                                                                 }
00776                                                         }
00777                                                 }
00778                                                 // Calculate and store distance from lumen
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                         // Copy lumens into a separate file (only used for visual inspection
00789                         // later)
00790                         short[] lumenOutput = new short[pixels.length];
00791                         for (int i = 0; i < lumenOutput.length; i++) {
00792                                 // copy only non-white pixels to output
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                                 // Join regions inside lumens
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                                 // Fill in watershed borders
00827                                 for (int x = 0; x < pixels.length; x++) {
00828                                         if (currentWall[x] == (short) 255) {
00829                                                 currentWall[x] = 0;
00830                                         }
00831                                 }
00832                                 // dilate to fill borders
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                                 // Obtain the original pixels ( without connecting paths written
00840                                 // to them)
00841                                 pixels = control.getSlice(z);
00842                                 // get median wallsize
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                                 // Mask out erroneously dilated pixels
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 // not
00870                                                                 // a
00871                                                                 // foreground
00872                                                                 // pixel
00873                                                                 || lumen.getDistanceMap()[i] > maxWallSize) { // outside
00874                                                         // max
00875                                                         // wallsize
00876                                                         currentWall[i] = 0;
00877                                                 }
00878                                                 // remove lumenmark from data
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; // set type to type of closest lumen
00910                                         }
00911                                 }
00912                         }
00913                         // Copy finished pixels and remove tracked walls from data source
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                                         // pixels[i] = 0;
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                 } // end z-loop
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                                                 // merge only fibres that touch from above or below
00946                                                 // mergeList[slice[index + 1] ] = 1;
00947                                                 // mergeList[slice[index - 1] ] = 1;
00948                                                 // mergeList[slice[index + width] ] = 1;
00949                                                 // mergeList[slice[index - width] ] = 1;
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                                                 // System.out.println(dist);
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                                 // Only merge regions of they have an average distance
00977                                 // across all coinciding lumen sections below this threshhold
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                 //int depth = control.getStack().getDepth();
01013                 short[] waterShed = control.getSlice(z).clone();
01014                 // Filling tracked lumen with white
01015                 for (int i = 0; i < waterShed.length; i++) {
01016                         if (waterShed[i] != 0) {
01017                                 waterShed[i] = (short) 0xff;
01018                         }
01019                 }
01020                 // make distancemap
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                 // invert map
01027                 for (int i = 0; i < waterShed.length; i++) {
01028                         waterShed[i] = (short) (255 - waterShed[i]);
01029                 }
01030                 // set pixels originally of value 0 (backgrund) to 0, because we don't
01031                 // want the watershed to grow into the background
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 }

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