diff --git a/pom.xml b/pom.xml index 4853dbb4..9e6b8c0e 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ imagej.public - http://maven.imagej.net/content/groups/public + https://maven.imagej.net/content/groups/public local-repo @@ -45,7 +45,7 @@ io.scif scifio - 0.37.3 + 0.45.0 io.scif @@ -115,18 +115,6 @@ 1.2 - - ome - formats-api - 5.5.3 - - - - ome - formats-bsd - 5.5.3 - - org.janelia.saalfeldlab n5-ij @@ -201,7 +189,7 @@ ome bioformats_package - 5.5.2 + 5.5.3 @@ -210,12 +198,6 @@ 1.8 - - net.imglib2 - imglib2 - 5.6.3 - - sc.fiji bigdataviewer_fiji @@ -228,6 +210,47 @@ 1.0.0-beta-16 + + net.imglib2 + imglib2-roi + 0.12.1 + + + + net.imglib2 + imglib2 + 5.12.0 + + + + net.preibisch + BigStitcher + 0.8.3 + + + + org.mastodon + mastodon + 1.0.0-beta-26 + + + + org.mastodon + mastodon-tracking + 1.0.0-beta-12 + + + + sc.fiji + bigdataviewer-core + 10.4.3 + + + + org.mastodon + mastodon-tomancak + 0.2.1 + @@ -241,7 +264,7 @@ com.zenjava javafx-maven-plugin - 8.8.4-SNAPSHOT + 8.8.3 Scientific Computing Facility spim.microOpenSPIM diff --git a/src/main/java/spim/hardware/BaslerCamera.java b/src/main/java/spim/hardware/BaslerCamera.java new file mode 100644 index 00000000..bf9640bf --- /dev/null +++ b/src/main/java/spim/hardware/BaslerCamera.java @@ -0,0 +1,23 @@ +package spim.hardware; + +import mmcorej.CMMCore; + +/** + * Author: HongKee Moon (moon@mpi-cbg.de), Scientific Computing Facility + * Organization: MPI-CBG Dresden + * Date: June 2023 + */ +public class BaslerCamera extends Camera { + static { + Device.installFactory(new Device.Factory() { + @Override + public Device manufacture(CMMCore core, String label ) { + return new BaslerCamera(core, label); + } + }, "BaslerCamera", SPIMSetup.SPIMDevice.CAMERA1, SPIMSetup.SPIMDevice.CAMERA2); + } + + public BaslerCamera(CMMCore core, String label) { + super(core, label); + } +} diff --git a/src/main/java/spim/hardware/SPIMSetup.java b/src/main/java/spim/hardware/SPIMSetup.java index e168adbc..abda608f 100644 --- a/src/main/java/spim/hardware/SPIMSetup.java +++ b/src/main/java/spim/hardware/SPIMSetup.java @@ -202,7 +202,7 @@ public void setPosition(Double x, Double y, Double z, Double t) { getZStage().setPosition(z); if (t != null) - getThetaStage().setPosition(t); + setAngle(t); } /** @@ -237,7 +237,11 @@ public Vector3D getPosition() { } public double getAngle() { - return getThetaStage().getPosition(); + return getThetaStage() == null ? 0d : getThetaStage().getPosition(); + } + + public void setAngle(double r) { + if (getThetaStage() != null) getThetaStage().setPosition(r); } public CMMCore getCore() { diff --git a/src/main/java/spim/hardware/TSICam.java b/src/main/java/spim/hardware/TSICam.java new file mode 100644 index 00000000..fc806435 --- /dev/null +++ b/src/main/java/spim/hardware/TSICam.java @@ -0,0 +1,23 @@ +package spim.hardware; + +import mmcorej.CMMCore; + +/** + * Author: HongKee Moon (moon@mpi-cbg.de), Scientific Computing Facility + * Organization: MPI-CBG Dresden + * Date: May 2023 + */ +public class TSICam extends Camera { + static { + Device.installFactory(new Device.Factory() { + @Override + public Device manufacture(CMMCore core, String label ) { + return new TSICam(core, label); + } + }, "TSICam", SPIMSetup.SPIMDevice.CAMERA1, SPIMSetup.SPIMDevice.CAMERA2); + } + + public TSICam(CMMCore core, String label) { + super(core, label); + } +} diff --git a/src/main/java/spim/io/BDVMicroManagerStorage.java b/src/main/java/spim/io/BDVMicroManagerStorage.java index c637bd08..cd21327c 100644 --- a/src/main/java/spim/io/BDVMicroManagerStorage.java +++ b/src/main/java/spim/io/BDVMicroManagerStorage.java @@ -31,6 +31,8 @@ import mpicbg.spim.data.sequence.Tile; import mpicbg.spim.data.sequence.TimePoint; import mpicbg.spim.data.sequence.TimePoints; +import net.haesleinhuepf.clij.clearcl.ClearCLBuffer; +import net.haesleinhuepf.clijx.CLIJx; import net.imglib2.Cursor; import net.imglib2.FinalDimensions; import net.imglib2.RandomAccessibleInterval; @@ -114,6 +116,7 @@ public class BDVMicroManagerStorage implements Storage { private final int timesteps_; private int angles_; private int channels_; + private boolean fusionChannel_; int width; int height; @@ -133,13 +136,14 @@ public class BDVMicroManagerStorage implements Storage { private Map< Integer, ExportMipmapInfo > perSetupExportMipmapInfo = new HashMap< Integer, ExportMipmapInfo >(); private ArrayList< ViewRegistration > registrations = new ArrayList< ViewRegistration >(); - public BDVMicroManagerStorage(DefaultDatastore store, String directory, String prefix, int channels, int timeSeqs, boolean newDataSet) throws IOException { + public BDVMicroManagerStorage(DefaultDatastore store, String directory, String prefix, int channels, int timeSeqs, boolean newDataSet, boolean fusionChannel) throws IOException { store_ = store; dir_ = directory; prefix_ = prefix; store_.setSavePath(dir_); store_.setName(new File(dir_).getName()); + fusionChannel_ = fusionChannel; isDatasetWritable_ = newDataSet; // Must be informed of events before traditional consumers, so that we @@ -298,6 +302,10 @@ public void freeze() throws IOException { e.printStackTrace(); } } + + if(reader != null) { + reader.close(); + } } private void saveXml(String n5FilePath, int numTimepoints) throws SpimDataException { @@ -409,7 +417,9 @@ public void putImage(Image image) throws IOException { final int time = coords.getT(); final int angle = coords.getP(); - final int setupId = channel + angle * channels; + final int setupId = (fusionChannel_ ? channel / 2 : channel) + angle * (fusionChannel_ ? channels / 2 : channels); + +// System.out.println("SetupID: " + setupId); String dataset = BdvN5Format.getPathName(setupId, time, 0); @@ -463,6 +473,7 @@ public void putImage(Image image) throws IOException { writer.setAttribute( pathName, DATA_TYPE_KEY, bytesPerPixel == 1 ? DataType.UINT8 : DataType.UINT16 ); final double pixelSizeUm = image.getMetadata().getPixelSizeUm(); + final double binning = image.getMetadata().getBinning(); final double zStepSize = image.getMetadata().getUserData().getDouble("Z-Step-um", 1); String punit = "µm"; @@ -472,7 +483,7 @@ public void putImage(Image image) throws IOException { final BasicViewSetup setup = new BasicViewSetup( setupId, "" + (setupId), size, voxelSize ); setup.setAttribute( new Angle( angle ) ); - setup.setAttribute( new Channel( channel ) ); + setup.setAttribute( new Channel( fusionChannel_ ? channel / 2 : channel ) ); setup.setAttribute( new Illumination( 0 ) ); setup.setAttribute( new Tile( 0 ) ); setups.put( setupId, setup ); @@ -480,9 +491,14 @@ public void putImage(Image image) throws IOException { final ExportMipmapInfo autoMipmapSettings = ProposeMipmaps.proposeMipmaps( new BasicViewSetup( 0, "", size, voxelSize ) ); perSetupExportMipmapInfo.put(setupId, autoMipmapSettings ); + double zUnit = 1.524d; + if (pixelSizeUm != 0d) { + zUnit = zStepSize / pixelSizeUm; + } + // create SourceTransform from the images calibration final AffineTransform3D sourceTransform = new AffineTransform3D(); - sourceTransform.set( 1.0, 0, 0, 0, 0, 1.0, 0, 0, 0, 0, 9.378, 0 ); + sourceTransform.set( 1.0, 0, 0, 0, 0, 1.0, 0, 0, 0, 0, zUnit, 0 ); registrations.add( new ViewRegistration( time, setupId, sourceTransform ) ); @@ -517,29 +533,99 @@ public void putImage(Image image) throws IOException { RandomAccessibleInterval source; if(!amLoading_) { - if (bytesPerPixel == 1) { - final Img rai = ImageJFunctions.wrap(new ImagePlus("t=" + time + "/angle=" + angle, imageStacks[channel])); - source = Views.zeroMin(rai); - } else { - final Img rai = ImageJFunctions.wrap(new ImagePlus("t=" + time + "/angle=" + angle, imageStacks[channel])); - source = Views.zeroMin(rai); + if(!fusionChannel_) { + if (bytesPerPixel == 1) { + final Img rai = ImageJFunctions.wrap(new ImagePlus("t=" + time + "/angle=" + angle, imageStacks[channel])); + source = Views.zeroMin(rai); + } else { + final Img rai = ImageJFunctions.wrap(new ImagePlus("t=" + time + "/angle=" + angle, imageStacks[channel])); + source = Views.zeroMin(rai); + } + + if (writer != null) { + try { + N5Utils.saveBlock(source, writer, dataset, gridPosition, exec); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + } } - if (writer != null) { + System.out.println(coords.getC() + "/" + channels); + + if(fusionChannel_ && coords.getC() % 2 == 1) + { + int nChannels = coords.getC(); + for(int i = 0; i < 2; i++) { + CLIJx clijx = CLIJx.getInstance(); + ClearCLBuffer gpu_input1 = clijx.push(new ImagePlus("gpu_input", imageStacks[nChannels])); + ClearCLBuffer gpu_input2 = clijx.push(new ImagePlus("gpu_input", imageStacks[nChannels - 1])); + + // create an image with correct size and type on GPU for output + ClearCLBuffer gpu_output = clijx.create(gpu_input1); + + clijx.maximumImages(gpu_input1, gpu_input2, gpu_output); + + ClearCLBuffer background_substrackted_image = clijx.create(gpu_input1); + float sigma1x = 1.0f; + float sigma1y = 1.0f; + float sigma1z = 1.0f; + float sigma2x = 5.0f; + float sigma2y = 5.0f; + float sigma2z = 5.0f; + clijx.differenceOfGaussian3D(gpu_output, background_substrackted_image, sigma1x, sigma1y, sigma1z, sigma2x, sigma2y, sigma2z); + + // uncomment the below if you want to see the result + ImagePlus imp_output = clijx.pull(gpu_output); + imp_output.setTitle("t=" + time + "/angle=" + angle); + // imp_output.getProcessor().resetMinAndMax(); + // imp_output.show(); + + // clean up memory on the GPU + gpu_input1.close(); + gpu_input2.close(); + gpu_output.close(); + background_substrackted_image.close(); + + if (bytesPerPixel == 1) { + final Img rai = ImageJFunctions.wrap(imp_output); + source = Views.zeroMin(rai); + } else { + final Img rai = ImageJFunctions.wrap(imp_output); + source = Views.zeroMin(rai); + } + + if (writer != null) { + try { + N5Utils.saveBlock(source, writer, dataset, gridPosition, exec); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + if(coords.getC() == channels - 1) { + // Save SpimData format for N5 storage +// System.out.println("xml Saved - t:" + time); try { - N5Utils.saveBlock(source, writer, dataset, gridPosition, exec); - } catch (InterruptedException e) { - e.printStackTrace(); - } catch (ExecutionException e) { - e.printStackTrace(); - } catch (Exception e) { + saveXml(dir_ + "/" + prefix_ + ".n5", time + 1); + } catch (SpimDataException e) { e.printStackTrace(); } } } - if(coords.getC() == image.getMetadata().getUserData().getInteger("Channels", 1) - 1) + if(coords.getC() == image.getMetadata().getUserData().getInteger("Channels", 1) - 1) { timeFinished_.put(time, true); + } } if (!coordsToFilename_.containsKey(coords)) { diff --git a/src/main/java/spim/io/OMETIFFStorage.java b/src/main/java/spim/io/OMETIFFStorage.java index a2a98153..d20a1510 100644 --- a/src/main/java/spim/io/OMETIFFStorage.java +++ b/src/main/java/spim/io/OMETIFFStorage.java @@ -683,10 +683,9 @@ public Image getImage(Coords coords) { ImageProcessor ip = null; try { ip = ipr.openProcessors( index )[ channel ]; - } catch (FormatException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); + } catch (IllegalArgumentException | FormatException | ArrayIndexOutOfBoundsException | IOException e) { +// e.printStackTrace(); + return null; } ImagePlus imp = new ImagePlus(prefix_, ip); diff --git a/src/main/java/spim/mm/MMUtils.java b/src/main/java/spim/mm/MMUtils.java index 7fbb5b39..29c66a6c 100644 --- a/src/main/java/spim/mm/MMUtils.java +++ b/src/main/java/spim/mm/MMUtils.java @@ -105,7 +105,7 @@ public static boolean fixSystemLibrairies( Stage stage ) try { openBrowser( - "http://www.micro-manager.org/wiki/Download%20Micro-Manager_Latest%20Release"); + "https://micro-manager.org/Download_Micro-Manager_Latest_Release"); } catch ( URISyntaxException e ) { diff --git a/src/main/java/spim/mm/MicroManager.java b/src/main/java/spim/mm/MicroManager.java index 508b6bb6..9357952f 100644 --- a/src/main/java/spim/mm/MicroManager.java +++ b/src/main/java/spim/mm/MicroManager.java @@ -19,6 +19,7 @@ import mmcorej.TaggedImage; import mmcorej.org.json.JSONObject; +import net.imagej.patcher.LegacyInjector; import org.micromanager.Studio; import org.micromanager.acquisition.SequenceSettings; import org.micromanager.acquisition.internal.AcquisitionWrapperEngine; @@ -76,6 +77,7 @@ public class MicroManager implements PlugIn, CommandListener static { WindowPositioningPatch.applyPatches(); WindowPositioningPatch.applyMMPatches(); + LegacyInjector.preinit(); } private MicroManager(ObjectProperty studioObjectProperty, ObjectProperty refreshEventProperty) { diff --git a/src/main/java/spim/model/data/AcquisitionSetting.java b/src/main/java/spim/model/data/AcquisitionSetting.java index c2eb1669..95cfeb0f 100644 --- a/src/main/java/spim/model/data/AcquisitionSetting.java +++ b/src/main/java/spim/model/data/AcquisitionSetting.java @@ -51,6 +51,7 @@ public class AcquisitionSetting // Save Image panel Boolean enabledSaveImages; String directory; + String folder; String filename; Object savingFormat; Boolean saveMIP; @@ -62,9 +63,6 @@ public class AcquisitionSetting // ExperimentNote String experimentNote; - // On-the-fly - Boolean onTheFly; - public Boolean getEnabledTimePoints() { return enabledTimePoints; @@ -185,6 +183,14 @@ public void setDirectory( String directory ) this.directory = directory; } + public String getFolder() { + return folder; + } + + public void setFolder(String folder) { + this.folder = folder; + } + public String getFilename() { return filename; @@ -239,19 +245,11 @@ public void setExperimentNote(String experimentNote) { this.experimentNote = experimentNote; } - public Boolean getOnTheFly() { - return onTheFly; - } - - public void setOnTheFly( Boolean onTheFly ) { - this.onTheFly = onTheFly; - } - public AcquisitionSetting() { } - public AcquisitionSetting(BooleanProperty enabledTimePoints, ArrayList< TimePointItem > timePointItems, BooleanProperty enabledPositions, ArrayList< PositionItem > positionItems, BooleanProperty enabledZStacks, ObjectProperty acquisitionOrder, BooleanProperty enabledChannels, int selectedTabIndex, ArrayList< ChannelItem > channelItems, ArrayList< ChannelItem > channelItemsArduino, BooleanProperty enabledSaveImages, StringProperty directory, StringProperty filename, ObjectProperty savingFormat, BooleanProperty saveMIP, ObjectProperty roiRectangle, DoubleProperty rotateStepSize, StringProperty experimentNote, BooleanProperty onTheFly) + public AcquisitionSetting(BooleanProperty enabledTimePoints, ArrayList< TimePointItem > timePointItems, BooleanProperty enabledPositions, ArrayList< PositionItem > positionItems, BooleanProperty enabledZStacks, ObjectProperty acquisitionOrder, BooleanProperty enabledChannels, int selectedTabIndex, ArrayList< ChannelItem > channelItems, ArrayList< ChannelItem > channelItemsArduino, BooleanProperty enabledSaveImages, StringProperty directory, StringProperty folder, StringProperty filename, ObjectProperty savingFormat, BooleanProperty saveMIP, ObjectProperty roiRectangle, DoubleProperty rotateStepSize, StringProperty experimentNote) { // 1.1 Time points panel this.enabledTimePoints = enabledTimePoints.get(); @@ -278,6 +276,7 @@ public AcquisitionSetting(BooleanProperty enabledTimePoints, ArrayList< TimePoin // 6. Save Image panel this.enabledSaveImages = enabledSaveImages.get(); this.directory = directory.get(); + this.folder = folder.get(); this.filename = filename.get(); this.savingFormat = savingFormat.get(); this.saveMIP = saveMIP.get(); @@ -288,9 +287,6 @@ public AcquisitionSetting(BooleanProperty enabledTimePoints, ArrayList< TimePoin // Experiment Note this.experimentNote = experimentNote.get(); - - // OnTheFly - this.onTheFly = onTheFly.get(); } public static AcquisitionSetting load( File file ) { diff --git a/src/main/java/spim/ui/view/component/AcquisitionPanel.java b/src/main/java/spim/ui/view/component/AcquisitionPanel.java index e5fd4693..693a1c93 100644 --- a/src/main/java/spim/ui/view/component/AcquisitionPanel.java +++ b/src/main/java/spim/ui/view/component/AcquisitionPanel.java @@ -44,6 +44,7 @@ import org.apache.commons.io.FileUtils; import org.micromanager.Studio; +import org.micromanager.display.DisplayWindow; import org.micromanager.events.internal.DefaultGUIRefreshEvent; import org.micromanager.internal.MMStudio; import spim.hardware.Camera; @@ -55,6 +56,7 @@ import spim.model.data.PositionItem; import spim.model.data.TimePointItem; import spim.model.event.ControlEvent; +import spim.ui.view.component.util.AdvancedPlugins; import spim.ui.view.component.widgets.cube.SliceCube; import spim.ui.view.component.widgets.cube.StackCube; import spim.ui.view.component.widgets.pane.CheckboxPane; @@ -68,11 +70,11 @@ import java.io.*; import java.nio.file.Path; import java.nio.file.Paths; +import java.text.DateFormat; import java.text.DecimalFormat; +import java.text.SimpleDateFormat; import java.util.*; import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.stream.Collectors; import static spim.ui.view.component.util.TableViewUtil.createTimePointItemDataView; @@ -130,10 +132,15 @@ public class AcquisitionPanel extends BorderPane implements SPIMSetupInjectable // Save Image panel BooleanProperty enabledSaveImages; StringProperty directory; + StringProperty folder; StringProperty filename; + SpinnerValueFactory.IntegerSpinnerValueFactory incSpinnerValueFactory; ObjectProperty savingFormat; BooleanProperty saveMIP; + BooleanProperty ablationSupport; + BooleanProperty ablationDisabled; ObjectProperty roiRectangle; + File folderFile; // Experiment note StringProperty experimentNote; @@ -200,6 +207,7 @@ public class AcquisitionPanel extends BorderPane implements SPIMSetupInjectable // On-the-fly BooleanProperty onTheFly; + BooleanProperty onChannelFusion; // Current Position Index IntegerProperty currentPositionIndex; @@ -441,8 +449,123 @@ public AcquisitionPanel(SPIMSetup setup, Studio studio, StagePanel stagePanel, T BorderPane.setMargin(acquireHBox, new Insets(12,12,12,12)); + // Acquisition Setting Buttons + final HBox acqSettings = new HBox(); + acqSettings.setSpacing(5); + acqSettings.setAlignment( Pos.CENTER_LEFT ); + + Button saveButton = new Button( "SAVE" ); + saveButton.setMinSize( 100, 30 ); + saveButton.setStyle("-fx-font: 12 arial; -fx-base: #69e760;"); + saveButton.setOnAction( new EventHandler< ActionEvent >() + { + @Override public void handle( ActionEvent event ) + { + final FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle( "µOpenSPIM AcquisitionSetting file" ); + fileChooser.getExtensionFilters().addAll( + new FileChooser.ExtensionFilter( "µOpenSPIM AcquisitionSetting file", "*.xml" ) + ); + + File file = fileChooser.showSaveDialog( getScene().getWindow() ); + if ( file != null ) + { + AcquisitionSetting.save( file, getAcquisitionSetting() ); + } + } + } ); + + Button loadButton = new Button( "LOAD" ); + loadButton.setMinSize( 100, 30 ); + loadButton.setStyle("-fx-font: 12 arial; -fx-base: #e7e45d;"); + loadButton.setOnAction( new EventHandler< ActionEvent >() + { + @Override public void handle( ActionEvent event ) + { + final FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle( "µOpenSPIM AcquisitionSetting file" ); + fileChooser.getExtensionFilters().addAll( + new FileChooser.ExtensionFilter( "µOpenSPIM AcquisitionSetting file", "*.xml" ) + ); + + File file = fileChooser.showOpenDialog( getScene().getWindow() ); + if ( file != null ) + { + AcquisitionSetting setting = AcquisitionSetting.load( file ); + if(setting != null) { + // Update GUI + updateUI( setting ); + } + } + } + } ); + + loadButton.setOnDragOver( new EventHandler< DragEvent >() + { + @Override public void handle( DragEvent event ) + { + Dragboard db = event.getDragboard(); + if ( db.hasFiles() && db.getFiles().size() == 1 ) + { + event.acceptTransferModes( TransferMode.COPY ); + } + else + { + event.consume(); + } + } + } ); + + loadButton.setOnDragDropped( new EventHandler< DragEvent >() + { + @Override public void handle( DragEvent event ) + { + Dragboard db = event.getDragboard(); + boolean success = false; + if ( db.hasFiles() && db.getFiles().size() == 1 ) + { + if(db.getFiles().get(0).isFile()) { + final File file = db.getFiles().get(0); + final AcquisitionSetting setting = AcquisitionSetting.load( file ); + if(setting != null) { + // Update GUI + updateUI( setting ); + success = true; + } + } + } + + event.setDropCompleted( success ); + event.consume(); + } + } ); + + Button clearButton = new Button( "CLEAR" ); + clearButton.setMinSize( 100, 30 ); + clearButton.setStyle("-fx-font: 12 arial; -fx-base: #ffbec4;"); + clearButton.setOnAction( new EventHandler< ActionEvent >() + { + @Override public void handle( ActionEvent event ) + { + Optional< ButtonType > results = new Alert( Alert.AlertType.WARNING, "Are you sure?", ButtonType.YES, ButtonType.CANCEL).showAndWait(); + + if( results.isPresent() && results.get() == ButtonType.YES) { + clearAcquisitionSetting(); + } else { + System.err.println("Clear acquisition setting stopped by user cancellation."); + } + } + } ); + + acqSettings.getChildren().addAll( saveButton, loadButton, clearButton ); + BorderPane.setMargin(acqSettings, new Insets(12,12,12,12)); + + final TitledPane acqSettingPane = new TitledPane( "Save/Load Settings", acqSettings ); + acqSettingPane.setCollapsible( false ); + // listbox for Position list SplitPane timePositionSplit = new SplitPane( + acqSettingPane, createPositionListPane(positionItemTableView, currentPositionItemTableView), createTimePointsPane() ); timePositionSplit.setOrientation( Orientation.VERTICAL ); @@ -594,6 +717,9 @@ public AcquisitionPanel(SPIMSetup setup, Studio studio, StagePanel stagePanel, T CheckBox onTheFlyCheckBox = new CheckBox("on-the-fly processing (based on clij)"); onTheFly = onTheFlyCheckBox.selectedProperty(); + CheckBox onFusionChannels = new CheckBox("2-channel fusion (based on clij)"); + onChannelFusion = onFusionChannels.selectedProperty(); + Hyperlink clijHyperlink = new Hyperlink("https://clij.github.io/"); clijHyperlink.setOnAction(new EventHandler() { @Override @@ -607,6 +733,10 @@ public void handle(ActionEvent actionEvent) { onTheFlyHBox.setAlignment( Pos.CENTER_LEFT ); onTheFlyHBox.setPadding(new Insets(5)); + HBox onFusionchannelHBox = new HBox(3, onFusionChannels); + onFusionchannelHBox.setAlignment( Pos.CENTER_LEFT ); + onFusionchannelHBox.setPadding(new Insets(5)); + Tab antiDriftTab = new Tab("Anti-drift", new VBox(2, antiDriftPane, chBox)); antiDriftTab.setClosable(false); @@ -616,10 +746,22 @@ public void handle(ActionEvent actionEvent) { Tab binningTab = new Tab("Binning", binningHBox); binningTab.setClosable(false); - Tab onTheFlyTab = new Tab("On-the-fly", onTheFlyHBox); + Tab onTheFlyTab = new Tab("On-the-fly", new VBox(2, onTheFlyHBox, onFusionchannelHBox)); onTheFlyTab.setClosable(false); - TabPane acquisitionTabPane = new TabPane( antiDriftTab, binningTab, onTheFlyTab, roiTab ); + CheckBox ablationCheckbox = new CheckBox( "Generate ablation.tiff (works with OMETIFF as saving format)" ); + + ablationSupport = ablationCheckbox.selectedProperty(); + ablationDisabled = ablationCheckbox.disableProperty(); + + HBox ablationHBox = new HBox(3, ablationCheckbox); + ablationHBox.setAlignment( Pos.CENTER_LEFT ); + ablationHBox.setPadding(new Insets(5)); + + Tab ablationTab = new Tab("Ablation", ablationHBox); + ablationTab.setClosable(false); + + TabPane acquisitionTabPane = new TabPane( antiDriftTab, binningTab, onTheFlyTab, ablationTab, roiTab ); acquisitionTabPane.setMinHeight(120); Button acqHelpButton = createHelpButton(); @@ -628,17 +770,81 @@ public void handle(ActionEvent actionEvent) { final TitledPane acqBoxPane = new TitledPane( "", acquireHBox ); acqBoxPane.setCollapsible( false ); - LabeledPane acquisitionPane = new LabeledPane( "Acquisition", new VBox(10, acquisitionTabPane, acqBoxPane), acqHelpButton, 0 ); + // Open with advanced plugins + final HBox pluginsBox = new HBox(); + pluginsBox.setSpacing(5); + pluginsBox.setAlignment( Pos.CENTER_LEFT ); + + Button bdvButton = new Button( "Open with BigDataViewer" ); + bdvButton.setMinSize( 100, 30 ); + bdvButton.setStyle("-fx-font: 12 arial; -fx-base: #69e760;"); + bdvButton.setOnAction( new EventHandler< ActionEvent >() + { + @Override public void handle( ActionEvent event ) + { + DisplayWindow displayWindow = studioProperty.get().displays().getCurrentWindow(); + + if(displayWindow == null) { + new Alert( Alert.AlertType.WARNING, "Please, load a dataset first").showAndWait(); + System.err.println("There is no dataset to be opened."); + return; + } + + AdvancedPlugins.loadDataWithBDV(displayWindow); + } + } ); + + Button bsButton = new Button( "Open with BigStitcher" ); + bsButton.setMinSize( 100, 30 ); + bsButton.setStyle("-fx-font: 12 arial; -fx-base: #e7e45d;"); + bsButton.setOnAction( new EventHandler< ActionEvent >() + { + @Override public void handle( ActionEvent event ) + { + DisplayWindow displayWindow = studioProperty.get().displays().getCurrentWindow(); + + if(displayWindow == null) { + System.err.println("There is no dataset to be opened. Trying to open the current folder."); + AdvancedPlugins.openBigStitcherWindow( folderFile.getAbsolutePath() ); + } else { + AdvancedPlugins.openBigStitcherWindow( displayWindow.getDatastore().getSavePath() ); + } + } + } ); + + Button mstdButton = new Button( "Open with Mastodon" ); + mstdButton.setMinSize( 100, 30 ); + mstdButton.setStyle("-fx-font: 12 arial; -fx-base: #ffbec4;"); + mstdButton.setOnAction( new EventHandler< ActionEvent >() + { + @Override public void handle( ActionEvent event ) + { + DisplayWindow displayWindow = studioProperty.get().displays().getCurrentWindow(); + if(displayWindow == null) { + System.err.println("There is no dataset to be opened. Trying to open the current folder."); + AdvancedPlugins.openMastodonWindow( folderFile.getAbsolutePath() ); + } else { + AdvancedPlugins.openMastodonWindow( displayWindow.getDatastore().getSavePath() ); + } + } + } ); + + pluginsBox.getChildren().addAll( bdvButton, bsButton, mstdButton ); + + final TitledPane pluginsPane = new TitledPane( "Open with advanced plugins", pluginsBox ); + pluginsPane.setCollapsible( false ); + + LabeledPane acquisitionPane = new LabeledPane( "Acquisition", new VBox(10, acquisitionTabPane, acqBoxPane, pluginsPane), acqHelpButton, 0 ); SplitPane zStackAcqTabs = new SplitPane(createZStackPane(stagePanel), acquisitionPane); zStackAcqTabs.setOrientation( Orientation.VERTICAL ); - zStackAcqTabs.setDividerPositions( 0.8 ); + zStackAcqTabs.setDividerPositions( 0.2 ); zStackAcqTabs.heightProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue observable, Number oldValue, Number newValue) { - zStackAcqTabs.setDividerPositions( 0.8 ); + zStackAcqTabs.setDividerPositions( 0.2, 0.65 ); } }); @@ -705,121 +911,6 @@ public void changed(ObservableValue observable, Number oldValu CheckboxPane channelPane = new CheckboxPane( "Select Channels/Pins", channelTabPane, helpButton); enabledChannels = channelPane.selectedProperty(); - // Acquisition Setting Buttons - final HBox acqSettings = new HBox(); - acqSettings.setSpacing(5); - acqSettings.setAlignment( Pos.CENTER_LEFT ); - - Button saveButton = new Button( "SAVE" ); - saveButton.setMinSize( 100, 30 ); - saveButton.setStyle("-fx-font: 12 arial; -fx-base: #69e760;"); - saveButton.setOnAction( new EventHandler< ActionEvent >() - { - @Override public void handle( ActionEvent event ) - { - final FileChooser fileChooser = new FileChooser(); - fileChooser.setTitle( "µOpenSPIM AcquisitionSetting file" ); - fileChooser.getExtensionFilters().addAll( - new FileChooser.ExtensionFilter( "µOpenSPIM AcquisitionSetting file", "*.xml" ) - ); - - File file = fileChooser.showSaveDialog( getScene().getWindow() ); - if ( file != null ) - { - AcquisitionSetting.save( file, getAcquisitionSetting() ); - } - } - } ); - - Button loadButton = new Button( "LOAD" ); - loadButton.setMinSize( 100, 30 ); - loadButton.setStyle("-fx-font: 12 arial; -fx-base: #e7e45d;"); - loadButton.setOnAction( new EventHandler< ActionEvent >() - { - @Override public void handle( ActionEvent event ) - { - final FileChooser fileChooser = new FileChooser(); - fileChooser.setTitle( "µOpenSPIM AcquisitionSetting file" ); - fileChooser.getExtensionFilters().addAll( - new FileChooser.ExtensionFilter( "µOpenSPIM AcquisitionSetting file", "*.xml" ) - ); - - File file = fileChooser.showOpenDialog( getScene().getWindow() ); - if ( file != null ) - { - AcquisitionSetting setting = AcquisitionSetting.load( file ); - if(setting != null) { - // Update GUI - updateUI( setting ); - } - } - } - } ); - - loadButton.setOnDragOver( new EventHandler< DragEvent >() - { - @Override public void handle( DragEvent event ) - { - Dragboard db = event.getDragboard(); - if ( db.hasFiles() && db.getFiles().size() == 1 ) - { - event.acceptTransferModes( TransferMode.COPY ); - } - else - { - event.consume(); - } - } - } ); - - loadButton.setOnDragDropped( new EventHandler< DragEvent >() - { - @Override public void handle( DragEvent event ) - { - Dragboard db = event.getDragboard(); - boolean success = false; - if ( db.hasFiles() && db.getFiles().size() == 1 ) - { - if(db.getFiles().get(0).isFile()) { - final File file = db.getFiles().get(0); - final AcquisitionSetting setting = AcquisitionSetting.load( file ); - if(setting != null) { - // Update GUI - updateUI( setting ); - success = true; - } - } - } - - event.setDropCompleted( success ); - event.consume(); - } - } ); - - Button clearButton = new Button( "CLEAR" ); - clearButton.setMinSize( 100, 30 ); - clearButton.setStyle("-fx-font: 12 arial; -fx-base: #ffbec4;"); - clearButton.setOnAction( new EventHandler< ActionEvent >() - { - @Override public void handle( ActionEvent event ) - { - Optional< ButtonType > results = new Alert( Alert.AlertType.WARNING, "Are you sure?", ButtonType.YES, ButtonType.CANCEL).showAndWait(); - - if( results.isPresent() && results.get() == ButtonType.YES) { - clearAcquisitionSetting(); - } else { - System.err.println("Clear acquisition setting stopped by user cancellation."); - } - } - } ); - - acqSettings.getChildren().addAll( saveButton, loadButton, clearButton ); - BorderPane.setMargin(acqSettings, new Insets(12,12,12,12)); - - final TitledPane acqSettingPane = new TitledPane( "Save/Load Settings", acqSettings ); - acqSettingPane.setCollapsible( false ); - - // Compute acquisition order logic BooleanBinding bb = new BooleanBinding() { @@ -892,9 +983,9 @@ public void changed(ObservableValue observable, Number oldValu }); timePositionSplit.getItems().add(channelSummary); - timePositionSplit.setDividerPositions( 0.3, 0.6 ); - zStackAcqTabs.getItems().add(1, createSaveImagesPane(acqSettingPane)); - zStackAcqTabs.setDividerPositions( 0.3, 0.7 ); + timePositionSplit.setDividerPositions( 0.3, 0.5, 0.7 ); + zStackAcqTabs.getItems().add(1, createSaveImagesPane()); + zStackAcqTabs.setDividerPositions( 0.2, 0.65 ); // Save image options // SplitPane channelListSaveImage = new SplitPane( @@ -1087,7 +1178,9 @@ void binningItemChanged(String item) { if(multi.isPresent()) channelItemTableView = TableViewUtil.createChannelItemDataView(setup, multi.get()); else channelItemTableView = TableViewUtil.createChannelItemDataView(setup, null); - Node viewContent = createChannelItemTable( channelItemTableView, setup.getCamera1().getLabel(), setup.getLaser().getLabel(), exposure ); + + String laserLabel = setup.getLaser() == null ? "Laser-1" : setup.getLaser().getLabel(); + Node viewContent = createChannelItemTable( channelItemTableView, setup.getCamera1().getLabel(), laserLabel, exposure ); laserTab.setContent( viewContent ); if(getSpimSetup() != null && getSpimSetup().getThetaStage() != null) { @@ -1139,7 +1232,10 @@ void binningItemChanged(String item) { filename.setValue(""); directory.setValue(""); + folder.setValue(""); + saveMIP.set(false); + ablationSupport.set(false); binningOptions.clear(); @@ -1246,6 +1342,7 @@ private void updateUI ( AcquisitionSetting setting ) { // 6. Save Image panel enabledSaveImages.set( setting.getEnabledSaveImages() ); directory.set( setting.getDirectory() ); + folder.set( setting.getFolder() ); filename.set( setting.getFilename() ); savingFormat.set( setting.getSavingFormat() ); saveMIP.set( setting.getSaveMIP() ); @@ -1262,9 +1359,6 @@ private void updateUI ( AcquisitionSetting setting ) { // Experiment Note experimentNote.set(setting.getExperimentNote()); - - // OnTheFly - onTheFly.set(setting.getOnTheFly()); } private AcquisitionSetting getAcquisitionSetting() { @@ -1276,8 +1370,8 @@ private AcquisitionSetting getAcquisitionSetting() { return new AcquisitionSetting( enabledTimePoints, timePointItems, enabledPositions, positionItems, enabledZStacks, acquisitionOrder, enabledChannels, channelTabPane.getSelectionModel().selectedIndexProperty().get(), channelItems, - channelItemsArduino, enabledSaveImages, directory, filename, savingFormat, saveMIP, roiRectangle, rotateStepSize, - experimentNote, onTheFly ); + channelItemsArduino, enabledSaveImages, directory, folder, filename, savingFormat, saveMIP, roiRectangle, rotateStepSize, + experimentNote); } private void clearAcquisitionSetting() { @@ -1296,14 +1390,14 @@ private void clearAcquisitionSetting() { enabledSaveImages.set( true ); directory.set( "" ); - filename.set( "Untitled" ); + folder.set( "" ); + filename.set( "" ); savingFormat.set( "Single Plane TIFF" ); saveMIP.set( false ); roiRectangle.set( null ); rotateStepSize.set(1); experimentNote.set( "" ); - onTheFly.set( false ); } public void stopAcquisition() @@ -1365,77 +1459,17 @@ public boolean startAcquisition( Button acquireButton ) } // Check if the specified file name exists - File folder = new File(directory.getValue()); + folderFile = new File( directory.getValue(), getCurrentTime() + "_" + folder.getValue() + "_" + incSpinnerValueFactory.getValue() ); if ( enabledSaveImages.get() ) { - if(!folder.exists()) { - Optional< ButtonType > results = new Alert( Alert.AlertType.WARNING, "The folder does not exist. Click Yes to create new folder.", ButtonType.YES, ButtonType.NO).showAndWait(); - - if( results.isPresent() ) { - if (results.get() == ButtonType.YES) { - try { - FileUtils.forceMkdir(folder); - } catch (IOException e) { - e.printStackTrace(); - } - } - else if(results.get() == ButtonType.NO) - { - return false; - } - } + while(folderFile.exists()) { + incSpinnerValueFactory.increment(1); + folderFile = new File( directory.getValue(), getCurrentTime() + "_" + folder.getValue() + "_" + incSpinnerValueFactory.getValue() ); } - if(null != folder.listFiles()) { - boolean found = folder.exists() && folder.listFiles().length > 1; - - if(found) { - Optional< ButtonType > results = new Alert( Alert.AlertType.WARNING, "The folder already exists. All files within this folder will be replaced. Do you want to proceed?\nPress No to create another folder and keep all files.", - ButtonType.YES, ButtonType.NO, ButtonType.CANCEL).showAndWait(); - - if( results.isPresent() ) { - if (results.get() == ButtonType.YES) { - try { - FileUtils.cleanDirectory(folder); - } catch (IOException e) { - System.err.println(e.getMessage()); - } - } else if( results.get() == ButtonType.NO ) { - int maxNumber = 0; - for (File acqDir : Objects.requireNonNull( folder.getParentFile().listFiles() ) ) { - String theName = acqDir.getName(); - int number; - if (theName.startsWith(folder.getName())) { - try { - //e.g.: "blah_32.ome.tiff" - Pattern p = Pattern.compile("\\Q" + folder.getName() + "_\\E" + "(\\d+)"); - Matcher m = p.matcher(theName); - if (m.matches()) { - number = Integer.parseInt(m.group(1)); - if (number >= maxNumber) { - maxNumber = number; - } - } - } catch (NumberFormatException e) { - } // Do nothing. - } - } - - folder = new File(directory.get() + "_" + (maxNumber + 1)); - try { - FileUtils.forceMkdir(folder); - } catch (IOException e) { - e.printStackTrace(); - } - - } else if( results.get() == ButtonType.CANCEL ) { - System.err.println("Acquisition stopped by user cancellation."); - return false; - } - } else { - System.err.println("Acquisition stopped by user cancellation."); - return false; - } - } + try { + FileUtils.forceMkdir(folderFile); + } catch (IOException e) { + e.printStackTrace(); } } @@ -1463,7 +1497,7 @@ else if(results.get() == ButtonType.NO) // Write the experiment note try { - writeNote(folder); + writeNote(folderFile); } catch (IOException e) { e.printStackTrace(); } @@ -1485,7 +1519,28 @@ else if(results.get() == ButtonType.NO) Platform.runLater( () -> processedImages.set( 0 ) ); - File finalFolder = folder; + File finalFolder = folderFile; + String fileName = filename.getValue(); + + Date date = Calendar.getInstance().getTime(); + + // yyyyMMdd_HHmm + // _ + DateFormat dateFormat = new SimpleDateFormat("yyyMMdd"); + String dateString = dateFormat.format(date); + dateFormat = new SimpleDateFormat("HHmmss"); + String timeString = dateFormat.format(date); + + fileName = fileName.replace("", dateString); + fileName = fileName.replace("", timeString); + String positions = positionItemTableView.getItems().filtered(p -> p.getSelected()).stream().map(PositionItem::getName).collect(Collectors.joining("_")); + + String timepoints = timePointItemTableView.getItems().filtered(t -> t.getType().equals(TimePointItem.Type.Acq)).stream().map(t -> String.format("%dx%d%s", t.getNoTimePoints(), t.getInterval(), t.getIntervalUnit())).collect(Collectors.joining("_")); + + fileName = fileName.replace("", positions); + fileName = fileName.replace("", timepoints); + + String finalFileName = fileName; acquisitionThread = new Thread(() -> { Thread.currentThread().setContextClassLoader( HalcyonMain.class.getClassLoader() ); @@ -1494,10 +1549,10 @@ else if(results.get() == ButtonType.NO) { engine.performAcquisition( getStudio(), getSpimSetup(), stagePanel, ( java.awt.Rectangle) roiRectangle.get(), tp, timePointItemTableView.getItems(), currentTP, waitSeconds, - arduinoSelected, finalFolder, filename.getValue(), + arduinoSelected, finalFolder, finalFileName, positionItemTableView.getItems().filtered(p -> p.getSelected()), channelItemList, processedImages, totalImages.getValue(), - enabledSaveImages.get(), savingFormat.getValue(), saveMIP.getValue(), antiDrift.getValue(), experimentNote.getValue(), - antiDriftLog, antiDriftRefCh.get(), antiDriftTypeToggle, onTheFly.getValue() ); + enabledSaveImages.get(), savingFormat.getValue(), saveMIP.getValue(), ablationSupport.getValue(), antiDrift.getValue(), experimentNote.getValue(), + antiDriftLog, antiDriftRefCh.get(), antiDriftTypeToggle, onTheFly.getValue(), onChannelFusion.getValue() ); // new MMAcquisitionRunner().runAcquisition(); @@ -1532,6 +1587,12 @@ private void writeNote(File folder) throws IOException { } } + static String getCurrentTime() { + Date date = Calendar.getInstance().getTime(); + DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmm"); + return dateFormat.format(date); + } + static double getUnit(String unitString) { double unit = 1; @@ -1554,7 +1615,7 @@ private Node createPositionListPane( TableView< PositionItem > positionItemTable EventHandler newEventHandler = ( EventHandler< ActionEvent > ) event -> { SPIMSetup spimSetup = getSpimSetup(); if(spimSetup != null ) { - double r = spimSetup.getThetaStage().getPosition(); + double r = spimSetup.getAngle(); double x = spimSetup.getXStage().getPosition(); double y = spimSetup.getYStage().getPosition(); double z = spimSetup.getZStage().getPosition(); @@ -1591,7 +1652,7 @@ private Node createPositionListPane( TableView< PositionItem > positionItemTable if(currentPosition.get() != null) { SPIMSetup spimSetup = getSpimSetup(); if(spimSetup != null ) { - double r = spimSetup.getThetaStage().getPosition(); + double r = spimSetup.getAngle(); double x = spimSetup.getXStage().getPosition(); double y = spimSetup.getYStage().getPosition(); currentPosition.get().setR(r); @@ -1694,14 +1755,14 @@ public void computeTotalPositionImages() { propertyMap.get("slices").setValue( totalImages + "" ); } - private Node createSaveImagesPane(Node buttonPane) { + private Node createSaveImagesPane() { GridPane gridpane = new GridPane(); gridpane.setVgap( 5 ); gridpane.setHgap( 5 ); TextField textField = new TextField(); - propertyMap.put( "folder", textField.textProperty() ); + propertyMap.put( "directory", textField.textProperty() ); directory = textField.textProperty(); @@ -1730,34 +1791,66 @@ private Node createSaveImagesPane(Node buttonPane) { @Override public void handle(ActionEvent event) { try { - if (!directory.get().isEmpty()) - Desktop.getDesktop().open(new File(directory.get())); + if ( folderFile != null && folderFile.exists() ) + Desktop.getDesktop().open( folderFile ); } catch (IOException e) { e.printStackTrace(); } } }); - gridpane.addRow( 0, new Label( "Directory:" ), textField, selectFolder, openFolder ); + GridPane.setConstraints(textField, 1, 0, 2, 1); // column=1 row=0 + GridPane.setConstraints(selectFolder, 3, 0, 1, 1); // column=3 row=0 + + gridpane.addRow( 0, new Label( "Base Directory:" ) ); + gridpane.getChildren().addAll(textField, selectFolder); + +// textField = new TextField(""); + textField = new TextField("ML-AblationPolarBodies-N01_CB-JG"); + folder = textField.textProperty(); - textField = new TextField("Untitled"); - filename = textField.textProperty(); + propertyMap.put( "folder", textField.textProperty() ); + + Spinner incSpinner = new Spinner<>(0, 99, 0, 1); + incSpinner.setMaxWidth(60); + + incSpinnerValueFactory = + (SpinnerValueFactory.IntegerSpinnerValueFactory) incSpinner.getValueFactory(); - propertyMap.put( "filename", textField.textProperty() ); - gridpane.addRow( 1, new Label( "File name:" ), textField ); + gridpane.addRow( 1, new Label( "Base Folder:" ), textField, incSpinner, openFolder ); + + TextArea textArea = new TextArea("___"); + textArea.setWrapText(true); + textArea.setMinHeight(80); + textArea.setMaxHeight(80); + GridPane.setConstraints(textArea, 1, 0, 2, 1); + + filename = textArea.textProperty(); + propertyMap.put( "filename", textArea.textProperty() ); + + gridpane.addRow(2, new Label( "Filename:" ), textArea); ComboBox c = new ComboBox<>( FXCollections.observableArrayList( "Single Plane TIFF", "OMETIFF Image stack", "BDV format", "N5 format", "On-the-fly" ) ); c.valueProperty().setValue("Single Plane TIFF"); + c.valueProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue observableValue, Object o, Object t1) { + if(ablationSupport != null && !t1.equals("OMETIFF Image stack")) { + ablationSupport.set(false); + } + } + }); savingFormat = c.valueProperty(); + ablationDisabled.bind(savingFormat.isNotEqualTo("OMETIFF Image stack")); - gridpane.addRow( 2, new Label( "Saving format:" ), c ); + gridpane.addRow( 3, new Label( "Saving format:" ), c ); CheckBox mip = new CheckBox( "Show/save Maximum Intensity Projection of each TP" ); - gridpane.addRow( 3, mip ); - gridpane.setColumnSpan( mip, 2 ); + gridpane.addRow( 4, mip ); + gridpane.setColumnSpan( mip, 3 ); saveMIP = mip.selectedProperty(); @@ -1770,7 +1863,7 @@ public void handle(ActionEvent event) { Tab saveOptionTab = new Tab("Saving option", pane); saveOptionTab.setClosable(false); - TextArea textArea = new TextArea(); + textArea = new TextArea(); textArea.setWrapText(true); experimentNote = textArea.textProperty(); @@ -1778,9 +1871,9 @@ public void handle(ActionEvent event) { noteTab.setClosable(false); TabPane tabPane = new TabPane(saveOptionTab, noteTab); - tabPane.setMinHeight(200); + tabPane.setMinHeight(190); - VBox vbox = new VBox( 12, tabPane, buttonPane ); + VBox vbox = new VBox( 12, tabPane ); return vbox; } @@ -2070,7 +2163,7 @@ private CheckboxPane createZStackPane( StagePanel stagePanel ) { } } ); - zStackGridPane.addRow( 0, new VBox( startButton, zStartField ) ); + zStackGridPane.addRow( 1, new VBox( startButton, zStartField ) ); TextField zStepField = createNumberTextField(); zStepField.textProperty().addListener(new ChangeListener() { @@ -2115,7 +2208,7 @@ public void changed(ObservableValue observable, String oldValu HBox zCenter = new HBox( 5, zStepComboBox, new Label( "Z-step (μm)" ) ); zCenter.setAlignment( Pos.CENTER_LEFT ); - zStackGridPane.addRow( 1, new VBox( midButton, zCenter ) ); + zStackGridPane.addRow( 2, new VBox( midButton, zCenter ) ); Button endButton = createZStackButton( "Z-end" ); TextField zEndField = createNumberTextField(); @@ -2141,7 +2234,7 @@ public void changed(ObservableValue observable, String oldValu } } ); - setupMouseClickedHandler(startButton, zStartField, endButton, zEndField, midButton); + setupMouseClickedHandler(startButton, zStartField, endButton, zEndField, midButton, zStepField, zStepComboBox); Button newButton = new Button( "Add Z-stack" ); @@ -2174,7 +2267,7 @@ public void changed(ObservableValue observable, String oldValu } } ); - zStackGridPane.addRow( 2, new VBox( endButton, zEndField ) ); + zStackGridPane.addRow( 3, new VBox( endButton, zEndField ) ); currentPosition.addListener( new ChangeListener< PositionItem >() { @@ -2214,7 +2307,7 @@ public void changed(ObservableValue observable, String oldValu } ); if(stagePanel == null && zSlider != null) - zStackGridPane.add( zSlider, 2, 0, 1, 3 ); + zStackGridPane.add( zSlider, 2, 1, 1, 3 ); // Button updateButton = new Button("Update"); // updateButton.setOnAction( new EventHandler< ActionEvent >() @@ -2255,7 +2348,8 @@ public void handle(ActionEvent event) { } }); - zStackGridPane.addRow( 3, new VBox( newButton, clearButton ) ); + zStackGridPane.addRow( 4, newButton); + zStackGridPane.addRow( 0, clearButton); // create a group HBox b = new HBox(new Label("Stage")); @@ -2279,7 +2373,7 @@ private void addNewPosition( int zStart, int zEnd, double zStep ) { InvalidationListener invalidationListener = observable -> computeTotalPositionImages(); if(spimSetup != null ) { - double r = spimSetup.getThetaStage().getPosition(); + double r = spimSetup.getAngle(); double x = spimSetup.getXStage().getPosition(); x = Math.ceil(x * 100) / 100; double y = spimSetup.getYStage().getPosition(); @@ -2301,7 +2395,7 @@ public void addNewPosition() { addNewPosition( (int) zStackStart, (int) zStackEnd, zStackStepSize ); } - private void setupMouseClickedHandler( Button startButton, TextField zStartField, Button endButton, TextField zEndField, Button midButton ) + private void setupMouseClickedHandler( Button startButton, TextField zStartField, Button endButton, TextField zEndField, Button midButton, TextField zStepField, ComboBox zStepComboBox ) { startButton.setOnAction( new EventHandler< ActionEvent >() { @@ -2337,6 +2431,11 @@ private void setupMouseClickedHandler( Button startButton, TextField zStartField { SPIMSetup spimSetup = getSpimSetup(); if(spimSetup != null && spimSetup.getZStage() != null) { + + if(zStepField.getText().isEmpty()) { + zStepComboBox.getSelectionModel().select(1); + } + int currPos = (int) spimSetup.getZStage().getPosition(); if(zStartField.getText().isEmpty()) { zEndField.setText(currPos + ""); diff --git a/src/main/java/spim/ui/view/component/BeanshellEditor.java b/src/main/java/spim/ui/view/component/BeanshellEditor.java index 4079f8e5..4c28e3e9 100644 --- a/src/main/java/spim/ui/view/component/BeanshellEditor.java +++ b/src/main/java/spim/ui/view/component/BeanshellEditor.java @@ -25,6 +25,7 @@ import java.io.PipedReader; import java.io.PipedWriter; import java.io.Reader; +import java.lang.reflect.Field; /** * Author: HongKee Moon (moon@mpi-cbg.de), Scientific Computing Facility @@ -114,14 +115,18 @@ public void setSetup(SPIMSetup setup, Studio studio) { } else { if(beanshellThread != null) { try { + commandWriter.write(0); commandWriter.close(); } catch (IOException e) { e.printStackTrace(); } + commandWriter = null; try { - beanshellThread.stop(); - } catch (ThreadDeath e) {} + beanshellThread.join(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } } beanshellThread = null; } @@ -189,6 +194,13 @@ final void createBeanshellREPL() { // Create console and REPL interpreter: beanshellREPLint_ = new Interpreter(in, System.out, System.err, true); + try { + Field field = Interpreter.class.getDeclaredField("exitOnEOF"); + field.setAccessible(true); + field.setBoolean( beanshellREPLint_, false ); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException(e); + } beanshellThread = new Thread(beanshellREPLint_, "BeanShell interpreter"); beanshellThread.start(); diff --git a/src/main/java/spim/ui/view/component/HalcyonMain.java b/src/main/java/spim/ui/view/component/HalcyonMain.java index 4daa95e3..7685c328 100644 --- a/src/main/java/spim/ui/view/component/HalcyonMain.java +++ b/src/main/java/spim/ui/view/component/HalcyonMain.java @@ -23,7 +23,7 @@ import org.micromanager.events.GUIRefreshEvent; import spim.hardware.SPIMSetup; import spim.hardware.VersaLase; -import spim.mm.MicroManager; + import spim.model.event.ControlEvent; import spim.ui.view.component.console.StdOutCaptureConsole; import spim.ui.view.component.util.ResourceUtil; @@ -153,7 +153,7 @@ private BorderPane createHalcyonBorderPane( Stage primaryStage, Studio studio ) addNode( arduino ); // Custom Toolbar provided here - toolbarPanel = new ToolbarPanel( studio, mmStudioProperty, mmStudioGUIRefreshEventProperty ); + toolbarPanel = new ToolbarPanel( primaryStage, studio, mmStudioProperty, mmStudioGUIRefreshEventProperty ); toolbarPanel.setPrefSize(300, 200); addToolbar(toolbarPanel); @@ -277,6 +277,7 @@ private BorderPane createHalcyonBorderPane( Stage primaryStage, Studio studio ) // String lLayoutFile = getUserDataDirectory( mWindowtitle ) // + "layout.pref"; checkLayoutPref(getFileFromResourceAsStream("spim/gui/layout.pref")); + checkOthersPref(getFileFromResourceAsStream("spim/gui/others.pref")); return createHalcyonFrame( primaryStage ); } diff --git a/src/main/java/spim/ui/view/component/MMAcquisitionEngine.java b/src/main/java/spim/ui/view/component/MMAcquisitionEngine.java index afe17ee2..5ccc98d6 100644 --- a/src/main/java/spim/ui/view/component/MMAcquisitionEngine.java +++ b/src/main/java/spim/ui/view/component/MMAcquisitionEngine.java @@ -110,16 +110,18 @@ public void stop() { * @param bSave the b save * @param savingFormatValue the value set from { "Single Plane TIFF", "OMETIFF Image stack", "N5 format" } * @param saveMIP Save Maximum Intensity Projection or not + * @param ablationSupport Support for ablation experiments * @param antiDrift Anti-Drift function used or not * @param experimentNote Experiment Note saved in the metadata * @param antiDriftLog holds anti drift log during acquisition * @param antiDriftReferenceChannel the reference channel for anti-drift * @param antiDriftTypeToggle the type of anti-drift, CentreOfMass or PhaseCorrelation * @param onTheFly onTheFly is enabled or not + * @param fusionChannel fuse all the channels into one * @throws Exception the exception */ @SuppressWarnings("Duplicates") - public void performAcquisition(Studio studio, SPIMSetup setup, StagePanel stagePanel, Rectangle roiRectangle, int timeSeqs, ObservableList timePointItems, DoubleProperty currentTP, DoubleProperty waitSeconds, boolean arduinoSelected, File output, String acqFilenamePrefix, ObservableList positionItems, List channelItems, LongProperty processedImages, long totalImages, boolean bSave, Object savingFormatValue, boolean saveMIP, boolean antiDrift, String experimentNote, StringProperty antiDriftLog, Integer antiDriftReferenceChannel, ReadOnlyObjectProperty antiDriftTypeToggle, Boolean onTheFly) throws Exception + public void performAcquisition(Studio studio, SPIMSetup setup, StagePanel stagePanel, Rectangle roiRectangle, int timeSeqs, ObservableList timePointItems, DoubleProperty currentTP, DoubleProperty waitSeconds, boolean arduinoSelected, File output, String acqFilenamePrefix, ObservableList positionItems, List channelItems, LongProperty processedImages, long totalImages, boolean bSave, Object savingFormatValue, boolean saveMIP, boolean ablationSupport, boolean antiDrift, String experimentNote, StringProperty antiDriftLog, Integer antiDriftReferenceChannel, ReadOnlyObjectProperty antiDriftTypeToggle, Boolean onTheFly, Boolean fusionChannel) throws Exception { final Studio frame = studio; @@ -144,7 +146,7 @@ public void performAcquisition(Studio studio, SPIMSetup setup, StagePanel stageP else if(savingFormatValue.equals( "OMETIFF Image stack" )) result.setStorage(new OMETIFFStorage(result, output.getAbsolutePath(), acqFilenamePrefix, true)); else if(savingFormatValue.equals( "BDV format" )) - result.setStorage(new BDVMicroManagerStorage(result, output.getAbsolutePath(), acqFilenamePrefix, channelItems.size(), timeSeqs, true)); + result.setStorage(new BDVMicroManagerStorage(result, output.getAbsolutePath(), acqFilenamePrefix, channelItems.size(), timeSeqs, true, fusionChannel)); else if(savingFormatValue.equals( "N5 format" )) result.setStorage(new N5MicroManagerStorage(result, output.getAbsolutePath(), acqFilenamePrefix, timeSeqs, true)); else if(savingFormatValue.equals( "On-the-fly" )) @@ -335,24 +337,23 @@ else if(savingFormatValue.equals( "On-the-fly" )) if(!bSave) { output = null; saveMIP = false; + ablationSupport = false; } - if(!saveMIP) acqFilenamePrefix = null; - - executeNormalAcquisition(setup, frame, store, stagePanel, currentCamera, cameras, output, acqFilenamePrefix, timePointItems, positionItems, channelItems, currentTP, waitSeconds, arduinoSelected, processedImages, totalImages, acqBegan, antiDrift, antiDriftLog, antiDriftReferenceChannel, antiDriftTypeToggle, onTheFly); + executeNormalAcquisition(setup, frame, store, stagePanel, currentCamera, cameras, output, acqFilenamePrefix, timePointItems, positionItems, channelItems, currentTP, waitSeconds, arduinoSelected, processedImages, totalImages, acqBegan, antiDrift, antiDriftLog, antiDriftReferenceChannel, antiDriftTypeToggle, saveMIP, onTheFly, ablationSupport); } private void executeNormalAcquisition(SPIMSetup setup, final Studio frame, Datastore store, StagePanel stagePanel, String currentCamera, List cameras, File outFolder, String acqFilenamePrefix, ObservableList timePointItems, ObservableList positionItems, List channelItems, DoubleProperty currentTP, DoubleProperty waitSeconds, boolean arduinoSelected, - LongProperty processedImages, long totalImages, final double acqBegan, final boolean antiDrift, StringProperty antiDriftLog, Integer adReferenceChannel, ReadOnlyObjectProperty antiDriftTypeToggle, Boolean onTheFly) throws Exception + LongProperty processedImages, long totalImages, final double acqBegan, final boolean antiDrift, StringProperty antiDriftLog, Integer adReferenceChannel, ReadOnlyObjectProperty antiDriftTypeToggle, Boolean saveMIP, Boolean onTheFly, boolean ablationSupport) throws Exception { // Dynamic timeline runNormalSmartImagingMMAcq(setup, frame, store, stagePanel, currentCamera, cameras, outFolder, acqFilenamePrefix, - timePointItems, positionItems, channelItems, currentTP, waitSeconds, arduinoSelected, processedImages, totalImages, antiDrift, antiDriftLog, adReferenceChannel, antiDriftTypeToggle, onTheFly); + timePointItems, positionItems, channelItems, currentTP, waitSeconds, arduinoSelected, processedImages, totalImages, antiDrift, antiDriftLog, adReferenceChannel, antiDriftTypeToggle, saveMIP, onTheFly, ablationSupport); finalize(true, setup, currentCamera, cameras, frame, 0, 0, store); } @@ -361,7 +362,7 @@ private void runNormalSmartImagingMMAcq(SPIMSetup setup, final Studio frame, Dat StagePanel stagePanel, String currentCamera, List cameras, File outFolder, String acqFilenamePrefix, ObservableList timePointItems, ObservableList positionItems, List channelItems, DoubleProperty currentTP, DoubleProperty waitSeconds, boolean arduinoSelected, - LongProperty processedImages, long totalImages, final boolean antiDrift, StringProperty antiDriftLog, Integer adReferenceChannel, ReadOnlyObjectProperty antiDriftTypeToggle, Boolean onTheFly) throws Exception + LongProperty processedImages, long totalImages, final boolean antiDrift, StringProperty antiDriftLog, Integer adReferenceChannel, ReadOnlyObjectProperty antiDriftTypeToggle, Boolean saveMIP, Boolean onTheFly, Boolean ablationSupport) throws Exception { final CMMCore core = frame.core(); @@ -387,7 +388,7 @@ private void runNormalSmartImagingMMAcq(SPIMSetup setup, final Studio frame, Dat } } - AcqWrapperEngine engine = new AcqWrapperEngine( setup, frame, store, currentCamera, cameras, outFolder, acqFilenamePrefix, channelItems, arduinoSelected, processedImages, driftCompMap, adReferenceChannel, onTheFly); + AcqWrapperEngine engine = new AcqWrapperEngine( setup, frame, store, currentCamera, cameras, outFolder, acqFilenamePrefix, channelItems, arduinoSelected, processedImages, driftCompMap, adReferenceChannel, saveMIP, onTheFly, ablationSupport); SystemInfo.dumpMemoryStatusToLog(core); @@ -686,14 +687,15 @@ private void finalize(boolean finalizeStack, SPIMSetup setup, final String curre captureThread = null; - for(String camera : cameras) - { - if(core.isSequenceRunning(camera)) - core.stopSequenceAcquisition( camera ); - } - - core.setCameraDevice( currentCamera ); +// for(String camera : cameras) +// { +// if(core.isSequenceRunning(camera)) +// core.stopSequenceAcquisition( camera ); +// } + core.stopSequenceAcquisition(); +// core.setCameraDevice( currentCamera ); +// // core.waitForDevice( currentCamera ); try { diff --git a/src/main/java/spim/ui/view/component/StagePanel.java b/src/main/java/spim/ui/view/component/StagePanel.java index 634880e9..f2b3889c 100644 --- a/src/main/java/spim/ui/view/component/StagePanel.java +++ b/src/main/java/spim/ui/view/component/StagePanel.java @@ -139,7 +139,7 @@ public StagePanel( SPIMSetup setup ) ChangeListener< Number > targetPropertyChangeListener = ( observable, oldValue, newValue ) -> { switch ( stage ) { case R: - spimSetup.getThetaStage().setPosition( newValue.doubleValue() ); + spimSetup.setAngle( newValue.doubleValue() ); break; case X: spimSetup.getXStage().setPosition( newValue.doubleValue() ); break; @@ -194,7 +194,7 @@ private void monitorSPIM() { double device = 0d; // read the value from the device switch ( stage ) { - case R: device = spimSetup.getThetaStage().getPosition(); + case R: device = spimSetup.getAngle(); break; case X: device = spimSetup.getXStage().getPosition(); break; diff --git a/src/main/java/spim/ui/view/component/ToolbarPanel.java b/src/main/java/spim/ui/view/component/ToolbarPanel.java index b31a80cc..4fe0c2d9 100644 --- a/src/main/java/spim/ui/view/component/ToolbarPanel.java +++ b/src/main/java/spim/ui/view/component/ToolbarPanel.java @@ -1,29 +1,6 @@ package spim.ui.view.component; -import bdv.BigDataViewer; -import bdv.ViewerImgLoader; -import bdv.cache.CacheControl; -import bdv.img.imagestack.ImageStackImageLoader; -import bdv.img.virtualstack.VirtualStackImageLoader; -import bdv.spimdata.SequenceDescriptionMinimal; -import bdv.spimdata.SpimDataMinimal; -import bdv.spimdata.WrapBasicImgLoader; -import bdv.tools.brightness.ConverterSetup; -import bdv.util.Prefs; -import bdv.viewer.ConverterSetups; -import bdv.viewer.DisplayMode; -import bdv.viewer.SourceAndConverter; -import bdv.viewer.SynchronizedViewerState; -import bdv.viewer.ViewerOptions; -import bdv.viewer.ViewerState; -import ij.CompositeImage; import ij.IJ; -import ij.ImagePlus; -import ij.ImageStack; -import ij.measure.Calibration; -import ij.process.ImageProcessor; -import ij.process.ImageStatistics; -import ij.process.LUT; import javafx.application.Platform; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleDoubleProperty; @@ -45,41 +22,23 @@ import javafx.scene.text.Font; import javafx.stage.DirectoryChooser; import javafx.stage.Stage; -import mpicbg.spim.data.generic.AbstractSpimData; -import mpicbg.spim.data.generic.sequence.BasicImgLoader; -import mpicbg.spim.data.generic.sequence.BasicViewSetup; -import mpicbg.spim.data.registration.ViewRegistration; -import mpicbg.spim.data.registration.ViewRegistrations; -import mpicbg.spim.data.sequence.Channel; -import mpicbg.spim.data.sequence.FinalVoxelDimensions; -import mpicbg.spim.data.sequence.TimePoint; -import mpicbg.spim.data.sequence.TimePoints; -import net.imglib2.FinalDimensions; -import net.imglib2.realtransform.AffineTransform3D; -import net.imglib2.type.numeric.ARGBType; import org.dockfx.DockNode; - import org.micromanager.Studio; -import org.micromanager.data.Coordinates; -import org.micromanager.data.Coords; -import org.micromanager.data.DataProvider; import org.micromanager.data.internal.DefaultDatastore; -import org.micromanager.data.internal.DefaultImageJConverter; -import org.micromanager.display.DisplayWindow; import org.micromanager.events.GUIRefreshEvent; +import org.micromanager.internal.MMStudio; import spim.hardware.SPIMSetup; import spim.io.*; import spim.mm.MMUtils; import spim.mm.MicroManager; import spim.model.event.ControlEvent; -import javax.swing.*; +import java.awt.*; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; import java.io.File; import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; import java.util.function.Supplier; @@ -104,7 +63,6 @@ public class ToolbarPanel extends DockNode implements SPIMSetupInjectable final Button mmButton; // final Button liveViewButton; final Button openDatasetButton; - final Button openDatasetWithBDV; final SimpleDoubleProperty waitSeconds; @@ -112,10 +70,14 @@ public class ToolbarPanel extends DockNode implements SPIMSetupInjectable final Text rotatorStepSizeValue; final Text zStageStepSizeValue; HalcyonMain halcyonMain; + final Stage primaryStage; + StorageType storageType; + String lastOpenedFolder; - public ToolbarPanel( Studio mmStudio, ObjectProperty< Studio > mmStudioObjectProperty, ObjectProperty refreshEventProperty ) + public ToolbarPanel(Stage primaryStage, Studio mmStudio, ObjectProperty< Studio > mmStudioObjectProperty, ObjectProperty refreshEventProperty ) { super(new VBox()); + this.primaryStage = primaryStage; this.studioProperty = new SimpleObjectProperty<>( mmStudio ); this.spimSetupObjectProperty = new SimpleObjectProperty<>(); this.waitSeconds = new SimpleDoubleProperty(-1); @@ -151,8 +113,21 @@ public ToolbarPanel( Studio mmStudio, ObjectProperty< Studio > mmStudioObjectPro { @Override public void handle( ActionEvent event ) { + if(null == MMStudio.getInstance()) { + mmButton.fire(); + } + if(null == IJ.getInstance() ) { ij.ImageJ ij = new ij.ImageJ(); + + ij.addWindowListener(new WindowAdapter() + { + @Override public void windowClosing( WindowEvent e ) + { + Frame frame = MMStudio.getInstance().uiManager().frame(); + frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING)); + } + }); ij.show(); } else { IJ.getInstance().show(); @@ -183,16 +158,12 @@ public ToolbarPanel( Studio mmStudio, ObjectProperty< Studio > mmStudioObjectPro MicroManager.init( stage, mmStudioObjectProperty, refreshEventProperty ); - while(MMUtils.invalidMMPath() && !MMUtils.cancelled()) + while(MMUtils.invalidMMPath() && !MMUtils.cancelled() && !MMUtils.isSystemLibrairiesLoaded()) { - if (!MMUtils.isSystemLibrairiesLoaded()) - { - // load micro manager libraries - if (!MMUtils.fixSystemLibrairies( stage )) - if(halcyonMain != null) - System.exit(0); - return; - } + // load micro manager libraries + if (!MMUtils.fixSystemLibrairies( stage )) + if(halcyonMain != null) + System.exit(0); MicroManager.init( stage, mmStudioObjectProperty, refreshEventProperty ); } @@ -251,19 +222,6 @@ public void handle(ActionEvent event) { } }); - openDatasetWithBDV = new Button("Show in BDV"); - openDatasetWithBDV.setStyle("-fx-font: 14 arial; -fx-base: #e7e45d;"); - openDatasetWithBDV.setOnAction(new EventHandler() { - @Override - public void handle(ActionEvent event) { - try { - loadDataWithBDV(); - } catch (IOException e) { - e.printStackTrace(); - } - } - }); - liveViewHbox = new VBox(3); liveViewHbox.setAlignment( Pos.CENTER ); @@ -367,7 +325,6 @@ public void loadData() throws IOException { if (list.length == 0) return; - StorageType storageType = null; String prefix = ""; if (list.length == 1) { @@ -379,6 +336,8 @@ public void loadData() throws IOException { } String directory = f.getAbsolutePath(); + lastOpenedFolder = directory; + DefaultDatastore result = new DefaultDatastore(studioProperty.get()); switch (storageType) { @@ -388,7 +347,7 @@ public void loadData() throws IOException { break; case N5: result.setStorage(new N5MicroManagerStorage(result, directory, prefix, 1, false)); break; - case BDV: result.setStorage(new BDVMicroManagerStorage(result, directory, prefix, 1, 1, false)); + case BDV: result.setStorage(new BDVMicroManagerStorage(result, directory, prefix, 1, 1, false, false)); break; } @@ -399,356 +358,6 @@ public void loadData() throws IOException { studioProperty.get().displays().loadDisplays(result); } - public ImagePlus toImageJ(DisplayWindow display) { - final boolean copy = false; - final boolean setProps = true; - // TODO: UI to set copy, give option to only do partial data, and multiple positions - - DataProvider dp = display.getDataProvider(); - Coords displayPosition = display.getDisplayPosition(); - int p = displayPosition.getP(); - - ImagePlus iPlus = null; - org.micromanager.data.Image image = null; - if (dp.getNumImages() == 1) { - try { - image = dp.getAnyImage(); - ImageProcessor iProc = DefaultImageJConverter.createProcessor(image, copy); - iPlus = new ImagePlus(dp.getName() + "-ij", iProc); - - } catch (IOException ex) { - // TODO: report error - } - if (setProps && iPlus != null && image != null) { - setCalibration(iPlus, dp, image); - } - if (iPlus != null) { - return iPlus; - } - } else if (dp.getNumImages() > 1) { - try { - ImageStack imgStack = new ImageStack(dp.getAnyImage().getWidth(), - dp.getAnyImage().getHeight()); - Coords.Builder cb = Coordinates.builder().c(0).t(0).p(p).z(0); - for (int t = 0; t < dp.getNextIndex(Coords.T); t++) { - for (int z = 0; z < dp.getNextIndex(Coords.Z); z++) { - for (int c = 0; c < dp.getNextIndex(Coords.C); c++) { - image = dp.getImage(cb.c(c).t(t).z(z).build()); - ImageProcessor iProc; - if (image != null) { - iProc = DefaultImageJConverter.createProcessor( - image, copy); - } else { // handle missing images - should be handled by MM - // so remove this code once this is done nicely in MM - iProc = DefaultImageJConverter.createBlankProcessor( - dp.getAnyImage()); - } - imgStack.addSlice(iProc); - } - } - } - iPlus = new ImagePlus(dp.getName() + "-ij"); - iPlus.setOpenAsHyperStack(true); - iPlus.setStack(imgStack, dp.getNextIndex(Coords.C), - dp.getNextIndex(Coords.Z), dp.getNextIndex(Coords.T)); - - int displayMode; - switch (display.getDisplaySettings().getColorMode()) { - case COLOR: { displayMode = IJ.COLOR; break; } - case COMPOSITE: { displayMode = IJ.COMPOSITE; break; } - case GRAYSCALE: { displayMode = IJ.GRAYSCALE; break; } - default: { displayMode = IJ.GRAYSCALE; break; } - } - iPlus.setDisplayMode(displayMode); - CompositeImage ci = new CompositeImage(iPlus, displayMode); - ci.setTitle(dp.getName() + "-ij"); - for (int c = 0; c < dp.getNextIndex(Coords.C); c++) { - ci.setChannelLut( - LUT.createLutFromColor(display.getDisplaySettings().getChannelColor(c)), - c + 1); - } - if (setProps && image != null) { - setCalibration(ci, dp, image); - } - return ci; - - } catch (IOException ex) { - // TODO: report - } - - return null; - } - - return null; - } - - private void setCalibration(ImagePlus iPlus, DataProvider dp, org.micromanager.data.Image image) { - Calibration cal = new Calibration(iPlus); - Double pSize = image.getMetadata().getPixelSizeUm(); - if (pSize != null && pSize != 0) { - cal.pixelWidth = pSize; - cal.pixelHeight = pSize; - } else { - cal.pixelWidth = 1; - cal.pixelHeight = 1; - } - Double zStep = dp.getSummaryMetadata().getZStepUm(); - if (zStep != null && zStep != 0) { - cal.pixelDepth = zStep; - } else { - cal.pixelDepth = 1; - } - Double waitInterval = dp.getSummaryMetadata().getWaitInterval(); - if (waitInterval != null) { - cal.frameInterval = waitInterval / 1000.0; // MM in ms, IJ in s - } - - if(pSize != null && pSize != 0) { - cal.setUnit("micron"); - } - - iPlus.setCalibration(cal); - } - - @SuppressWarnings("Duplicates") - public void loadDataWithBDV() throws IOException { - Prefs.showScaleBar(true); - DisplayWindow displayWindow = studioProperty.get().displays().getCurrentWindow(); - - if(displayWindow == null) { - new Alert( Alert.AlertType.WARNING, "Please, load a dataset first").showAndWait(); - System.err.println("There is no dataset to be opened."); - return; - } - - ArrayList< ImagePlus > inputImgList = new ArrayList<>(); - inputImgList.add( toImageJ(displayWindow) ); - - SwingUtilities.invokeLater(() -> { - final ArrayList converterSetups = new ArrayList<>(); - final ArrayList> sources = new ArrayList<>(); - - final CacheControl.CacheControls cache = new CacheControl.CacheControls(); - int nTimepoints = 1; - int setup_id_offset = 0; - final ArrayList< ImagePlus > imgList = new ArrayList<>(); - boolean is2D = true; - - for ( ImagePlus imp : inputImgList ) - { - if ( imp.getNSlices() > 1 ) - is2D = false; - final AbstractSpimData< ? > spimData = load( imp, converterSetups, sources, setup_id_offset ); - if ( spimData != null ) - { - imgList.add( imp ); - cache.addCacheControl( ( (ViewerImgLoader) spimData.getSequenceDescription().getImgLoader() ).getCacheControl() ); - setup_id_offset += imp.getNChannels(); - nTimepoints = Math.max( nTimepoints, imp.getNFrames() ); - } - } - - if ( !imgList.isEmpty() ) - { - final BigDataViewer bdv = BigDataViewer.open( converterSetups, sources, - nTimepoints, cache, - "BigDataViewer", null, - ViewerOptions.options().is2D( is2D ) ); - - final SynchronizedViewerState state = bdv.getViewer().state(); - synchronized ( state ) - { - int channelOffset = 0; - int numActiveChannels = 0; - for ( ImagePlus imp : imgList ) - { - numActiveChannels += transferChannelVisibility( channelOffset, imp, state ); - transferChannelSettings( channelOffset, imp, state, bdv.getConverterSetups() ); - channelOffset += imp.getNChannels(); - } - state.setDisplayMode( numActiveChannels > 1 ? DisplayMode.FUSED : DisplayMode.SINGLE ); - } - } - }); - } - - protected AbstractSpimData< ? > load( ImagePlus imp, ArrayList< ConverterSetup > converterSetups, ArrayList< SourceAndConverter< ? > > sources, - int setup_id_offset ) - { - // check the image type - switch ( imp.getType() ) - { - case ImagePlus.GRAY8: - case ImagePlus.GRAY16: - case ImagePlus.GRAY32: - case ImagePlus.COLOR_RGB: - break; - default: - IJ.showMessage( imp.getShortTitle() + ": Only 8, 16, 32-bit images and RGB images are supported currently!" ); - return null; - } - - // get calibration and image size - final double pw = imp.getCalibration().pixelWidth; - final double ph = imp.getCalibration().pixelHeight; - final double pd = imp.getCalibration().pixelDepth; - String punit = imp.getCalibration().getUnit(); - if ( punit == null || punit.isEmpty() ) - punit = "px"; - final FinalVoxelDimensions voxelSize = new FinalVoxelDimensions( punit, pw, ph, pd ); - final int w = imp.getWidth(); - final int h = imp.getHeight(); - final int d = imp.getNSlices(); - final FinalDimensions size = new FinalDimensions( w, h, d ); - - // propose reasonable mipmap settings -// final ExportMipmapInfo autoMipmapSettings = ProposeMipmaps.proposeMipmaps( new BasicViewSetup( 0, "", size, voxelSize ) ); - - // create ImgLoader wrapping the image - final BasicImgLoader imgLoader; - if ( imp.getStack().isVirtual() ) - { - switch ( imp.getType() ) - { - case ImagePlus.GRAY8: - imgLoader = VirtualStackImageLoader.createUnsignedByteInstance( imp, setup_id_offset ); - break; - case ImagePlus.GRAY16: - imgLoader = VirtualStackImageLoader.createUnsignedShortInstance( imp, setup_id_offset ); - break; - case ImagePlus.GRAY32: - imgLoader = VirtualStackImageLoader.createFloatInstance( imp, setup_id_offset ); - break; - case ImagePlus.COLOR_RGB: - default: - imgLoader = VirtualStackImageLoader.createARGBInstance( imp, setup_id_offset ); - break; - } - } - else - { - switch ( imp.getType() ) - { - case ImagePlus.GRAY8: - imgLoader = ImageStackImageLoader.createUnsignedByteInstance( imp, setup_id_offset ); - break; - case ImagePlus.GRAY16: - imgLoader = ImageStackImageLoader.createUnsignedShortInstance( imp, setup_id_offset ); - break; - case ImagePlus.GRAY32: - imgLoader = ImageStackImageLoader.createFloatInstance( imp, setup_id_offset ); - break; - case ImagePlus.COLOR_RGB: - default: - imgLoader = ImageStackImageLoader.createARGBInstance( imp, setup_id_offset ); - break; - } - } - - final int numTimepoints = imp.getNFrames(); - final int numSetups = imp.getNChannels(); - - // create setups from channels - final HashMap< Integer, BasicViewSetup> setups = new HashMap<>( numSetups ); - for ( int s = 0; s < numSetups; ++s ) - { - final BasicViewSetup setup = new BasicViewSetup( setup_id_offset + s, String.format( imp.getTitle() + " channel %d", s + 1 ), size, voxelSize ); - setup.setAttribute( new Channel( s + 1 ) ); - setups.put( setup_id_offset + s, setup ); - } - - // create timepoints - final ArrayList timepoints = new ArrayList<>( numTimepoints ); - for ( int t = 0; t < numTimepoints; ++t ) - timepoints.add( new TimePoint( t ) ); - final SequenceDescriptionMinimal seq = new SequenceDescriptionMinimal( new TimePoints( timepoints ), setups, imgLoader, null ); - - // create ViewRegistrations from the images calibration - final AffineTransform3D sourceTransform = new AffineTransform3D(); - sourceTransform.set( pw, 0, 0, 0, 0, ph, 0, 0, 0, 0, pd, 0 ); - final ArrayList registrations = new ArrayList<>(); - for ( int t = 0; t < numTimepoints; ++t ) - for ( int s = 0; s < numSetups; ++s ) - registrations.add( new ViewRegistration( t, setup_id_offset + s, sourceTransform ) ); - - final File basePath = new File( "." ); - final AbstractSpimData< ? > spimData = new SpimDataMinimal( basePath, seq, new ViewRegistrations( registrations ) ); - WrapBasicImgLoader.wrapImgLoaderIfNecessary( spimData ); - BigDataViewer.initSetups( spimData, converterSetups, sources ); - - return spimData; - } - - /** - * @return number of setups that were set active. - */ - protected int transferChannelVisibility( int channelOffset, final ImagePlus imp, final ViewerState state ) - { - final int nChannels = imp.getNChannels(); - final CompositeImage ci = imp.isComposite() ? ( CompositeImage ) imp : null; - final List< SourceAndConverter< ? > > sources = state.getSources(); - if ( ci != null && ci.getCompositeMode() == IJ.COMPOSITE ) - { - final boolean[] activeChannels = ci.getActiveChannels(); - int numActiveChannels = 0; - for ( int i = 0; i < Math.min( activeChannels.length, nChannels ); ++i ) - { - final SourceAndConverter< ? > source = sources.get( channelOffset + i ); - state.setSourceActive( source, activeChannels[ i ] ); - state.setCurrentSource( source ); - numActiveChannels += activeChannels[ i ] ? 1 : 0; - } - return numActiveChannels; - } - else - { - final int activeChannel = imp.getChannel() - 1; - for ( int i = 0; i < nChannels; ++i ) - state.setSourceActive( sources.get( channelOffset + i ), i == activeChannel ); - state.setCurrentSource( sources.get( channelOffset + activeChannel ) ); - return 1; - } - } - - protected void transferChannelSettings( int channelOffset, final ImagePlus imp, final ViewerState state, final ConverterSetups converterSetups ) - { - final int nChannels = imp.getNChannels(); - final CompositeImage ci = imp.isComposite() ? ( CompositeImage ) imp : null; - final List< SourceAndConverter< ? > > sources = state.getSources(); - if ( ci != null ) - { - final int mode = ci.getCompositeMode(); - final boolean transferColor = mode == IJ.COMPOSITE || mode == IJ.COLOR; - for ( int c = 0; c < nChannels; ++c ) - { - final LUT lut = ci.getChannelLut( c + 1 ); - ImageProcessor ip = ci.getChannelProcessor(); - ImageStatistics s = ip.getStats(); - - final ConverterSetup setup = converterSetups.getConverterSetup( sources.get( channelOffset + c ) ); - if ( transferColor ) - setup.setColor( new ARGBType( lut.getRGB( 255 ) ) ); - setup.setDisplayRange( s.min, s.max ); - } - } - else - { - ImageProcessor ip = imp.getChannelProcessor(); - ImageStatistics s = ip.getStats(); - - final double displayRangeMin = s.min; - final double displayRangeMax = s.max; - for ( int i = 0; i < nChannels; ++i ) - { - final ConverterSetup setup = converterSetups.getConverterSetup( sources.get( channelOffset + i ) ); - final LUT[] luts = imp.getLuts(); - if ( luts.length != 0 ) - setup.setColor( new ARGBType( luts[ 0 ].getRGB( 255 ) ) ); - setup.setDisplayRange( displayRangeMin, displayRangeMax ); - } - } - } - public SimpleDoubleProperty waitSecondsProperty() { return waitSeconds; } @@ -762,20 +371,23 @@ public SimpleDoubleProperty waitSecondsProperty() { topHbox.getChildren().remove( liveDemoLabel ); buttonHbox.getChildren().remove( mmButton ); // liveViewHbox.getChildren().add( 0, liveViewButton); - liveViewHbox.getChildren().addAll( openDatasetButton, openDatasetWithBDV ); + liveViewHbox.getChildren().addAll( openDatasetButton ); pixelSizeValue.setText(studio.core().getPixelSizeUm() + ""); - rotatorStepSizeValue.setText(setup.getThetaStage().getStepSize() + ""); + + if(setup.getThetaStage() != null) + rotatorStepSizeValue.setText(setup.getThetaStage().getStepSize() + ""); + zStageStepSizeValue.setText(setup.getZStage().getStepSize() + ""); // roi = new java.awt.Rectangle(0, 0, 0, 0); } else { topHbox.getChildren().add( liveDemoLabel ); buttonHbox.getChildren().add( mmButton ); // liveViewHbox.getChildren().remove( liveViewButton ); - liveViewHbox.getChildren().removeAll( openDatasetButton, openDatasetWithBDV ); + liveViewHbox.getChildren().removeAll( openDatasetButton ); pixelSizeValue.setText("N.A."); rotatorStepSizeValue.setText("N.A."); zStageStepSizeValue.setText("N.A."); // roi = new java.awt.Rectangle( 0, 0, 0, 0 ); } } -} \ No newline at end of file +} diff --git a/src/main/java/spim/ui/view/component/acquisition/AcqWrapperEngine.java b/src/main/java/spim/ui/view/component/acquisition/AcqWrapperEngine.java index 8f0d2ef9..b941c1ff 100644 --- a/src/main/java/spim/ui/view/component/acquisition/AcqWrapperEngine.java +++ b/src/main/java/spim/ui/view/component/acquisition/AcqWrapperEngine.java @@ -4,6 +4,8 @@ import java.awt.Color; import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; import java.util.*; import java.util.concurrent.BlockingQueue; import java.util.concurrent.locks.ReentrantLock; @@ -109,12 +111,17 @@ public class AcqWrapperEngine implements AcquisitionEngine private DefaultAntiDrift currentAntiDrift_; private Integer antiDriftReferenceChannel_; private Boolean onTheFly_; + private Boolean ablationSupport_; + private String prefix_; + private File outFolder_; final String channelGroupName = "OpenSPIM-channels"; private ReentrantLock rlock = new ReentrantLock(true); Datastore mpStore_; + Pipeline mpPipeline_; + String ablationFilePrefix_; TreeMap[] mpImages_; TaggedImageSink sink; @@ -124,7 +131,7 @@ public AcqWrapperEngine(SPIMSetup setup, final Studio frame, Datastore store, String currentCamera, List cameras, File outFolder, String acqFilenamePrefix, List channelItems, boolean arduinoSelected, - LongProperty processedImages, HashMap driftCompMap, Integer adReferenceChannel, Boolean onTheFly) throws Exception + LongProperty processedImages, HashMap driftCompMap, Integer adReferenceChannel, Boolean saveMIP, Boolean onTheFly, Boolean ablationSupport) throws Exception { curStore_ = store; @@ -156,6 +163,9 @@ public AcqWrapperEngine(SPIMSetup setup, final Studio frame, Datastore store, posList_ = new PositionList(); antiDriftReferenceChannel_ = adReferenceChannel; onTheFly_ = onTheFly; + ablationSupport_ = ablationSupport; + prefix_ = acqFilenamePrefix; + outFolder_ = outFolder; // Initial setting @@ -201,15 +211,18 @@ public AcqWrapperEngine(SPIMSetup setup, final Studio frame, Datastore store, { String config = "Ch-" + ch++; - if(spimSetup_.getLaser().getLabel().startsWith("VLT_VersaLase")) { - core_.defineConfig(channelGroupName, config, "Core", "Shutter", spimSetup_.getLaser().getLabel()); - VersaLase.VersaLaseLaser laser = ((VersaLase)spimSetup_.getLaser()).getLaser(channelItem.getLaser()); + if(spimSetup_.getLaser() != null) { + if(spimSetup_.getLaser().getLabel().startsWith("VLT_VersaLase")) { + core_.defineConfig(channelGroupName, config, "Core", "Shutter", spimSetup_.getLaser().getLabel()); + VersaLase.VersaLaseLaser laser = ((VersaLase)spimSetup_.getLaser()).getLaser(channelItem.getLaser()); - core_.defineConfig(channelGroupName, config, spimSetup_.getLaser().getLabel(), laser.getShutter(), "ON"); - } else { - core_.defineConfig(channelGroupName, config, "Core", "Shutter", channelItem.getLaser()); + core_.defineConfig(channelGroupName, config, spimSetup_.getLaser().getLabel(), laser.getShutter(), "ON"); + } else { + core_.defineConfig(channelGroupName, config, "Core", "Shutter", channelItem.getLaser()); + } } + core_.defineConfig(channelGroupName, config, "Core", "Camera", channelItem.getName()); double exp = channelItem.getValue().doubleValue(); @@ -228,7 +241,11 @@ public AcqWrapperEngine(SPIMSetup setup, final Studio frame, Datastore store, channels_ = channels; setChannelGroup( channelGroupName ); - if ( outFolder != null && acqFilenamePrefix != null ) { + if (ablationSupport) { + ablationFilePrefix_ = acqFilenamePrefix + "_ablation"; + } + + if ( outFolder != null && saveMIP ) { File saveDir = new File(outFolder, acqFilenamePrefix + "-MIP"); if (!outFolder.exists() && !outFolder.mkdirs()) { @@ -240,13 +257,16 @@ public AcqWrapperEngine(SPIMSetup setup, final Studio frame, Datastore store, saveDir.delete(); } saveDir.mkdirs(); - if (null != acqFilenamePrefix) { + if (saveMIP) { List multis = MMAcquisitionEngine.getMultiCams(core_); DefaultDatastore result = new DefaultDatastore(frame); result.setStorage(new OpenSPIMSinglePlaneTiffSeries(result, saveDir.getAbsolutePath(), acqFilenamePrefix, true)); mpStore_ = result; + mpPipeline_ = studio_.data().copyApplicationPipeline(mpStore_, false); + mpStore_.registerForEvents(this); + DisplayWindow display = frame.displays().createDisplay(mpStore_); // Channel setting for the display @@ -383,9 +403,26 @@ protected Datastore runAcquisition(SequenceSettings acquisitionSettings) { sink.start(() -> getAcquisitionEngine2010().stop(), () -> { rlock.lock(); + if(ablationSupport_) { + File latestFile = new File(outFolder_, getLatestFile(t_, angle_)); + if (latestFile.exists()) { + File ablationFile = new File(outFolder_, getAblationFilename(angle_)); + try { + Files.copy(latestFile.toPath(), ablationFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + e.printStackTrace(); + } + } + } generateMIP(); rlock.unlock(); curStore_.unregisterForEvents(AcqWrapperEngine.this); + if (mpStore_ != null) { + studio_.events().post(new DefaultAcquisitionEndedEvent( + mpStore_, this)); + + mpStore_.unregisterForEvents(this); + } }); return curStore_; @@ -399,11 +436,11 @@ protected Datastore runAcquisition(SequenceSettings acquisitionSettings) { } private void generateMIP() { - if(null != mpImages_) - for(TreeMap stack : mpImages_) { + if(null != mpImages_) { + for (TreeMap stack : mpImages_) { // Could be moved outside processImage() ? Image img = stack.get(0); - if(img == null) continue; + if (img == null) continue; int bitDepth = img.getMetadata().getBitDepth(); int width = img.getWidth(); @@ -494,18 +531,32 @@ private void generateMIP() { Image processedImage_ = studio_.data().createImage(resultPixels, width, height, bytesPerPixel, numComponents, coords, metadata); - if(null != mpStore_ && !mpStore_.isFrozen()) + if (null != mpStore_ && !mpStore_.isFrozen()) try { - mpStore_.putImage(processedImage_); + mpPipeline_.insertImage(processedImage_); } catch (IOException e) { e.printStackTrace(); + } catch (PipelineErrorException e) { + e.printStackTrace(); } } - if(null != mpImages_) - for(int i = 0; i < mpImages_.length; i++) { - mpImages_[i] = new TreeMap<>(); - } + for(int i = 0; i < mpImages_.length; i++) { + mpImages_[i] = new TreeMap<>(); + } + } + } + + private String getLatestFile(int t, int p) { + String posString = String.format("_Pos%02d", p); + + return String.format(prefix_ + "_TL%04d" + posString + ".tiff", t); + } + + private String getAblationFilename(int p) { + String posString = String.format("_Pos%02d", p); + + return String.format(ablationFilePrefix_ + posString + ".tiff"); } private int getNumChannels() { @@ -762,6 +813,7 @@ public void stop(boolean interrupted) { if (acquisitionEngine2010_ != null) { acquisitionEngine2010_.stop(); } + acquisitionEngine2010_ = null; } catch (Exception ex) { ReportingUtils.showError(ex, "Acquisition engine stop request failed"); } diff --git a/src/main/java/spim/ui/view/component/acquisition/TaggedImageSink.java b/src/main/java/spim/ui/view/component/acquisition/TaggedImageSink.java index 5a8b9648..53df240e 100644 --- a/src/main/java/spim/ui/view/component/acquisition/TaggedImageSink.java +++ b/src/main/java/spim/ui/view/component/acquisition/TaggedImageSink.java @@ -19,6 +19,7 @@ import org.micromanager.data.Coordinates; import org.micromanager.data.Coords; import org.micromanager.data.Datastore; +import org.micromanager.data.DatastoreFrozenException; import org.micromanager.data.Image; import org.micromanager.data.Metadata; import org.micromanager.data.Pipeline; @@ -229,6 +230,10 @@ public void run() { handleOutOfMemory(e, sinkFullCallback); break; } + catch (DatastoreFrozenException ex) { + pipeline_.clearExceptions(); + break; + } } } } catch (Exception ex2) { diff --git a/src/main/java/spim/ui/view/component/util/AdvancedPlugins.java b/src/main/java/spim/ui/view/component/util/AdvancedPlugins.java new file mode 100644 index 00000000..787bac14 --- /dev/null +++ b/src/main/java/spim/ui/view/component/util/AdvancedPlugins.java @@ -0,0 +1,463 @@ +package spim.ui.view.component.util; + +import bdv.BigDataViewer; +import bdv.ViewerImgLoader; +import bdv.cache.CacheControl; +import bdv.img.imagestack.ImageStackImageLoader; +import bdv.img.virtualstack.VirtualStackImageLoader; +import bdv.spimdata.SequenceDescriptionMinimal; +import bdv.spimdata.SpimDataMinimal; +import bdv.spimdata.WrapBasicImgLoader; +import bdv.tools.brightness.ConverterSetup; +import bdv.util.Prefs; +import bdv.viewer.ConverterSetups; +import bdv.viewer.DisplayMode; +import bdv.viewer.SourceAndConverter; +import bdv.viewer.SynchronizedViewerState; +import bdv.viewer.ViewerOptions; +import bdv.viewer.ViewerState; +import ij.CompositeImage; +import ij.IJ; +import ij.ImagePlus; +import ij.ImageStack; +import ij.measure.Calibration; +import ij.process.ImageProcessor; +import ij.process.ImageStatistics; +import ij.process.LUT; +import mpicbg.spim.data.SpimDataException; +import mpicbg.spim.data.generic.AbstractSpimData; +import mpicbg.spim.data.generic.sequence.BasicImgLoader; +import mpicbg.spim.data.generic.sequence.BasicViewSetup; +import mpicbg.spim.data.registration.ViewRegistration; +import mpicbg.spim.data.registration.ViewRegistrations; +import mpicbg.spim.data.sequence.Channel; +import mpicbg.spim.data.sequence.FinalVoxelDimensions; +import mpicbg.spim.data.sequence.TimePoint; +import mpicbg.spim.data.sequence.TimePoints; +import net.imagej.ops.OpService; +import net.imglib2.FinalDimensions; +import net.imglib2.realtransform.AffineTransform3D; +import net.imglib2.type.numeric.ARGBType; +import net.preibisch.mvrecon.fiji.plugin.queryXML.LoadParseQueryXML; +import net.preibisch.mvrecon.fiji.spimdata.SpimData2; +import net.preibisch.mvrecon.fiji.spimdata.XmlIoSpimData2; +import net.preibisch.stitcher.gui.StitchingExplorer; +import org.mastodon.feature.FeatureComputerService; +import org.mastodon.feature.FeatureSpecsService; +import org.mastodon.feature.io.FeatureSerializationService; +import org.mastodon.mamut.MainWindow; +import org.mastodon.mamut.WindowManager; +import org.mastodon.mamut.project.MamutProject; +import org.mastodon.mamut.project.MamutProjectIO; +import org.micromanager.data.Coordinates; +import org.micromanager.data.Coords; +import org.micromanager.data.DataProvider; +import org.micromanager.data.internal.DefaultImageJConverter; +import org.micromanager.display.DisplayWindow; +import org.scijava.Context; +import org.scijava.app.StatusService; +import org.scijava.plugin.PluginService; + +import javax.swing.*; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * Author: HongKee Moon (moon@mpi-cbg.de), Scientific Computing Facility + * Organization: MPI-CBG Dresden + * Date: June 2023 + */ +public class AdvancedPlugins { + static class ParseQueryXML extends LoadParseQueryXML { + public void open(String xmlFilename) { + this.tryParsing(xmlFilename, true); + } + } + + private static ImagePlus toImageJ(DisplayWindow display) { + final boolean copy = false; + final boolean setProps = true; + // TODO: UI to set copy, give option to only do partial data, and multiple positions + + DataProvider dp = display.getDataProvider(); + Coords displayPosition = display.getDisplayPosition(); + int p = displayPosition.getP(); + + ImagePlus iPlus = null; + org.micromanager.data.Image image = null; + if (dp.getNumImages() == 1) { + try { + image = dp.getAnyImage(); + ImageProcessor iProc = DefaultImageJConverter.createProcessor(image, copy); + iPlus = new ImagePlus(dp.getName() + "-ij", iProc); + + } catch (IOException ex) { + // TODO: report error + } + if (setProps && iPlus != null && image != null) { + setCalibration(iPlus, dp, image); + } + if (iPlus != null) { + return iPlus; + } + } else if (dp.getNumImages() > 1) { + try { + ImageStack imgStack = new ImageStack(dp.getAnyImage().getWidth(), + dp.getAnyImage().getHeight()); + Coords.Builder cb = Coordinates.builder().c(0).t(0).p(p).z(0); + for (int t = 0; t < dp.getNextIndex(Coords.T); t++) { + for (int z = 0; z < dp.getNextIndex(Coords.Z); z++) { + for (int c = 0; c < dp.getNextIndex(Coords.C); c++) { + image = dp.getImage(cb.c(c).t(t).z(z).build()); + ImageProcessor iProc; + if (image != null) { + iProc = DefaultImageJConverter.createProcessor( + image, copy); + } else { // handle missing images - should be handled by MM + // so remove this code once this is done nicely in MM + iProc = DefaultImageJConverter.createBlankProcessor( + dp.getAnyImage()); + } + imgStack.addSlice(iProc); + } + } + } + iPlus = new ImagePlus(dp.getName() + "-ij"); + iPlus.setOpenAsHyperStack(true); + iPlus.setStack(imgStack, dp.getNextIndex(Coords.C), + dp.getNextIndex(Coords.Z), dp.getNextIndex(Coords.T)); + + int displayMode; + switch (display.getDisplaySettings().getColorMode()) { + case COLOR: { displayMode = IJ.COLOR; break; } + case COMPOSITE: { displayMode = IJ.COMPOSITE; break; } + case GRAYSCALE: { displayMode = IJ.GRAYSCALE; break; } + default: { displayMode = IJ.GRAYSCALE; break; } + } + iPlus.setDisplayMode(displayMode); + CompositeImage ci = new CompositeImage(iPlus, displayMode); + ci.setTitle(dp.getName() + "-ij"); + for (int c = 0; c < dp.getNextIndex(Coords.C); c++) { + ci.setChannelLut( + LUT.createLutFromColor(display.getDisplaySettings().getChannelColor(c)), + c + 1); + } + if (setProps && image != null) { + setCalibration(ci, dp, image); + } + return ci; + + } catch (IOException ex) { + // TODO: report + } + + return null; + } + + return null; + } + + private static void setCalibration(ImagePlus iPlus, DataProvider dp, org.micromanager.data.Image image) { + Calibration cal = new Calibration(iPlus); + Double pSize = image.getMetadata().getPixelSizeUm(); + if (pSize != null && pSize != 0) { + cal.pixelWidth = pSize; + cal.pixelHeight = pSize; + } else { + cal.pixelWidth = 1; + cal.pixelHeight = 1; + } + Double zStep = dp.getSummaryMetadata().getZStepUm(); + if (zStep != null && zStep != 0) { + cal.pixelDepth = zStep; + } else { + cal.pixelDepth = 1; + } + Double waitInterval = dp.getSummaryMetadata().getWaitInterval(); + if (waitInterval != null) { + cal.frameInterval = waitInterval / 1000.0; // MM in ms, IJ in s + } + + if(pSize != null && pSize != 0) { + cal.setUnit("micron"); + } + + iPlus.setCalibration(cal); + } + + @SuppressWarnings("Duplicates") + public static void loadDataWithBDV(DisplayWindow displayWindow) { + Prefs.showScaleBar(true); +// DisplayWindow displayWindow = studioProperty.get().displays().getCurrentWindow(); + + ArrayList< ImagePlus > inputImgList = new ArrayList<>(); + inputImgList.add( toImageJ(displayWindow) ); + + SwingUtilities.invokeLater(() -> { + final ArrayList converterSetups = new ArrayList<>(); + final ArrayList> sources = new ArrayList<>(); + + final CacheControl.CacheControls cache = new CacheControl.CacheControls(); + int nTimepoints = 1; + int setup_id_offset = 0; + final ArrayList< ImagePlus > imgList = new ArrayList<>(); + boolean is2D = true; + + for ( ImagePlus imp : inputImgList ) + { + if ( imp.getNSlices() > 1 ) + is2D = false; + final AbstractSpimData< ? > spimData = load( imp, converterSetups, sources, setup_id_offset ); + if ( spimData != null ) + { + imgList.add( imp ); + cache.addCacheControl( ( (ViewerImgLoader) spimData.getSequenceDescription().getImgLoader() ).getCacheControl() ); + setup_id_offset += imp.getNChannels(); + nTimepoints = Math.max( nTimepoints, imp.getNFrames() ); + } + } + + if ( !imgList.isEmpty() ) + { + final BigDataViewer bdv = BigDataViewer.open( converterSetups, sources, + nTimepoints, cache, + "BigDataViewer", null, + ViewerOptions.options().is2D( is2D ) ); + + bdv.getViewerFrame().requestFocus(); + + final SynchronizedViewerState state = bdv.getViewer().state(); + synchronized ( state ) + { + int channelOffset = 0; + int numActiveChannels = 0; + for ( ImagePlus imp : imgList ) + { + numActiveChannels += transferChannelVisibility( channelOffset, imp, state ); + transferChannelSettings( channelOffset, imp, state, bdv.getConverterSetups() ); + channelOffset += imp.getNChannels(); + } + state.setDisplayMode( numActiveChannels > 1 ? DisplayMode.FUSED : DisplayMode.SINGLE ); + } + } + }); + } + + protected static AbstractSpimData< ? > load(ImagePlus imp, ArrayList converterSetups, ArrayList> sources, + int setup_id_offset) + { + // check the image type + switch ( imp.getType() ) + { + case ImagePlus.GRAY8: + case ImagePlus.GRAY16: + case ImagePlus.GRAY32: + case ImagePlus.COLOR_RGB: + break; + default: + IJ.showMessage( imp.getShortTitle() + ": Only 8, 16, 32-bit images and RGB images are supported currently!" ); + return null; + } + + // get calibration and image size + final double pw = imp.getCalibration().pixelWidth; + final double ph = imp.getCalibration().pixelHeight; + final double pd = imp.getCalibration().pixelDepth; + String punit = imp.getCalibration().getUnit(); + if ( punit == null || punit.isEmpty() ) + punit = "px"; + final FinalVoxelDimensions voxelSize = new FinalVoxelDimensions( punit, pw, ph, pd ); + final int w = imp.getWidth(); + final int h = imp.getHeight(); + final int d = imp.getNSlices(); + final FinalDimensions size = new FinalDimensions( w, h, d ); + + // propose reasonable mipmap settings +// final ExportMipmapInfo autoMipmapSettings = ProposeMipmaps.proposeMipmaps( new BasicViewSetup( 0, "", size, voxelSize ) ); + + // create ImgLoader wrapping the image + final BasicImgLoader imgLoader; + if ( imp.getStack().isVirtual() ) + { + switch ( imp.getType() ) + { + case ImagePlus.GRAY8: + imgLoader = VirtualStackImageLoader.createUnsignedByteInstance( imp, setup_id_offset ); + break; + case ImagePlus.GRAY16: + imgLoader = VirtualStackImageLoader.createUnsignedShortInstance( imp, setup_id_offset ); + break; + case ImagePlus.GRAY32: + imgLoader = VirtualStackImageLoader.createFloatInstance( imp, setup_id_offset ); + break; + case ImagePlus.COLOR_RGB: + default: + imgLoader = VirtualStackImageLoader.createARGBInstance( imp, setup_id_offset ); + break; + } + } + else + { + switch ( imp.getType() ) + { + case ImagePlus.GRAY8: + imgLoader = ImageStackImageLoader.createUnsignedByteInstance( imp, setup_id_offset ); + break; + case ImagePlus.GRAY16: + imgLoader = ImageStackImageLoader.createUnsignedShortInstance( imp, setup_id_offset ); + break; + case ImagePlus.GRAY32: + imgLoader = ImageStackImageLoader.createFloatInstance( imp, setup_id_offset ); + break; + case ImagePlus.COLOR_RGB: + default: + imgLoader = ImageStackImageLoader.createARGBInstance( imp, setup_id_offset ); + break; + } + } + + final int numTimepoints = imp.getNFrames(); + final int numSetups = imp.getNChannels(); + + // create setups from channels + final HashMap< Integer, BasicViewSetup> setups = new HashMap<>( numSetups ); + for ( int s = 0; s < numSetups; ++s ) + { + final BasicViewSetup setup = new BasicViewSetup( setup_id_offset + s, String.format( imp.getTitle() + " channel %d", s + 1 ), size, voxelSize ); + setup.setAttribute( new Channel( s + 1 ) ); + setups.put( setup_id_offset + s, setup ); + } + + // create timepoints + final ArrayList timepoints = new ArrayList<>( numTimepoints ); + for ( int t = 0; t < numTimepoints; ++t ) + timepoints.add( new TimePoint( t ) ); + final SequenceDescriptionMinimal seq = new SequenceDescriptionMinimal( new TimePoints( timepoints ), setups, imgLoader, null ); + + // create ViewRegistrations from the images calibration + final AffineTransform3D sourceTransform = new AffineTransform3D(); + sourceTransform.set( pw, 0, 0, 0, 0, ph, 0, 0, 0, 0, pd, 0 ); + final ArrayList registrations = new ArrayList<>(); + for ( int t = 0; t < numTimepoints; ++t ) + for ( int s = 0; s < numSetups; ++s ) + registrations.add( new ViewRegistration( t, setup_id_offset + s, sourceTransform ) ); + + final File basePath = new File( "." ); + final AbstractSpimData< ? > spimData = new SpimDataMinimal( basePath, seq, new ViewRegistrations( registrations ) ); + WrapBasicImgLoader.wrapImgLoaderIfNecessary( spimData ); + BigDataViewer.initSetups( spimData, converterSetups, sources ); + + return spimData; + } + + /** + * @return number of setups that were set active. + */ + protected static int transferChannelVisibility(int channelOffset, final ImagePlus imp, final ViewerState state) + { + final int nChannels = imp.getNChannels(); + final CompositeImage ci = imp.isComposite() ? ( CompositeImage ) imp : null; + final List< SourceAndConverter< ? > > sources = state.getSources(); + if ( ci != null && ci.getCompositeMode() == IJ.COMPOSITE ) + { + final boolean[] activeChannels = ci.getActiveChannels(); + int numActiveChannels = 0; + for ( int i = 0; i < Math.min( activeChannels.length, nChannels ); ++i ) + { + final SourceAndConverter< ? > source = sources.get( channelOffset + i ); + state.setSourceActive( source, activeChannels[ i ] ); + state.setCurrentSource( source ); + numActiveChannels += activeChannels[ i ] ? 1 : 0; + } + return numActiveChannels; + } + else + { + final int activeChannel = imp.getChannel() - 1; + for ( int i = 0; i < nChannels; ++i ) + state.setSourceActive( sources.get( channelOffset + i ), i == activeChannel ); + state.setCurrentSource( sources.get( channelOffset + activeChannel ) ); + return 1; + } + } + + protected static void transferChannelSettings(int channelOffset, final ImagePlus imp, final ViewerState state, final ConverterSetups converterSetups) + { + final int nChannels = imp.getNChannels(); + final CompositeImage ci = imp.isComposite() ? ( CompositeImage ) imp : null; + final List< SourceAndConverter< ? > > sources = state.getSources(); + if ( ci != null ) + { + final int mode = ci.getCompositeMode(); + final boolean transferColor = mode == IJ.COMPOSITE || mode == IJ.COLOR; + for ( int c = 0; c < nChannels; ++c ) + { + final LUT lut = ci.getChannelLut( c + 1 ); + ImageProcessor ip = ci.getChannelProcessor(); + ImageStatistics s = ip.getStats(); + + final ConverterSetup setup = converterSetups.getConverterSetup( sources.get( channelOffset + c ) ); + if ( transferColor ) + setup.setColor( new ARGBType( lut.getRGB( 255 ) ) ); + setup.setDisplayRange( s.min, s.max ); + } + } + else + { + ImageProcessor ip = imp.getChannelProcessor(); + ImageStatistics s = ip.getStats(); + + final double displayRangeMin = s.min; + final double displayRangeMax = s.max; + for ( int i = 0; i < nChannels; ++i ) + { + final ConverterSetup setup = converterSetups.getConverterSetup( sources.get( channelOffset + i ) ); + final LUT[] luts = imp.getLuts(); + if ( luts.length != 0 ) + setup.setColor( new ARGBType( luts[ 0 ].getRGB( 255 ) ) ); + setup.setDisplayRange( displayRangeMin, displayRangeMax ); + } + } + } + + public static void openBigStitcherWindow(String folder) { + SwingUtilities.invokeLater(() -> { + final ParseQueryXML result = new ParseQueryXML(); + + result.open(folder + File.separator + "dataset.xml"); + + final SpimData2 data = result.getData(); + final String xml = result.getXMLFileName(); + final XmlIoSpimData2 io = result.getIO(); + + final StitchingExplorer explorer = + new StitchingExplorer< >( data, xml, io ); + }); + } + + public static void openMastodonWindow(String folder) { + SwingUtilities.invokeLater(() -> { +// final File file = new File("/Users/moon/Desktop/cap_3" + File.separator + "dataset.xml"); + final File datasetFile = new File(folder + File.separator + "dataset.xml"); + final File mastodonFile = new File(folder + File.separator + "dataset.mastodon"); + new Thread(() -> { + try { + final WindowManager windowManager = new WindowManager( new Context(PluginService.class, FeatureSpecsService.class, FeatureSerializationService.class, FeatureComputerService.class, StatusService.class, OpService.class)); + if (mastodonFile.exists()) { + windowManager.getProjectManager().open(new MamutProjectIO().load( mastodonFile.getPath() )); + } else if (datasetFile.exists()) { + windowManager.getProjectManager().open(new MamutProject(null, datasetFile)); + } + MainWindow mastodonWindow = new MainWindow(windowManager); + mastodonWindow.setVisible(true); + mastodonWindow.requestFocus(); + } catch (IOException | SpimDataException e) { + + } + }).start(); + }); + } +} diff --git a/src/main/java/spim/ui/view/component/util/CellUtils.java b/src/main/java/spim/ui/view/component/util/CellUtils.java new file mode 100644 index 00000000..75f58673 --- /dev/null +++ b/src/main/java/spim/ui/view/component/util/CellUtils.java @@ -0,0 +1,217 @@ +package spim.ui.view.component.util; + +import javafx.beans.property.ObjectProperty; +import javafx.collections.ObservableList; +import javafx.scene.Node; +import javafx.scene.control.Cell; +import javafx.scene.control.ChoiceBox; +import javafx.scene.control.ComboBox; +import javafx.scene.control.TextField; +import javafx.scene.control.TreeItem; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; +import javafx.scene.layout.HBox; +import javafx.util.StringConverter; + +/** + * Author: HongKee Moon (moon@mpi-cbg.de), Scientific Computing Facility + * Organization: MPI-CBG Dresden + * Date: August 2023 + */ +class CellUtils { + static int TREE_VIEW_HBOX_GRAPHIC_PADDING = 3; + private static final StringConverter defaultStringConverter = new StringConverter() { + public String toString(Object var1) { + return var1 == null ? null : var1.toString(); + } + + public Object fromString(String var1) { + return var1; + } + }; + private static final StringConverter defaultTreeItemStringConverter = new StringConverter>() { + public String toString(TreeItem var1) { + return var1 != null && var1.getValue() != null ? var1.getValue().toString() : ""; + } + + public TreeItem fromString(String var1) { + return new TreeItem(var1); + } + }; + + CellUtils() { + } + + static StringConverter defaultStringConverter() { + return defaultStringConverter; + } + + static StringConverter defaultTreeItemStringConverter() { + return defaultTreeItemStringConverter; + } + + private static String getItemText(Cell var0, StringConverter var1) { + return var1 == null ? (var0.getItem() == null ? "" : var0.getItem().toString()) : var1.toString(var0.getItem()); + } + + static Node getGraphic(TreeItem var0) { + return var0 == null ? null : var0.getGraphic(); + } + + static void updateItem(Cell var0, StringConverter var1, ChoiceBox var2) { + updateItem(var0, var1, (HBox)null, (Node)null, (ChoiceBox)var2); + } + + static void updateItem(Cell var0, StringConverter var1, HBox var2, Node var3, ChoiceBox var4) { + if (var0.isEmpty()) { + var0.setText((String)null); + var0.setGraphic((Node)null); + } else if (var0.isEditing()) { + if (var4 != null) { + var4.getSelectionModel().select(var0.getItem()); + } + + var0.setText((String)null); + if (var3 != null) { + var2.getChildren().setAll(new Node[]{var3, var4}); + var0.setGraphic(var2); + } else { + var0.setGraphic(var4); + } + } else { + var0.setText(getItemText(var0, var1)); + var0.setGraphic(var3); + } + + } + + static ChoiceBox createChoiceBox(Cell var0, ObservableList var1, ObjectProperty> var2) { + ChoiceBox var3 = new ChoiceBox(var1); + var3.setMaxWidth(1.7976931348623157E308D); + var3.converterProperty().bind(var2); + var3.getSelectionModel().selectedItemProperty().addListener((var1x, var2x, var3x) -> { + if (var0.isEditing()) { + var0.commitEdit((T) var3x); + } + + }); + return var3; + } + + static void updateItem(Cell var0, StringConverter var1, TextField var2) { + updateItem(var0, var1, (HBox)null, (Node)null, (TextField)var2); + } + + static void updateItem(Cell var0, StringConverter var1, HBox var2, Node var3, TextField var4) { + if (var0.isEmpty()) { + var0.setText((String)null); + var0.setGraphic((Node)null); + } else if (var0.isEditing()) { + if (var4 != null) { + var4.setText(getItemText(var0, var1)); + } + + var0.setText((String)null); + if (var3 != null) { + var2.getChildren().setAll(new Node[]{var3, var4}); + var0.setGraphic(var2); + } else { + var0.setGraphic(var4); + } + } else { + var0.setText(getItemText(var0, var1)); + var0.setGraphic(var3); + } + + } + + static void startEdit(Cell var0, StringConverter var1, HBox var2, Node var3, TextField var4) { + if (var4 != null) { + var4.setText(getItemText(var0, var1)); + } + + var0.setText((String)null); + if (var3 != null) { + var2.getChildren().setAll(new Node[]{var3, var4}); + var0.setGraphic(var2); + } else { + var0.setGraphic(var4); + } + + var4.selectAll(); + var4.requestFocus(); + } + + static void cancelEdit(Cell var0, StringConverter var1, Node var2) { + var0.setText(getItemText(var0, var1)); + var0.setGraphic(var2); + } + + static TextField createTextField(Cell var0, StringConverter var1) { + TextField var2 = new TextField(getItemText(var0, var1)); + var2.setOnAction((var3) -> { + if (var1 == null) { + throw new IllegalStateException("Attempting to convert text input into Object, but provided StringConverter is null. Be sure to set a StringConverter in your cell factory."); + } else { + var0.commitEdit(var1.fromString(var2.getText())); + var3.consume(); + } + }); + var2.addEventFilter(KeyEvent.KEY_PRESSED, event -> { + if (event.getCode() == KeyCode.ESCAPE) { + var2.setText(var1.toString(var0.getItem())); + var0.cancelEdit(); + event.consume(); + } + }); +// var2.setOnKeyReleased((var1x) -> { +// if (var1x.getCode() == KeyCode.ESCAPE) { +// System.out.println("Escape"); +// var0.cancelEdit(); +// .consume(); +// } +// +// }); + return var2; + } + + static void updateItem(Cell var0, StringConverter var1, ComboBox var2) { + updateItem(var0, var1, (HBox)null, (Node)null, (ComboBox)var2); + } + + static void updateItem(Cell var0, StringConverter var1, HBox var2, Node var3, ComboBox var4) { + if (var0.isEmpty()) { + var0.setText((String)null); + var0.setGraphic((Node)null); + } else if (var0.isEditing()) { + if (var4 != null) { + var4.getSelectionModel().select(var0.getItem()); + } + + var0.setText((String)null); + if (var3 != null) { + var2.getChildren().setAll(new Node[]{var3, var4}); + var0.setGraphic(var2); + } else { + var0.setGraphic(var4); + } + } else { + var0.setText(getItemText(var0, var1)); + var0.setGraphic(var3); + } + + } + + static ComboBox createComboBox(Cell var0, ObservableList var1, ObjectProperty> var2) { + ComboBox var3 = new ComboBox(var1); + var3.converterProperty().bind(var2); + var3.setMaxWidth(1.7976931348623157E308D); + var3.getSelectionModel().selectedItemProperty().addListener((var1x, var2x, var3x) -> { + if (var0.isEditing()) { + var0.commitEdit((T) var3x); + } + + }); + return var3; + } +} diff --git a/src/main/java/spim/ui/view/component/util/EditTextFieldTableCell.java b/src/main/java/spim/ui/view/component/util/EditTextFieldTableCell.java new file mode 100644 index 00000000..c623eaf8 --- /dev/null +++ b/src/main/java/spim/ui/view/component/util/EditTextFieldTableCell.java @@ -0,0 +1,133 @@ +package spim.ui.view.component.util; + +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.event.Event; +import javafx.scene.Node; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.control.Control; +import javafx.scene.control.TableCell; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TablePosition; +import javafx.scene.control.TableView; +import javafx.scene.control.TextField; +import javafx.scene.layout.HBox; +import javafx.util.Callback; +import javafx.util.StringConverter; +import javafx.util.converter.DefaultStringConverter; + +/** + * Author: HongKee Moon (moon@mpi-cbg.de), Scientific Computing Facility + * Organization: MPI-CBG Dresden + * Date: August 2023 + */ +public class EditTextFieldTableCell extends TableCell +{ + private TextField textField; + private ObjectProperty> converter; + private boolean isCancelling = false; + + public static Callback, TableCell> forTableColumn() { + return forTableColumn(new DefaultStringConverter()); + } + + public static Callback, TableCell> forTableColumn(StringConverter var0) { + return (var1) -> { + return new EditTextFieldTableCell(var0); + }; + } + + public EditTextFieldTableCell() { + this((StringConverter)null); + } + + public EditTextFieldTableCell(StringConverter var1) { + this.converter = new SimpleObjectProperty(this, "converter"); + this.getStyleClass().add("text-field-table-cell"); + this.setConverter(var1); + } + + public final ObjectProperty> converterProperty() { + return this.converter; + } + + public final void setConverter(StringConverter var1) { + this.converterProperty().set(var1); + } + + public final StringConverter getConverter() { + return (StringConverter)this.converterProperty().get(); + } + + public void startEdit() { + if (this.isEditable() && this.getTableView().isEditable() && this.getTableColumn().isEditable()) { + super.startEdit(); + if (this.isEditing()) { + if (this.textField == null) { + this.textField = CellUtils.createTextField(this, this.getConverter()); + textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> { + if (!isNowFocused) { + this.commitEdit(getConverter().fromString(this.textField.getText())); + } + }); + } + + CellUtils.startEdit(this, this.getConverter(), (HBox)null, (Node)null, this.textField); + } + } + } + + static void requestFocusOnControlOnlyIfCurrentFocusOwnerIsChild(Control var0) { + Scene var1 = var0.getScene(); + Node var2 = var1 == null ? null : var1.getFocusOwner(); + if (var2 == null) { + var0.requestFocus(); + } else if (!var0.equals(var2)) { + for(Parent var3 = var2.getParent(); var3 != null; var3 = var3.getParent()) { + if (var0.equals(var3)) { + var0.requestFocus(); + break; + } + } + } + + } + + public void commitEdit(T var1) { + if (this.isEditing()) { + TableView var2 = this.getTableView(); + if (var2 != null) { + TableColumn.CellEditEvent var3 = new TableColumn.CellEditEvent(var2, var2.getEditingCell(), TableColumn.editCommitEvent(), var1); + Event.fireEvent(this.getTableColumn(), var3); + } + + super.commitEdit(var1); + this.updateItem(var1, true); +// if (var2 != null) { +// var2.edit(-1, (TableColumn)null); +// requestFocusOnControlOnlyIfCurrentFocusOwnerIsChild(var2); +// } + } else { + TableView var2 = this.getTableView(); + if (var2 != null) { + TablePosition position = new TablePosition(var2, this.getTableRow().getIndex(), this.getTableColumn()); + TableColumn.CellEditEvent var3 = new TableColumn.CellEditEvent(var2, position, TableColumn.editCommitEvent(), var1); + Event.fireEvent(this.getTableColumn(), var3); + } + + this.updateItem(var1, true); + } + } + + public void cancelEdit() { + isCancelling = true; + super.cancelEdit(); + CellUtils.cancelEdit(this, this.getConverter(), (Node)null); + } + + public void updateItem(T var1, boolean var2) { + super.updateItem(var1, var2); + CellUtils.updateItem(this, this.getConverter(), (HBox)null, (Node)null, this.textField); + } +} diff --git a/src/main/java/spim/ui/view/component/util/TableViewUtil.java b/src/main/java/spim/ui/view/component/util/TableViewUtil.java index 68dc1f74..0beeedb9 100644 --- a/src/main/java/spim/ui/view/component/util/TableViewUtil.java +++ b/src/main/java/spim/ui/view/component/util/TableViewUtil.java @@ -25,7 +25,6 @@ import javafx.scene.control.cell.CheckBoxTableCell; import javafx.scene.control.cell.ChoiceBoxTableCell; import javafx.scene.control.cell.PropertyValueFactory; -import javafx.scene.control.cell.TextFieldTableCell; import javafx.scene.input.ClipboardContent; import javafx.scene.input.DataFormat; import javafx.scene.input.Dragboard; @@ -71,7 +70,7 @@ public static TableView< PositionItem > createCurrentPositionItemDataView(Acquis textColumn.setCellValueFactory( param -> new ReadOnlyStringWrapper(param.getValue().getName()) ); - textColumn.setCellFactory( TextFieldTableCell.forTableColumn() ); + textColumn.setCellFactory( EditTextFieldTableCell.forTableColumn() ); textColumn.setOnEditCommit( event -> event.getRowValue().setName( event.getNewValue() ) ); tv.getColumns().add(textColumn); tv.prefWidthProperty().bind(textColumn.widthProperty().add(316)); @@ -232,7 +231,7 @@ public void handle(MouseEvent event) { TableColumn textColumn = new TableColumn<>("Position Name"); textColumn.setPrefWidth(100); textColumn.setCellValueFactory( param -> param.getValue().getNameProperty() ); - textColumn.setCellFactory( TextFieldTableCell.forTableColumn() ); + textColumn.setCellFactory( EditTextFieldTableCell.forTableColumn() ); textColumn.setOnEditCommit( event -> event.getRowValue().setName( event.getNewValue() ) ); tv.getColumns().add(textColumn); diff --git a/src/main/resource/lib/macosx/libblosc.dylib b/src/main/resource/darwin/libblosc.dylib similarity index 100% rename from src/main/resource/lib/macosx/libblosc.dylib rename to src/main/resource/darwin/libblosc.dylib diff --git a/src/main/resource/lib/macosx/libffmpeg.dylib b/src/main/resource/darwin/libffmpeg.dylib similarity index 100% rename from src/main/resource/lib/macosx/libffmpeg.dylib rename to src/main/resource/darwin/libffmpeg.dylib diff --git a/src/main/resource/lib/linux64/libblosc.so b/src/main/resource/lib/linux64/libblosc.so deleted file mode 100644 index 8312c137..00000000 Binary files a/src/main/resource/lib/linux64/libblosc.so and /dev/null differ diff --git a/src/main/resource/lib/linux64/libffmpeg.so b/src/main/resource/lib/linux64/libffmpeg.so deleted file mode 100644 index faddb6dd..00000000 Binary files a/src/main/resource/lib/linux64/libffmpeg.so and /dev/null differ diff --git a/src/main/resource/spim/gui/others.pref b/src/main/resource/spim/gui/others.pref new file mode 100644 index 00000000..1fa6cd6b --- /dev/null +++ b/src/main/resource/spim/gui/others.pref @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/main/resource/spim/help/Acquisition.html b/src/main/resource/spim/help/Acquisition.html index 553bda80..8b6bd3a7 100644 --- a/src/main/resource/spim/help/Acquisition.html +++ b/src/main/resource/spim/help/Acquisition.html @@ -41,14 +41,22 @@

