1 package com.github.celldynamics.quimp;
2
3 import java.awt.BorderLayout;
4 import java.awt.Button;
5 import java.awt.Checkbox;
6 import java.awt.CheckboxMenuItem;
7 import java.awt.Choice;
8 import java.awt.Color;
9 import java.awt.Container;
10 import java.awt.Dimension;
11 import java.awt.FlowLayout;
12 import java.awt.Graphics;
13 import java.awt.GridBagConstraints;
14 import java.awt.GridBagLayout;
15 import java.awt.GridLayout;
16 import java.awt.Label;
17 import java.awt.Menu;
18 import java.awt.MenuBar;
19 import java.awt.MenuItem;
20 import java.awt.Panel;
21 import java.awt.Rectangle;
22 import java.awt.TextArea;
23 import java.awt.event.ActionEvent;
24 import java.awt.event.ActionListener;
25 import java.awt.event.ItemEvent;
26 import java.awt.event.ItemListener;
27 import java.awt.event.MouseAdapter;
28 import java.awt.event.MouseEvent;
29 import java.awt.event.WindowAdapter;
30 import java.awt.event.WindowEvent;
31 import java.io.File;
32 import java.io.FileNotFoundException;
33 import java.io.IOException;
34 import java.net.URI;
35 import java.nio.file.Path;
36 import java.nio.file.Paths;
37 import java.util.ArrayList;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.concurrent.ExecutionException;
41 import java.util.stream.Collectors;
42 import java.util.stream.IntStream;
43
44 import javax.swing.BoxLayout;
45 import javax.swing.JOptionPane;
46 import javax.swing.JScrollPane;
47 import javax.swing.JSpinner;
48 import javax.swing.SpinnerNumberModel;
49 import javax.swing.SwingWorker;
50 import javax.swing.event.ChangeEvent;
51 import javax.swing.event.ChangeListener;
52
53 import org.scijava.vecmath.Point2d;
54 import org.scijava.vecmath.Vector2d;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
57
58 import com.github.celldynamics.quimp.BOAState.BOAp;
59 import com.github.celldynamics.quimp.BOAState.SegParam;
60 import com.github.celldynamics.quimp.QuimpException.MessageSinkTypes;
61 import com.github.celldynamics.quimp.SnakePluginList.Plugin;
62 import com.github.celldynamics.quimp.filesystem.DataContainer;
63 import com.github.celldynamics.quimp.filesystem.DataContainerInstanceCreator;
64 import com.github.celldynamics.quimp.filesystem.FileDialogEx;
65 import com.github.celldynamics.quimp.filesystem.FileExtensions;
66 import com.github.celldynamics.quimp.filesystem.StatsCollection;
67 import com.github.celldynamics.quimp.filesystem.versions.Converter170202;
68 import com.github.celldynamics.quimp.geom.ExtendedVector2d;
69 import com.github.celldynamics.quimp.plugin.IQuimpCorePlugin;
70 import com.github.celldynamics.quimp.plugin.IQuimpPluginAttachImage;
71 import com.github.celldynamics.quimp.plugin.QuimpPluginException;
72 import com.github.celldynamics.quimp.plugin.binaryseg.BinarySegmentation_;
73 import com.github.celldynamics.quimp.plugin.engine.PluginFactory;
74 import com.github.celldynamics.quimp.plugin.engine.PluginProperties;
75 import com.github.celldynamics.quimp.plugin.snakes.IQuimpBOAPoint2dFilter;
76 import com.github.celldynamics.quimp.plugin.snakes.IQuimpBOASnakeFilter;
77 import com.github.celldynamics.quimp.plugin.utils.QuimpDataConverter;
78 import com.github.celldynamics.quimp.registration.Registration;
79 import com.github.celldynamics.quimp.utils.QuimPArrayUtils;
80 import com.github.celldynamics.quimp.utils.QuimpToolsCollection;
81 import com.github.celldynamics.quimp.utils.graphics.GraphicsElements;
82 import com.google.gson.JsonSyntaxException;
83
84 import ij.IJ;
85 import ij.ImagePlus;
86 import ij.ImageStack;
87 import ij.WindowManager;
88 import ij.gui.GenericDialog;
89 import ij.gui.ImageCanvas;
90 import ij.gui.NewImage;
91 import ij.gui.Overlay;
92 import ij.gui.PointRoi;
93 import ij.gui.PolygonRoi;
94 import ij.gui.Roi;
95 import ij.gui.StackWindow;
96 import ij.gui.TextRoi;
97 import ij.gui.Toolbar;
98 import ij.gui.YesNoCancelDialog;
99 import ij.io.OpenDialog;
100 import ij.io.SaveDialog;
101 import ij.plugin.PlugIn;
102 import ij.plugin.frame.RoiManager;
103 import ij.process.Blitter;
104 import ij.process.FloatPolygon;
105 import ij.process.ImageConverter;
106 import ij.process.ImageProcessor;
107 import ij.process.StackConverter;
108
109
110
111
112
113
114
115
116 public class BOA_ implements PlugIn {
117 private static final Logger LOGGER = LoggerFactory.getLogger(BOA_.class.getName());
118
119
120
121
122
123
124
125
126
127 boolean isSegRunning = false;
128
129
130
131
132
133
134 private boolean isSegBreakHit = false;
135
136
137
138
139 CustomCanvas canvas;
140
141
142
143
144 CustomStackWindow window;
145
146
147
148
149 static TextArea logArea;
150
151
152
153
154 static boolean isBoaRunning = false;
155
156
157
158
159 ImageGroup imageGroup;
160 private Constrictor constrictor;
161 private PluginFactory pluginFactory;
162
163
164
165
166
167
168 private String lastTool;
169
170
171
172
173 public static final String NONE = "NONE";
174
175
176
177 private static final String fullZoom = "Frame zoom";
178
179
180
181
182
183
184 public static ViewUpdater viewUpdater;
185
186
187
188
189
190
191 public static QuimpVersion quimpInfo;
192 private static int logCount;
193
194
195
196 public static final int NUM_SNAKE_PLUGINS = 3;
197
198
199
200
201
202
203
204 public static BOAStateBOAState.html#BOAState">BOAState qState = new BOAState(null);
205
206
207
208
209
210
211
212 public BOA_() {
213 LOGGER.trace("Starting BOA from default constructor");
214 qState = new BOAState(null);
215 logCount = 1;
216 }
217
218
219
220
221
222
223
224 @Override
225 public void run(final String arg) {
226 if (IJ.versionLessThan("1.45")) {
227 return;
228 }
229
230 if (BOA_.isBoaRunning) {
231 BOA_.isBoaRunning = false;
232 IJ.error("Warning: Only have one instance of BOA running at a time");
233 return;
234 }
235
236 viewUpdater = new ViewUpdater(this);
237
238 quimpInfo = QuimP.TOOL_VERSION;
239
240
241
242
243 try {
244 String path;
245 if (QuimP.PLUGIN_DIR == null) {
246 path = IJ.getDirectory("plugins");
247 } else {
248 path = QuimP.PLUGIN_DIR;
249 }
250 if (path == null) {
251 IJ.log("BOA: Plugin directory not found, use provided with arg: " + arg);
252 LOGGER.debug("BOA: Plugin directory not found, use provided with arg: " + arg);
253 path = arg;
254 }
255
256 pluginFactory = PluginFactoryFactory.getPluginFactory(path);
257 } catch (Exception e) {
258 IJ.error("Error during initialisation of plugin engine", e.getMessage());
259 LOGGER.debug(e.getMessage(), e);
260 return;
261 }
262
263 ImagePlus ip = WindowManager.getCurrentImage();
264
265 qState = new BOAState(ip, pluginFactory, viewUpdater);
266 if (IJ.getVersion().compareTo("1.46") < 0) {
267 qState.boap.useSubPixel = false;
268 } else {
269 qState.boap.useSubPixel = true;
270 }
271
272 lastTool = IJ.getToolName();
273
274 if (ip == null || ip.getNChannels() > 1) {
275 IJ.error("Single channel image required");
276 return;
277 } else if (ip.getStackSize() == 1) {
278 qState.boap.singleImage = true;
279 } else {
280 qState.boap.singleImage = false;
281 }
282
283 if (ip.getType() != ImagePlus.GRAY8) {
284 YesNoCancelDialog ync =
285 new YesNoCancelDialog(window, "Image bit depth", "8-bit Image required. Convert?");
286 if (ync.yesPressed()) {
287 if (qState.boap.singleImage) {
288 new ImageConverter(ip).convertToGray8();
289 } else {
290 new StackConverter(ip).convertToGray8();
291 }
292 } else {
293 return;
294 }
295 }
296 BOA_.isBoaRunning = true;
297 setup(ip);
298
299 if (qState.boap.useSubPixel == false) {
300 BOA_.log("Upgrade to ImageJ 1.46, or higher," + "\nto get sub-pixel editing.");
301 }
302 if (IJ.getVersion().compareTo("1.49a") > 0) {
303 BOA_.log("(ImageJ " + IJ.getVersion() + " untested)");
304 }
305
306 try {
307 if (!qState.nest.isVacant()) {
308 runBoa(1, 1);
309 }
310 } catch (BoaException be) {
311 be.setMessageSinkType(MessageSinkTypes.GUI);
312 be.handleException(IJ.getInstance(), "Inital preview failed");
313 }
314 }
315
316
317
318
319
320
321
322
323 void setup(final ImagePlus ip) {
324 if (qState.boap.paramsExist == null) {
325 qState.segParam.setDefaults();
326 }
327 qState.boap.setup(ip);
328
329 qState.nest = new Nest();
330 imageGroup = new ImageGroup(ip, qState.nest);
331 qState.boap.frame = 1;
332
333 canvas = new CustomCanvas(imageGroup.getOrgIpl());
334 window = new CustomStackWindow(imageGroup.getOrgIpl(), canvas);
335 window.buildWindow();
336 window.setTitle(window.getTitle() + " :QuimP: " + quimpInfo.getVersion());
337
338 new Registration(window, "QuimP Registration");
339
340 if (qState.boap.isScaleAdjusted()) {
341 BOA_.log("WARNING Scale was zero - set to 1");
342 }
343 if (qState.boap.isfIAdjusted()) {
344 BOA_.log("WARNING Frame interval was zero - set to 1");
345 }
346
347
348 window.addWindowListener(new CustomWindowAdapter());
349
350 setScales();
351 updateImageScale();
352 window.setScalesText();
353
354
355 new RoiManager();
356 RoiManager rm = RoiManager.getInstance();
357 if (rm.getRoisAsArray().length != 0) {
358 qState.nest.addHandlers(rm.getRoisAsArray(), 1);
359 } else {
360 BOA_.log("No cells from ROI manager");
361 if (ip.getRoi() != null) {
362 qState.nest.addHandler(ip.getRoi(), 1);
363 } else {
364 BOA_.log("No cells from selection");
365 }
366 }
367 rm.close();
368 ip.killRoi();
369
370 constrictor = new Constrictor();
371 }
372
373
374
375
376
377 void about() {
378 AboutDialog/AboutDialog.html#AboutDialog">AboutDialog ad = new AboutDialog(window);
379 ad.appendLine(QuimpToolsCollection.getFormattedQuimPversion(quimpInfo));
380 ad.appendLine("List of found plugins:");
381 ad.appendDistance();
382 Map<String, PluginProperties> mp = pluginFactory.getRegisterdPlugins();
383
384 for (Map.Entry<String, PluginProperties> entry : mp.entrySet()) {
385 ad.appendLine("Plugin name: " + entry.getKey());
386 ad.appendLine(" Plugin type: " + entry.getValue().getType());
387 ad.appendLine(" Plugin path: " + entry.getValue().getFile().toString());
388 ad.appendLine(" Plugin vers: " + entry.getValue().getVersion());
389
390 ad.appendLine(" About (returned by plugin):");
391 IQuimpCorePlugin tmpinst = pluginFactory.getInstance(entry.getKey());
392 if (tmpinst != null) {
393 String about = tmpinst.about();
394 if (about != null) {
395 ad.appendLine(about);
396 } else {
397 ad.appendLine("Plugin does not provide about note");
398 }
399 }
400 ad.appendDistance();
401 }
402 ad.setVisible(true);
403 }
404
405
406
407
408
409
410 static void log(final String s) {
411 if (logArea == null) {
412 LOGGER.debug("[" + logCount++ + "] " + s + '\n');
413 } else {
414 logArea.append("[" + logCount++ + "] " + s + '\n');
415 }
416 }
417
418
419
420
421
422
423
424 void recalculatePlugins() {
425 LOGGER.trace("BOA: recalculatePlugins called");
426 SnakeHandler sh;
427 if (qState.nest.isVacant()) {
428 imageGroup.updateOverlay(qState.boap.frame);
429 return;
430 }
431 imageGroup.updateToFrame(qState.boap.frame);
432 try {
433 for (int s = 0; s < qState.nest.size(); s++) {
434 sh = qState.nest.getHandler(s);
435 if (sh.isSnakeHandlerFrozen()) {
436 LOGGER.debug("SnakeHandler " + sh.getID() + " is frozen");
437 continue;
438 }
439 if (qState.boap.frame < sh.getStartFrame()) {
440 continue;
441 }
442
443
444
445
446 Snake snake = sh.getBackupSnake(qState.boap.frame);
447
448 if (snake == null || !snake.alive) {
449 continue;
450 }
451 try {
452 Snake out = iterateOverSnakePlugins(snake);
453 sh.storeThisSnake(out, qState.boap.frame);
454 } catch (QuimpPluginException qpe) {
455
456 qpe.setMessageSinkType(MessageSinkTypes.IJERROR);
457 BOA_.log(qpe.handleException(null, "Error in filter module"));
458 sh.storeLiveSnake(qState.boap.frame);
459 } catch (BoaException be) {
460 be.setMessageSinkType(MessageSinkTypes.IJERROR);
461 BOA_.log(be.handleException(null, "Defective snake returned"));
462 }
463 }
464 } catch (Exception e) {
465 IJ.error("Plugin error", "Output snake may be defective. Reason: " + e.getMessage());
466 LOGGER.debug(e.getMessage(), e);
467 } finally {
468
469 qState.store(qState.boap.frame);
470 }
471 imageGroup.updateOverlay(qState.boap.frame);
472 }
473
474
475
476
477
478
479
480
481
482
483
484
485
486 class CustomWindowAdapter extends WindowAdapter {
487
488
489
490
491
492
493 @Override
494
495
496 public void windowClosed(final WindowEvent arg0) {
497 LOGGER.trace("CLOSED");
498 BOA_.isBoaRunning = false;
499 qState.snakePluginList.clear();
500 if (qState.binarySegmentationPlugin != null) {
501 qState.binarySegmentationPlugin.showUi(false);
502 }
503 canvas = null;
504 imageGroup = null;
505 window = null;
506
507 qState = null;
508 viewUpdater = null;
509 }
510
511
512
513
514
515
516 @Override
517 public void windowClosing(final WindowEvent arg0) {
518 LOGGER.trace("CLOSING");
519 }
520
521
522
523
524
525
526 @Override
527 public void windowActivated(final WindowEvent e) {
528 LOGGER.trace("ACTIVATED");
529
530
531
532
533 window.setMenuBar(window.menuBar);
534 }
535 }
536
537
538
539
540
541
542
543 @SuppressWarnings("serial")
544 class CustomCanvas extends ImageCanvas {
545
546
547
548
549
550
551 CustomCanvas(final ImagePlus imp) {
552 super(imp);
553 }
554
555
556
557
558 @Override
559 public void paint(final Graphics g) {
560 super.paint(g);
561
562
563
564
565
566
567 }
568
569
570
571
572
573
574
575
576
577 @Override
578 public void mousePressed(final MouseEvent e) {
579 super.mousePressed(e);
580 if (qState.boap.doDelete) {
581
582
583 deleteCell(offScreenX(e.getX()), offScreenY(e.getY()), qState.boap.frame);
584 IJ.setTool(lastTool);
585 }
586 if (qState.boap.doFreeze) {
587 freezeCell(offScreenX(e.getX()), offScreenY(e.getY()), qState.boap.frame);
588 }
589 if (qState.boap.doDeleteSeg) {
590
591
592 deleteSegmentation(offScreenX(e.getX()), offScreenY(e.getY()), qState.boap.frame);
593 }
594 if (qState.boap.editMode && qState.boap.editingID == -1) {
595
596
597 editSeg(offScreenX(e.getX()), offScreenY(e.getY()), qState.boap.frame);
598 }
599 }
600 }
601
602
603
604
605
606
607
608
609
610
611 @SuppressWarnings("serial")
612 class CustomStackWindow extends StackWindow
613 implements ActionListener, ItemListener, ChangeListener {
614
615
616
617
618 static final int DEFAULT_SPINNER_SIZE = 5;
619
620
621
622
623 static final int SNAKE_PLUGIN_NUM = 3;
624
625
626
627
628
629
630 private SwingWorker<Boolean, Object> sww = null;
631
632
633
634
635
636 private boolean supressStateChangeBOArun = false;
637 private Button bnSeg;
638 private Button bnFinish;
639 private Button bnLoad;
640 private Button bnEdit;
641 private Button bnQuit;
642 private Button bnDefault;
643 private Button bnScale;
644 private Button bnCopyLast;
645 private Button bnSave;
646
647 private Button bnAdd;
648 private Button bnDel;
649 private Button bnDelSeg;
650 private Button bnFreezeCell;
651
652 private Checkbox cbPrevSnake;
653 private Checkbox cbExpSnake;
654 private Checkbox cbContractingDirection;
655 private Checkbox cbPath;
656 private Choice chZoom;
657
658
659
660
661 JScrollPane logPanel;
662
663 private Label fpsLabel;
664 private Label pixelLabel;
665 private Label frameLabel;
666
667 private JSpinner dsNodeRes;
668 private JSpinner dsVelCrit;
669 private JSpinner dsFImage;
670 private JSpinner dsFCentral;
671 private JSpinner dsFContract;
672 private JSpinner dsFinalShrink;
673
674 private JSpinner isMaxIterations;
675 private JSpinner isBlowup;
676 private JSpinner isSampletan;
677 private JSpinner isSamplenorm;
678 private Choice chFirstPluginName;
679 private Choice chSecondPluginName;
680 private Choice chThirdPluginName;
681 private Button bnFirstPluginGUI;
682 private Button bnSecondPluginGUI;
683 private Button bnThirdPluginGUI;
684 private Checkbox cbFirstPluginActiv;
685 private Checkbox cbSecondPluginActiv;
686 private Checkbox cbThirdPluginActiv;
687 private Button bnPopulatePlugin;
688 private Button bnCopyLastPlugin;
689
690 private MenuBar menuBar;
691 private MenuItem menuAbout;
692 private MenuItem menuOpenHelp;
693 private MenuItem menuSaveConfig;
694 private MenuItem menuLoadConfig;
695 private MenuItem menuShowHistory;
696 private MenuItem menuLoad;
697 private MenuItem menuSave;
698 private MenuItem menuSaveAs;
699 private MenuItem menuDeletePlugin;
700 private MenuItem menuApplyPlugin;
701 private MenuItem menuSegmentationRun;
702 private MenuItem menuSegmentationReset;
703 private CheckboxMenuItem cbMenuPlotOriginalSnakes;
704 private CheckboxMenuItem cbMenuPlotHead;
705
706 private MenuItem menuPopulatePlugin;
707 private CheckboxMenuItem cbMenuZoomFreeze;
708
709
710
711
712
713
714
715 CustomStackWindow(final ImagePlus imp, final ImageCanvas ic) {
716 super(imp, ic);
717
718 }
719
720
721
722
723
724
725 public void enableUi(boolean state) {
726 bnSeg.setEnabled(state);
727 bnFinish.setEnabled(state);
728 bnLoad.setEnabled(state);
729 bnEdit.setEnabled(state);
730 bnQuit.setEnabled(state);
731 bnDefault.setEnabled(state);
732 bnScale.setEnabled(state);
733 bnCopyLast.setEnabled(state);
734 bnSave.setEnabled(state);
735
736 bnAdd.setEnabled(state);
737 bnDel.setEnabled(state);
738 bnDelSeg.setEnabled(state);
739 bnFreezeCell.setEnabled(state);
740
741 cbPrevSnake.setEnabled(state);
742 cbExpSnake.setEnabled(false);
743 cbContractingDirection.setEnabled(state);
744 cbPath.setEnabled(state);
745 chZoom.setEnabled(state);
746
747 dsNodeRes.setEnabled(state);
748 dsVelCrit.setEnabled(state);
749 dsFImage.setEnabled(state);
750 dsFCentral.setEnabled(state);
751 dsFContract.setEnabled(state);
752 dsFinalShrink.setEnabled(state);
753
754 isMaxIterations.setEnabled(state);
755 isBlowup.setEnabled(state);
756 isSampletan.setEnabled(state);
757 isSamplenorm.setEnabled(state);
758 chFirstPluginName.setEnabled(state);
759 chSecondPluginName.setEnabled(state);
760 chThirdPluginName.setEnabled(state);
761
762 if (chFirstPluginName.getSelectedItem() == NONE) {
763 bnFirstPluginGUI.setEnabled(false);
764 cbFirstPluginActiv.setEnabled(false);
765 } else {
766 bnFirstPluginGUI.setEnabled(state);
767 cbFirstPluginActiv.setEnabled(state);
768 }
769 if (chSecondPluginName.getSelectedItem() == NONE) {
770 bnSecondPluginGUI.setEnabled(false);
771 cbSecondPluginActiv.setEnabled(false);
772 } else {
773 bnSecondPluginGUI.setEnabled(state);
774 cbSecondPluginActiv.setEnabled(state);
775 }
776 if (chThirdPluginName.getSelectedItem() == NONE) {
777 bnThirdPluginGUI.setEnabled(false);
778 cbThirdPluginActiv.setEnabled(false);
779 } else {
780 bnThirdPluginGUI.setEnabled(state);
781 cbThirdPluginActiv.setEnabled(state);
782 }
783 bnPopulatePlugin.setEnabled(state);
784 bnCopyLastPlugin.setEnabled(state);
785 for (int i = 0; i < menuBar.getMenuCount(); i++) {
786 menuBar.getMenu(i).setEnabled(state);
787 }
788 }
789
790
791
792
793
794
795 public void enableUiInterruptile(boolean state) {
796 enableUi(state);
797 bnSeg.setEnabled(true);
798 }
799
800
801
802
803
804
805
806
807
808
809 public void buildWindow() {
810
811 setLayout(new BorderLayout(10, 3));
812
813 if (!qState.boap.singleImage) {
814 remove(sliceSelector);
815 }
816 if (!qState.boap.singleImage) {
817 remove(this.getComponent(1));
818 }
819 Panel cp = buildControlPanel();
820 Panel sp = buildSetupPanel();
821 add(new Label(""), BorderLayout.NORTH);
822 add(cp, BorderLayout.WEST);
823 add(ic, BorderLayout.CENTER);
824 add(sp, BorderLayout.EAST);
825 add(new Label(""), BorderLayout.SOUTH);
826
827 LOGGER.debug("Menu: " + getMenuBar());
828 menuBar = buildMenu();
829 setMenuBar(menuBar);
830 pack();
831 updateWindowState();
832 }
833
834
835
836
837
838
839
840
841
842
843
844 final MenuBar buildMenu() {
845 Menu menuHelp;
846 Menu menuConfig;
847 Menu menuFile;
848 Menu menuPlugin;
849
850 menuBar = new MenuBar();
851
852 menuConfig = new Menu("Preferences");
853 menuHelp = new Menu("Help");
854 menuFile = new Menu("File");
855 menuPlugin = new Menu("Plugin");
856 Menu menuSegmentation;
857 menuSegmentation = new Menu("Segmentation");
858
859
860 menuBar.add(menuFile);
861 menuBar.add(menuConfig);
862 menuBar.add(menuPlugin);
863 menuBar.add(menuSegmentation);
864 menuBar.add(menuHelp);
865
866
867 menuLoad = new MenuItem("Load experiment");
868 menuLoad.addActionListener(this);
869 menuFile.add(menuLoad);
870 menuSave = new MenuItem("Save experiment");
871 menuSave.addActionListener(this);
872 menuFile.add(menuSave);
873 menuSaveAs = new MenuItem("Save experiment as..");
874 menuSaveAs.addActionListener(this);
875 menuFile.add(menuSaveAs);
876
877 menuFile.addSeparator();
878 menuLoadConfig = new MenuItem("Load plugin preferences");
879 menuLoadConfig.addActionListener(this);
880 menuFile.add(menuLoadConfig);
881 menuSaveConfig = new MenuItem("Save plugin preferences");
882 menuSaveConfig.addActionListener(this);
883 menuFile.add(menuSaveConfig);
884
885 menuOpenHelp = new MenuItem("Help Contents");
886 menuOpenHelp.addActionListener(this);
887 menuHelp.add(menuOpenHelp);
888 menuAbout = new MenuItem("About");
889 menuAbout.addActionListener(this);
890 menuHelp.add(menuAbout);
891
892 cbMenuPlotOriginalSnakes = new CheckboxMenuItem("Plot original");
893 cbMenuPlotOriginalSnakes.setState(qState.boap.isProcessedSnakePlotted);
894 cbMenuPlotOriginalSnakes.addItemListener(this);
895 menuConfig.add(cbMenuPlotOriginalSnakes);
896 cbMenuPlotHead = new CheckboxMenuItem("Plot head");
897 cbMenuPlotHead.setState(qState.boap.isHeadPlotted);
898 cbMenuPlotHead.addItemListener(this);
899 menuConfig.add(cbMenuPlotHead);
900 cbMenuZoomFreeze = new CheckboxMenuItem("Zoom freezes");
901 cbMenuZoomFreeze.setState(qState.boap.isZoomFreeze);
902 cbMenuZoomFreeze.addItemListener(this);
903 menuConfig.add(cbMenuZoomFreeze);
904
905 menuShowHistory = new MenuItem("Show history");
906 menuShowHistory.addActionListener(this);
907 menuConfig.add(menuShowHistory);
908
909 menuDeletePlugin = new MenuItem("Discard all");
910 menuDeletePlugin.addActionListener(this);
911 menuPlugin.add(menuDeletePlugin);
912 menuApplyPlugin = new MenuItem("Re-apply all");
913 menuApplyPlugin.addActionListener(this);
914 menuPlugin.add(menuApplyPlugin);
915 menuPopulatePlugin = new MenuItem("Populate to all frames");
916 menuPopulatePlugin.addActionListener(this);
917 menuPlugin.add(menuPopulatePlugin);
918
919 menuSegmentationRun = new MenuItem("Binary segmentation");
920 menuSegmentationRun.addActionListener(this);
921 menuSegmentationReset = new MenuItem("Clear all");
922 menuSegmentationReset.addActionListener(this);
923 menuSegmentation.add(menuSegmentationRun);
924 menuSegmentation.add(menuSegmentationReset);
925
926 return menuBar;
927 }
928
929
930
931
932
933
934 final Panel buildSetupPanel() {
935 Panel setupPanel = new Panel();
936 Panel northPanel = new Panel();
937 Panel southPanel = new Panel();
938 Panel centerPanel = new Panel();
939 Panel pluginPanelButtons = new Panel();
940
941 setupPanel.setLayout(new BorderLayout());
942 northPanel.setLayout(new GridLayout(4, 2));
943 southPanel.setLayout(new GridLayout(2, 2));
944 centerPanel.setLayout(new BoxLayout(centerPanel, BoxLayout.PAGE_AXIS));
945
946
947 pluginPanelButtons.setLayout(new GridLayout(1, 2));
948 bnPopulatePlugin = addButton("Populate fwd", pluginPanelButtons);
949 bnCopyLastPlugin = addButton("Copy prev", pluginPanelButtons);
950
951
952 GridBagLayout gridbag = new GridBagLayout();
953 GridBagConstraints c = new GridBagConstraints();
954 c.weightx = 0.5;
955 c.fill = GridBagConstraints.HORIZONTAL;
956 c.anchor = GridBagConstraints.LINE_START;
957 Panel pluginPanel = new Panel();
958 pluginPanel.setLayout(gridbag);
959
960 fpsLabel = new Label("F Interval: " + IJ.d2s(qState.boap.getImageFrameInterval(), 3) + " s");
961 northPanel.add(fpsLabel);
962 pixelLabel = new Label("Scale: " + IJ.d2s(qState.boap.getImageScale(), 6) + " \u00B5m");
963 northPanel.add(pixelLabel);
964
965 bnScale = addButton("Set Scale", northPanel);
966 bnDelSeg = addButton("Truncate Seg", northPanel);
967 bnAdd = addButton("Add cell", northPanel);
968 bnDel = addButton("Delete cell", northPanel);
969 bnFreezeCell = addButton("Freeze", northPanel);
970
971
972
973 ArrayList<String> pluginList =
974 qState.snakePluginList.getPluginNames(IQuimpCorePlugin.DOES_SNAKES);
975
976 pluginList.add(0, NONE);
977
978
979
980
981
982
983
984 chFirstPluginName = addComboBox(pluginList.toArray(new String[0]), pluginPanel);
985 c.gridx = 0;
986 c.gridy = 0;
987 pluginPanel.add(chFirstPluginName, c);
988 bnFirstPluginGUI = addButton("GUI", pluginPanel);
989 c.gridx = 1;
990 c.gridy = 0;
991 pluginPanel.add(bnFirstPluginGUI, c);
992 cbFirstPluginActiv = addCheckbox("A", pluginPanel, qState.snakePluginList.isActive(0));
993 c.gridx = 2;
994 c.gridy = 0;
995 pluginPanel.add(cbFirstPluginActiv, c);
996
997 chSecondPluginName = addComboBox(pluginList.toArray(new String[0]), pluginPanel);
998 c.gridx = 0;
999 c.gridy = 1;
1000 pluginPanel.add(chSecondPluginName, c);
1001 bnSecondPluginGUI = addButton("GUI", pluginPanel);
1002 c.gridx = 1;
1003 c.gridy = 1;
1004 pluginPanel.add(bnSecondPluginGUI, c);
1005 cbSecondPluginActiv = addCheckbox("A", pluginPanel, qState.snakePluginList.isActive(1));
1006 c.gridx = 2;
1007 c.gridy = 1;
1008 pluginPanel.add(cbSecondPluginActiv, c);
1009
1010 chThirdPluginName = addComboBox(pluginList.toArray(new String[0]), pluginPanel);
1011 c.gridx = 0;
1012 c.gridy = 2;
1013 pluginPanel.add(chThirdPluginName, c);
1014 bnThirdPluginGUI = addButton("GUI", pluginPanel);
1015 c.gridx = 1;
1016 c.gridy = 2;
1017 pluginPanel.add(bnThirdPluginGUI, c);
1018 cbThirdPluginActiv = addCheckbox("A", pluginPanel, qState.snakePluginList.isActive(2));
1019 c.gridx = 2;
1020 c.gridy = 2;
1021 pluginPanel.add(cbThirdPluginActiv, c);
1022
1023 c.gridx = 0;
1024 c.gridy = 3;
1025 c.gridwidth = 3;
1026 c.fill = GridBagConstraints.HORIZONTAL;
1027 pluginPanel.add(pluginPanelButtons, c);
1028
1029
1030 Panel tp = new Panel();
1031 tp.setLayout(new GridLayout(1, 1));
1032 logArea = new TextArea(15, 15);
1033 logArea.setEditable(false);
1034 tp.add(logArea);
1035 logPanel = new JScrollPane(tp);
1036
1037
1038
1039
1040 southPanel.add(new Label(""));
1041 southPanel.add(new Label(""));
1042 bnQuit = addButton("Quit", southPanel);
1043 bnFinish = addButton("Save & Quit", southPanel);
1044
1045
1046 centerPanel.add(new Label("Snake Plugins:"));
1047 centerPanel.add(pluginPanel);
1048 centerPanel.add(new Label("Logs:"));
1049 centerPanel.add(logPanel);
1050 setupPanel.add(northPanel, BorderLayout.PAGE_START);
1051 setupPanel.add(centerPanel, BorderLayout.CENTER);
1052 setupPanel.add(southPanel, BorderLayout.PAGE_END);
1053
1054 if (pluginList.isEmpty()) {
1055 BOA_.log("No plugins found");
1056 } else {
1057 BOA_.log("Found " + (pluginList.size() - 1) + " plugins (see About)");
1058 }
1059
1060 return setupPanel;
1061 }
1062
1063
1064
1065
1066
1067
1068 final Panel buildControlPanel() {
1069 Panel controlPanel = new Panel();
1070 Panel topPanel = new Panel();
1071 Panel paramPanel = new Panel();
1072 Panel bottomPanel = new Panel();
1073
1074 controlPanel.setLayout(new BorderLayout());
1075 topPanel.setLayout(new GridLayout(2, 2));
1076 paramPanel.setLayout(new GridLayout(15, 1));
1077 bottomPanel.setLayout(new GridLayout(1, 2));
1078
1079
1080 bnLoad = addButton("Load", topPanel);
1081 bnSave = addButton("Save", topPanel);
1082 bnCopyLast = addButton("Copy prev", topPanel);
1083 bnDefault = addButton("Default", topPanel);
1084
1085
1086
1087
1088 dsNodeRes = addDoubleSpinner("Node Spacing:", paramPanel, qState.segParam.getNodeRes(), 1.,
1089 20., 0.2, CustomStackWindow.DEFAULT_SPINNER_SIZE);
1090 isMaxIterations = addIntSpinner("Max Iterations:", paramPanel, qState.segParam.max_iterations,
1091 100, 10000, 100, CustomStackWindow.DEFAULT_SPINNER_SIZE);
1092 isBlowup = addIntSpinner("Blowup:", paramPanel, qState.segParam.blowup, -200, 200, 1,
1093 CustomStackWindow.DEFAULT_SPINNER_SIZE);
1094 dsVelCrit = addDoubleSpinner("Crit velocity:", paramPanel, qState.segParam.vel_crit, -2, 2.,
1095 0.001, CustomStackWindow.DEFAULT_SPINNER_SIZE);
1096 dsFImage = addDoubleSpinner("Image F:", paramPanel, qState.segParam.f_image, -10.0, 10.0,
1097 0.01, CustomStackWindow.DEFAULT_SPINNER_SIZE);
1098 dsFCentral = addDoubleSpinner("Central F:", paramPanel, qState.segParam.f_central, -1, 1,
1099 0.002, CustomStackWindow.DEFAULT_SPINNER_SIZE);
1100 dsFContract = addDoubleSpinner("Contract F:", paramPanel, qState.segParam.f_contract, -1, 1,
1101 0.001, CustomStackWindow.DEFAULT_SPINNER_SIZE);
1102 dsFinalShrink = addDoubleSpinner("Final Shrink:", paramPanel, qState.segParam.finalShrink,
1103 -100, 100, 0.5, CustomStackWindow.DEFAULT_SPINNER_SIZE);
1104 isSampletan = addIntSpinner("Sample tan:", paramPanel, qState.segParam.sample_tan, 1, 30, 1,
1105 CustomStackWindow.DEFAULT_SPINNER_SIZE);
1106 isSamplenorm = addIntSpinner("Sample norm:", paramPanel, qState.segParam.sample_norm, 1, 60,
1107 1, CustomStackWindow.DEFAULT_SPINNER_SIZE);
1108
1109 cbPrevSnake =
1110 addCheckbox("Use Previous Snake", paramPanel, qState.segParam.use_previous_snake);
1111 cbExpSnake = addCheckbox("Expanding Snake", paramPanel, qState.segParam.expandSnake);
1112 cbExpSnake.setEnabled(false);
1113 cbContractingDirection =
1114 addCheckbox("Contracing Snake", paramPanel, qState.segParam.contractingDirection);
1115
1116 Panel segEditPanel = new Panel();
1117 segEditPanel.setLayout(new GridLayout(1, 2));
1118 bnSeg = addButton("SEGMENT", segEditPanel);
1119 bnEdit = addButton("Edit", segEditPanel);
1120 paramPanel.add(segEditPanel);
1121
1122
1123 Panel sliderPanel = new Panel();
1124 sliderPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
1125
1126 if (!qState.boap.singleImage) {
1127 sliceSelector.setPreferredSize(new Dimension(165, 20));
1128 sliceSelector.addAdjustmentListener(this);
1129 sliderPanel.add(sliceSelector);
1130
1131 frameLabel = new Label(imageGroup.getOrgIpl().getSlice() + " ");
1132 sliderPanel.add(frameLabel);
1133 }
1134 paramPanel.add(sliderPanel);
1135
1136
1137
1138 cbPath = addCheckbox("Show paths", bottomPanel, qState.segParam.showPaths);
1139 chZoom = addComboBox(new String[] { fullZoom }, bottomPanel);
1140
1141 chZoom.addMouseListener(new MouseAdapter() {
1142 @Override
1143 public void mousePressed(MouseEvent e) {
1144 LOGGER.trace("EVENT:mousePressed");
1145 fillZoomChoice();
1146 }
1147 });
1148
1149
1150
1151 controlPanel.add(topPanel, BorderLayout.NORTH);
1152 controlPanel.add(paramPanel, BorderLayout.CENTER);
1153 controlPanel.add(bottomPanel, BorderLayout.SOUTH);
1154
1155 return controlPanel;
1156 }
1157
1158
1159
1160
1161
1162
1163
1164 private void fillZoomChoice() {
1165 String prev = chZoom.getSelectedItem();
1166 LOGGER.trace(prev);
1167 chZoom.removeAll();
1168 chZoom.add(fullZoom);
1169 List<Integer> frames = qState.nest.getSnakesforFrame(qState.boap.frame);
1170 for (Integer i : frames) {
1171 chZoom.add(i.toString());
1172 }
1173 if (chZoom.getItemCount() == 1) {
1174 chZoom.add("");
1175 chZoom.select("");
1176 } else {
1177 chZoom.select(prev);
1178 }
1179 }
1180
1181
1182
1183
1184
1185
1186
1187
1188 private Button addButton(final String label, final Container p) {
1189 Button b = new Button(label);
1190 b.addActionListener(this);
1191 p.add(b);
1192 return b;
1193 }
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203 private Checkbox addCheckbox(final String label, final Container p, boolean d) {
1204 Checkbox c = new Checkbox(label, d);
1205 c.addItemListener(this);
1206 p.add(c);
1207 return c;
1208 }
1209
1210
1211
1212
1213
1214
1215
1216
1217 private Choice addComboBox(final String[] s, final Container mp) {
1218 Choice c = new Choice();
1219 for (String st : s) {
1220 c.add(st);
1221 }
1222 c.select(0);
1223 c.addItemListener(this);
1224 mp.add(c);
1225 return c;
1226 }
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240 private JSpinner addDoubleSpinner(final String s, final Container mp, double d, double min,
1241 double max, double step, int columns) {
1242 SpinnerNumberModel model = new SpinnerNumberModel(d, min, max, step);
1243 JSpinner spinner = new JSpinner(model);
1244 ((JSpinner.DefaultEditor) spinner.getEditor()).getTextField().setColumns(columns);
1245 spinner.addChangeListener(this);
1246
1247 Panel p = new Panel();
1248 p.setLayout(new FlowLayout(FlowLayout.RIGHT));
1249 Label label = new Label(s);
1250 p.add(label);
1251 p.add(spinner);
1252 mp.add(p);
1253 return spinner;
1254 }
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268 private JSpinner addIntSpinner(final String s, final Container mp, int d, int min, int max,
1269 int step, int columns) {
1270 SpinnerNumberModel model = new SpinnerNumberModel(d, min, max, step);
1271 JSpinner spinner = new JSpinner(model);
1272 ((JSpinner.DefaultEditor) spinner.getEditor()).getTextField().setColumns(columns);
1273 spinner.addChangeListener(this);
1274
1275 Panel p = new Panel();
1276 p.setLayout(new FlowLayout(FlowLayout.RIGHT));
1277 Label label = new Label(s);
1278 p.add(label);
1279 p.add(spinner);
1280 mp.add(p);
1281 return spinner;
1282 }
1283
1284
1285
1286
1287
1288
1289
1290 private void setDefualts() {
1291 qState.segParam.setDefaults();
1292 updateSpinnerValues();
1293 cbContractingDirection.setState(qState.segParam.contractingDirection);
1294 }
1295
1296
1297
1298
1299
1300
1301
1302 private void updateSpinnerValues() {
1303
1304 supressStateChangeBOArun = true;
1305 dsNodeRes.setValue(qState.segParam.getNodeRes());
1306 dsVelCrit.setValue(qState.segParam.vel_crit);
1307 dsFImage.setValue(qState.segParam.f_image);
1308 dsFCentral.setValue(qState.segParam.f_central);
1309 dsFContract.setValue(qState.segParam.f_contract);
1310 dsFinalShrink.setValue(qState.segParam.finalShrink);
1311 isMaxIterations.setValue(qState.segParam.max_iterations);
1312 isBlowup.setValue(qState.segParam.blowup);
1313 isSampletan.setValue(qState.segParam.sample_tan);
1314 isSamplenorm.setValue(qState.segParam.sample_norm);
1315 supressStateChangeBOArun = false;
1316 }
1317
1318
1319
1320
1321
1322
1323
1324 private void updateCheckBoxes() {
1325
1326 cbFirstPluginActiv.setState(qState.snakePluginList.isActive(0));
1327
1328 cbSecondPluginActiv.setState(qState.snakePluginList.isActive(1));
1329
1330 cbThirdPluginActiv.setState(qState.snakePluginList.isActive(2));
1331 }
1332
1333
1334
1335
1336 private void updateMenus() {
1337 cbMenuPlotOriginalSnakes.setState(qState.boap.isProcessedSnakePlotted);
1338 cbMenuPlotHead.setState(qState.boap.isHeadPlotted);
1339 cbMenuZoomFreeze.setState(qState.boap.isZoomFreeze);
1340 }
1341
1342
1343
1344
1345 private void updateStatics() {
1346 setScalesText();
1347 }
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358 private void updateChoices() {
1359 final Color ok = new Color(178, 255, 102);
1360 final Color bad = new Color(255, 153, 153);
1361
1362 if (qState.snakePluginList.getName(0).isEmpty()) {
1363 chFirstPluginName.select(NONE);
1364 chFirstPluginName.setBackground(null);
1365 } else {
1366
1367 chFirstPluginName.select(qState.snakePluginList.getName(0));
1368
1369
1370
1371 if (chFirstPluginName.getSelectedItem().equals(NONE)) {
1372 chFirstPluginName.add(qState.snakePluginList.getName(0));
1373 chFirstPluginName.setBackground(bad);
1374 } else if (qState.snakePluginList.getInstance(0) == null) {
1375
1376 chFirstPluginName.setBackground(bad);
1377 } else {
1378 chFirstPluginName.setBackground(ok);
1379 }
1380 }
1381
1382 if (qState.snakePluginList.getName(1).isEmpty()) {
1383 chSecondPluginName.select(NONE);
1384 chSecondPluginName.setBackground(null);
1385 } else {
1386 chSecondPluginName.select(qState.snakePluginList.getName(1));
1387 if (chSecondPluginName.getSelectedItem().equals(NONE)) {
1388 chSecondPluginName.add(qState.snakePluginList.getName(1));
1389 chSecondPluginName.setBackground(bad);
1390 } else if (qState.snakePluginList.getInstance(1) == null) {
1391 chSecondPluginName.setBackground(bad);
1392 } else {
1393 chSecondPluginName.setBackground(ok);
1394 }
1395 }
1396
1397 if (qState.snakePluginList.getName(2).isEmpty()) {
1398 chThirdPluginName.select(NONE);
1399 chThirdPluginName.setBackground(null);
1400 } else {
1401 chThirdPluginName.select(qState.snakePluginList.getName(2));
1402 if (chThirdPluginName.getSelectedItem().equals(NONE)) {
1403 chThirdPluginName.add(qState.snakePluginList.getName(2));
1404 chThirdPluginName.setBackground(bad);
1405 } else if (qState.snakePluginList.getInstance(2) == null) {
1406 chThirdPluginName.setBackground(bad);
1407 } else {
1408 chThirdPluginName.setBackground(ok);
1409 }
1410 }
1411
1412 if (qState.boap.snakeToZoom > -1) {
1413 chZoom.select(String.valueOf(qState.boap.snakeToZoom));
1414 }
1415
1416 }
1417
1418
1419
1420
1421
1422
1423 private void updateWindowState() {
1424 updateCheckBoxes();
1425 updateChoices();
1426 updateStatics();
1427
1428
1429
1430 if (sww == null || sww.getState() == SwingWorker.StateValue.DONE) {
1431 if (chFirstPluginName.getSelectedItem() == NONE) {
1432 cbFirstPluginActiv.setEnabled(false);
1433 bnFirstPluginGUI.setEnabled(false);
1434 } else {
1435 cbFirstPluginActiv.setEnabled(true);
1436 bnFirstPluginGUI.setEnabled(true);
1437 }
1438 if (chSecondPluginName.getSelectedItem() == NONE) {
1439 cbSecondPluginActiv.setEnabled(false);
1440 bnSecondPluginGUI.setEnabled(false);
1441 } else {
1442 cbSecondPluginActiv.setEnabled(true);
1443 bnSecondPluginGUI.setEnabled(true);
1444 }
1445 if (chThirdPluginName.getSelectedItem() == NONE) {
1446 cbThirdPluginActiv.setEnabled(false);
1447 bnThirdPluginGUI.setEnabled(false);
1448 } else {
1449 cbThirdPluginActiv.setEnabled(true);
1450 bnThirdPluginGUI.setEnabled(true);
1451 }
1452 }
1453
1454 }
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466 @Override
1467 public void actionPerformed(final ActionEvent e) {
1468 LOGGER.trace("EVENT:actionPerformed");
1469 boolean run = false;
1470 Object b = e.getSource();
1471 if (b == bnDel && !qState.boap.editMode && !qState.boap.doDeleteSeg
1472 && !qState.boap.doFreeze) {
1473 if (qState.boap.doDelete == false) {
1474 bnDel.setLabel("*STOP DEL*");
1475 qState.boap.doDelete = true;
1476 lastTool = IJ.getToolName();
1477 IJ.setTool(Toolbar.LINE);
1478 } else {
1479 qState.boap.doDelete = false;
1480 bnDel.setLabel("Delete cell");
1481 IJ.setTool(lastTool);
1482 }
1483 return;
1484 }
1485 if (qState.boap.doDelete) {
1486 BOA_.log("**DELETE IS ON**");
1487 return;
1488 }
1489 if (b == bnFreezeCell && !qState.boap.editMode && !qState.boap.doDeleteSeg
1490 && !qState.boap.doDelete) {
1491 setFreeze(!qState.boap.doFreeze);
1492 return;
1493 }
1494 if (qState.boap.doFreeze) {
1495 BOA_.log("**FREEZE IS ON**");
1496 return;
1497 }
1498
1499 if (b == bnDelSeg && !qState.boap.editMode) {
1500 if (!qState.boap.doDeleteSeg) {
1501 bnDelSeg.setLabel("*STOP TRUNCATE*");
1502 qState.boap.doDeleteSeg = true;
1503 lastTool = IJ.getToolName();
1504 IJ.setTool(Toolbar.LINE);
1505 } else {
1506 qState.boap.doDeleteSeg = false;
1507 bnDelSeg.setLabel("Truncate Seg");
1508 IJ.setTool(lastTool);
1509 }
1510 return;
1511 }
1512 if (qState.boap.doDeleteSeg) {
1513 BOA_.log("**TRUNCATE SEG IS ON**");
1514 return;
1515 }
1516 if (b == bnEdit) {
1517 if (qState.boap.editMode == false) {
1518 bnEdit.setLabel("*STOP EDIT*");
1519 BOA_.log("**EDIT IS ON**");
1520 qState.boap.editMode = true;
1521 lastTool = IJ.getToolName();
1522 IJ.setTool(Toolbar.LINE);
1523 if (qState.nest.size() == 1) {
1524 editSeg(0, 0, qState.boap.frame);
1525 }
1526
1527
1528 qState.storeOnlyEdited(qState.boap.frame);
1529 } else {
1530 qState.boap.editMode = false;
1531 if (qState.boap.editingID != -1) {
1532 stopEdit();
1533 }
1534 bnEdit.setLabel("Edit");
1535 IJ.setTool(lastTool);
1536 }
1537 return;
1538 }
1539 if (qState.boap.editMode) {
1540 BOA_.log("**EDIT IS ON**");
1541 return;
1542 }
1543 if (b == bnDefault) {
1544 this.setDefualts();
1545 run = true;
1546 } else if (b == bnSeg) {
1547 if (sww != null && sww.getState() != SwingWorker.StateValue.DONE) {
1548
1549 isSegBreakHit = true;
1550 return;
1551 }
1552 runBoaThread(qState.boap.frame, qState.boap.getFrames(), true);
1553 } else if (b == bnScale) {
1554 setScales();
1555 pixelLabel.setText("Scale: " + IJ.d2s(qState.boap.getImageScale(), 6) + " \u00B5m");
1556 fpsLabel.setText("F Interval: " + IJ.d2s(qState.boap.getImageFrameInterval(), 3) + " s");
1557 } else if (b == bnAdd) {
1558 addCell(canvas.getImage().getRoi(), qState.boap.frame);
1559 canvas.getImage().killRoi();
1560 } else if (b == bnFinish) {
1561 fpsLabel.setName("moo");
1562 finish(true);
1563 quit();
1564 } else if (b == bnQuit) {
1565 YesNoCancelDialog ync;
1566 ync = new YesNoCancelDialog(window, "Quit", "Quit without saving?");
1567 if (!ync.yesPressed()) {
1568 return;
1569 }
1570 quit();
1571 } else if (b == bnSave || b == menuSave) {
1572 finish(false);
1573 }
1574 if (b == menuSaveAs) {
1575 finish(true);
1576 }
1577
1578 if (b == bnFirstPluginGUI) {
1579 LOGGER.debug("First plugin GUI, state of BOAp is " + qState.snakePluginList.getInstance(0));
1580 if (qState.snakePluginList.getInstance(0) != null) {
1581 qState.snakePluginList.getInstance(0).showUi(true);
1582 }
1583 }
1584 if (b == bnSecondPluginGUI) {
1585 LOGGER.debug(
1586 "Second plugin GUI, state of BOAp is " + qState.snakePluginList.getInstance(1));
1587 if (qState.snakePluginList.getInstance(1) != null) {
1588 qState.snakePluginList.getInstance(1).showUi(true);
1589 }
1590 }
1591 if (b == bnThirdPluginGUI) {
1592 LOGGER.debug("Third plugin GUI, state of BOAp is " + qState.snakePluginList.getInstance(2));
1593 if (qState.snakePluginList.getInstance(2) != null) {
1594 qState.snakePluginList.getInstance(2).showUi(true);
1595 }
1596 }
1597 if (b == bnCopyLastPlugin) {
1598 int frameCopyFrom = qState.boap.frame - 1;
1599 if (frameCopyFrom < 1 || frameCopyFrom > qState.boap.getFrames()) {
1600 return;
1601 }
1602 LOGGER.debug(
1603 "Copy config from frame " + frameCopyFrom + " current frame " + qState.boap.frame);
1604 qState.copyPluginListFromSnapshot(frameCopyFrom);
1605 setBusyStatus(true, false);
1606 recalculatePlugins();
1607 setBusyStatus(false, true);
1608 }
1609
1610 if (b == bnCopyLast) {
1611 int frameCopyFrom = qState.boap.frame - 1;
1612 if (frameCopyFrom < 1 || frameCopyFrom > qState.boap.getFrames()) {
1613 return;
1614 }
1615 LOGGER.debug(
1616 "Copy config from frame " + frameCopyFrom + " current frame " + qState.boap.frame);
1617 qState.copySegParamFromSnapshot(frameCopyFrom);
1618 updateSpinnerValues();
1619 run = true;
1620 }
1621 if (b == bnPopulatePlugin) {
1622 List<Integer> range = IntStream.rangeClosed(qState.boap.frame + 1, qState.boap.getFrames())
1623 .boxed().collect(Collectors.toList());
1624 populatePlugins(range);
1625 }
1626
1627 if (b == menuAbout) {
1628 about();
1629 }
1630 if (b == menuOpenHelp) {
1631 String url = new PropertyReader().readProperty("quimpconfig.properties", "manualURL");
1632 try {
1633 java.awt.Desktop.getDesktop().browse(new URI(url));
1634 } catch (Exception e1) {
1635 IJ.error("Could not open help", e1.getMessage());
1636 LOGGER.debug(e1.getMessage(), e1);
1637 }
1638 return;
1639 }
1640 if (b == menuSaveConfig) {
1641 String saveIn = qState.boap.getOutputFileCore().getParent();
1642 saveIn = (saveIn == null) ? System.getProperty("user.dir") : saveIn;
1643 SaveDialog sd = new SaveDialog("Save plugin config data...", saveIn,
1644 qState.boap.getFileName(), FileExtensions.pluginFileExt);
1645 if (sd.getFileName() != null) {
1646 try {
1647
1648 Serializer<SnakePluginList> s;
1649 s = new Serializer<>(qState.snakePluginList, quimpInfo);
1650 s.setPretty();
1651 s.save(sd.getDirectory() + sd.getFileName());
1652 s = null;
1653 } catch (FileNotFoundException e1) {
1654 IJ.error("Problem with saving plugin config", e1.getMessage());
1655 LOGGER.debug(e1.getMessage(), e1);
1656 }
1657 }
1658 }
1659
1660
1661
1662
1663
1664
1665
1666
1667 if (b == menuLoadConfig) {
1668 OpenDialog od = new OpenDialog("Load plugin config data...", "");
1669 if (od.getFileName() != null) {
1670 try {
1671 Serializer<SnakePluginList> loaded;
1672
1673 Serializer<SnakePluginList> s =
1674 new Serializer<>(SnakePluginList.class, QuimP.TOOL_VERSION);
1675 s.registerConverter(new Converter170202<>(QuimP.TOOL_VERSION));
1676
1677
1678 s.registerInstanceCreator(SnakePluginList.class,
1679 new SnakePluginListInstanceCreator(3, pluginFactory, viewUpdater));
1680 loaded = s.load(od.getDirectory() + od.getFileName());
1681
1682 qState.snakePluginList.clear();
1683 qState.snakePluginList = loaded.obj;
1684 qState.store(qState.boap.frame);
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695 recalculatePlugins();
1696 } catch (IOException e1) {
1697 IJ.error("Problem with loading plugin config", e1.getMessage());
1698 LOGGER.debug(e1.getMessage(), e1);
1699 } catch (JsonSyntaxException e1) {
1700 IJ.error("Problem with configuration file", e1.getMessage());
1701 LOGGER.debug(e1.getMessage(), e1);
1702 } catch (Exception e1) {
1703 IJ.error("Error", "File can not be loaded or parsed" + e1.getMessage());
1704 LOGGER.debug(e1.getMessage(), e1);
1705 }
1706 }
1707 }
1708
1709
1710
1711
1712
1713
1714 if (b == menuShowHistory) {
1715 JOptionPane.showMessageDialog(window,
1716 "The full history of changes is avaiable after saving your work in the" + " file "
1717 + FileExtensions.newConfigFileExt);
1718
1719
1720
1721
1722 }
1723
1724
1725
1726
1727
1728
1729
1730
1731 if (b == menuLoad || b == bnLoad) {
1732 FileDialogExfilesystem/FileDialogEx.html#FileDialogEx">FileDialogEx od = new FileDialogEx(IJ.getInstance());
1733 od.setDirectory(OpenDialog.getLastDirectory());
1734 try {
1735 if (QuimP.newFileFormat.get() == true) {
1736 od.setExtension(FileExtensions.newConfigFileExt);
1737 if (od.showOpenDialog() == null) {
1738 return;
1739 }
1740 loadQconfConfiguration(Paths.get(od.getDirectory(), od.getFile()));
1741 }
1742 if (QuimP.newFileFormat.get() == false) {
1743 od.setExtension(FileExtensions.configFileExt);
1744 if (od.showOpenDialog() == null) {
1745 return;
1746 }
1747 if (qState.readParams(od.getPath().toFile())) {
1748 updateSpinnerValues();
1749 if (loadSnakes()) {
1750 run = false;
1751 } else {
1752 run = true;
1753 }
1754 }
1755 }
1756 } catch (IOException e1) {
1757 IJ.error("Problem with loading plugin config", e1.getMessage());
1758 LOGGER.debug(e1.getMessage(), e1);
1759 } catch (JsonSyntaxException e1) {
1760 IJ.error("Problem with configuration file", e1.getMessage());
1761 LOGGER.debug(e1.getMessage(), e1);
1762 } catch (Exception e1) {
1763 IJ.error("Error", "File can not be loaded or parsed" + e1.getMessage());
1764 LOGGER.debug(e1.getMessage(), e1);
1765 }
1766 }
1767
1768
1769
1770
1771
1772
1773
1774 if (b == menuDeletePlugin) {
1775
1776 qState.snakePluginList.clear();
1777 for (SnakePluginList sp : qState.snakePluginListSnapshots) {
1778 if (sp != null) {
1779 sp.clear();
1780 }
1781 }
1782
1783 for (int i = 0; i < qState.nest.size(); i++) {
1784 SnakeHandler snakeHandler = qState.nest.getHandler(i);
1785 snakeHandler.copyFromSegToFinal();
1786 }
1787
1788 imageGroup.updateOverlay(qState.boap.frame);
1789 }
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800 if (b == menuApplyPlugin) {
1801
1802 for (SnakePluginList sp : qState.snakePluginListSnapshots) {
1803 sp.afterSerialize();
1804 }
1805
1806 qState.restore(qState.boap.frame);
1807
1808 }
1809
1810
1811
1812
1813 if (b == menuPopulatePlugin) {
1814 List<Integer> range = IntStream.rangeClosed(1, qState.boap.getFrames()).boxed()
1815 .collect(Collectors.toList());
1816 populatePlugins(range);
1817 }
1818
1819
1820
1821
1822 if (b == menuSegmentationRun) {
1823 if (qState.binarySegmentationPlugin != null) {
1824 if (!qState.binarySegmentationPlugin.isWindowVisible()) {
1825 qState.binarySegmentationPlugin.showUi(true);
1826 }
1827 } else {
1828 qState.binarySegmentationPlugin = new BinarySegmentation_();
1829 qState.binarySegmentationPlugin.attachNest(qState.nest);
1830
1831 qState.binarySegmentationPlugin.attachContext(viewUpdater);
1832
1833 qState.binarySegmentationPlugin.showUi(true);
1834 }
1835 qState.binarySegmentationPlugin.attachImagePlus(imageGroup.getOrgIpl());
1836
1837 for (int f = 1; f <= qState.boap.getFrames(); f++) {
1838 qState.store(f);
1839 }
1840 BOA_.log("Run segmentation from mask file");
1841 }
1842
1843
1844
1845
1846 if (b == menuSegmentationReset) {
1847 qState.reset(WindowManager.getCurrentImage(), pluginFactory, viewUpdater);
1848 qState.nest.cleanNest();
1849 imageGroup.clearPaths(1);
1850 setDefualts();
1851 if (qState.boap.frame != imageGroup.getOrgIpl().getSlice()) {
1852 imageGroup.updateToFrame(qState.boap.frame);
1853 } else {
1854 updateSliceSelector();
1855 }
1856 }
1857
1858 updateWindowState();
1859
1860
1861 if (run) {
1862 runBoaThread(qState.boap.frame, qState.boap.frame, false);
1863 }
1864 }
1865
1866
1867
1868
1869
1870
1871 private void setFreeze(boolean status) {
1872 if (status) {
1873 bnFreezeCell.setLabel("*CANCEL*");
1874 qState.boap.doFreeze = true;
1875 lastTool = IJ.getToolName();
1876 IJ.setTool(Toolbar.LINE);
1877 } else {
1878 bnFreezeCell.setLabel("Freeze");
1879 qState.boap.doFreeze = false;
1880 IJ.setTool(lastTool);
1881 }
1882 }
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892 private void runBoaThread(int startFrame, int endFrame, boolean interruptible) {
1893
1894 sww = new SwingWorker<Boolean, Object>() {
1895 @Override
1896 protected Boolean doInBackground() throws Exception {
1897 setBusyStatus(true, interruptible);
1898 IJ.showStatus("SEGMENTING...");
1899 runBoa(startFrame, endFrame);
1900 return true;
1901 }
1902
1903 @Override
1904 protected void done() {
1905 try {
1906 get();
1907 } catch (ExecutionException e) {
1908 Throwable cause = e.getCause();
1909 if (cause instanceof BoaException) {
1910 ((BoaException) cause).setMessageSinkType(MessageSinkTypes.NONE);
1911 int framesCompleted = ((BoaException) cause).getFrame();
1912 String ret = ((BoaException) cause).handleException(IJ.getInstance(),
1913 "FAIL AT " + framesCompleted);
1914 IJ.showStatus("FAIL AT " + framesCompleted);
1915 BOA_.log(ret);
1916 }
1917 } catch (InterruptedException e) {
1918
1919 e.printStackTrace();
1920 } finally {
1921 setBusyStatus(false, true);
1922 IJ.showStatus("COMPLETE");
1923 }
1924 }
1925
1926 };
1927 sww.execute();
1928 }
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938 private void populatePlugins(List<Integer> frames) {
1939 if (frames.isEmpty()) {
1940 return;
1941 }
1942 SnakePluginList tmp = qState.snakePluginList.getDeepCopy();
1943 int cf = qState.boap.frame;
1944
1945 sww = new SwingWorker<Boolean, Object>() {
1946 @Override
1947 protected Boolean doInBackground() throws Exception {
1948 setBusyStatus(true, true);
1949 IJ.showStatus("SEGMENTING...");
1950 IJ.showProgress(0, frames.get(frames.size() - 1) - frames.get(0));
1951
1952 for (int f : frames) {
1953
1954 qState.snakePluginListSnapshots.set(f - 1, tmp.getDeepCopy());
1955 qState.snakePluginListSnapshots.set(f - 1, tmp.getDeepCopy());
1956
1957
1958 qState.snakePluginListSnapshots.get(f - 1).afterSerialize();
1959
1960 qState.boap.frame = f;
1961 imageGroup.updateToFrame(qState.boap.frame);
1962 recalculatePlugins();
1963 if (isSegBreakHit == true) {
1964 isSegBreakHit = false;
1965 break;
1966 }
1967 IJ.showProgress(f, frames.get(frames.size() - 1));
1968 }
1969 qState.boap.frame = cf;
1970 imageGroup.updateToFrame(qState.boap.frame);
1971 return true;
1972 }
1973
1974 @Override
1975 protected void done() {
1976 setBusyStatus(false, true);
1977 IJ.showStatus("COMPLETE");
1978 IJ.showProgress(2.0);
1979 }
1980
1981 };
1982 sww.execute();
1983
1984 }
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997 private void loadQconfConfiguration(Path configPath) throws IOException, Exception {
1998 Path filename = configPath.getFileName();
1999 if (filename == null) {
2000 throw new IllegalAccessException("Input path is not file");
2001 }
2002 Serializer<DataContainer> loaded;
2003
2004 Serializer<DataContainer> s = new Serializer<>(DataContainer.class, QuimP.TOOL_VERSION);
2005 s.registerConverter(new Converter170202<>(QuimP.TOOL_VERSION));
2006 s.registerInstanceCreator(DataContainer.class,
2007 new DataContainerInstanceCreator(pluginFactory, viewUpdater));
2008 loaded = s.load(configPath.toString());
2009
2010 if (!loaded.obj.BOAState.boap.getOrgFile().getName()
2011 .equals(qState.boap.getOrgFile().getName())) {
2012 LOGGER.warn("The image opened currently in BOA is different from those"
2013 + " pointed in configuration file");
2014 log("Trying to apply configuration saved for other image");
2015 YesNoCancelDialog yncd = new YesNoCancelDialog(IJ.getInstance(), "Warning",
2016 "Trying to load configuration that does not\nmath to"
2017 + " opened image.\nAre you sure?");
2018 if (!yncd.yesPressed()) {
2019 return;
2020 }
2021 }
2022
2023
2024 loaded.obj.BOAState.boap.setOrgFile(qState.boap.getOrgFile());
2025
2026 String parent;
2027 if (configPath.getParent() != null) {
2028 parent = configPath.getParent().toString();
2029 } else {
2030 parent = "";
2031 }
2032 loaded.obj.BOAState.boap.setOutputFileCore(parent + File.separator + filename.toString());
2033
2034 qState.reset(WindowManager.getCurrentImage(), pluginFactory, viewUpdater);
2035 qState = loaded.obj.BOAState;
2036 imageGroup.updateNest(qState.nest);
2037 qState.restore(qState.boap.frame);
2038 updateSpinnerValues();
2039
2040
2041 fillZoomChoice();
2042
2043
2044
2045
2046
2047
2048 updateMenus();
2049 if (qState.boap.frame != imageGroup.getOrgIpl().getSlice()) {
2050
2051 imageGroup.updateToFrame(qState.boap.frame);
2052 } else {
2053 updateSliceSelector();
2054 }
2055 BOA_.log("Configuration read successfully");
2056 }
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067 @Override
2068 public void itemStateChanged(final ItemEvent e) {
2069 LOGGER.trace("EVENT:itemStateChanged");
2070 setFreeze(false);
2071 if (qState.boap.doDelete) {
2072 BOA_.log("**WARNING:DELETE IS ON**");
2073 }
2074 boolean run = false;
2075 Object source = e.getItemSelectable();
2076 if (source == cbPath) {
2077 qState.segParam.showPaths = cbPath.getState();
2078 if (qState.segParam.showPaths) {
2079 this.setImage(imageGroup.getPathsIpl());
2080 } else {
2081 this.setImage(imageGroup.getOrgIpl());
2082 }
2083 if (qState.boap.zoom && !qState.nest.isVacant()) {
2084 imageGroup.zoom(canvas, qState.boap.frame, qState.boap.snakeToZoom);
2085 }
2086 } else if (source == cbPrevSnake) {
2087 qState.segParam.use_previous_snake = cbPrevSnake.getState();
2088 } else if (source == cbExpSnake) {
2089 qState.segParam.expandSnake = cbExpSnake.getState();
2090 run = true;
2091 } else if (source == cbContractingDirection) {
2092 qState.segParam.contractingDirection = cbContractingDirection.getState();
2093 qState.segParam.reverseForces();
2094 updateSpinnerValues();
2095 run = true;
2096 } else if (source == cbFirstPluginActiv) {
2097 qState.snakePluginList.setActive(0, cbFirstPluginActiv.getState());
2098 setBusyStatus(true, false);
2099 recalculatePlugins();
2100 } else if (source == cbSecondPluginActiv) {
2101 qState.snakePluginList.setActive(1, cbSecondPluginActiv.getState());
2102 setBusyStatus(true, false);
2103 recalculatePlugins();
2104 } else if (source == cbThirdPluginActiv) {
2105 qState.snakePluginList.setActive(2, cbThirdPluginActiv.getState());
2106 setBusyStatus(true, false);
2107 recalculatePlugins();
2108 }
2109
2110
2111 if (source == cbMenuPlotOriginalSnakes) {
2112 qState.boap.isProcessedSnakePlotted = cbMenuPlotOriginalSnakes.getState();
2113 setBusyStatus(true, false);
2114 recalculatePlugins();
2115 }
2116 if (source == cbMenuPlotHead) {
2117 qState.boap.isHeadPlotted = cbMenuPlotHead.getState();
2118 imageGroup.updateOverlay(qState.boap.frame);
2119 }
2120 if (source == cbMenuZoomFreeze) {
2121 qState.boap.isZoomFreeze = cbMenuZoomFreeze.getState();
2122 if (qState.boap.isZoomFreeze && qState.boap.zoom) {
2123 JOptionPane.showMessageDialog(window, QuimpToolsCollection
2124 .stringWrap("Please un-zoom your view first.", QuimP.LINE_WRAP), "Error",
2125 JOptionPane.ERROR_MESSAGE);
2126 qState.boap.isZoomFreeze = false;
2127 cbMenuZoomFreeze.setState(false);
2128 }
2129 }
2130
2131 if (source == chFirstPluginName) {
2132 LOGGER.debug("Used firstPluginName, val: " + chFirstPluginName.getSelectedItem());
2133 instanceSnakePlugin((String) chFirstPluginName.getSelectedItem(), 0,
2134 cbFirstPluginActiv.getState());
2135 setBusyStatus(true, false);
2136 recalculatePlugins();
2137 }
2138 if (source == chSecondPluginName) {
2139 LOGGER.debug("Used secondPluginName, val: " + chSecondPluginName.getSelectedItem());
2140 instanceSnakePlugin((String) chSecondPluginName.getSelectedItem(), 1,
2141 cbSecondPluginActiv.getState());
2142 setBusyStatus(true, false);
2143 recalculatePlugins();
2144 }
2145 if (source == chThirdPluginName) {
2146 LOGGER.debug("Used thirdPluginName, val: " + chThirdPluginName.getSelectedItem());
2147 instanceSnakePlugin((String) chThirdPluginName.getSelectedItem(), 2,
2148 cbThirdPluginActiv.getState());
2149 setBusyStatus(true, false);
2150 recalculatePlugins();
2151 }
2152
2153
2154 if (source == chZoom) {
2155 LOGGER.trace("zoom val " + chZoom.getSelectedItem());
2156 if (chZoom.getSelectedItem().equals(fullZoom)) {
2157 qState.boap.snakeToZoom = -1;
2158 qState.boap.zoom = false;
2159 imageGroup.unzoom(canvas);
2160
2161 if (qState.boap.isZoomFreeze == true) {
2162 for (SnakeHandler sh : qState.nest.getHandlers()) {
2163 sh.unfreezeHandler();
2164 }
2165 }
2166 } else {
2167 if (!qState.nest.isVacant()) {
2168 qState.boap.snakeToZoom = Integer.parseInt(chZoom.getSelectedItem());
2169 qState.boap.zoom = true;
2170
2171 SnakeHandler snakeH = qState.nest.getHandlerofId(qState.boap.snakeToZoom);
2172 if (qState.boap.isZoomFreeze == true && snakeH != null
2173 && snakeH.isStoredAt(qState.boap.frame)) {
2174 for (SnakeHandler sh : qState.nest.getHandlers()) {
2175 sh.freezeHandler();
2176 }
2177 snakeH.unfreezeHandler();
2178 }
2179 imageGroup.zoom(canvas, qState.boap.frame, qState.boap.snakeToZoom);
2180 }
2181 }
2182 imageGroup.updateOverlay(qState.boap.frame);
2183 }
2184
2185 updateWindowState();
2186 updateChoices();
2187
2188 try {
2189 if (run) {
2190 if (supressStateChangeBOArun) {
2191
2192 LOGGER.debug("supressState");
2193 return;
2194 }
2195
2196 runBoa(qState.boap.frame, qState.boap.frame);
2197 }
2198 } catch (BoaException be) {
2199 be.setMessageSinkType(MessageSinkTypes.NONE);
2200 BOA_.log(be.handleException(IJ.getInstance(), "Segmentation failed at " + be.getFrame()));
2201 } finally {
2202 setBusyStatus(false, true);
2203 }
2204 }
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215 @Override
2216 public void stateChanged(final ChangeEvent ce) {
2217 LOGGER.trace("EVENT:stateChanged");
2218 setFreeze(false);
2219 if (qState.boap.doDelete) {
2220 BOA_.log("**WARNING:DELETE IS ON**");
2221 }
2222 boolean run = false;
2223 Object source = ce.getSource();
2224
2225 if (source == dsNodeRes) {
2226 JSpinner spinner = (JSpinner) source;
2227 qState.segParam.setNodeRes((Double) spinner.getValue());
2228 run = true;
2229 } else if (source == dsVelCrit) {
2230 JSpinner spinner = (JSpinner) source;
2231 qState.segParam.vel_crit = (Double) spinner.getValue();
2232 run = true;
2233 } else if (source == dsFImage) {
2234 JSpinner spinner = (JSpinner) source;
2235 qState.segParam.f_image = (Double) spinner.getValue();
2236 run = true;
2237 } else if (source == dsFCentral) {
2238 JSpinner spinner = (JSpinner) source;
2239 qState.segParam.f_central = (Double) spinner.getValue();
2240 run = true;
2241 } else if (source == dsFContract) {
2242 JSpinner spinner = (JSpinner) source;
2243 qState.segParam.f_contract = (Double) spinner.getValue();
2244 run = true;
2245 } else if (source == dsFinalShrink) {
2246 JSpinner spinner = (JSpinner) source;
2247 qState.segParam.finalShrink = (Double) spinner.getValue();
2248 run = true;
2249 } else if (source == isMaxIterations) {
2250 JSpinner spinner = (JSpinner) source;
2251 qState.segParam.max_iterations = (Integer) spinner.getValue();
2252 run = true;
2253 } else if (source == isBlowup) {
2254 JSpinner spinner = (JSpinner) source;
2255 qState.segParam.blowup = (Integer) spinner.getValue();
2256 run = true;
2257 } else if (source == isSampletan) {
2258 JSpinner spinner = (JSpinner) source;
2259 qState.segParam.sample_tan = (Integer) spinner.getValue();
2260 run = true;
2261 } else if (source == isSamplenorm) {
2262 JSpinner spinner = (JSpinner) source;
2263 qState.segParam.sample_norm = (Integer) spinner.getValue();
2264 run = true;
2265 }
2266
2267 updateWindowState();
2268
2269 try {
2270 if (run) {
2271
2272
2273 if (supressStateChangeBOArun) {
2274 LOGGER.debug("supressState");
2275 return;
2276 }
2277
2278 runBoa(qState.boap.frame, qState.boap.frame);
2279 }
2280 } catch (BoaException be) {
2281 be.setMessageSinkType(MessageSinkTypes.NONE);
2282 BOA_.log(be.handleException(IJ.getInstance(), "Segmentation failed " + be.getFrame()));
2283 }
2284 }
2285
2286
2287
2288
2289
2290 @Override
2291 public void updateSliceSelector() {
2292 super.updateSliceSelector();
2293 LOGGER.trace("EVENT:updateSliceSelector");
2294 if (!qState.boap.singleImage) {
2295 zSelector.setValue(imp.getCurrentSlice());
2296
2297 }
2298 if (qState.boap.editMode) {
2299
2300 stopEdit();
2301 }
2302
2303 if (!qState.boap.singleImage) {
2304 qState.boap.frame = imp.getCurrentSlice();
2305 frameLabel.setText("" + qState.boap.frame);
2306 }
2307 imageGroup.updateOverlay(qState.boap.frame);
2308 imageGroup.setIpSliceAll(qState.boap.frame);
2309
2310
2311 if (qState.boap.zoom && !qState.nest.isVacant()) {
2312 imageGroup.zoom(canvas, qState.boap.frame, qState.boap.snakeToZoom);
2313 }
2314
2315 boolean wasInEdit = qState.boap.editMode;
2316 if (wasInEdit) {
2317 bnEdit.setLabel("*STOP EDIT*");
2318 BOA_.log("**EDIT IS ON**");
2319 qState.boap.editMode = true;
2320 lastTool = IJ.getToolName();
2321 IJ.setTool(Toolbar.LINE);
2322 editSeg(0, 0, qState.boap.frame);
2323 IJ.setTool(lastTool);
2324 }
2325 LOGGER.trace("Snakes at this frame: " + qState.nest.getSnakesforFrame(qState.boap.frame));
2326 if (!isSegRunning) {
2327
2328
2329 qState.restore(qState.boap.frame);
2330 updateSpinnerValues();
2331 updateWindowState();
2332 }
2333 }
2334
2335
2336
2337
2338
2339 void switchOffDelete() {
2340 qState.boap.doDelete = false;
2341 bnDel.setLabel("Delete cell");
2342 }
2343
2344
2345
2346
2347
2348
2349 @Override
2350 public void setImage(ImagePlus imp2) {
2351 double m = this.ic.getMagnification();
2352 Dimension dem = this.ic.getSize();
2353 super.setImage(imp2);
2354 this.ic.setMagnification(m);
2355 this.ic.setSize(dem);
2356 }
2357
2358
2359
2360
2361
2362 void switchOfftruncate() {
2363 qState.boap.doDeleteSeg = false;
2364 bnDelSeg.setLabel("Truncate Seg");
2365 }
2366
2367
2368
2369
2370 void setScalesText() {
2371 pixelLabel.setText("Scale: " + IJ.d2s(qState.boap.getImageScale(), 6) + " \u00B5m");
2372 fpsLabel.setText("F Interval: " + IJ.d2s(qState.boap.getImageFrameInterval(), 3) + " s");
2373 }
2374
2375 }
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387 private void instanceSnakePlugin(final String selectedPlugin, int slot, boolean act) {
2388
2389 try {
2390
2391 if (selectedPlugin != NONE) {
2392 qState.snakePluginList.setInstance(slot, selectedPlugin, act);
2393 } else {
2394 if (qState.snakePluginList.getInstance(slot) != null) {
2395 qState.snakePluginList.getInstance(slot).showUi(false);
2396 }
2397 qState.snakePluginList.deletePlugin(slot);
2398 }
2399 } catch (QuimpPluginException e) {
2400 IJ.error("Plugin " + selectedPlugin + " cannot be loaded.", "Reason: " + e.getMessage());
2401 LOGGER.debug(e.getMessage(), e);
2402 }
2403 }
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414 private void setBusyStatus(boolean busy, boolean interruptible) {
2415 if (busy == true) {
2416 window.bnSeg.setBackground(Color.RED);
2417 window.bnSeg.setLabel("Busy");
2418 if (interruptible) {
2419 window.enableUiInterruptile(false);
2420 } else {
2421 window.enableUi(false);
2422 }
2423 } else {
2424 window.bnSeg.setBackground(null);
2425 window.bnSeg.setLabel("SEGMENT");
2426 window.enableUi(true);
2427 }
2428
2429 }
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441 public void runBoa(int startF, int endF) throws BoaException {
2442 LOGGER.debug("run BOA");
2443 isSegBreakHit = false;
2444 isSegRunning = true;
2445 if (qState.nest.isVacant() || qState.nest.allFrozen()) {
2446 BOA_.log("Nothing to segment!");
2447 isSegRunning = false;
2448 return;
2449 }
2450 try {
2451 IJ.showProgress(0, endF - startF);
2452
2453 qState.nest.resetForFrame(startF);
2454 if (!qState.segParam.expandSnake) {
2455
2456 constrictor.loosen(qState.nest, startF);
2457 } else {
2458 constrictor.implode(qState.nest, startF);
2459 }
2460 SnakeHandler snH;
2461 int s = 0;
2462 Snake snake;
2463 imageGroup.clearPaths(startF);
2464 LOGGER.debug("Use options: " + qState.segParam.toString());
2465 for (qState.boap.frame = startF; qState.boap.frame <= endF; qState.boap.frame++) {
2466 if (isSegBreakHit == true) {
2467 qState.boap.frame--;
2468 isSegBreakHit = false;
2469 break;
2470 }
2471
2472 imageGroup.setProcessor(qState.boap.frame);
2473 imageGroup.setIpSliceAll(qState.boap.frame);
2474
2475 try {
2476 if (qState.boap.frame != startF) {
2477 if (!qState.segParam.use_previous_snake) {
2478 qState.nest.resetForFrame(qState.boap.frame);
2479 } else {
2480 if (!qState.segParam.expandSnake) {
2481 constrictor.loosen(qState.nest, qState.boap.frame);
2482 } else {
2483 constrictor.implode(qState.nest, qState.boap.frame);
2484 }
2485 }
2486 }
2487
2488 for (s = 0; s < qState.nest.size(); s++) {
2489 snH = qState.nest.getHandler(s);
2490 snake = snH.getLiveSnake();
2491 try {
2492 if (!snake.alive || qState.boap.frame < snH.getStartFrame()) {
2493 continue;
2494 }
2495
2496
2497 if (!snH.isSnakeHandlerFrozen()) {
2498 imageGroup.drawPath(snake, qState.boap.frame);
2499 tightenSnake(snake);
2500 imageGroup.drawPath(snake, qState.boap.frame);
2501 snH.backupLiveSnake(qState.boap.frame);
2502 Snake out = iterateOverSnakePlugins(snake);
2503 snH.storeThisSnake(out, qState.boap.frame);
2504 } else {
2505
2506
2507 snH.copyFromFinalToLive(qState.boap.frame);
2508 LOGGER.debug("SnakeHandler " + snH.getID() + " is frozen");
2509 }
2510 } catch (QuimpPluginException qpe) {
2511
2512 qpe.setMessageSinkType(MessageSinkTypes.NONE);
2513 BOA_.log(qpe.handleException(null, "Error in filter module"));
2514 snH.storeLiveSnake(qState.boap.frame);
2515 } catch (BoaException be) {
2516 imageGroup.drawPath(snake, qState.boap.frame);
2517 snH.storeLiveSnake(qState.boap.frame);
2518 snH.backupLiveSnake(qState.boap.frame);
2519 qState.nest.kill(snH);
2520 snake.unfreezeAll();
2521 be.setMessageSinkType(MessageSinkTypes.NONE);
2522 BOA_.log(be.handleException(null,
2523 "Snake " + snake.getSnakeID() + " died, frame " + qState.boap.frame));
2524 isSegRunning = false;
2525 if (qState.nest.allDead()) {
2526 throw new BoaException("All snakes dead: " + be.getMessage(), qState.boap.frame, 1);
2527 }
2528 }
2529 }
2530
2531 IJ.showProgress(qState.boap.frame, endF);
2532 } catch (BoaException be) {
2533 isSegRunning = false;
2534 if (!qState.segParam.use_previous_snake) {
2535 imageGroup.setIpSliceAll(qState.boap.frame);
2536 imageGroup.updateOverlay(qState.boap.frame);
2537 } else {
2538 throw be;
2539 }
2540 } finally {
2541
2542 qState.store(qState.boap.frame);
2543
2544 }
2545 }
2546 qState.boap.frame = endF;
2547 } catch (BoaException be) {
2548 if (be.getFrame() == 0) {
2549 be.setFrame(qState.boap.frame);
2550 }
2551 throw be;
2552 } catch (Exception e) {
2553
2554 BoaExceptionBoaException.html#BoaException">BoaException be = new BoaException(e);
2555 be.setFrame(qState.boap.frame);
2556 throw be;
2557 } finally {
2558 isSegRunning = false;
2559 imageGroup.updateOverlay(qState.boap.frame);
2560 IJ.showProgress(2.0);
2561 }
2562
2563 }
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580 private void tightenSnake(final Snake snake) throws BoaException {
2581
2582 int i;
2583
2584 for (i = 0; i < qState.segParam.max_iterations; i++) {
2585
2586
2587
2588
2589
2590 if (i % qState.boap.cut_every == 0) {
2591 snake.cutLoops();
2592 }
2593 if (i % 10 == 0 && i != 0) {
2594 snake.correctDistance(true);
2595 }
2596 if (constrictor.constrict(snake, imageGroup.getOrgIp())) {
2597 break;
2598 }
2599 if (i % 4 == 0) {
2600 imageGroup.drawPath(snake, qState.boap.frame);
2601 }
2602
2603 if ((snake.getNumPoints() / snake.startingNnodes) > qState.boap.NMAX) {
2604
2605 if (qState.segParam.use_previous_snake) {
2606
2607
2608 throw new BoaException(
2609 "Frame " + qState.boap.frame + "-max nodes reached " + snake.getNumPoints(),
2610 qState.boap.frame, 1);
2611 } else {
2612 BOA_.log("Frame " + qState.boap.frame + "-max nodes reached..continue");
2613 break;
2614 }
2615 }
2616 }
2617 snake.unfreezeAll();
2618
2619 if (!qState.segParam.expandSnake) {
2620 if (BOA_.qState.segParam.contractingDirection) {
2621 snake.scaleSnake(-BOA_.qState.segParam.finalShrink, 0.5, false);
2622 } else {
2623 snake.scaleSnake(-BOA_.qState.segParam.finalShrink, 0.5, false);
2624 }
2625 }
2626 snake.cutLoops();
2627 snake.cutIntersects();
2628 }
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652 private Snakeke">Snake iterateOverSnakePlugins(final Snake snake)
2653 throws QuimpPluginException, BoaException {
2654 final int ipoint = 0;
2655 final int isnake = 1;
2656
2657 int previousConversion = isnake;
2658 Snake outsnake = snake;
2659 Snake snakeToProcess = snake;
2660
2661
2662
2663 List<Point2d> dataToProcess = null;
2664 if (!qState.snakePluginList.isRefListEmpty()) {
2665 LOGGER.debug("sPluginList not empty");
2666 for (Plugin qsP : qState.snakePluginList.getList()) {
2667 if (!qsP.isExecutable()) {
2668 continue;
2669 }
2670 if (qsP.getRef() instanceof IQuimpPluginAttachImage) {
2671 ((IQuimpPluginAttachImage) qsP.getRef()).attachImage(imageGroup.getOrgIp());
2672 }
2673 if (qsP.getRef() instanceof IQuimpBOAPoint2dFilter) {
2674 if (previousConversion == isnake) {
2675 dataToProcess = snakeToProcess.asList();
2676 }
2677 IQuimpBOAPoint2dFilterithub/celldynamics/quimp/plugin/snakes/IQuimpBOAPoint2dFilter.html#IQuimpBOAPoint2dFilter">IQuimpBOAPoint2dFilter qsPcast = (IQuimpBOAPoint2dFilter) qsP.getRef();
2678 qsPcast.attachData(dataToProcess);
2679 dataToProcess = qsPcast.runPlugin();
2680 previousConversion = ipoint;
2681 }
2682 if (qsP.getRef() instanceof IQuimpBOASnakeFilter) {
2683 if (previousConversion == ipoint) {
2684
2685 snakeToProcess = new QuimpDataConverter(dataToProcess).getSnake(snake.getSnakeID());
2686 }
2687 IQuimpBOASnakeFilter/github/celldynamics/quimp/plugin/snakes/IQuimpBOASnakeFilter.html#IQuimpBOASnakeFilter">IQuimpBOASnakeFilter qsPcast = (IQuimpBOASnakeFilter) qsP.getRef();
2688 qsPcast.attachData(snakeToProcess);
2689 snakeToProcess = qsPcast.runPlugin();
2690 previousConversion = isnake;
2691 }
2692 }
2693
2694
2695 switch (previousConversion) {
2696 case ipoint:
2697 outsnake = new QuimpDataConverter(dataToProcess).getSnake(snake.getSnakeID());
2698 break;
2699 case isnake:
2700 outsnake = snakeToProcess;
2701 outsnake.setSnakeID(snake.getSnakeID());
2702 break;
2703 default:
2704 throw new IllegalArgumentException("Unknown previousConversion");
2705 }
2706 } else {
2707 LOGGER.debug("sPluginList empty");
2708 }
2709 return outsnake;
2710 }
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723 void setScales() {
2724 GenericDialog gd = new GenericDialog("Set image scale", window);
2725 gd.addNumericField("Frame interval (seconds)", qState.boap.getImageFrameInterval(), 3);
2726 gd.addNumericField("Pixel width (\u00B5m)", qState.boap.getImageScale(), 6);
2727 gd.showDialog();
2728
2729 double tempFI = gd.getNextNumber();
2730 double tempP = gd.getNextNumber();
2731
2732 if (gd.invalidNumber()) {
2733 IJ.error("Values invalid");
2734 BOA_.log("Scale was not updated:\n\tinvalid input");
2735 } else if (gd.wasOKed()) {
2736 qState.boap.setImageFrameInterval(tempFI);
2737 qState.boap.setImageScale(tempP);
2738 updateImageScale();
2739 BOA_.log("Scale successfully updated");
2740 }
2741
2742 }
2743
2744
2745
2746
2747 void updateImageScale() {
2748 imageGroup.getOrgIpl().getCalibration().frameInterval = qState.boap.getImageFrameInterval();
2749 imageGroup.getOrgIpl().getCalibration().pixelHeight = qState.boap.getImageScale();
2750 imageGroup.getOrgIpl().getCalibration().pixelWidth = qState.boap.getImageScale();
2751 }
2752
2753
2754
2755
2756
2757
2758 private boolean loadSnakes() {
2759
2760 YesNoCancelDialog yncd = new YesNoCancelDialog(IJ.getInstance(), "Load associated snakes?",
2761 "\tLoad associated snakes?\n");
2762 if (!yncd.yesPressed()) {
2763 return false;
2764 }
2765
2766 OutlineHandlerHandler.html#OutlineHandler">OutlineHandler otlineH = new OutlineHandler(qState.boap.readQp);
2767 if (!otlineH.readSuccess) {
2768 BOA_.log("Could not read in snakes");
2769 return false;
2770 }
2771
2772
2773 qState.nest.addOutlinehandler(otlineH);
2774 imageGroup.setProcessor(otlineH.getStartFrame());
2775 imageGroup.updateOverlay(otlineH.getStartFrame());
2776 BOA_.log("Successfully read snakes");
2777 return true;
2778 }
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791 void addCell(final Roi r, int f) {
2792 SnakeHandler snakeH = qState.nest.addHandler(r, f);
2793 if (snakeH == null) {
2794 return;
2795 }
2796 Snake snake = snakeH.getLiveSnake();
2797 imageGroup.setProcessor(f);
2798 try {
2799 LOGGER.debug("Use options: " + qState.segParam.toString());
2800 imageGroup.drawPath(snake, f);
2801 tightenSnake(snake);
2802 imageGroup.drawPath(snake, f);
2803 snakeH.backupLiveSnake(f);
2804 Snake out = iterateOverSnakePlugins(snake);
2805 snakeH.storeThisSnake(out, f);
2806
2807
2808
2809 } catch (QuimpPluginException qpe) {
2810 qpe.setMessageSinkType(MessageSinkTypes.NONE);
2811 BOA_.log(qpe.handleException(null, "Error in filter module"));
2812 snakeH.storeLiveSnake(f);
2813 } catch (BoaException be) {
2814 snakeH.deleteStoreAt(f);
2815 snakeH.kill();
2816 snakeH.backupLiveSnake(f);
2817 snakeH.storeLiveSnake(f);
2818 be.setMessageSinkType(MessageSinkTypes.NONE);
2819 BOA_.log(be.handleException(null, "New snake failed to converge"));
2820 } catch (Exception e) {
2821 IJ.error("Error", e.getMessage());
2822 LOGGER.debug(e.getMessage(), e);
2823 } finally {
2824 imageGroup.updateOverlay(f);
2825
2826 qState.store(f);
2827 }
2828
2829 }
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844 boolean deleteCell(int offScreenX, int offScreenY, int frame) {
2845 if (qState.nest.isVacant()) {
2846 return false;
2847 }
2848
2849 SnakeHandler sh = qState.nest.findClosestTo(offScreenX, offScreenY, frame, 10);
2850 if (sh != null) {
2851 BOA_.log("Deleted cell " + sh.getID());
2852 qState.nest.removeHandler(sh);
2853 imageGroup.updateOverlay(frame);
2854 window.switchOffDelete();
2855 return true;
2856 } else {
2857 BOA_.log("Click the cell centre to delete");
2858 }
2859 return false;
2860 }
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873 boolean freezeCell(int offScreenX, int offScreenY, int frame) {
2874 if (qState.nest.isVacant()) {
2875 return false;
2876 }
2877
2878 SnakeHandler sh = qState.nest.findClosestTo(offScreenX, offScreenY, frame, 10);
2879 if (sh != null) {
2880 if (sh.isSnakeHandlerFrozen()) {
2881 sh.unfreezeHandler();
2882 BOA_.log("Unfreezed cell " + sh.getID());
2883 } else {
2884 sh.freezeHandler();
2885 BOA_.log("Freezed cell " + sh.getID());
2886 }
2887 imageGroup.updateOverlay(frame);
2888 window.setFreeze(false);
2889 return true;
2890 } else {
2891 BOA_.log("Click the cell centre to freeze");
2892 }
2893 return false;
2894
2895 }
2896
2897
2898
2899
2900
2901
2902
2903
2904 void deleteSegmentation(int x, int y, int frame) {
2905 SnakeHandler snakeH;
2906 Snake snake;
2907 ExtendedVector2d snakeV;
2908 ExtendedVector2dExtendedVector2d.html#ExtendedVector2d">ExtendedVector2d mmV = new ExtendedVector2d(x, y);
2909 List<Double> distance = new ArrayList<Double>();
2910
2911 for (int i = 0; i < qState.nest.size(); i++) {
2912 snakeH = qState.nest.getHandler(i);
2913
2914 if (snakeH.isStoredAt(frame)) {
2915 snake = snakeH.getStoredSnake(frame);
2916 snakeV = snake.getCentroid();
2917 distance.add(ExtendedVector2d.lengthP2P(mmV, snakeV));
2918 } else {
2919 distance.add(9999.0);
2920 }
2921 }
2922
2923 int minIndex = QuimPArrayUtils.minListIndex(distance);
2924
2925
2926 if (distance.get(minIndex) < 10) {
2927 BOA_.log("Deleted snake " + qState.nest.getHandler(minIndex).getID() + " from " + frame
2928 + " onwards");
2929 snakeH = qState.nest.getHandler(minIndex);
2930 snakeH.deleteStoreFrom(frame);
2931 imageGroup.updateOverlay(frame);
2932 window.switchOfftruncate();
2933 } else {
2934 BOA_.log("Click the cell centre to delete");
2935 }
2936 }
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947 void editSeg(int x, int y, int frame) {
2948 SnakeHandler snakeH;
2949 Snake snake;
2950 ExtendedVector2d snakeV;
2951 ExtendedVector2dExtendedVector2d.html#ExtendedVector2d">ExtendedVector2d mmV = new ExtendedVector2d(x, y);
2952 double[] distance = new double[qState.nest.size()];
2953
2954 for (int i = 0; i < qState.nest.size(); i++) {
2955 snakeH = qState.nest.getHandler(i);
2956 if (snakeH.isStoredAt(frame)) {
2957 snake = snakeH.getStoredSnake(frame);
2958 snakeV = snake.getCentroid();
2959 distance[i] = ExtendedVector2d.lengthP2P(mmV, snakeV);
2960 }
2961 }
2962 int minIndex = QuimPArrayUtils.minArrayIndex(distance);
2963 if (distance[minIndex] < 10 || qState.nest.size() == 1) {
2964 snakeH = qState.nest.getHandler(minIndex);
2965 qState.boap.editingID = minIndex;
2966 BOA_.log("Editing cell " + snakeH.getID());
2967 imageGroup.clearOverlay();
2968
2969 Roi r;
2970 if (qState.boap.useSubPixel == true) {
2971 r = snakeH.getStoredSnake(frame).asPolyLine();
2972 } else {
2973 r = snakeH.getStoredSnake(frame).asIntRoi();
2974 }
2975
2976 Roi.setColor(Color.cyan);
2977 canvas.getImage().setRoi(r);
2978 } else {
2979 BOA_.log("Click a cell centre to edit");
2980 }
2981 }
2982
2983
2984
2985
2986
2987
2988 void stopEdit() {
2989 Roi r = canvas.getImage().getRoi();
2990 Roi.setColor(Color.yellow);
2991 SnakeHandler snakeH = qState.nest.getHandler(qState.boap.editingID);
2992 snakeH.storeRoi((PolygonRoi) r, qState.boap.frame);
2993
2994 Snake stored = snakeH.getStoredSnake(qState.boap.frame);
2995 snakeH.backupThisSnake(stored, qState.boap.frame);
2996
2997 try {
2998 Snake out = iterateOverSnakePlugins(stored);
2999 snakeH.storeThisSnake(out, qState.boap.frame);
3000 } catch (QuimpException e) {
3001 e.setMessageSinkType(MessageSinkTypes.NONE);
3002 BOA_.log(e.handleException(null, "Error in filter module."));
3003
3004 }
3005 canvas.getImage().killRoi();
3006 imageGroup.updateOverlay(qState.boap.frame);
3007 qState.boap.editingID = -1;
3008 }
3009
3010
3011
3012
3013
3014
3015
3016 void deleteSeg(int x, int y) {
3017 }
3018
3019
3020
3021
3022
3023
3024 private void finish(boolean finish) {
3025 IJ.showStatus("BOA-FINISHING");
3026 YesNoCancelDialog ync;
3027 File testF;
3028 boolean saveStats = true;
3029 LOGGER.debug(qState.segParam.toString());
3030 for (SnakeHandler sh : qState.nest.getHandlers()) {
3031 sh.findLastFrame();
3032 }
3033 imageGroup.getOrgIpl().deleteRoi();
3034 if (qState.boap.saveSnake) {
3035 try {
3036
3037 testF = new File(qState.boap.deductNewParamFileName());
3038
3039 boolean testFileExists = testF.exists() && !testF.isDirectory();
3040 LOGGER.trace("Test for QCONF: " + testF.toString());
3041
3042
3043
3044
3045 if (finish || !testFileExists) {
3046 String saveIn = BOA_.qState.boap.getOutputFileCore().getParent();
3047 SaveDialog sd = new SaveDialog("Save segmentation data...", saveIn,
3048 BOA_.qState.boap.getFileName() + FileExtensions.newConfigFileExt, "");
3049 if (sd.getFileName() == null) {
3050 BOA_.log("Save canceled");
3051 return;
3052 }
3053
3054 BOA_.qState.boap.setOutputFileCore(sd.getDirectory() + sd.getFileName());
3055 testF = new File(qState.boap.deductNewParamFileName());
3056 testFileExists = testF.exists() && !testF.isDirectory();
3057 }
3058
3059 if (testFileExists) {
3060 ync = new YesNoCancelDialog(window, "Save Segmentation",
3061 QuimpToolsCollection.stringWrap("You are about to override previous results ("
3062 + testF.toString() + "). Is it ok?", QuimP.LINE_WRAP));
3063 if (!ync.yesPressed()) {
3064 return;
3065 }
3066 }
3067
3068
3069 if (QuimP.newFileFormat.get() == false) {
3070 qState.nest.writeSnakes();
3071 saveStats = true;
3072
3073 }
3074
3075
3076 List<CellStatsEval> ret =
3077 qState.nest.analyse(imageGroup.getOrgIpl().duplicate(), saveStats);
3078
3079 if (!qState.nest.isVacant()) {
3080
3081 Serializer<SnakePluginList> s;
3082 s = new Serializer<>(qState.snakePluginList, quimpInfo);
3083 s.setPretty();
3084 s.save(qState.boap.deductFilterFileName());
3085 s = null;
3086
3087 DataContainerilesystem/DataContainer.html#DataContainer">DataContainer dt = new DataContainer();
3088 dt.BOAState = qState;
3089
3090 dt.Stats = new StatsCollection();
3091 dt.Stats.copyFromCellStat(ret);
3092 Serializer<DataContainer> n = new Serializer<>(dt, quimpInfo);
3093 if (qState.boap.savePretty) {
3094 n.setPretty();
3095 }
3096 n.save(qState.boap.deductNewParamFileName());
3097 n = null;
3098 BOA_.log("Updated file " + BOA_.qState.boap.deductNewParamFileName());
3099 } else {
3100 BOA_.log("Nest empty. Nothing saved.");
3101 JOptionPane.showMessageDialog(window,
3102 QuimpToolsCollection.stringWrap(
3103 "There are not any cell segmented! Nothing has been saved.",
3104 QuimP.LINE_WRAP),
3105 "Info", JOptionPane.INFORMATION_MESSAGE);
3106 }
3107 } catch (IOException e) {
3108 IJ.error("Problem with saving files", e.getMessage());
3109 LOGGER.debug(e.getMessage(), e);
3110 return;
3111 }
3112 }
3113 }
3114
3115
3116
3117
3118
3119 void quit() {
3120 BOA_.log("Finish: Exiting BOA...");
3121 imageGroup.makeContourImage();
3122 BOA_.isBoaRunning = false;
3123 qState.nest = null;
3124 imageGroup.getOrgIpl().setOverlay(new Overlay());
3125 new StackWindow(imageGroup.getOrgIpl());
3126
3127 window.setImage(new ImagePlus());
3128 window.close();
3129 }
3130
3131 }
3132
3133
3134
3135
3136
3137 class ImageGroup {
3138
3139
3140
3141
3142 private ImagePlus orgIpl;
3143 private ImagePlus pathsIpl;
3144 private ImageStack orgStack;
3145 private ImageStack pathsStack;
3146 private ImageProcessor orgIp;
3147 private ImageProcessor pathsIp;
3148 private Overlay overlay;
3149 private Nest nest;
3150 private int iplWidth;
3151 private int iplHeight;
3152 private int iplStack;
3153
3154 private static final Logger LOGGER = LoggerFactory.getLogger(ImageGroup.class.getName());
3155
3156
3157
3158
3159
3160
3161
3162 public ImageGroup(ImagePlus ipl, Nest n) {
3163 nest = n;
3164
3165
3166
3167 orgIpl = ipl;
3168 orgIpl.setSlice(1);
3169 orgIpl.getCanvas().unzoom();
3170 orgIpl.getCanvas().getMagnification();
3171
3172 orgStack = orgIpl.getStack();
3173 orgIp = orgStack.getProcessor(1);
3174
3175 iplWidth = orgIp.getWidth();
3176 iplHeight = orgIp.getHeight();
3177 iplStack = orgIpl.getStackSize();
3178
3179
3180 pathsIpl = NewImage.createByteImage("Node Paths", iplWidth, iplHeight, iplStack,
3181 NewImage.FILL_BLACK);
3182 pathsStack = pathsIpl.getStack();
3183 pathsIpl.setSlice(1);
3184 pathsIp = pathsStack.getProcessor(1);
3185
3186 setIpSliceAll(1);
3187 setProcessor(1);
3188 }
3189
3190
3191
3192
3193
3194
3195
3196
3197 public void updateNest(Nest newNest) {
3198 nest = newNest;
3199 }
3200
3201 public ImagePlus getOrgIpl() {
3202 return orgIpl;
3203 }
3204
3205
3206
3207
3208
3209
3210
3211 public ImagePlus getPathsIpl() {
3212 return pathsIpl;
3213 }
3214
3215 public ImageProcessor getOrgIp() {
3216 return orgIp;
3217 }
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234 public void updateOverlay(int frame) {
3235 LOGGER.trace("Update overlay for frame " + frame);
3236 SnakeHandler snakeH;
3237 Snake snake;
3238 Snake back;
3239 int x;
3240 int y;
3241 TextRoi text;
3242 Roi r;
3243 overlay = new Overlay();
3244 BOA_.viewUpdater.connectSnakeObject(null);
3245 Color snakeColor = Color.YELLOW;
3246 for (int i = 0; i < nest.size(); i++) {
3247 snakeH = nest.getHandler(i);
3248 if (snakeH.isSnakeHandlerFrozen()) {
3249 snakeColor = Color.BLUE;
3250 } else {
3251 snakeColor = Color.YELLOW;
3252 }
3253 if (snakeH.isStoredAt(frame)) {
3254
3255
3256 if (BOA_.qState.boap.isProcessedSnakePlotted == true) {
3257 back = snakeH.getBackupSnake(frame);
3258
3259 r = back.asFloatRoi();
3260 r.setStrokeColor(Color.RED);
3261 overlay.add(r);
3262 }
3263
3264 BOA_.viewUpdater.connectSnakeObject(snakeH.getBackupSnake(frame));
3265
3266 snake = snakeH.getStoredSnake(frame);
3267
3268 r = snake.asFloatRoi();
3269 r.setStrokeColor(snakeColor);
3270 overlay.add(r);
3271 x = (int) Math.round(snake.getCentroid().getX()) - 15;
3272 y = (int) Math.round(snake.getCentroid().getY()) - 15;
3273 text = new TextRoi(x, y, " " + snake.getSnakeID());
3274 overlay.add(text);
3275
3276
3277 PointRoi pointR =
3278 new PointRoi((int) snake.getCentroid().getX(), (int) snake.getCentroid().getY());
3279 overlay.add(pointR);
3280
3281
3282 if (BOA_.qState.boap.isHeadPlotted == true) {
3283
3284 Point2d bp = new Point2d(snake.getHead().getX(), snake.getHead().getY());
3285
3286
3287 Vector2d dir =
3288 new Vector2d(snake.getHead().getNext().getNext().getNext().getX() - bp.getX(),
3289 snake.getHead().getNext().getNext().getNext().getY() - bp.getY());
3290 FloatPolygon fp = GraphicsElements.plotArrow(dir, bp, 12.0f, 0.3f);
3291 PolygonRoi polygonR = new PolygonRoi(fp, Roi.POLYGON);
3292 polygonR.setStrokeColor(Color.MAGENTA);
3293 polygonR.setFillColor(Color.MAGENTA);
3294 overlay.add(polygonR);
3295
3296
3297 FloatPolygon fp1 = GraphicsElements.getCircle(bp, 10);
3298 PolygonRoi polyginR1 = new PolygonRoi(fp1, Roi.POLYGON);
3299 polyginR1.setStrokeColor(Color.GREEN);
3300 polyginR1.setFillColor(Color.GREEN);
3301 overlay.add(polyginR1);
3302 }
3303 } else {
3304 BOA_.viewUpdater.connectSnakeObject(null);
3305 }
3306 }
3307 orgIpl.setOverlay(overlay);
3308 }
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318 public void updateToFrame(int frame) {
3319 clearPaths(frame);
3320 setProcessor(frame);
3321 setIpSliceAll(frame);
3322 }
3323
3324 public void clearOverlay() {
3325
3326 orgIpl.setOverlay(null);
3327 }
3328
3329
3330
3331
3332
3333
3334
3335
3336 public final void setProcessor(int i) {
3337 orgIp = orgStack.getProcessor(i);
3338 pathsIp = pathsStack.getProcessor(i);
3339
3340 }
3341
3342
3343
3344
3345
3346
3347 public final void setIpSliceAll(int i) {
3348
3349 pathsIpl.setSlice(i);
3350 orgIpl.setSlice(i);
3351 }
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364 public void clearPaths(int fromFrame) {
3365 for (int i = fromFrame; i <= BOA_.qState.boap.getFrames(); i++) {
3366 pathsIp = pathsStack.getProcessor(i);
3367 pathsIp.setValue(0);
3368 pathsIp.fill();
3369 }
3370 pathsIp = pathsStack.getProcessor(fromFrame);
3371 }
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381 public void drawPath(Snake snake, int frame) {
3382 pathsIp = pathsStack.getProcessor(frame);
3383 drawSnake(pathsIp, snake, false);
3384 }
3385
3386 private void drawSnake(final ImageProcessor ip, final Snake snake, boolean contrast) {
3387
3388 int x;
3389 int y;
3390 int intensity;
3391
3392 Node n = snake.getHead();
3393 do {
3394 x = (int) (n.getPoint().getX());
3395 y = (int) (n.getPoint().getY());
3396
3397 if (!contrast) {
3398 intensity = 245;
3399 } else {
3400
3401 if (ip.getPixel(x, y) > 127) {
3402 intensity = 10;
3403 } else {
3404 intensity = 245;
3405 }
3406 }
3407
3408
3409
3410 if (BOA_.qState.boap.getHeight() > 800) {
3411 drawPixel(x, y, intensity, true, ip);
3412 } else {
3413 drawPixel(x, y, intensity, false, ip);
3414 }
3415 n = n.getNext();
3416 } while (!n.isHead());
3417 }
3418
3419 private void drawPixel(int x, int y, int intensity, boolean fat, ImageProcessor ip) {
3420 ip.putPixel(x, y, intensity);
3421 if (fat) {
3422 ip.putPixel(x + 1, y, intensity);
3423 ip.putPixel(x + 1, y + 1, intensity);
3424 ip.putPixel(x, y + 1, intensity);
3425 ip.putPixel(x - 1, y + 1, intensity);
3426 ip.putPixel(x - 1, y, intensity);
3427 ip.putPixel(x - 1, y - 1, intensity);
3428 ip.putPixel(x, y - 1, intensity);
3429 ip.putPixel(x + 1, y - 1, intensity);
3430 }
3431 }
3432
3433 void makeContourImage() {
3434 ImagePlus contourIpl = NewImage.createByteImage("Contours", iplWidth, iplHeight, iplStack,
3435 NewImage.FILL_BLACK);
3436 ImageStack contourStack = contourIpl.getStack();
3437 contourIpl.setSlice(1);
3438 ImageProcessor contourIp;
3439
3440 for (int i = 1; i <= BOA_.qState.boap.getFrames(); i++) {
3441 orgIp = orgStack.getProcessor(i);
3442 contourIp = contourStack.getProcessor(i);
3443 contourIp.copyBits(orgIp, 0, 0, Blitter.COPY);
3444 }
3445
3446 drawCellRois(contourStack);
3447 new ImagePlus(orgIpl.getTitle() + "_Segmentation", contourStack).show();
3448
3449 }
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460 void zoom(final ImageCanvas ic, int frame, int snakeID) {
3461 LOGGER.trace("Zoom to frame: " + frame + " ID " + snakeID);
3462 if (nest.isVacant() || snakeID < 0) {
3463 return;
3464 }
3465 SnakeHandler snakeH;
3466 Snake snake;
3467
3468 try {
3469 snakeH = nest.getHandlerofId(snakeID);
3470 if (snakeH != null && snakeH.isStoredAt(frame)) {
3471 snake = snakeH.getStoredSnake(frame);
3472 } else {
3473 return;
3474 }
3475 } catch (IndexOutOfBoundsException | IllegalArgumentException e) {
3476 LOGGER.debug(e.getMessage(), e);
3477 return;
3478 }
3479
3480 Rectangle r = snake.getBounds();
3481 int border = 40;
3482
3483
3484 r.setBounds(r.x - border, r.y - border, r.width + border * 2, r.height + border * 2);
3485
3486
3487 double icAspect = (double) ic.getWidth() / (double) ic.getHeight();
3488 double rectAspect = r.getWidth() / r.getHeight();
3489 int newDim;
3490
3491 if (icAspect < rectAspect) {
3492
3493 newDim = (int) (r.getWidth() / icAspect);
3494 r.y = r.y - ((newDim - r.height) / 2);
3495 r.height = newDim;
3496 } else {
3497
3498 newDim = (int) (r.getHeight() * icAspect);
3499 r.x = r.x - ((newDim - r.width) / 2);
3500 r.width = newDim;
3501 }
3502
3503
3504
3505
3506 double newMag;
3507
3508 newMag = (double) ic.getHeight() / r.getHeight();
3509
3510 ic.setMagnification(newMag);
3511 Rectangle sr = ic.getSrcRect();
3512 sr.setBounds(r);
3513
3514 ic.repaint();
3515
3516 }
3517
3518 void unzoom(final ImageCanvas ic) {
3519
3520
3521 ic.unzoom();
3522
3523
3524 }
3525
3526
3527
3528
3529
3530
3531
3532
3533 void drawCellRois(final ImageStack stack) {
3534 Snake snake;
3535 SnakeHandler snakeH;
3536 ImageProcessor ip;
3537
3538 int x;
3539 int y;
3540 for (int s = 0; s < nest.size(); s++) {
3541 snakeH = nest.getHandler(s);
3542 for (int i = 1; i <= BOA_.qState.boap.getFrames(); i++) {
3543 if (snakeH.isStoredAt(i)) {
3544 snake = snakeH.getStoredSnake(i);
3545 ip = stack.getProcessor(i);
3546 ip.setColor(255);
3547 ip.draw(snake.asFloatRoi());
3548 x = (int) Math.round(snake.getHead().getX()) - 15;
3549 y = (int) Math.round(snake.getHead().getY()) - 15;
3550 ip.moveTo(x, y);
3551 ip.drawString(" " + snake.getSnakeID());
3552 LOGGER.trace("Snake head is at: " + snake.getHead().toString());
3553 }
3554 }
3555 }
3556 }
3557 }