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.ImageFunctions;
00015 import theba.core.LumenCandidate;
00016 import theba.core.RegionMask2D;
00017 import theba.core.Seed;
00018 import theba.core.Stack;
00019 import theba.core.Tracker;
00020 import theba.core.gui.ThebaGUI;
00021 import theba.core.math.Point3D;
00022 import theba.descriptors.AvgYoungCurvature;
00023 import theba.descriptors.CircularityDescriptor;
00024 import theba.descriptors.ConvexityDescriptor;
00025 import theba.descriptors.EccentricityDescriptor;
00026 import theba.descriptors.InternalRegionDescriptor;
00027 import theba.descriptors.SolidityDescriptor;
00028 import theba.descriptors.YoungBendingEnergy;
00029
00030
00039 public class FiberTracker extends Tracker {
00040
00041
00042 private JToggleButton seedButton;
00043
00044 private LinkedList<Seed> seedQueue;
00045
00046 private LinkedList<LumenCandidate> seeds = new LinkedList<LumenCandidate>();
00047
00048 boolean seedStillValid = true;
00049
00050 private short currentId;
00051
00052 private short[] map = null;
00053
00054 private short[] map2;
00055
00056
00057 private boolean automerge = false;
00058
00059 private int minimumFiberSize = 50;
00060
00061 private boolean removeShortFibers = false;
00062
00066 @SuppressWarnings("unchecked")
00067 @Override
00068 public void track() {
00069
00070 int width = control.getStack().getWidth();
00071 int height = control.getStack().getHeight();
00072 int depth = control.getStack().getDepth();
00073
00074 automerge = (1 == control.getPreferences().getInt("autoMerge", 0));
00075
00076
00077 Point[][] skeleton = new Point[control.MAX_FIBERS][depth];
00078
00079 Collections.sort(seeds);
00080 Point3D s = null;
00081 Iterator<LumenCandidate> iter = seeds.iterator();
00082 int count = 0;
00083 keepTracking = true;
00084
00085 while (iter.hasNext() && keepTracking && !control.isStopped()) {
00086 s = iter.next();
00087 seedQueue = new LinkedList<Seed>();
00088 count++;
00089 System.out.println("\nTracking seed " + count + " of "
00090 + seeds.size() + " starting at " + s);
00091 short[] data = control.getSlice(s.z);
00092 if (data[s.x + s.y * width] != 0) {
00093 continue;
00094 }
00095
00096 currentId = (control.getNewFiberId());
00097 ImageFunctions.delete3D(control.getStack(), s.x, s.y, s.z);
00098
00099 seedQueue.addLast(new Seed(s.x, s.y, s.z, 1, null, null, 0));
00100 log("***************************");
00101 log("Tracking seed with origin " + s);
00102 StringBuffer results = new StringBuffer();
00103 results.append("Tracking seed " + count + " of " + seeds.size()
00104 + "\n from " + s);
00105 control.showResults(results, "Lumen tracking");
00106 short[] lumenMask = new short[data.length];
00107 int slice = s.z;
00108
00109 ImageFunctions.floodFill2D(s.x, s.y, width, height, data, lumenMask, currentId);
00110 short[] lcopy = lumenMask.clone();
00111
00112 seedStillValid = true;
00113 int length = 0;
00114
00115
00116
00117 while (slice < depth && seedStillValid && keepTracking) {
00118 control.setProgress(slice);
00119 removeCrack(slice, lumenMask);
00120 removeStuff(slice, lumenMask);
00121 if (automerge)
00122 skeleton[currentId][slice] = ImageFunctions.getAveragePoint(lumenMask, width, height);
00123 if (slice % 2 == 0)
00124 control.showImage(control.getSlice(slice));
00125 slice++;
00126 }
00127
00128 seedStillValid = true;
00129 slice = s.z - 1;
00130 lumenMask = lcopy;
00131
00132 while (slice >= 0 && seedStillValid && keepTracking
00133 && !control.isStopped()) {
00134 control.setProgress(slice);
00135 removeCrack(slice, lumenMask);
00136 removeStuff(slice, lumenMask);
00137 if (slice % 2 == 0)
00138 control.showImage(control.getSlice(slice));
00139 if (automerge)
00140 skeleton[currentId][slice] = ImageFunctions
00141 .getAveragePoint(lumenMask, width, height);
00142 slice--;
00143 }
00144
00145 control.showImage(control.getSlice(slice + 1));
00146
00147 if (automerge)
00148 checkMerges(skeleton);
00149
00150
00151 length = 0;
00152 for (int z = 0; z < depth; z++) {
00153 if (skeleton[currentId][z] != null)
00154 length++;
00155 }
00156
00157 if (removeShortFibers) {
00158 if (length < minimumFiberSize) {
00159
00160 for (int z = 0; z < depth; z++) {
00161 short[] pixels = control.getSlice(z);
00162 for (int i = 0; i < pixels.length; i++) {
00163 if (pixels[i] == currentId)
00164 pixels[i] = control.INVALID;
00165 }
00166 }
00167 control.releaseFiberId(currentId);
00168 skeleton[currentId] = new Point[depth];
00169 }
00170 }
00171
00172 }
00173
00174 control.setProgressComplete();
00175 control.updateImage();
00176 seeds.clear();
00177 control.showResults("Tracking lumens complete");
00178 }
00179
00180 public FiberTracker(ThebaGUI f) {
00181 super(f);
00182 }
00183
00184 @Override
00185 public void setup() {
00186 seedButton = new JToggleButton("Mark seed");
00187 control.addToolbarButton(seedButton);
00188 Runnable lumenAction = new Runnable() {
00189 public void run() {
00190 track();
00191 }
00192 };
00193
00194 Runnable wallAction2 = new Runnable() {
00195 public void run() {
00196 traceWalls3d();
00197 }
00198
00199 };
00200
00201 FindCandidatesAction findCandidates = new FindCandidatesAction();
00202 FindAllCandidatesAction findAllCandidates = new FindAllCandidatesAction();
00203 Runnable clearAction = new Runnable() {
00204 public void run() {
00205 seeds.clear();
00206 }
00207 };
00208 control.addMenuItem("Track lumens", lumenAction);
00209 control.addMenuItem("Track walls 3D", wallAction2);
00210 control.addMenuSeparator();
00211 control.addMenuItem("Find seeds in this slice", findCandidates);
00212 control.addMenuItem("Find seeds for every n'th slice",
00213 findAllCandidates);
00214 control.addMenuSeparator();
00215 control.addMenuItem("Autosegment", new AutosegmentAction());
00216 control.addMenuSeparator();
00217 control.addMenuItem("Clear seeds", clearAction);
00218
00219 }
00220
00221 public void autoSegment() {
00222
00223 Stack v = control.getStack();
00224 for (int i = 0; i < v.getDepth(); i += 100) {
00225 findCandidates(i);
00226 }
00227 track();
00228 control.flipVolume();
00229 v = control.getStack();
00230 for (int i = 0; i < v.getDepth(); i += 100) {
00231 findCandidates(i);
00232 }
00233 track();
00234 control.flipVolume();
00235
00236
00237 }
00238
00239 static final short max(short x, short y) {
00240 if ((x) > (y))
00241 return x;
00242 return y;
00243 }
00244
00250 public static void dilate3d_toWhite(Stack voxels, long[] counts) {
00251
00252 int width = voxels.getWidth();
00253 int height = voxels.getHeight();
00254 int depth = voxels.getDepth();
00255
00256 short[] prevdata = new short[width * height];
00257 short[] data = new short[width * height];
00258 short[] nextdata = new short[width * height];
00259
00260 ThebaGUI control = ThebaGUI.getInstance();
00261
00262 System.arraycopy(voxels.getSlice(1), 0, nextdata, 0, nextdata.length);
00263 System.arraycopy(voxels.getSlice(0), 0, data, 0, data.length);
00264
00265 for (int z = 0; z < depth; z++) {
00266
00267 short[] origdata = voxels.getSlice(z);
00268
00269 control.setProgress(z);
00270
00271 if (control.isStopped())
00272 return;
00273
00274 int index = 0;
00275
00276 for (int y = 0; y < height; y++) {
00277 for (int x = 0; x < width; x++) {
00278
00279 short max = data[index];
00280
00281 if (max == 255) {
00282
00283
00284
00285
00286
00287
00288 if (x > 0)
00289 max = max(max, data[index - 1]);
00290
00291 if (x < width - 1)
00292 max = max(max, data[index + 1]);
00293
00294 if (y > 0)
00295 max = max(max, data[index - width]);
00296
00297 if (y < height - 1)
00298 max = max(max, data[index + width]);
00299
00300 if (z > 0)
00301 max = max(max, prevdata[index]);
00302
00303 if (z < depth - 1)
00304 max = max(max, nextdata[index]);
00305
00306 int id = max & (~(1 << 12));
00307 if (max > data[index] && counts[id] != -1) {
00308 origdata[index] = (short) (max | (1 << 12));
00309 counts[id]++;
00310 }
00311 }
00312 index++;
00313 }
00314 }
00315
00316 if (z > 0)
00317 System.arraycopy(data, 0, prevdata, 0, prevdata.length);
00318
00319 System.arraycopy(nextdata, 0, data, 0, data.length);
00320
00321 if (z < depth - 2)
00322 System.arraycopy(voxels.getSlice(z + 2), 0, nextdata, 0,
00323 nextdata.length);
00324
00325 }
00326 }
00327
00333 private void traceWalls3d() {
00334
00335 Stack stack = control.getStack();
00336
00337 int width = stack.getWidth();
00338 int height = stack.getHeight();
00339 int depth = stack.getDepth();
00340
00341 long[] startvals = new long[control.MAX_FIBERS];
00342 long[] lumenvals = new long[control.MAX_FIBERS];
00343
00344 for (int i = 0; i < 15; i++) {
00345
00346 for (int k = 0; k < lumenvals.length; k++) {
00347 if (lumenvals[k] > 0)
00348 lumenvals[k] = 0;
00349 }
00355 dilate3d_toWhite(control.getStack(), lumenvals);
00356 for (int k = 0; k < lumenvals.length; k++) {
00357 if (lumenvals[k] > 0) {
00358 if (i == 0)
00359 startvals[k] = lumenvals[k];
00360 if (lumenvals[k] < startvals[k] / 4) {
00361 lumenvals[k] = -1;
00362 }
00363 }
00364 }
00365 control.updateImage();
00366 }
00367
00368 ThebaGUI control = ThebaGUI.getInstance();
00369 for (int z = 0; z < depth; z++) {
00370
00371 short[] data = control.getSlice(z);
00372 control.setProgress(z);
00373 if (control.isStopped())
00374 return;
00375
00376 for (int x = 0; x < width; x++) {
00377 for (int y = 0; y < height; y++) {
00378 int index = x + y * width;
00379 if (data[index] == 255)
00380 continue;
00381 if ((data[index] & (1 << 12)) != 0) {
00382 data[index] = (short) (data[index] & ~(1 << 12));
00383 } else {
00384 data[index] = 0;
00385 }
00386 }
00387 }
00388 }
00389
00390 control.updateImage();
00391
00392 }
00393
00394
00395
00396
00397
00398
00399 public float checkReg(int xp, int yp, final short[] input) {
00400 short[] mark = new short[input.length];
00401 int width = control.getStack().getWidth();
00402 int height = control.getStack().getHeight();
00403
00404 LinkedList<Point> queue = new LinkedList<Point>();
00405 queue.addFirst(new Point(xp, yp));
00406 float whiteCount = 0;
00407 float borderCount = 0;
00408 short col = input[xp + yp * width];
00409 while (queue.isEmpty() == false) {
00410 Point p = queue.removeLast();
00411 int x = p.x;
00412 int y = p.y;
00413 int index = x + y * width;
00414 if (x + 1 < width) {
00415 if (input[index + 1] == 255)
00416 whiteCount++;
00417 else if (input[index + 1] == 0)
00418 borderCount++;
00419 if (mark[index + 1] == 0 && input[index + 1] == col) {
00420 mark[index + 1] = 1;
00421 queue.addFirst(new Point(x + 1, y));
00422 }
00423 }
00424 if (y + 1 < height) {
00425 if (input[index + width] == 255)
00426 whiteCount++;
00427 else if (input[index + width] == 0)
00428 borderCount++;
00429 if (mark[index + width] == 0 && input[index + width] == col) {
00430 mark[index + width] = 1;
00431 queue.addFirst(new Point(x, y + 1));
00432 }
00433 }
00434 if (y - 1 >= 0) {
00435 if (input[index - width] == 255)
00436 whiteCount++;
00437 else if (input[index - width] == 0)
00438 borderCount++;
00439 if (mark[index - width] == 0 && input[index - width] == col) {
00440 mark[index - width] = 1;
00441 queue.addFirst(new Point(x, y - 1));
00442 }
00443 }
00444 if (x - 1 >= 0) {
00445 if (input[index - 1] == 255)
00446 whiteCount++;
00447 else if (input[index - 1] == 0)
00448 borderCount++;
00449 if (mark[index - 1] == 0 && input[index - 1] == col) {
00450 mark[index - 1] = 1;
00451 queue.addFirst(new Point(x - 1, y));
00452 }
00453 }
00454 }
00455 borderCount += whiteCount;
00456 return whiteCount / borderCount;
00457 }
00458
00466 public void findCandidates(int slice) {
00467 int width = control.getStack().getWidth();
00468 int height = control.getStack().getHeight();
00469
00470 short[] pixels = control.getSlice(slice);
00471 short[] copy = pixels.clone();
00472 for (int y = 0; y < height; y++) {
00473 for (int x = 0; x < width; x++) {
00474 int index = x + y * width;
00475 if (copy[index] == 0) {
00476 short[] mask = new short[pixels.length];
00477 short color = (short) 0xf0;
00478 int lumenArea = ImageFunctions.floodFill2D(x, y, width,
00479 height, copy, mask, color);
00480 for (int i = 0; i < pixels.length; i++)
00481 if (mask[i] != 0)
00482 copy[i] = (short) 1;
00483
00484
00485 boolean okLumen = true;
00486
00487 okLumen = okLumen && lumenArea < 8000 && lumenArea > 500;
00488
00489 RegionMask2D maskreg = new RegionMask2D(mask, width, height);
00490
00491
00492 if (okLumen) {
00493 double circularity = (new CircularityDescriptor())
00494 .measure(mask, width, height);
00495 okLumen = okLumen && circularity < 3.8
00496 && circularity > 0.7;
00497 }
00498
00499 if (okLumen) {
00500 double curvature = (new AvgYoungCurvature()).measure(
00501 mask, width, height);
00502 okLumen = okLumen && curvature > 0 && curvature < 1.6;
00503 }
00504 if (okLumen) {
00505 double bending_energy = (new YoungBendingEnergy())
00506 .measure(mask, width, height);
00507 okLumen = okLumen && bending_energy > 0
00508 && bending_energy < 8.0;
00509 }
00510 if (okLumen) {
00511 double convexity = (Double) (new ConvexityDescriptor())
00512 .measure(maskreg);
00513 okLumen = okLumen && convexity > 0.8 && convexity < 1.0;
00514 }
00515 if (okLumen) {
00516 double solidity = (new SolidityDescriptor()).measure(
00517 mask, width, height);
00518 okLumen = okLumen && solidity < 1.3 && solidity > 0.7;
00519 }
00520
00521 if (okLumen) {
00522 double eccentricity = (new EccentricityDescriptor())
00523 .measure(mask, width, height);
00524 okLumen = okLumen && eccentricity < 15
00525 && eccentricity > 1;
00526 }
00527
00528
00529
00530
00531 if (!okLumen && lumenArea < 200 && lumenArea > 80) {
00532 double eccentricity = (new EccentricityDescriptor())
00533 .measure(mask, width, height);
00534 okLumen = eccentricity > 15;
00535 }
00536
00537 if (okLumen) {
00538 int internalRegionCount = (Integer) (new InternalRegionDescriptor())
00539 .measure(maskreg);
00540 okLumen = okLumen && internalRegionCount < 2;
00541 }
00542
00543 if (okLumen) {
00544 okLumen = okLumen
00545 && !ImageFunctions.touchBorder(mask, width,
00546 height);
00547 }
00548
00549
00550 if (okLumen) {
00551 LumenCandidate newSeed = new LumenCandidate(x, y,
00552 slice, (lumenArea));
00553 seeds.add(newSeed);
00554 for (int i = 0; i < pixels.length; i++)
00555 if (mask[i] != 0)
00556 copy[i] = 0xcc;
00557 }
00558 }
00559 }
00560 }
00561 control.showImage(copy);
00562 }
00563
00564 public boolean isReserved(short val) {
00565 if (val == 255 || val == control.INVALID)
00566 return true;
00567 return false;
00568 }
00569
00570 @Override
00571 public void mouseClicked(Point3D p) {
00572 if (seedButton.isSelected())
00573 selectSeed(new Point(p.x, p.y));
00574 }
00575
00576 public boolean removeCrack(int slice, short[] lumen) {
00577 int width = control.getStack().getWidth();
00578 int height = control.getStack().getHeight();
00579 int depth = control.getStack().getDepth();
00580 short[] data = control.getSlice(slice);
00581 short[] invdata = ImageFunctions.invertMask(data, false);
00582 ImageFunctions.invertMask(lumen, false);
00583 Rectangle bounds = ImageFunctions.getBounds(lumen, width, height, 12);
00584
00585
00586 if (bounds == null || bounds.x < 0 || bounds.y < 0
00587 || bounds.width > width || bounds.height > height) {
00588 seedStillValid = false;
00589 slice = depth;
00590 return true;
00591 }
00592
00593 if (map == null || map.length != data.length) {
00594 map = new short[data.length];
00595 map2 = new short[data.length];
00596 }
00597
00598
00599 for (int i = 0; i < map.length; i++) {
00600 if (lumen[i] != 0)
00601 map[i] = 1;
00602 else
00603 map[i] = 0;
00604 }
00605 map = ImageFunctions.dilate(lumen, map, width, height, bounds, (short) 2);
00606 int maxDilations = control.getPreferences().getInt("maxDilations", 12);
00607 for (int i = 3; i < maxDilations; i++) {
00608 System.arraycopy(map, 0, map2, 0, map.length);
00609 map = ImageFunctions.dilateMasked(map2, map, invdata, width, height, bounds, (short) i);
00610 }
00611
00612 map = ImageFunctions.mask(map, invdata, true);
00613
00614
00615 BackTrack.backTrack(map, 9, width, height, bounds);
00616
00617
00618 for (int i = 0; i < map.length; i++) {
00619 lumen[i] = map[i];
00620 if (lumen[i] != 0)
00621 data[i] = currentId;
00622 }
00623 return false;
00624 }
00625
00632 @SuppressWarnings("unchecked")
00633 public void removeStuff(int slice, short[] lumen) {
00634 int width = control.getStack().getWidth();
00635 int height = control.getStack().getHeight();
00636
00637 int regId = 1;
00638 short[] mark = new short[width * height];
00639 short[] slicedata = control.getSlice(slice);
00640 ArrayList<LumenCandidate> liste = new ArrayList<LumenCandidate>();
00641 for (int x = 0; x < width; x++) {
00642 for (int y = 0; y < height; y++) {
00643 if (mark[x + y * width] == 0 && lumen[x + y * width] != 0) {
00644 regId++;
00645 int size = ImageFunctions.floodFill2D(x, y, width, height, lumen, mark, (short) regId);
00646 LumenCandidate thisCandidate = new LumenCandidate(x, y, 0,
00647 size);
00648 float borderRatio = checkReg(x, y, slicedata);
00649 if (borderRatio < 0.7) {
00650
00651 log("Removed isolated region");
00652 ImageFunctions.floodFill2D(x, y, width, height, slicedata, (short) 0);
00653 ImageFunctions.floodFill2D(x, y, width, height, lumen, (short) 0);
00654 } else {
00655 liste.add(thisCandidate);
00656 }
00657
00658 }
00659 }
00660 }
00661
00662 int numberOfRegionsToKeep = 2;
00663
00664 if (liste.size() > 1) {
00665 Collections.sort(liste);
00666 boolean useMR_rules = true;
00667 if (useMR_rules) {
00668 LumenCandidate A = liste.get(0);
00669 LumenCandidate B = liste.get(1);
00670
00671 if (A.getSize() > 5 * B.getSize()) {
00672 numberOfRegionsToKeep--;
00673 }
00674 }
00675 for (int i = numberOfRegionsToKeep; i < liste.size(); i++) {
00676 LumenCandidate l = liste.remove(i);
00677 ImageFunctions.floodFill2D(l.x, l.y, width, height, slicedata, (short) 0);
00678 ImageFunctions.floodFill2D(l.x, l.y, width, height, lumen, (short) 0);
00679 }
00680 }
00681 }
00682
00683 public void selectSeed(Point e) {
00684 int width = control.getStack().getWidth();
00685 int height = control.getStack().getHeight();
00686
00687 short[] data = control.getCurrentPixels();
00688 short[] filledRegion = new short[data.length];
00689 if (data[(int) e.getX() + (int) e.getY() * width] == 255)
00690 return;
00691 int count = ImageFunctions.floodFill2D(data, filledRegion, (int) e
00692 .getX(), (int) e.getY(), width, height, (short) 200);
00693
00694 Point p = e.getLocation();
00695
00696 short[] displayedImage = data.clone();
00697 if (p != null) {
00698
00699 for (int i = 0; i < data.length; i++) {
00700 if (filledRegion[i] != 0) {
00701 displayedImage[i] = filledRegion[i];
00702 }
00703 }
00704 control.showImage(displayedImage);
00705
00706 if (count > 10000) {
00707 int ans = JOptionPane
00708 .showConfirmDialog(
00709 null,
00710 "This regions has "
00711 + count
00712 + " pixels \nAre you sure you want to add this region?",
00713 "Confirm seed", JOptionPane.YES_NO_OPTION);
00714 if (ans == JOptionPane.NO_OPTION) {
00715 control.updateImage();
00716 return;
00717 }
00718
00719 }
00720 seeds.add(new LumenCandidate(p.x, p.y, control.currentSlice(),
00721 count));
00722 }
00723 }
00724
00725 private void checkMerges(Point[][] skel) {
00726 int width = control.getStack().getWidth();
00727 int height = control.getStack().getHeight();
00728 int depth = control.getStack().getDepth();
00729 int[] mergeList = new int[control.MAX_FIBERS];
00730 for (int z = 1; z < depth - 1; z++) {
00731 short[] slice = control.getSlice(z);
00732 short[] next = control.getSlice(z + 1);
00733 short[] prev = control.getSlice(z - 1);
00734 for (int x = 1; x < width - 1; x++) {
00735 for (int y = 1; y < height - 1; y++) {
00736 int index = x + y * width;
00737 if (slice[index] == currentId) {
00738
00739 mergeList[next[index]] = 1;
00740 mergeList[prev[index]] = 1;
00741 }
00742 }
00743 }
00744 }
00745 for (short color = 1; color < control.MAX_FIBERS; color++) {
00746 if (isReserved(color))
00747 continue;
00748 if (mergeList[color] != 0 && color != currentId) {
00749 double avgDist = 0;
00750 double count = 0;
00751 for (int i = 0; i < depth; i++) {
00752 if (skel[currentId][i] != null && skel[color][i] != null) {
00753 double a = skel[currentId][i].x - skel[color][i].x;
00754 double b = skel[currentId][i].y - skel[color][i].y;
00755 double dist = Math.sqrt(a * a + b * b);
00756 avgDist += dist;
00757 count++;
00758 }
00759 }
00760 if (count > 0)
00761 avgDist /= count;
00762 System.out.println("Overlaps " + count);
00763 System.out.println("Avgdist " + avgDist);
00764
00765
00766 if (avgDist < 50 || count == 0) {
00767 System.out.println("Merging " + color + " with "
00768 + (currentId));
00769 control.releaseFiberId(color);
00770 for (int z = 0; z < depth; z++) {
00771 short[] slice = control.getSlice(z);
00772 for (int x = 1; x < width - 1; x++) {
00773 for (int y = 1; y < height - 1; y++) {
00774 int index = x + y * width;
00775 if (slice[index] == color) {
00776 slice[index] = currentId;
00777 }
00778 }
00779 }
00780 skel[currentId][z] = ImageFunctions.getAveragePoint(
00781 slice, width, height, currentId);
00782 skel[color][z] = null;
00783 }
00784 } else {
00785 System.out.println("Rejected merge!");
00786 }
00787 }
00788 }
00789 }
00790
00791 @Override
00792 public void reset() {
00793 }
00794
00795 @Override
00796 public void stop() {
00797 }
00798
00799
00800
00801 private final class FindAllCandidatesAction implements Runnable {
00802 public void run() {
00803 int depth = control.getStack().getDepth();
00804 String s = JOptionPane.showInputDialog("How many slices to skip?",
00805 "10");
00806 try {
00807 final int num = Integer.parseInt(s);
00808 for (int z = 0; z < depth; z += num) {
00809 control.setProgress(z);
00810 findCandidates(z);
00811 }
00812 } catch (NumberFormatException ee) {
00813 }
00814 }
00815 }
00816
00817 private final class FindCandidatesAction implements Runnable {
00818 public void run() {
00819 findCandidates(control.currentSlice());
00820 }
00821 }
00822
00823 private final class AutosegmentAction implements Runnable {
00824 public void run() {
00825 automerge = (1 == control.getPreferences().getInt("autoMerge", 0));
00826 autoSegment();
00827 }
00828 }
00829
00830 }