Anti-Drift

Click the Anti-drift tab to enable it with the aim to prevent the sample from leaving its initial predefined position. One can choose between Phase Correlation (whereby entire volume of a 3d-stack is taken into account) or Centre of Mass (whereby drifts are only corrected in x, y but not in z). Note that high concentrations of fluorescent beads surrounding the sample may disarrange the Anti-Drift functionality.

-

ROI

-

- Within this tab a region of interest (ROI) can be specified and applied to the field of view of the camera. The region is specified in the preview window of µManager. Select the Rectangle tool and create a selection inside the preview window. The window will open up by clicking Live view. When the ROI is specified, click the Apply button. -

Binning

Different binning options can be selected before acquisition, e.g. 2x2 or 3x3 binning. Higher Binning settings combines the charge of more pixels, which increases the signal to noise ratio (SNR) and results in higher camera frame rates but on the expanse of pixel resolution.

+

On-the-fly

+

+ On-the-fly will enable the “process()” method of a running script, which can be created or edited within the Java Editor (Editor > Java). +

+

Ablation

+

+ This is an experimental option to generate next to the acquired image(s) an additional single image slice or stack. The name of this additional file will end with "_ablation.tiff". This option works only with OMETIFF as saving format. +

+

ROI

+

+ Within this tab a region of interest (ROI) can be specified and applied to the field of view of the camera. The region is specified in the preview window of µManager. Select the Rectangle tool and create a selection inside the preview window. The window will open up by clicking Live view. When the ROI is specified, click the Apply button. +

Live

Clicking this button will toggle the camera into live view. The exposure values can be changed below. diff --git a/src/main/resource/lib/win64/blosc.dll b/src/main/resource/win32-x86-64/blosc.dll similarity index 100% rename from src/main/resource/lib/win64/blosc.dll rename to src/main/resource/win32-x86-64/blosc.dll diff --git a/src/main/resource/lib/win64/ffmpeg.dll b/src/main/resource/win32-x86-64/ffmpeg.dll similarity index 100% rename from src/main/resource/lib/win64/ffmpeg.dll rename to src/main/resource/win32-x86-64/ffmpeg.dll diff --git a/src/test/java/spim/ui/view/component/AcquisitionPanelTest.java b/src/test/java/spim/ui/view/component/AcquisitionPanelTest.java new file mode 100644 index 00000000..dfcd83d7 --- /dev/null +++ b/src/test/java/spim/ui/view/component/AcquisitionPanelTest.java @@ -0,0 +1,27 @@ +package spim.ui.view.component; + +import org.junit.Test; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; + +import static org.junit.Assert.*; + +/** + * Author: HongKee Moon (moon@mpi-cbg.de), Scientific Computing Facility + * Organization: MPI-CBG Dresden + * Date: October 2023 + */ +public class AcquisitionPanelTest { + + @Test + public void getCurrentTime() { + Date date = Calendar.getInstance().getTime(); + DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmm"); + String strDate = dateFormat.format(date); + + assertEquals(strDate, AcquisitionPanel.getCurrentTime()); + } +} \ No newline at end of file