diff --git a/.gitignore b/.gitignore
index b9bb0f54..dbb66312 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,3 +30,10 @@ hs_err_pid*
/bin/
/target/
+# ignore files generated during test
+/src/test/resources/bigwarp/url/imgDir3d/img3d0000.tif
+/src/test/resources/bigwarp/url/imgDir3d/img3d0001.tif
+/src/test/resources/bigwarp/url/imgDir3d/img3d0002.tif
+/src/test/resources/bigwarp/url/imgDir3d/img3d0003.tif
+/src/test/resources/bigwarp/url/img.tif
+/src/test/resources/bigwarp/url/img2d.png
diff --git a/pom.xml b/pom.xml
index d6c40c6e..c5897d9c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,7 +11,7 @@
sc.fijibigwarp_fiji
- 8.0.1-SNAPSHOT
+ 9.0.0-SNAPSHOTBigWarp plugin for FijiA tool for manual pointwise deformable registration using bigdataviewer.
@@ -118,16 +118,25 @@
BigWarp plugin for Fiji.**/resources/*.xml
+ 6.2.0
+ 4.0.1
+
1.481.4.1
+ 3.0.3sign,deploy-to-scijava
- 3.0.0
- 3.2.4
+ 3.0.2
+ 3.2.6
+ 2.0.1
+ 4.0.1
+ 4.0.07.0.0
- 1.0.1
+ 1.2.0
+ 5.3.1
+ 1.0.110.4.8
@@ -145,6 +154,10 @@
net.imagejij
+
+ net.imagej
+ imagej
+
@@ -236,13 +249,27 @@
org.janelia.saalfeldlabn5-imglib2
+
+ org.janelia.saalfeldlab
+ n5-aws-s3
+
+
+ org.janelia.saalfeldlab
+ n5-google-cloud
+
+
+ org.janelia.saalfeldlab
+ n5-viewer_fiji
+
+
+ org.janelia.saalfeldlab
+ n5-zarr
+ org.janelia.saalfeldlabn5-universe${n5-universe.version}
-
-
ome
@@ -306,6 +333,10 @@
alphanumeric-comparator${alphanumeric-comparator.version}
+
+ com.formdev
+ flatlaf
+
@@ -313,5 +344,45 @@
junittest
+
+ xmlunit
+ xmlunit
+ 1.5
+
+
+
+
+
+ default
+
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ **/SerializationTest.java
+
+
+
+
+
+
+
+ uiTests
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+
+
+
+
diff --git a/src/main/java/bdv/gui/AutosaveOptionsPanel.java b/src/main/java/bdv/gui/AutosaveOptionsPanel.java
new file mode 100644
index 00000000..01107601
--- /dev/null
+++ b/src/main/java/bdv/gui/AutosaveOptionsPanel.java
@@ -0,0 +1,184 @@
+package bdv.gui;
+
+import bigwarp.BigWarp;
+import bigwarp.BigWarpAutoSaver;
+import java.awt.Container;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.io.File;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JFileChooser;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JSpinner;
+import javax.swing.JSpinner.DefaultEditor;
+import javax.swing.JTextField;
+import javax.swing.SpinnerNumberModel;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+public class AutosaveOptionsPanel extends JPanel
+{
+ private static final long serialVersionUID = 2449704984531905538L;
+
+ private final BigWarp< ? > bw;
+
+ private final SpinnerNumberModel savePeriodModel;
+ private final JSpinner autoSavePeriodSpinner;
+ private final JCheckBox doAutoSaveBox;
+ private final JTextField autoSaveFolderText;
+
+ private int lastAutoSaveFreq = 5;
+ private boolean updating = false;
+
+ public AutosaveOptionsPanel( final BigWarp< ? > bw, final Container content )
+ {
+ super( new GridBagLayout() );
+ this.bw = bw;
+
+ doAutoSaveBox = new JCheckBox( "Auto-save landmarks" );
+
+ final JLabel autoSavePeriodLabel = new JLabel( "Frequency (minutes)" );
+ autoSavePeriodSpinner = new JSpinner();
+ savePeriodModel = new SpinnerNumberModel( 0, 0, 0, 1 );
+ getAutoSavePeriodSpinner().setModel( savePeriodModel );
+ ((DefaultEditor)getAutoSavePeriodSpinner().getEditor()).getTextField().setEditable( false );
+ ((DefaultEditor)getAutoSavePeriodSpinner().getEditor()).getTextField().setEnabled( false );
+ getAutoSavePeriodSpinner().addChangeListener( new ChangeListener()
+ {
+ @Override
+ public void stateChanged( ChangeEvent e )
+ {
+ if ( getDoAutoSaveBox().isSelected() && !updating )
+ {
+ long periodMillis = ((Integer) savePeriodModel.getValue()).longValue() * 60000;
+ BigWarpAutoSaver autoSaver = bw.getAutoSaver();
+ if ( autoSaver != null )
+ autoSaver.stop();
+
+ bw.setAutoSaver( new BigWarpAutoSaver( bw, periodMillis ));
+ }
+ }
+ } );
+
+ getDoAutoSaveBox().addItemListener( new ItemListener()
+ {
+ @Override
+ public void itemStateChanged( ItemEvent e )
+ {
+ bw.stopAutosave();
+ if ( getDoAutoSaveBox().isSelected() )
+ {
+ updating = true;
+ ((DefaultEditor)getAutoSavePeriodSpinner().getEditor()).getTextField().setEditable( true );
+ ((DefaultEditor)getAutoSavePeriodSpinner().getEditor()).getTextField().setEnabled( true );
+ savePeriodModel.setMinimum( 1 );
+ savePeriodModel.setMaximum( 5000 );
+ savePeriodModel.setValue( lastAutoSaveFreq );
+
+ long periodMillis = ((Integer) savePeriodModel.getValue()).longValue() * 60000;
+ bw.setAutoSaver( new BigWarpAutoSaver( bw, periodMillis ));
+ updating = false;
+ }
+ else
+ {
+ lastAutoSaveFreq = ((Integer) savePeriodModel.getValue());
+ savePeriodModel.setMinimum( 0 );
+ savePeriodModel.setMaximum( 0 );
+ savePeriodModel.setValue( 0 );
+ ((DefaultEditor)getAutoSavePeriodSpinner().getEditor()).getTextField().setEditable( false );
+ ((DefaultEditor)getAutoSavePeriodSpinner().getEditor()).getTextField().setEnabled( false );
+ }
+ }
+ } );
+
+ final JLabel destDirLabel = new JLabel( "Directory" );
+ final File startingFolder = bw.getBigwarpSettingsFolder();
+ autoSaveFolderText = new JTextField();
+ getAutoSaveFolderText().setText( startingFolder.getAbsolutePath() );
+
+ final JButton browseBtn = new JButton( "Browse" );
+ browseBtn.addActionListener( e -> {
+
+ final JFileChooser fileChooser = new JFileChooser();
+ fileChooser.setFileSelectionMode( JFileChooser.DIRECTORIES_ONLY );
+ fileChooser.setCurrentDirectory( startingFolder );
+
+ final int ret = fileChooser.showOpenDialog( content );
+ if ( ret == JFileChooser.APPROVE_OPTION )
+ {
+ final File folder = fileChooser.getSelectedFile();
+ getAutoSaveFolderText().setText( folder.getAbsolutePath() );
+ bw.getAutoSaver().setAutosaveFolder( folder );
+ }
+ } );
+
+ final GridBagConstraints gbc = new GridBagConstraints();
+ gbc.gridx = 1;
+ gbc.gridy = 0;
+ gbc.gridwidth = 1;
+ gbc.gridheight = 1;
+ gbc.weightx = 1.0;
+ gbc.anchor = GridBagConstraints.LINE_END;
+ gbc.fill = GridBagConstraints.NONE;
+ gbc.insets = new Insets( 5, 5, 5, 5 );
+
+ add( getDoAutoSaveBox(), gbc );
+
+ gbc.weightx = 1.0;
+ gbc.gridx = 2;
+ gbc.gridwidth = 1;
+ gbc.gridy = 0;
+ gbc.anchor = GridBagConstraints.LINE_END;
+ add( autoSavePeriodLabel, gbc );
+
+ gbc.gridx = 3;
+ gbc.gridwidth = 1;
+ gbc.weightx = 1.0;
+ gbc.anchor = GridBagConstraints.LINE_START;
+ gbc.fill = GridBagConstraints.HORIZONTAL;
+ add( getAutoSavePeriodSpinner(), gbc );
+
+ gbc.gridy = 2;
+ gbc.gridx = 0;
+ gbc.gridwidth = 1;
+ gbc.weightx = 0.0;
+ gbc.anchor = GridBagConstraints.LINE_START;
+ gbc.fill = GridBagConstraints.NONE;
+ add( destDirLabel, gbc );
+
+ gbc.gridy = 2;
+ gbc.gridx = 1;
+ gbc.gridwidth = 3;
+ gbc.weightx = 1.0;
+ gbc.anchor = GridBagConstraints.LINE_START;
+ gbc.fill = GridBagConstraints.HORIZONTAL;
+ add( getAutoSaveFolderText(), gbc );
+
+ gbc.gridx = 4;
+ gbc.weightx = 0.0;
+ gbc.anchor = GridBagConstraints.LINE_END;
+ gbc.fill = GridBagConstraints.NONE;
+ add( browseBtn, gbc );
+ }
+
+ public JSpinner getAutoSavePeriodSpinner()
+ {
+ return autoSavePeriodSpinner;
+ }
+
+ public JCheckBox getDoAutoSaveBox()
+ {
+ return doAutoSaveBox;
+ }
+
+ public JTextField getAutoSaveFolderText()
+ {
+ return autoSaveFolderText;
+ }
+
+}
diff --git a/src/main/java/bdv/gui/BigWarpInitDialog.java b/src/main/java/bdv/gui/BigWarpInitDialog.java
new file mode 100644
index 00000000..72e7829c
--- /dev/null
+++ b/src/main/java/bdv/gui/BigWarpInitDialog.java
@@ -0,0 +1,1092 @@
+package bdv.gui;
+
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.JTree;
+import javax.swing.Timer;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.filechooser.FileFilter;
+import javax.swing.filechooser.FileNameExtensionFilter;
+import javax.swing.tree.TreePath;
+import javax.swing.tree.TreeSelectionModel;
+
+import org.janelia.saalfeldlab.n5.bdv.N5ViewerTreeCellRenderer;
+import org.janelia.saalfeldlab.n5.ij.N5Importer.N5BasePathFun;
+import org.janelia.saalfeldlab.n5.ij.N5Importer.N5ViewerReaderFun;
+import org.janelia.saalfeldlab.n5.metadata.N5ViewerMultichannelMetadata;
+import org.janelia.saalfeldlab.n5.metadata.imagej.ImagePlusLegacyMetadataParser;
+import org.janelia.saalfeldlab.n5.ui.DataSelection;
+import org.janelia.saalfeldlab.n5.ui.DatasetSelectorDialog;
+import org.janelia.saalfeldlab.n5.ui.N5SwingTreeNode;
+import org.janelia.saalfeldlab.n5.universe.metadata.N5CosemMetadataParser;
+import org.janelia.saalfeldlab.n5.universe.metadata.N5CosemMultiScaleMetadata;
+import org.janelia.saalfeldlab.n5.universe.metadata.N5GenericSingleScaleMetadataParser;
+import org.janelia.saalfeldlab.n5.universe.metadata.N5Metadata;
+import org.janelia.saalfeldlab.n5.universe.metadata.N5MetadataParser;
+import org.janelia.saalfeldlab.n5.universe.metadata.N5SingleScaleMetadataParser;
+import org.janelia.saalfeldlab.n5.universe.metadata.N5ViewerMultiscaleMetadataParser;
+import org.janelia.saalfeldlab.n5.universe.metadata.canonical.CanonicalMetadataParser;
+import org.jdom2.JDOMException;
+
+import com.formdev.flatlaf.util.UIScale;
+
+import bdv.gui.sourceList.BigWarpSourceListPanel;
+import bdv.gui.sourceList.BigWarpSourceTableModel;
+import bdv.gui.sourceList.BigWarpSourceTableModel.SourceRow;
+import bdv.gui.sourceList.BigWarpSourceTableModel.SourceType;
+import bdv.ij.util.ProgressWriterIJ;
+import bdv.viewer.Source;
+import bigwarp.BigWarp;
+import bigwarp.BigWarpData;
+import bigwarp.BigWarpInit;
+import bigwarp.source.SourceInfo;
+import bigwarp.transforms.NgffTransformations;
+import bigwarp.transforms.metadata.N5TransformMetadata;
+import bigwarp.transforms.metadata.N5TransformMetadataParser;
+import bigwarp.transforms.metadata.N5TransformTreeCellRenderer;
+import ij.IJ;
+import ij.ImagePlus;
+import ij.Macro;
+import ij.Prefs;
+import ij.WindowManager;
+import ij.plugin.frame.Recorder;
+import mpicbg.spim.data.SpimDataException;
+import net.imagej.Dataset;
+import net.imagej.DatasetService;
+import net.imglib2.realtransform.RealTransform;
+import net.imglib2.type.NativeType;
+
+public class BigWarpInitDialog extends JFrame
+{
+ private static final long serialVersionUID = -2914972130819029899L;
+
+ public static String listSeparator = ",";
+
+ private boolean imageJOpen;
+ private DatasetService datasetService;
+
+ private String initialPath;
+ private JTextField projectPathTxt, containerPathText, transformPathText;
+ private JLabel messageLabel;
+ private JButton okBtn, cancelBtn;
+ private JPanel listPanel;
+ private JTable sourceTable;
+ private JButton browseProjectButton, browseBtn, addN5Button, addN5TransformButton, browseTransformButton;
+ private BigWarpSourceTableModel sourceTableModel;
+ private JComboBox imagePlusDropdown;
+ private JButton addImageButton, addPathButton, addTransformButton;
+ private DatasetSelectorDialog selectionDialog, transformSelectionDialog;
+
+ private String lastOpenedContainer = "";
+ private String lastBrowsePath = null;
+ private ExecutorService exec;
+
+ private Consumer okayCallback;
+ private Consumer cancelCallback;
+ private Consumer< String > imagePathUpdateCallback, transformPathUpdateCallback, projectPathUpdateCallback;
+
+ public static final int DEFAULT_OUTER_PAD = 8;
+ public static final int DEFAULT_BUTTON_PAD = 3;
+ public static final int DEFAULT_MID_PAD = 5;
+
+ private static final String commandName = "BigWarp";
+ private static final String projectKey = "project";
+ private static final String imagesKey = "images";
+ private static final String movingKey = "moving";
+ private static final String transformsKey = "transforms";
+
+ public static final String ImageJPrefix = "imagej://";
+
+ private String projectLandmarkPath;
+ private String imageList;
+ private String movingList;
+ private String transformList;
+
+ private boolean initialRecorderState;
+
+
+
+ public BigWarpInitDialog( final String title )
+ {
+ this( title, null );
+ }
+
+ public BigWarpInitDialog( final String title, final DatasetService datasetService )
+ {
+ super( title );
+ this.datasetService = datasetService;
+ initialPath = "";
+ imageJOpen = IJ.getInstance() != null;
+
+ buildN5SelectionDialog();
+ final Container content = getContentPane();
+ content.add( createContent() );
+ pack();
+
+ initializeImagePlusSources();
+
+ cancelCallback = x -> {
+ setVisible( false );
+ dispose();
+ Recorder.record = initialRecorderState;
+ };
+
+ okayCallback = x -> {
+ macroRecord();
+ runBigWarp();
+ Recorder.record = initialRecorderState;
+ setVisible( false );
+ };
+
+ imagePathUpdateCallback = ( p ) -> {
+ addPath();
+ };
+
+ transformPathUpdateCallback = ( p ) -> {
+ addTransform();
+ };
+ }
+
+
+ public void setInitialRecorderState( final boolean initialRecorderState )
+ {
+ this.initialRecorderState = initialRecorderState;
+ }
+
+ public static < T extends NativeType > BigWarp> runBigWarp( final String projectLandmarkPath, final String[] images, final String[] moving, final String[] transforms )
+ {
+ final BigWarpData< T > data = BigWarpInit.initData();
+ final boolean haveProjectLandmarkArg = projectLandmarkPath != null && !projectLandmarkPath.isEmpty();
+ final boolean haveProject = haveProjectLandmarkArg && projectLandmarkPath.endsWith(".json");
+ final boolean haveLandmarks = haveProjectLandmarkArg && projectLandmarkPath.endsWith(".csv");
+
+ final int nThreads = IJ.getInstance() != null ? Prefs.getThreads() : 1;
+ final BigWarpViewerOptions bwOpts = ( BigWarpViewerOptions ) BigWarpViewerOptions.options().numRenderingThreads( nThreads );
+
+ if( !haveProject )
+ {
+ int id = 0;
+ final int N = images.length;
+ for( int i = 0; i < N; i++ )
+ {
+ // TODO better messages for exceptions?
+ try
+ {
+ final LinkedHashMap< Source< T >, SourceInfo > infos = BigWarpInit.createSources( data, images[ i ], id, moving[ i ].equals( "true" ) );
+
+ RealTransform transform = null;
+ final Supplier transformSupplier;
+ if( transforms != null && transforms.length > i )
+ {
+ final String transformUrl = transforms[ i ];
+ if( transformUrl!= null && !transformUrl.isEmpty() )
+ {
+ transform = NgffTransformations.open( transformUrl );
+ transformSupplier = () -> transformUrl;
+ }
+ else {
+ transformSupplier = null;
+ }
+ }
+ else
+ {
+ transformSupplier = null;
+ }
+
+ // add performs a null check on transform
+ BigWarpInit.add( data, infos, transform, transformSupplier );
+
+ id += infos.size();
+ }
+ catch ( final URISyntaxException e )
+ {
+ e.printStackTrace();
+ }
+ catch ( final IOException e )
+ {
+ e.printStackTrace();
+ }
+ catch ( final SpimDataException e )
+ {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ BigWarp> bw = null;
+ try
+ {
+ bwOpts.is2D(BigWarp.detectNumDims( data.sources ) == 2);
+ data.applyTransformations();
+
+ bw = new BigWarp<>( data, bwOpts, new ProgressWriterIJ() );
+ if( haveProject )
+ bw.loadSettings( projectLandmarkPath, true );
+ else if( haveLandmarks )
+ bw.loadLandmarks(projectLandmarkPath);
+ }
+ catch ( final SpimDataException e )
+ {
+ e.printStackTrace();
+ }
+ catch ( final IOException e )
+ {
+ e.printStackTrace();
+ }
+ catch ( final JDOMException e )
+ {
+ e.printStackTrace();
+ }
+
+ return bw;
+ }
+
+ public > void runBigWarp()
+ {
+ if (Recorder.record)
+ {
+ Recorder.setCommand(commandName);
+ macroRecord();
+ Recorder.saveCommand();
+ }
+
+ final BigWarpData< T > data = BigWarpInit.initData();
+
+ projectLandmarkPath = projectLandmarkPath == null ? projectPathTxt.getText() : projectLandmarkPath;
+ final boolean haveProjectLandmarkArg = projectLandmarkPath != null && !projectLandmarkPath.isEmpty();
+ final boolean haveProject = haveProjectLandmarkArg && projectLandmarkPath.endsWith(".json");
+ final boolean haveLandmarks = haveProjectLandmarkArg && projectLandmarkPath.endsWith(".csv");
+
+ if( !haveProject )
+ {
+ int id = 0;
+ final int N = sourceTable.getRowCount();
+ for( int i = 0; i < N; i++ )
+ {
+ final SourceRow tableRow = sourceTableModel.get( i );
+ if( tableRow.type.equals( SourceType.IMAGEPLUS ) )
+ {
+ // strip off prefix if present
+ final ImagePlus imp = WindowManager.getImage( tableRow.srcName.replaceAll( "^"+ImageJPrefix, "" ) );
+ final LinkedHashMap< Source< T >, SourceInfo > infos = BigWarpInit.createSources( data, imp, id, 0, tableRow.moving );
+ BigWarpInit.add( data, infos, tableRow.getTransform(), tableRow.getTransformUri() );
+ id += infos.size();
+ }
+ else if( tableRow.type.equals( SourceType.DATASET ))
+ {
+ final Dataset dataset = datasetService.getDatasets().stream()
+ .filter( x -> x.getSource().equals( tableRow.srcName ) )
+ .findFirst().get();
+ final LinkedHashMap< Source< T >, SourceInfo > infos = BigWarpInit.createSources( data, dataset, id, tableRow.moving );
+ BigWarpInit.add( data, infos, tableRow.getTransform(), tableRow.getTransformUri() );
+ id += infos.size();
+ }
+ else
+ {
+ // deal with exceptions differently?
+ try
+ {
+ final LinkedHashMap< Source< T >, SourceInfo > infos = BigWarpInit.createSources( data, tableRow.srcName, id, tableRow.moving );
+ BigWarpInit.add( data, infos, tableRow.getTransform(), tableRow.getTransformUri() );
+ id += infos.size();
+ }
+ catch ( final URISyntaxException e )
+ {
+ e.printStackTrace();
+ }
+ catch ( final IOException e )
+ {
+ e.printStackTrace();
+ }
+ catch ( final SpimDataException e )
+ {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ BigWarp> bw;
+ try
+ {
+ data.applyTransformations();
+ bw = new BigWarp<>( data, new ProgressWriterIJ() );
+ if( haveProject )
+ bw.loadSettings( projectLandmarkPath, true );
+ else if( haveLandmarks )
+ bw.loadLandmarks(projectLandmarkPath);
+ }
+ catch ( final SpimDataException e )
+ {
+ e.printStackTrace();
+ }
+ catch ( final IOException e )
+ {
+ e.printStackTrace();
+ }
+ catch ( final JDOMException e )
+ {
+ e.printStackTrace();
+ }
+
+ }
+
+ public JPanel createContent()
+ {
+ final int OUTER_PAD = DEFAULT_OUTER_PAD;
+ final int BUTTON_PAD = DEFAULT_BUTTON_PAD;
+ final int MID_PAD = DEFAULT_MID_PAD;
+
+ final int frameSizeX = UIScale.scale( 600 );
+ final JPanel panel = new JPanel(false);
+ panel.setPreferredSize( new Dimension( frameSizeX, UIScale.scale( 335 ) )); // ~ 16:9
+ panel.setLayout(new GridBagLayout());
+
+ final GridBagConstraints ctxt = new GridBagConstraints();
+ ctxt.gridx = 0;
+ ctxt.gridy = 0;
+ ctxt.gridwidth = 1;
+ ctxt.gridheight = 1;
+ ctxt.weightx = 0.0;
+ ctxt.weighty = 0.0;
+ ctxt.anchor = GridBagConstraints.LINE_END;
+ ctxt.fill = GridBagConstraints.NONE;
+ ctxt.insets = new Insets(OUTER_PAD, OUTER_PAD, MID_PAD, BUTTON_PAD);
+ panel.add( new JLabel("BigWarp project or landmarks:"), ctxt );
+
+ final GridBagConstraints gbcBar = new GridBagConstraints();
+ gbcBar.gridx = 1;
+ gbcBar.gridy = 0;
+ gbcBar.gridwidth = 6;
+ gbcBar.gridheight = 1;
+ gbcBar.weightx = 1.0;
+ gbcBar.weighty = 0.0;
+ gbcBar.fill = GridBagConstraints.HORIZONTAL;
+ gbcBar.insets = new Insets(OUTER_PAD, OUTER_PAD, MID_PAD, BUTTON_PAD);
+
+ projectPathTxt = new JTextField();
+ projectPathTxt.setPreferredSize( new Dimension( frameSizeX / 3, projectPathTxt.getPreferredSize().height ) );
+ panel.add(projectPathTxt, gbcBar);
+
+ // gbc bars below are width 4
+ gbcBar.gridwidth = 4;
+
+ final GridBagConstraints cProjBrowse = new GridBagConstraints();
+ cProjBrowse.gridx = 7;
+ cProjBrowse.gridy = 0;
+ cProjBrowse.gridwidth = 1;
+ cProjBrowse.weightx = 0.0;
+ cProjBrowse.fill = GridBagConstraints.HORIZONTAL;
+ cProjBrowse.insets = new Insets(OUTER_PAD, BUTTON_PAD, MID_PAD, BUTTON_PAD);
+ browseProjectButton = new JButton("Browse");
+ browseProjectButton.addActionListener( e -> { browseProjectDialog(); } );
+ panel.add(browseProjectButton, cProjBrowse);
+
+
+ // Add open imagej image
+ ctxt.gridy = 1;
+ panel.add( new JLabel("Add open image:"), ctxt );
+
+ final GridBagConstraints gbc = new GridBagConstraints();
+ gbc.gridx = 1;
+ gbc.gridy = 1;
+ gbc.gridwidth = 3;
+ gbc.weightx = 1.0;
+ gbc.weighty = 0.0;
+ gbc.fill = GridBagConstraints.HORIZONTAL;
+ gbc.insets = new Insets(OUTER_PAD, BUTTON_PAD, MID_PAD, BUTTON_PAD);
+ gbc.anchor = GridBagConstraints.LINE_END;
+ imagePlusDropdown = new JComboBox<>( new String[] { "" } );
+ panel.add( imagePlusDropdown, gbc );
+ updateImagePlusDropdown();
+
+ final GridBagConstraints cadd = new GridBagConstraints();
+ cadd.gridx = 5;
+ cadd.gridy = 1;
+ cadd.gridwidth = 1;
+ cadd.weightx = 0.0;
+ cadd.fill = GridBagConstraints.NONE;
+ cadd.anchor = GridBagConstraints.LINE_START;
+ cadd.insets = new Insets(OUTER_PAD, BUTTON_PAD, MID_PAD, BUTTON_PAD);
+
+ cadd.gridy = 1;
+ addImageButton = new JButton("+");
+ panel.add( addImageButton, cadd );
+ addImageButton.addActionListener( e -> { addImage(); });
+
+ ctxt.gridy = 2;
+ final JLabel addFileLabel = new JLabel( "Add image file/folder:");
+ panel.add(addFileLabel, ctxt);
+
+ gbcBar.gridy = 2;
+ containerPathText = new JTextField();
+ containerPathText.setText( initialPath );
+ containerPathText.setPreferredSize( new Dimension( frameSizeX / 3, containerPathText.getPreferredSize().height ) );
+// containerPathText.addActionListener( e -> openContainer( n5Fun, () -> getN5RootPath(), pathFun ) );
+ panel.add(containerPathText, gbcBar);
+
+ cadd.gridy = 2;
+ addPathButton = new JButton("+");
+ addPathButton.addActionListener( e -> addPath() );
+ panel.add(addPathButton, cadd);
+
+ final GridBagConstraints cbrowse = new GridBagConstraints();
+ cbrowse.gridx = 6;
+ cbrowse.gridy = 2;
+ cbrowse.gridwidth = 1;
+ cbrowse.weightx = 0.0;
+ cbrowse.fill = GridBagConstraints.HORIZONTAL;
+ cbrowse.insets = new Insets(OUTER_PAD, BUTTON_PAD, MID_PAD, BUTTON_PAD);
+ browseBtn = new JButton("Browse");
+ browseBtn.addActionListener( e -> { browseImageDialog(); } );
+ panel.add(browseBtn, cbrowse);
+
+ final GridBagConstraints cn5 = new GridBagConstraints();
+ cn5.gridx = 7;
+ cn5.gridy = 2;
+ cn5.gridwidth = 1;
+ cn5.weightx = 0.0;
+ cn5.fill = GridBagConstraints.HORIZONTAL;
+ cn5.insets = new Insets(OUTER_PAD, BUTTON_PAD, MID_PAD, BUTTON_PAD);
+ addN5Button = new JButton( "H5/N5/Zarr" );
+ panel.add( addN5Button, cn5 );
+
+ addN5Button.addActionListener( e -> {
+
+ selectionDialog = new DatasetSelectorDialog( new N5ViewerReaderFun(), new N5BasePathFun(),
+ lastOpenedContainer,
+ BigWarpInit.GROUP_PARSERS,
+ BigWarpInit.PARSERS);
+
+ selectionDialog.setLoaderExecutor( exec );
+ selectionDialog.setTreeRenderer(new N5ViewerTreeCellRenderer(false));
+
+ selectionDialog.setContainerPathUpdateCallback( x -> {
+ if ( x != null )
+ lastOpenedContainer = x;
+ } );
+
+ // figure this out
+// selectionDialog.setCancelCallback( x -> {
+// // set back recorder state if canceled
+// Recorder.record = initialRecorderState;
+// } );
+
+ selectionDialog.setVirtualOption( false );
+ selectionDialog.setCropOption( false );
+
+ selectionDialog.run( this::n5DialogCallback );
+ });
+
+ // add transforms
+ ctxt.gridy = 3;
+ panel.add( new JLabel("Add transformation:"), ctxt );
+
+ transformPathText = new JTextField();
+ transformPathText.setPreferredSize( new Dimension( frameSizeX / 3, transformPathText.getPreferredSize().height ) );
+ gbcBar.gridy = 3;
+ panel.add( transformPathText, gbcBar );
+
+ addTransformButton = new JButton( "+" );
+ addTransformButton.addActionListener( e -> addTransform() );
+ cadd.gridy = 3;
+ panel.add( addTransformButton, cadd );
+
+ browseTransformButton = new JButton("Browse");
+ browseTransformButton.addActionListener( e -> { browseTransformDialog(); } );
+ cbrowse.gridy = 3;
+ panel.add( browseTransformButton, cbrowse );
+
+ cn5.gridy = 3;
+ addN5TransformButton = new JButton( "H5/N5/Zarr" );
+ panel.add( addN5TransformButton, cn5 );
+
+ addN5TransformButton.addActionListener( e -> {
+ if (sourceTable.getSelectedRow() < 0)
+ IJ.showMessage("Please highlight the row you would like to transform.");
+ else
+ {
+
+ final N5MetadataParser>[] tformParsers = new N5MetadataParser>[]{ new N5TransformMetadataParser() };
+
+ transformSelectionDialog = new DatasetSelectorDialog( new N5ViewerReaderFun(), new N5BasePathFun(),
+ lastOpenedContainer, new N5MetadataParser[] {}, tformParsers );
+
+ transformSelectionDialog.setLoaderExecutor( exec );
+ transformSelectionDialog.setTreeRenderer( new N5TransformTreeCellRenderer( true ) );
+ transformSelectionDialog.setContainerPathUpdateCallback( x -> {
+ if ( x != null )
+ lastOpenedContainer = x;
+ } );
+
+ transformSelectionDialog.run(this::n5DialogTransformCallback);
+
+ // remove any existing selection listeners
+ final JTree tree = transformSelectionDialog.getJTree();
+ for ( final TreeSelectionListener l : tree.getTreeSelectionListeners())
+ tree.removeTreeSelectionListener(l);
+
+ // add a new listener for transform metadata
+ tree.addTreeSelectionListener(new N5TransformTreeSelectionListener(tree.getSelectionModel()));
+ }
+ });
+
+ // source list
+ final GridBagConstraints clist = new GridBagConstraints();
+ clist.gridx = 0;
+ clist.gridy = 4;
+ clist.gridwidth = 8;
+ clist.gridheight = 3;
+ clist.weightx = 1.0;
+ clist.weighty = 1.0;
+ clist.fill = GridBagConstraints.BOTH;
+ clist.insets = new Insets(OUTER_PAD, BUTTON_PAD, MID_PAD, BUTTON_PAD);
+
+ sourceTableModel = new BigWarpSourceTableModel( t -> {
+ final String val = NgffTransformations.detectTransforms(t);
+ if( val == null )
+ showMessage(1000, "No transformation found");
+ else
+ showMessage(1000, "Found transformation");
+
+ return val;
+ });
+
+ final BigWarpSourceListPanel srcListPanel = new BigWarpSourceListPanel( sourceTableModel );
+ sourceTableModel.setContainer( srcListPanel );
+ sourceTable = srcListPanel.getJTable();
+ sourceTable.putClientProperty("terminateEditOnFocusLost", true);
+ panel.add( srcListPanel, clist );
+
+ // bottom button section
+ final GridBagConstraints cbot = new GridBagConstraints();
+ cbot.gridx = 0;
+ cbot.gridy = 7;
+ cbot.gridwidth = 4;
+ cbot.gridheight = 1;
+ cbot.weightx = 1.0;
+ cbot.weighty = 0.0;
+ cbot.fill = GridBagConstraints.HORIZONTAL;
+ cbot.anchor = GridBagConstraints.WEST;
+ cbot.insets = new Insets(OUTER_PAD, OUTER_PAD, OUTER_PAD, OUTER_PAD);
+
+ messageLabel = new JLabel("");
+ messageLabel.setVisible(true);
+ panel.add(messageLabel, cbot);
+
+ okBtn = new JButton("OK");
+ okBtn.addActionListener( e -> okayCallback.accept( sourceTableModel ));
+ cbot.gridx = 6;
+ cbot.weightx = 0.0;
+ cbot.gridwidth = 1;
+ cbot.ipadx = (int)(40);
+ cbot.fill = GridBagConstraints.HORIZONTAL;
+ cbot.anchor = GridBagConstraints.EAST;
+ cbot.insets = new Insets(MID_PAD, OUTER_PAD, OUTER_PAD, BUTTON_PAD);
+ panel.add(okBtn, cbot);
+
+ cancelBtn = new JButton("Cancel");
+ cancelBtn.addActionListener( e -> cancelCallback.accept( sourceTableModel ));
+ cbot.gridx = 7;
+ cbot.ipadx = 0;
+ cbot.gridwidth = 1;
+ cbot.fill = GridBagConstraints.HORIZONTAL;
+ cbot.anchor = GridBagConstraints.EAST;
+ cbot.insets = new Insets(MID_PAD, BUTTON_PAD, OUTER_PAD, OUTER_PAD);
+ panel.add(cancelBtn, cbot);
+
+ return panel;
+ }
+
+ public void buildN5SelectionDialog()
+ {
+ exec = Executors.newFixedThreadPool( Prefs.getThreads() );
+
+
+ /*
+ * The Dialogs need to be created anew by the action listener
+ */
+
+// selectionDialog = new DatasetSelectorDialog( new N5ViewerReaderFun(), new N5BasePathFun(),
+// lastOpenedContainer,
+// n5vGroupParsers,
+// n5Parsers);
+//
+// selectionDialog.setLoaderExecutor( exec );
+// selectionDialog.setTreeRenderer(new N5ViewerTreeCellRenderer(false));
+//
+// selectionDialog.setContainerPathUpdateCallback( x -> {
+// if ( x != null )
+// lastOpenedContainer = x;
+// } );
+//
+// // figure this out
+//// selectionDialog.setCancelCallback( x -> {
+//// // set back recorder state if canceled
+//// Recorder.record = initialRecorderState;
+//// } );
+//
+// selectionDialog.setVirtualOption( false );
+// selectionDialog.setCropOption( false );
+
+
+// // transform
+//
+// final N5MetadataParser>[] tformParsers = new N5MetadataParser>[]{ new N5TransformMetadataParser() };
+//
+// transformSelectionDialog = new DatasetSelectorDialog( new N5ViewerReaderFun(), new N5BasePathFun(),
+// lastOpenedContainer, new N5MetadataParser[] {}, tformParsers );
+//
+// transformSelectionDialog.setLoaderExecutor( exec );
+// transformSelectionDialog.setTreeRenderer( new N5TransformTreeCellRenderer( true ) );
+// transformSelectionDialog.setContainerPathUpdateCallback( x -> {
+// if ( x != null )
+// lastOpenedContainer = x;
+// } );
+
+ }
+
+ public void n5DialogCallback( final DataSelection selection )
+ {
+ final String n5RootPath = selectionDialog.getN5RootPath();
+ for( final N5Metadata m : selection.metadata )
+ sourceTableModel.add( n5RootPath + "?" + m.getPath() );
+
+ repaint();
+ }
+
+ public void n5DialogTransformCallback( final DataSelection selection )
+ {
+ final String n5RootPath = transformSelectionDialog.getN5RootPath();
+ final int i = sourceTable.getSelectedRow();
+ if( selection.metadata.size() > 0 )
+ sourceTableModel.setTransform(i, n5RootPath + "?" + selection.metadata.get(0).getPath() );
+
+ repaint();
+ }
+
+ protected void addImage()
+ {
+ if ( !imageJOpen && datasetService == null)
+ return;
+
+ final String title = (String)(imagePlusDropdown.getSelectedItem());
+ if (!addDataset(title, false))
+ addImagePlus(title);
+
+ this.repaint();
+ }
+
+ protected boolean addDataset( final String datasetSource, final boolean moving )
+ {
+ if( datasetService.getDatasets().stream().filter( x -> x.getSource().equals(datasetSource)).count() > 0 )
+ {
+ sourceTableModel.addDataset( datasetSource, moving );
+ return true;
+ }
+ else
+ return false;
+ }
+
+ protected void addImagePlus( final String title )
+ {
+ addImagePlus( title, true );
+ }
+
+ protected void addImagePlus( final String title, final boolean moving )
+ {
+ if ( IJ.getInstance() == null )
+ return;
+
+ final ImagePlus imp = WindowManager.getImage( title );
+
+ // TODO consider giving the user information if
+ // an image is not added, and / or updating the dropdown menu periodically
+ if( !title.isEmpty() && imp != null )
+ {
+ sourceTableModel.addImagePlus( ImageJPrefix + title, moving );
+ repaint();
+ }
+ }
+
+ protected void addPath()
+ {
+ final String path = containerPathText.getText();
+ if( !path.isEmpty() )
+ {
+ sourceTableModel.add( path );
+ repaint();
+ }
+ }
+
+ protected void addTransform()
+ {
+ final String path = transformPathText.getText();
+ final int row = sourceTable.getSelectedRow();
+ if( !path.isEmpty() && row >= 0 )
+ {
+ sourceTableModel.setTransform( row, path );
+ repaint();
+ }
+ }
+
+ protected void updateImagePlusDropdown()
+ {
+ if( !imageJOpen && datasetService == null )
+ return;
+
+ // add both images from dataset service and ij1 window manager but avoid duplicates
+ final ArrayList titleList = new ArrayList<>();
+ if( datasetService != null )
+ {
+ for( final Dataset d : datasetService.getDatasets() )
+ titleList.add(d.getSource());
+ }
+
+ // don't need any open windows if we're using N5
+ final int[] ids = WindowManager.getIDList();
+
+ // Find any open images
+ final int N = ids == null ? 0 : ids.length;
+ for ( int i = 0; i < N; ++i )
+ {
+ final String t = ( WindowManager.getImage( ids[ i ] )).getTitle();
+ if( !titleList.contains(t))
+ titleList.add(t);
+ }
+
+ final String[] titles = new String[titleList.size()];
+ for (int i = 0; i < titleList.size(); i++)
+ titles[i] = titleList.get(i);
+
+ imagePlusDropdown.setModel( new DefaultComboBoxModel<>( titles ));
+ }
+
+ /**
+ * Adds first two image plus images to the source list automatically.
+ */
+ public void initializeImagePlusSources()
+ {
+ final int N = imagePlusDropdown.getModel().getSize();
+ if ( N > 0 )
+ {
+ if( datasetService == null )
+ addImagePlus( ( String ) imagePlusDropdown.getItemAt( 0 ), true );
+ else
+ addDataset( ( String ) imagePlusDropdown.getItemAt( 0 ), true );
+ }
+
+ if ( N > 1 )
+ {
+ if( datasetService == null )
+ addImagePlus( ( String ) imagePlusDropdown.getItemAt( 1 ), false );
+ else
+ addDataset( ( String ) imagePlusDropdown.getItemAt( 1 ), false );
+ }
+ }
+
+ public static BigWarpInitDialog createAndShow()
+ {
+ return createAndShow( null );
+ }
+
+ public static BigWarpInitDialog createAndShow( final DatasetService datasets )
+ {
+ // Create and set up the window.
+ final BigWarpInitDialog frame = new BigWarpInitDialog( "BigWarp", datasets );
+ frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
+ frame.setVisible( true );
+ return frame;
+ }
+
+ private String browseDialogGeneral( final int mode, final FileFilter filefilter )
+ {
+
+ final JFileChooser fileChooser = new JFileChooser();
+ /*
+ * Need to allow files so h5 containers can be opened, and directories
+ * so that filesystem n5's and zarrs can be opened.
+ */
+ fileChooser.setFileSelectionMode( mode );
+ if( filefilter == null )
+ {
+ fileChooser.setFileFilter( filefilter );
+ }
+
+ if ( lastBrowsePath != null && !lastBrowsePath.isEmpty() )
+ fileChooser.setCurrentDirectory( new File( lastBrowsePath ) );
+ else if ( initialPath != null && !initialPath.isEmpty() )
+ fileChooser.setCurrentDirectory( new File( initialPath ) );
+ else if ( imageJOpen )
+ {
+ File f = null;
+
+ final String currDir = IJ.getDirectory( "current" );
+ final String homeDir = IJ.getDirectory( "home" );
+ if ( currDir != null )
+ f = new File( currDir );
+ else if ( homeDir != null )
+ f = new File( homeDir );
+
+ fileChooser.setCurrentDirectory( f );
+ }
+
+ final int ret = fileChooser.showOpenDialog( this );
+ if ( ret != JFileChooser.APPROVE_OPTION )
+ return null;
+
+ final String path = fileChooser.getSelectedFile().getAbsolutePath();
+ lastBrowsePath = path;
+
+ return path;
+ }
+
+ public void setImagePathUpdateCallback( final Consumer< String > callback )
+ {
+ this.imagePathUpdateCallback = callback;
+ }
+
+ public void setTransformPathUpdateCallback( final Consumer< String > callback )
+ {
+ this.transformPathUpdateCallback = callback;
+ }
+
+ public void setProjectPathUpdateCallback( final Consumer< String > callback )
+ {
+ this.projectPathUpdateCallback = callback;
+ }
+
+ private String browseProjectDialog()
+ {
+ final String s = browseDialogGeneral( JFileChooser.FILES_ONLY, new FileNameExtensionFilter( "JSON file", "json" ) );
+ projectPathTxt.setText( s );
+ if ( projectPathUpdateCallback != null )
+ projectPathUpdateCallback.accept( s );
+
+ return s;
+ }
+
+ private String browseImageDialog()
+ {
+ final String s = browseDialogGeneral( JFileChooser.FILES_AND_DIRECTORIES, null );
+ containerPathText.setText( s );
+ if ( imagePathUpdateCallback != null )
+ imagePathUpdateCallback.accept( s );
+
+ return s;
+ }
+
+ private String browseTransformDialog()
+ {
+ final String s = browseDialogGeneral( JFileChooser.FILES_AND_DIRECTORIES, null );
+ transformPathText.setText( s );
+ if ( transformPathUpdateCallback != null )
+ transformPathUpdateCallback.accept( s );
+
+ return s;
+ }
+
+ public void setParameters( final String projectLandmarkPath, final String images, final String moving, final String transforms ) {
+ this.projectLandmarkPath = projectLandmarkPath;
+ this.imageList = images;
+ this.movingList = moving;
+ this.transformList = transforms;
+ }
+
+ public void updateTableFromParameters()
+ {
+ for( int i = 0; i < sourceTableModel.getRowCount(); i++ )
+ sourceTableModel.remove( i );
+
+ final String[] imageParams = imageList.split( listSeparator, -1 );
+ final String[] movingParams = movingList.split( listSeparator, -1 );
+ final String[] transformParams = transformList.split( listSeparator, -1 );
+
+ final int N = imageParams.length;
+ if( movingParams.length != N || transformParams.length != N )
+ {
+ System.err.println("Parameter arrays must have identical lengths");
+ return;
+ }
+
+ for( int i = 0; i < N; i++ )
+ {
+ sourceTableModel.add( imageParams[ i ], movingParams[ i ].trim().equals( "true" ) );
+ sourceTableModel.setTransform( i, transformParams[ i ] );
+ }
+ }
+
+ public void updateParametersFromTable()
+ {
+ // make source list
+ final StringBuffer imageList = new StringBuffer();
+ final StringBuffer movingList = new StringBuffer();
+ final StringBuffer transformList = new StringBuffer();
+
+ final int N = sourceTable.getRowCount();
+ for( int i = 0; i < N; i++ )
+ {
+ imageList.append( sourceTableModel.get( i ).srcName );
+ movingList.append( sourceTableModel.get( i ).moving );
+ transformList.append( sourceTableModel.get( i ).transformUrl );
+ if( i < N -1 )
+ {
+ imageList.append( listSeparator );
+ movingList.append( listSeparator );
+ transformList.append( listSeparator );
+ }
+ }
+
+ this.imageList = imageList.toString();
+ this.movingList = movingList.toString();
+ this.transformList = transformList.toString();
+ }
+
+ public String macroRecord()
+ {
+ if( !Recorder.record )
+ return "";
+
+ updateParametersFromTable();
+// return String.format( "images=[%s], moving=[%s], transformations=[%s]",
+// imageList.toString(), movingList.toString(), transformList.toString() );
+
+ Recorder.resetCommandOptions();
+ Recorder.recordOption(imagesKey, imageList.toString());
+ Recorder.recordOption(movingKey, movingList.toString());
+
+ if( transformList != null )
+ Recorder.recordOption(transformsKey, transformList.toString());
+
+ return Recorder.getCommandOptions();
+ }
+
+ protected void showMessage( int timeMillis, String message )
+ {
+ messageLabel.setText(message);
+ final Timer timer = new Timer( timeMillis, new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ messageLabel.setText("");
+ }
+ });
+ timer.setRepeats(false);
+ timer.start();
+ }
+
+ public static void runMacro( final String args )
+ {
+ final String project = Macro.getValue( args, projectKey, "" );
+
+ final String[] images = Macro.getValue( args, imagesKey, "" ).split( ",", -1 );
+ final String[] moving = Macro.getValue( args, movingKey, "" ).split( ",", -1 );
+ final String[] transforms = Macro.getValue( args, transformsKey, "" ).split( ",", -1 );
+
+ if( !project.isEmpty())
+ {
+ runBigWarp( project, null, null, null );
+ }
+ else
+ {
+ if( images.length == 0 || moving.length == 0 )
+ {
+ System.err.println( "images and moving keys required" );
+ return;
+ }
+ // TODO fix transforms
+ runBigWarp( null, images, moving, transforms );
+ }
+
+// System.out.println( "BigWarpInitDialog runMacro");
+// System.out.println( args );
+// final HashMap< String, String > keyVals = BigWarpUtils.parseMacroArguments( args );
+// final Set< String > keys = keyVals.keySet();
+
+// if( keys.contains("project"))
+// {
+// runBigWarp( keyVals.get( "project" ), null, null, null );
+// }
+// else
+// {
+// if( !keys.contains( "images" ) || !keys.contains( "moving" ))
+// {
+// System.out.println( "images and moving keys required" );
+// return;
+// }
+//
+// final String[] images = keyVals.get( "images" ).split( ",", -1 );
+// final String[] moving = keyVals.get( "moving" ).split( ",", -1 );
+//
+//// final Boolean[] moving = Arrays.stream( keyVals.get( "moving" ).split( ",", -1 ) ).map( x -> {
+//// return x.equals( "true" );
+//// } ).toArray( Boolean[]::new );
+//
+//// final String transforms;
+//// if( keys.contains( "transforms" ) )
+//// transforms = keyVals.get( "transforms" );
+//// else
+//// transforms = "";
+//
+// // TOD fix transforms
+// runBigWarp( null, images, moving, null );
+// }
+ }
+
+ /**
+ * Removes selected nodes that do not have transformation metadata.
+ */
+ public static class N5TransformTreeSelectionListener implements TreeSelectionListener {
+
+ private TreeSelectionModel selectionModel;
+
+ public N5TransformTreeSelectionListener(final TreeSelectionModel selectionModel) {
+
+ this.selectionModel = selectionModel;
+ }
+
+ @Override
+ public void valueChanged(final TreeSelectionEvent sel) {
+
+ int i = 0;
+ for (final TreePath path : sel.getPaths()) {
+ if (!sel.isAddedPath(i))
+ continue;
+
+ final Object last = path.getLastPathComponent();
+ if (last instanceof N5SwingTreeNode) {
+ final N5SwingTreeNode node = ((N5SwingTreeNode)last);
+ if (node.getMetadata() == null || !(node.getMetadata() instanceof N5TransformMetadata)) {
+ selectionModel.removeSelectionPath(path);
+ }
+ }
+ i++;
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/bdv/gui/BigWarpLandmarkPanel.java b/src/main/java/bdv/gui/BigWarpLandmarkPanel.java
index 961af7f1..1cfcc96b 100644
--- a/src/main/java/bdv/gui/BigWarpLandmarkPanel.java
+++ b/src/main/java/bdv/gui/BigWarpLandmarkPanel.java
@@ -8,12 +8,12 @@
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 2 of the
* License, or (at your option) any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* .
@@ -37,6 +37,7 @@
import javax.swing.KeyStroke;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
+import javax.swing.table.AbstractTableModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -44,23 +45,23 @@
import bigwarp.landmarks.LandmarkTableModel;
public class BigWarpLandmarkPanel extends JPanel {
-
+
private static final long serialVersionUID = 8470689265638231579L;
protected LandmarkTableModel tableModel;
protected JTable table;
-
+
public final Logger logger = LoggerFactory.getLogger( BigWarpLandmarkPanel.class );
public BigWarpLandmarkPanel( LandmarkTableModel tableModel ) {
-
+
super(new GridLayout(1,0));
setTableModel( tableModel );
-
+
genJTable();
-
+
//Create the scroll pane and add the table to it.
- JScrollPane scrollPane = new JScrollPane(table);
+ final JScrollPane scrollPane = new JScrollPane(table);
//Add the scroll pane to this panel.
add(scrollPane);
@@ -69,7 +70,11 @@ public BigWarpLandmarkPanel( LandmarkTableModel tableModel ) {
public LandmarkTableModel getTableModel() {
return tableModel;
}
-
+
+ public int numDimensions() {
+ return tableModel.getNumdims();
+ }
+
public void genJTable()
{
table = new JTableChecking( getTableModel() );
@@ -92,7 +97,7 @@ public void genJTable()
/*
* Add a listener to update the next row the tableModel will edit
* based on the selected row.
- *
+ *
* Specifically, when the user changes the selected row of the table, the
* listener checks whether any of those rows are "incomplete."
* If so, the first row in the selection that does not have a moving image
@@ -107,7 +112,7 @@ public void valueChanged( ListSelectionEvent e )
logger.trace( "table selection changed" );
boolean setMoving = false;
boolean setFixed = false;
- int row = table.getSelectedRow();
+ final int row = table.getSelectedRow();
// if no rows are selected, the next edit should add a new row
if( row < 0 )
@@ -136,32 +141,33 @@ public void valueChanged( ListSelectionEvent e )
}
});
}
-
+
public void setTableModel( LandmarkTableModel tableModel )
{
this.tableModel = tableModel;
genJTable();
}
-
+
public JTable getJTable()
{
return table;
}
-
+
/**
* A JTable implementation that prevents keybindings from being propagated
* while editing cells. This prevents hotkeys from being activated.
*
*/
- public class JTableChecking extends JTable
+ public static class JTableChecking extends JTable
{
private static final long serialVersionUID = 1437406384583869710L;
- public JTableChecking( LandmarkTableModel tableModel )
+ public JTableChecking( AbstractTableModel tableModel )
{
super( tableModel );
}
+ @Override
protected boolean processKeyBinding(
KeyStroke ks, KeyEvent e,
int condition, boolean pressed )
@@ -175,7 +181,7 @@ protected boolean processKeyBinding(
}
}
- public class TextFieldCell extends JTextField
+ public static class TextFieldCell extends JTextField
{
private static final long serialVersionUID = -4327183047476876882L;
@@ -195,7 +201,7 @@ public void focusGained( FocusEvent e )
@Override
public void focusLost( FocusEvent e )
{
- CellEditor cellEditor = table.getCellEditor();
+ final CellEditor cellEditor = table.getCellEditor();
if ( cellEditor != null )
{
if ( cellEditor.getCellEditorValue() != null )
@@ -211,7 +217,7 @@ public void focusLost( FocusEvent e )
}
}
- public class TextFieldCellEditor extends DefaultCellEditor
+ public static class TextFieldCellEditor extends DefaultCellEditor
{
private static final long serialVersionUID = 9185738725311357320L;
@@ -239,7 +245,7 @@ public boolean stopCellEditing()
public Component getTableCellEditorComponent( JTable table, Object value,
boolean isSelected, int row, int column )
{
- TextFieldCell tf = (TextFieldCell) super.getTableCellEditorComponent( table,
+ final TextFieldCell tf = (TextFieldCell) super.getTableCellEditorComponent( table,
value, isSelected, row, column );
if ( value != null )
{
@@ -267,7 +273,7 @@ else if (columnClass.equals(Byte.class))
else if (columnClass.equals(String.class))
return textField.getText();
}
- catch (NumberFormatException ex) {
+ catch (final NumberFormatException ex) {
}
diff --git a/src/main/java/bdv/gui/BigWarpViewerFrame.java b/src/main/java/bdv/gui/BigWarpViewerFrame.java
index e7dcba3b..e892e9cc 100644
--- a/src/main/java/bdv/gui/BigWarpViewerFrame.java
+++ b/src/main/java/bdv/gui/BigWarpViewerFrame.java
@@ -8,12 +8,12 @@
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 2 of the
* License, or (at your option) any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* .
@@ -45,18 +45,20 @@
import bdv.tools.brightness.ConverterSetup;
import bdv.ui.BdvDefaultCards;
import bdv.ui.CardPanel;
+import bdv.ui.appearance.AppearanceManager;
import bdv.ui.splitpanel.SplitPanel;
import bdv.viewer.BigWarpViewerPanel;
import bdv.viewer.BigWarpViewerSettings;
import bdv.viewer.ConverterSetups;
import bdv.viewer.SourceAndConverter;
import bigwarp.BigWarp;
+import bigwarp.ui.keymap.KeymapManager;
public class BigWarpViewerFrame extends JFrame
{
protected final BigWarpViewerPanel viewer;
-
+
private final InputActionBindings keybindings;
private final TriggerBehaviourBindings triggerbindings;
@@ -67,8 +69,14 @@ public class BigWarpViewerFrame extends JFrame
private CardPanel cards;
+ private final Behaviours transformBehaviours;
+
private final ConverterSetups setups;
+ private final KeymapManager keymapManager;
+
+ private final AppearanceManager appearanceManager;
+
private static final long serialVersionUID = -7630931733043185034L;
public BigWarpViewerFrame(
@@ -79,13 +87,14 @@ public BigWarpViewerFrame(
final BigWarpViewerSettings viewerSettings,
final CacheControl cache,
final String title,
- final boolean isMoving,
- final int[] movingIndexList,
- final int[] targetIndexList )
+ final boolean isMoving )
{
- this( bw, width, height, sources, converterSetups, viewerSettings, cache, BigWarpViewerOptions.options(), title, isMoving, movingIndexList, targetIndexList );
+ this( bw, width, height, sources, converterSetups, viewerSettings, cache,
+ new KeymapManager( BigWarp.configDir ),
+ new AppearanceManager( BigWarp.configDir ),
+ BigWarpViewerOptions.options(), title, isMoving );
}
-
+
public BigWarpViewerFrame(
BigWarp> bw,
final int width, final int height,
@@ -93,33 +102,34 @@ public BigWarpViewerFrame(
final List< ConverterSetup > converterSetups,
final BigWarpViewerSettings viewerSettings,
final CacheControl cache,
+ final KeymapManager keymapManager,
+ final AppearanceManager appearanceManager,
final BigWarpViewerOptions optional,
final String title,
- final boolean isMoving,
- final int[] movingIndexList,
- final int[] targetIndexList )
+ final boolean isMoving )
{
super( title, AWTUtils.getSuitableGraphicsConfiguration( AWTUtils.RGB_COLOR_MODEL ) );
this.bw = bw;
- viewer = new BigWarpViewerPanel( sources, viewerSettings, cache, optional.size( width / 2, height ), isMoving, movingIndexList, targetIndexList );
+ this.keymapManager = keymapManager;
+ this.appearanceManager = appearanceManager;
+
+ viewer = new BigWarpViewerPanel( bw.getData(), viewerSettings, cache, optional.size( width / 2, height ), isMoving );
setups = new ConverterSetups( viewer.state() );
setups.listeners().add( s -> viewer.requestRepaint() );
- if ( converterSetups.size() != sources.size() )
+ if ( converterSetups.size() != bw.getData().sources.size() )
System.err.println( "WARNING! Constructing BigWarp with converterSetups.size() that is not the same as sources.size()." );
- final int numSetups = Math.min( converterSetups.size(), sources.size() );
+ final int numSetups = Math.min( converterSetups.size(), bw.getData().sources.size() );
for ( int i = 0; i < numSetups; ++i )
{
- final SourceAndConverter< ? > source = sources.get( i );
+ final SourceAndConverter< ? > source = bw.getData().sources.get( i );
final ConverterSetup setup = converterSetups.get( i );
if ( setup != null )
setups.put( source, setup );
}
- if ( !isMoving )
- {
- viewer.state().setCurrentSource( viewer.state().getSources().get( bw.getData().targetSourceIndices[ 0 ] ));
- }
+ if ( !isMoving && bw.getData().numTargetSources() > 0 )
+ viewer.state().setCurrentSource( bw.getData().getTargetSource( 0 ) );
keybindings = new InputActionBindings();
triggerbindings = new TriggerBehaviourBindings();
@@ -154,20 +164,28 @@ public void valueChanged( ListSelectionEvent e )
}
});
- SwingUtilities.replaceUIActionMap( getRootPane(), keybindings.getConcatenatedActionMap() );
- SwingUtilities.replaceUIInputMap( getRootPane(), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, keybindings.getConcatenatedInputMap() );
+ SwingUtilities.replaceUIActionMap( viewer, keybindings.getConcatenatedActionMap() );
+ SwingUtilities.replaceUIInputMap( viewer, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, keybindings.getConcatenatedInputMap() );
final MouseAndKeyHandler mouseAndKeyHandler = new MouseAndKeyHandler();
mouseAndKeyHandler.setInputMap( triggerbindings.getConcatenatedInputTriggerMap() );
mouseAndKeyHandler.setBehaviourMap( triggerbindings.getConcatenatedBehaviourMap() );
+ mouseAndKeyHandler.setKeypressManager( optional.values.getKeyPressedManager(), viewer.getDisplayComponent() );
viewer.getDisplay().addHandler( mouseAndKeyHandler );
+ transformBehaviours = new Behaviours( optional.values.getInputTriggerConfig(), "bigwarp", "navigation" );
// TODO: should be a field?
+ updateTransformBehaviors( optional );
+ }
+
+ public void updateTransformBehaviors(BigWarpViewerOptions optional) {
final Behaviours transformBehaviours = new Behaviours( optional.values.getInputTriggerConfig(), "bdv" );
transformBehaviours.install( triggerbindings, "transform" );
final TransformEventHandler tfHandler = viewer.getTransformEventHandler();
tfHandler.install( transformBehaviours );
+
+ viewer.getDisplay().setTransformEventHandler(tfHandler);
}
public boolean isMoving()
@@ -206,12 +224,17 @@ public void collapseCardPanel()
getSplitPanel().setCollapsed( true );
viewer.requestFocusInWindow();
}
-
+
public InputActionBindings getKeybindings()
{
return keybindings;
}
+ public TriggerBehaviourBindings getTriggerBindings() {
+
+ return triggerbindings;
+ }
+
public void setTransformEnabled( final boolean enabled )
{
viewer.setTransformEnabled( enabled );
@@ -220,4 +243,15 @@ public void setTransformEnabled( final boolean enabled )
else
triggerbindings.addInputTriggerMap( "block_transform", new InputTriggerMap(), "transform" );
}
+
+ /**
+ * Get {@code Behaviours} hook where TransformEventHandler behaviours are installed.
+ * This is installed in {@link #getTriggerBindings} with the id "transform".
+ * The hook can be used to update the keymap and install additional behaviours.
+ */
+ public Behaviours getTransformBehaviours()
+ {
+ return transformBehaviours;
+ }
+
}
diff --git a/src/main/java/bdv/gui/BigWarpViewerOptions.java b/src/main/java/bdv/gui/BigWarpViewerOptions.java
index 9574e296..338a0194 100644
--- a/src/main/java/bdv/gui/BigWarpViewerOptions.java
+++ b/src/main/java/bdv/gui/BigWarpViewerOptions.java
@@ -23,7 +23,8 @@
import bdv.viewer.ViewerOptions;
import bdv.viewer.ViewerPanel;
-import bdv.viewer.animate.MessageOverlayAnimator;
+import bigwarp.ui.keymap.KeymapManager;
+
import org.scijava.ui.behaviour.io.InputTriggerConfig;
public class BigWarpViewerOptions extends ViewerOptions
@@ -40,6 +41,11 @@ public static BigWarpViewerOptions options()
return new BigWarpViewerOptions();
}
+ public static BigWarpViewerOptions options( final boolean is2D )
+ {
+ return options().is2D( is2D );
+ }
+
@Override
public BigWarpViewerOptions inputTriggerConfig( final InputTriggerConfig c )
{
@@ -54,6 +60,12 @@ public BigWarpViewerOptions is2D( final boolean is2D )
return this;
}
+ public BigWarpViewerOptions bwKeymapManager( final KeymapManager keymapManager )
+ {
+ bwValues.keymapManager = keymapManager;
+ return this;
+ }
+
public BwValues getValues()
{
return bwValues;
@@ -76,6 +88,8 @@ public BigWarpViewerOptions size( final int width, final int height )
public BigWarpViewerOptions copy()
{
BigWarpViewerOptions out = new BigWarpViewerOptions();
+ out.bwKeymapManager( bwValues.keymapManager );
+
out.
width( values.getWidth() ).
height( values.getHeight() ).
@@ -88,9 +102,8 @@ public BigWarpViewerOptions copy()
is2D( values.is2D() ).
transformEventHandlerFactory( values.getTransformEventHandlerFactory() ).
accumulateProjectorFactory( values.getAccumulateProjectorFactory() ).
- inputTriggerConfig( values.getInputTriggerConfig() ).
+ inputTriggerConfig( bwValues.getInputTriggerConfig() ).
shareKeyPressedEvents( values.getKeyPressedManager() ).
- keymapManager( values.getKeymapManager() ).
appearanceManager( values.getAppearanceManager() );
return out;
}
@@ -104,6 +117,18 @@ public ViewerOptions getViewerOptions( final boolean isMoving )
public static class BwValues
{
+ private KeymapManager keymapManager = null;
private BigWarpMessageAnimator messageAnimator = new BigWarpMessageAnimator( 1500, 0.01, 0.1 );
+ private InputTriggerConfig inputTriggerConfig = null;
+
+ public KeymapManager getKeymapManager()
+ {
+ return keymapManager;
+ }
+
+ public InputTriggerConfig getInputTriggerConfig()
+ {
+ return inputTriggerConfig;
+ }
}
}
diff --git a/src/main/java/bdv/gui/BigwarpLandmarkSelectionPanel.java b/src/main/java/bdv/gui/BigwarpLandmarkSelectionPanel.java
index 4c61ae62..63d46382 100644
--- a/src/main/java/bdv/gui/BigwarpLandmarkSelectionPanel.java
+++ b/src/main/java/bdv/gui/BigwarpLandmarkSelectionPanel.java
@@ -45,7 +45,7 @@
import bdv.ij.ApplyBigwarpPlugin.WriteDestinationOptions;
import bdv.viewer.Interpolation;
import bdv.viewer.SourceAndConverter;
-import bigwarp.BigWarp.BigWarpData;
+import bigwarp.BigWarpData;
import net.imglib2.Interval;
public class BigwarpLandmarkSelectionPanel extends JPanel
@@ -138,12 +138,8 @@ public BigwarpLandmarkSelectionPanel(
@Override
public void actionPerformed( ActionEvent e )
{
- System.out.println("ok");
filterPoints( matchedPtNames, outputIntervalList, selectionTable );
- System.out.println( matchedPtNames );
- System.out.println( outputIntervalList );
-
ApplyBigwarpPlugin.runExport( data, sources, fieldOfViewOption,
outputIntervalList, matchedPtNames, interp,
offsetIn, resolution, isVirtual, nThreads,
@@ -158,7 +154,6 @@ public void actionPerformed( ActionEvent e )
@Override
public void actionPerformed( ActionEvent e )
{
- //System.out.println("cancel");
frame.dispatchEvent(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING));
}
});
diff --git a/src/main/java/bdv/gui/ExportDisplacementFieldFrame.java b/src/main/java/bdv/gui/ExportDisplacementFieldFrame.java
new file mode 100644
index 00000000..ddb00f12
--- /dev/null
+++ b/src/main/java/bdv/gui/ExportDisplacementFieldFrame.java
@@ -0,0 +1,782 @@
+package bdv.gui;
+
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.io.File;
+import java.util.Arrays;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JFileChooser;
+import javax.swing.JFormattedTextField;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JSpinner;
+import javax.swing.JTextField;
+import javax.swing.SpinnerNumberModel;
+import javax.swing.SwingUtilities;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.filechooser.FileFilter;
+import javax.swing.filechooser.FileNameExtensionFilter;
+
+import com.formdev.flatlaf.util.UIScale;
+
+import bdv.ij.ApplyBigwarpPlugin;
+import bdv.ij.BigWarpToDeformationFieldPlugIn;
+import bdv.ij.BigWarpToDeformationFieldPlugIn.DeformationFieldExportParameters;
+import bdv.viewer.Source;
+import bigwarp.BigWarp;
+import bigwarp.BigWarpData;
+import bigwarp.landmarks.LandmarkTableModel;
+import bigwarp.transforms.BigWarpTransform;
+import ij.IJ;
+import ij.Macro;
+import ij.plugin.frame.Recorder;
+
+public class ExportDisplacementFieldFrame extends JFrame
+{
+ private static final long serialVersionUID = -6179153725489981013L;
+
+ // formats
+ public static final String FMT_NGFF = "NGFF";
+ public static final String FMT_SLICER = "Slicer";
+
+ // macro recording
+ public static final String commandName = "Big Warp to Displacement field";
+ protected static final String landmarksKey = "landmarks";
+ protected static final String splitAffineKey = "split_affine";
+ protected static final String typeKey = "type";
+ protected static final String directionKey = "direction";
+ protected static final String inverseToleranceKey = "inverseTolerance";
+ protected static final String inverseMaxIterationsKey = "inverseMaxIterations";
+
+ protected static final String virtualKey = "virtual";
+ protected static final String threadsKey = "threads";
+ protected static final String formatKey = "format";
+ protected static final String sizeKey = "pixel_size";
+ protected static final String spacingKey = "pixel_spacing";
+ protected static final String minKey = "min";
+ protected static final String unitKey = "unit";
+ protected static final String n5RootKey = "n5_root";
+ protected static final String n5DatasetKey = "n5_dataset";
+ protected static final String n5BlockSizeKey = "n5_block_size";
+ protected static final String n5CompressionKey = "n5_compression";
+
+ private String lastBrowsePath = null;
+ private String initialPath = null;
+
+ // no need at the moment for these callbacks to consume parameters, but will leave it this way
+ private Consumer okayCallback;
+ private Consumer cancelCallback;
+
+ private BigWarpData< ? > data;
+ private BigWarpTransform bwTransform;
+ private LandmarkTableModel ltm;
+
+ private boolean imageJOpen;
+ private boolean initialRecorderState;
+
+ private boolean n5DatasetChanged;
+ private DocumentListener docListener;
+
+ private JPanel contentPanel;
+ private JPanel invPanel;
+
+ private JTextField landmarkPathTxt;
+ private JButton browseLandmarksButton;
+ private JTextField n5RootTxt;
+ private JButton browseN5Button;
+ private JTextField n5DatasetTxt;
+ private JTextField n5BlockSizeTxt;
+ private JComboBox< String > n5CompressionDropdown;
+ private JCheckBox splitAffineCheckBox;
+ private JCheckBox virtualCheckBox;
+ private JComboBox< String > typeComboBox;
+ private JComboBox< String > directionComboBox;
+ private JSpinner nThreadsField;
+ private JComboBox< String > formatComboBox;
+ private JButton okBtn;
+ private JButton cancelBtn;
+
+ // inverse options
+ private JSpinner invMaxIterationsSpinner;
+ private JSpinner invToleranceSpinner;
+
+ private FieldOfViewPanel fovPanel;
+
+ public ExportDisplacementFieldFrame( BigWarp> bw )
+ {
+ this( bw.getData(), bw.getBwTransform(), bw.getLandmarkPanel().getTableModel());
+ }
+
+ public ExportDisplacementFieldFrame( BigWarpData> data, BigWarpTransform bwTransform, LandmarkTableModel ltm )
+ {
+ super( "Export transformation" );
+ initialPath = "";
+ imageJOpen = IJ.getInstance() != null;
+
+ this.data = data;
+ this.bwTransform = bwTransform;
+ this.ltm = ltm;
+
+ cancelCallback = x -> {
+ dispose();
+ setVisible( false );
+ Recorder.record = initialRecorderState;
+ };
+
+ okayCallback = x -> {
+ macroRecord();
+ run();
+ Recorder.record = initialRecorderState;
+ dispose();
+ setVisible( false );
+ };
+
+ // attach to the n5Dataset text field, keep track of whether user changes it
+ // once user change occurs, default values from direction dropdown no longer affect it
+ docListener = new DocumentListener() {
+ @Override
+ public void changedUpdate( DocumentEvent e ) { }
+
+ @Override
+ public void insertUpdate( DocumentEvent e ) { n5DatasetChanged = true; }
+
+ @Override
+ public void removeUpdate( DocumentEvent e ) { n5DatasetChanged = true; }
+ };
+ }
+
+ public static void createAndShow()
+ {
+ createAndShow( null, null, null );
+ }
+
+ public static void createAndShow( final BigWarp< ? > bw )
+ {
+ ExportDisplacementFieldFrame frame = new ExportDisplacementFieldFrame( bw );
+ if ( bw == null )
+ frame = new ExportDisplacementFieldFrame( null, null, null );
+ else
+ frame = new ExportDisplacementFieldFrame( bw );
+
+ frame.createContent();
+ frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
+ frame.pack();
+ frame.setVisible( true );
+ }
+
+ public static void createAndShow( BigWarpData< ? > data, BigWarpTransform bwTransform, LandmarkTableModel ltm )
+ {
+ final ExportDisplacementFieldFrame frame = new ExportDisplacementFieldFrame( data, bwTransform, ltm );
+ frame.createContent();
+ frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
+ frame.pack();
+ frame.setVisible( true );
+ }
+
+ public void createContent()
+ {
+ final boolean isNonlinear = bwTransform.isNonlinear();
+
+ n5DatasetChanged = false;
+ final int frameSizeX = UIScale.scale( 600 );
+
+ final JPanel panel = basicPanel();
+
+ invPanel = inverseOptionsPanel( frameSizeX );
+ invPanel.setBorder( BorderFactory.createCompoundBorder(
+ BorderFactory.createEmptyBorder( 4, 2, 4, 2 ),
+ BorderFactory.createCompoundBorder(
+ BorderFactory.createTitledBorder(
+ BorderFactory.createEtchedBorder(),
+ "Inverse options" ),
+ BorderFactory.createEmptyBorder( 2, 2, 2, 2 ) ) ) );
+
+
+ final JPanel n5Panel = n5OptionsPanel( frameSizeX );
+ n5Panel.setBorder( BorderFactory.createCompoundBorder(
+ BorderFactory.createEmptyBorder( 4, 2, 4, 2 ),
+ BorderFactory.createCompoundBorder(
+ BorderFactory.createTitledBorder(
+ BorderFactory.createEtchedBorder(),
+ "N5 options" ),
+ BorderFactory.createEmptyBorder( 2, 2, 2, 2 ) ) ) );
+
+ // field of view panel
+ String unit = "pixel";
+ if( data != null )
+ {
+ final Source< ? > src = data.getMovingSource( 0 ).getSpimSource();
+ unit = src.getVoxelDimensions().unit();
+ }
+
+ if( isNonlinear )
+ {
+ fovPanel = new FieldOfViewPanel( data, ltm, bwTransform, unit, 150,
+ new double[] { 0, 0, 0 },
+ new double[] { 1, 1, 1 },
+ new long[] { 256, 256, 128 }
+ );
+
+ fovPanel.setBorder( BorderFactory.createCompoundBorder(
+ BorderFactory.createEmptyBorder( 4, 2, 4, 2 ),
+ BorderFactory.createCompoundBorder(
+ BorderFactory.createTitledBorder(
+ BorderFactory.createEtchedBorder(),
+ "Field of view" ),
+ BorderFactory.createEmptyBorder( 2, 2, 2, 2 ) ) ) );
+ }
+
+ contentPanel = new JPanel();
+ contentPanel.setLayout( new GridBagLayout() );
+
+ final GridBagConstraints cGbc = new GridBagConstraints();
+ cGbc.gridx = 0;
+ cGbc.gridwidth = 4;
+ cGbc.fill = GridBagConstraints.HORIZONTAL;
+
+ cGbc.gridy = 0;
+ contentPanel.add( panel, cGbc );
+ cGbc.gridy = 1;
+ contentPanel.add( invPanel, cGbc );
+ invPanel.setEnabled( false );
+ invPanel.setVisible( false );
+ cGbc.gridy = 2;
+ contentPanel.add( n5Panel, cGbc );
+ cGbc.gridy = 3;
+ if( isNonlinear )
+ contentPanel.add( fovPanel, cGbc );
+
+ // bottom button section
+ final GridBagConstraints cbot = new GridBagConstraints();
+ cbot.gridx = 0;
+ cbot.gridy = 4;
+ cbot.gridwidth = 1;
+ cbot.gridheight = 1;
+ cbot.weightx = 1.0;
+ cbot.weighty = 0.0;
+ cbot.fill = GridBagConstraints.NONE;
+
+ okBtn = new JButton( "OK" );
+ okBtn.setPreferredSize( new Dimension( okBtn.getPreferredSize().width, okBtn.getPreferredSize().height ) );
+ okBtn.addActionListener( e -> okayCallback.accept( getParams() ) );
+ cbot.gridx = 2;
+ cbot.gridwidth = 1;
+ cbot.insets = new Insets( 20, ( int ) ( frameSizeX * 0.8 ), 20, 2 );
+ cbot.anchor = GridBagConstraints.LINE_END;
+ contentPanel.add( okBtn, cbot );
+
+ cancelBtn = new JButton( "Cancel" );
+ cancelBtn.setPreferredSize( new Dimension( cancelBtn.getPreferredSize().width, cancelBtn.getPreferredSize().height ) );
+ cancelBtn.addActionListener( e -> cancelCallback.accept( null ) );
+ cbot.gridx = 3;
+ cbot.anchor = GridBagConstraints.LINE_START;
+ cbot.ipadx = 0;
+ cbot.insets = new Insets( 2, 2, 2, 2 );
+ contentPanel.add( cancelBtn, cbot );
+
+ final Container content = getContentPane();
+ content.add( contentPanel );
+
+ if( isNonlinear )
+ {
+ if( data != null )
+ {
+ // set default resolution to target image resolution
+ final double[] res = ApplyBigwarpPlugin.getResolution( data, ApplyBigwarpPlugin.TARGET, null );
+ fovPanel.setSpacing(res);
+ fovPanel.updateFieldsFromReference();
+ }
+ else
+ fovPanel.updateFieldsFromImageJReference();
+ }
+
+ addDefaultN5DatasetAction();
+ }
+
+ public JPanel basicPanel()
+ {
+ final int OUTER_PAD = BigWarpInitDialog.DEFAULT_OUTER_PAD;
+ final int BUTTON_PAD = BigWarpInitDialog.DEFAULT_BUTTON_PAD;
+ final int MID_PAD = BigWarpInitDialog.DEFAULT_MID_PAD;
+ final Insets defaultInsets = new Insets( OUTER_PAD, BUTTON_PAD, MID_PAD, BUTTON_PAD );
+
+ final int szX = UIScale.scale( 600 );
+
+ final JPanel panel = new JPanel( false );
+ panel.setLayout( new GridBagLayout() );
+
+ final GridBagConstraints ctxt = new GridBagConstraints();
+ ctxt.gridx = 0;
+ ctxt.gridy = 0;
+ ctxt.gridwidth = 1;
+ ctxt.gridheight = 1;
+ ctxt.weightx = 0.0;
+ ctxt.weighty = 0.0;
+ ctxt.anchor = GridBagConstraints.LINE_END;
+ ctxt.fill = GridBagConstraints.NONE;
+ ctxt.insets = defaultInsets;
+
+ final GridBagConstraints gbcBar = new GridBagConstraints();
+ gbcBar.gridx = 1;
+ gbcBar.gridy = 0;
+ gbcBar.gridwidth = 6;
+ gbcBar.gridheight = 1;
+ gbcBar.weightx = 1.0;
+ gbcBar.weighty = 0.0;
+ gbcBar.fill = GridBagConstraints.HORIZONTAL;
+ gbcBar.insets = defaultInsets;
+
+ final GridBagConstraints cProjBrowse = new GridBagConstraints();
+ cProjBrowse.gridx = 7;
+ cProjBrowse.gridy = 0;
+ cProjBrowse.gridwidth = 1;
+ cProjBrowse.weightx = 0.0;
+ cProjBrowse.fill = GridBagConstraints.HORIZONTAL;
+ cProjBrowse.insets = defaultInsets;
+
+ // Don't ask for landmarks if running from a bigwarp instance
+ if( bwTransform == null )
+ {
+ panel.add( new JLabel( "Landmarks:" ), ctxt );
+
+ landmarkPathTxt = new JTextField();
+ landmarkPathTxt.setPreferredSize( new Dimension( szX / 3, landmarkPathTxt.getPreferredSize().height ) );
+ panel.add( landmarkPathTxt, gbcBar );
+
+ browseLandmarksButton = new JButton( "Browse" );
+ browseLandmarksButton.addActionListener( e -> {
+ browseLandmarksDialog();
+ } );
+ panel.add( browseLandmarksButton, cProjBrowse );
+ }
+
+ ctxt.gridx = 0;
+ ctxt.gridy = 1;
+ ctxt.anchor = GridBagConstraints.LINE_END;
+ panel.add( new JLabel( "Type:" ), ctxt );
+
+ final GridBagConstraints gbcCheck = new GridBagConstraints();
+ gbcCheck.gridx = 1;
+ gbcCheck.gridy = 1;
+ gbcCheck.insets = defaultInsets;
+ gbcCheck.anchor = GridBagConstraints.LINE_START;
+ typeComboBox = new JComboBox< String >( new String[] {
+ BigWarpToDeformationFieldPlugIn.flattenOption,
+ BigWarpToDeformationFieldPlugIn.sequenceOption } );
+ panel.add( typeComboBox, gbcCheck );
+
+ // want some more padding for direction
+ ctxt.gridx = 3;
+ ctxt.insets = new Insets( OUTER_PAD, 10*BUTTON_PAD, MID_PAD, BUTTON_PAD );
+ panel.add( new JLabel( "Direction:" ), ctxt );
+ ctxt.insets = defaultInsets;
+
+ gbcCheck.gridx = 4;
+ directionComboBox = new JComboBox< String >( new String[] {
+ BigWarpToDeformationFieldPlugIn.INVERSE_OPTIONS.FORWARD.toString(),
+ BigWarpToDeformationFieldPlugIn.INVERSE_OPTIONS.INVERSE.toString(),
+ BigWarpToDeformationFieldPlugIn.INVERSE_OPTIONS.BOTH.toString() } );
+ panel.add( directionComboBox, gbcCheck );
+
+ ctxt.gridx = 5;
+ ctxt.anchor = GridBagConstraints.LINE_END;
+ panel.add( new JLabel( "Split affine:" ), ctxt );
+
+ gbcCheck.gridx = 6;
+ splitAffineCheckBox = new JCheckBox();
+ panel.add( splitAffineCheckBox, gbcCheck );
+
+ // second row
+ ctxt.gridx = 0;
+ ctxt.gridy = 2;
+ ctxt.anchor = GridBagConstraints.LINE_END;
+ panel.add( new JLabel( "Threads:" ), ctxt );
+
+ gbcCheck.gridx = 1;
+ gbcCheck.gridy = 2;
+ gbcCheck.fill = GridBagConstraints.HORIZONTAL;
+ nThreadsField = new JSpinner( new SpinnerNumberModel( 1, 1, 9999, 1 ) );
+ panel.add( nThreadsField, gbcCheck );
+
+ ctxt.gridx = 3;
+ panel.add( new JLabel( "Format:" ), ctxt );
+ gbcCheck.gridx = 4;
+// formatComboBox = new JComboBox< String >( new String[] { FMT_NGFF, FMT_SLICER } );
+ formatComboBox = new JComboBox< String >( new String[] { FMT_NGFF } );
+ panel.add( formatComboBox, gbcCheck );
+
+ ctxt.gridx = 5;
+ ctxt.anchor = GridBagConstraints.LINE_END;
+ ctxt.insets = new Insets( OUTER_PAD, BUTTON_PAD, MID_PAD, BUTTON_PAD );
+ ctxt.weightx = 0.1;
+ panel.add( new JLabel( "Virtual:" ), ctxt );
+
+ gbcCheck.gridx = 6;
+ gbcCheck.weightx = 0.1;
+ virtualCheckBox = new JCheckBox();
+ panel.add( virtualCheckBox, gbcCheck );
+
+ return panel;
+ }
+
+ public JPanel inverseOptionsPanel( final int frameSizeX )
+ {
+ final int OUTER_PAD = BigWarpInitDialog.DEFAULT_OUTER_PAD;
+ final int BUTTON_PAD = BigWarpInitDialog.DEFAULT_BUTTON_PAD;
+ final int MID_PAD = BigWarpInitDialog.DEFAULT_MID_PAD;
+
+ final JPanel panel = new JPanel();
+ panel.setLayout( new GridBagLayout() );
+
+ final GridBagConstraints ctxt = new GridBagConstraints();
+ ctxt.gridx = 0;
+ ctxt.gridy = 0;
+ ctxt.gridwidth = 1;
+ ctxt.gridheight = 1;
+ ctxt.weightx = 0.0;
+ ctxt.weighty = 0.0;
+ ctxt.anchor = GridBagConstraints.LINE_END;
+ ctxt.fill = GridBagConstraints.NONE;
+ ctxt.insets = new Insets( OUTER_PAD, OUTER_PAD, MID_PAD, BUTTON_PAD );
+
+ final GridBagConstraints gbcBar = new GridBagConstraints();
+ gbcBar.gridx = 1;
+ gbcBar.gridy = 0;
+ gbcBar.gridwidth = 1;
+ gbcBar.gridheight = 1;
+ gbcBar.weightx = 1.0;
+ gbcBar.weighty = 0.0;
+ gbcBar.fill = GridBagConstraints.HORIZONTAL;
+ gbcBar.insets = new Insets( OUTER_PAD, OUTER_PAD, MID_PAD, BUTTON_PAD );
+
+ ctxt.gridx = 0;
+ ctxt.anchor = GridBagConstraints.LINE_END;
+ panel.add( new JLabel( "Tolerance:" ), ctxt );
+
+ gbcBar.gridx = 1;
+ gbcBar.fill = GridBagConstraints.HORIZONTAL;
+ invToleranceSpinner = new JSpinner( new SpinnerNumberModel( 0.5, 1e-9, 999999, 0.01 ) );
+
+ final JSpinner.NumberEditor editor = new JSpinner.NumberEditor(invToleranceSpinner, "###,###.######");
+ final JFormattedTextField textField = editor.getTextField();
+ textField.setColumns(12);
+ invToleranceSpinner.setEditor(editor);
+
+ panel.add( invToleranceSpinner, gbcBar );
+
+ ctxt.gridx = 3;
+ ctxt.anchor = GridBagConstraints.LINE_END;
+ panel.add( new JLabel( "Max iterations:" ), ctxt );
+
+ gbcBar.gridx = 4;
+ gbcBar.fill = GridBagConstraints.HORIZONTAL;
+ invMaxIterationsSpinner = new JSpinner( new SpinnerNumberModel( 200, 1, 999999, 1 ) );
+ panel.add( invMaxIterationsSpinner, gbcBar );
+
+ return panel;
+ }
+
+ public JPanel n5OptionsPanel( final int frameSizeX )
+ {
+ final int OUTER_PAD = BigWarpInitDialog.DEFAULT_OUTER_PAD;
+ final int BUTTON_PAD = BigWarpInitDialog.DEFAULT_BUTTON_PAD;
+ final int MID_PAD = BigWarpInitDialog.DEFAULT_MID_PAD;
+
+ final JPanel panel = new JPanel();
+ panel.setLayout( new GridBagLayout() );
+
+ final GridBagConstraints ctxt = new GridBagConstraints();
+ ctxt.gridx = 0;
+ ctxt.gridy = 0;
+ ctxt.gridwidth = 1;
+ ctxt.gridheight = 1;
+ ctxt.weightx = 0.0;
+ ctxt.weighty = 0.0;
+ ctxt.anchor = GridBagConstraints.LINE_END;
+ ctxt.fill = GridBagConstraints.NONE;
+ ctxt.insets = new Insets( OUTER_PAD, OUTER_PAD, MID_PAD, BUTTON_PAD );
+ panel.add( new JLabel( "Root folder:" ), ctxt );
+
+ final GridBagConstraints gbcBar = new GridBagConstraints();
+ gbcBar.gridx = 1;
+ gbcBar.gridy = 0;
+ gbcBar.gridwidth = 6;
+ gbcBar.gridheight = 1;
+ gbcBar.weightx = 1.0;
+ gbcBar.weighty = 0.0;
+ gbcBar.fill = GridBagConstraints.HORIZONTAL;
+ gbcBar.insets = new Insets( OUTER_PAD, OUTER_PAD, MID_PAD, BUTTON_PAD );
+
+ n5RootTxt = new JTextField();
+ n5RootTxt.setPreferredSize( new Dimension( frameSizeX / 3, n5RootTxt.getPreferredSize().height ) );
+ panel.add( n5RootTxt, gbcBar );
+
+ final GridBagConstraints cProjBrowse = new GridBagConstraints();
+ cProjBrowse.gridx = 7;
+ cProjBrowse.gridy = 0;
+ cProjBrowse.gridwidth = 1;
+ cProjBrowse.weightx = 0.0;
+ cProjBrowse.fill = GridBagConstraints.HORIZONTAL;
+ cProjBrowse.insets = new Insets( OUTER_PAD, BUTTON_PAD, MID_PAD, BUTTON_PAD );
+ browseN5Button = new JButton( "Browse" );
+ browseN5Button.addActionListener( e -> {
+ browseN5Root();
+ } );
+ panel.add( browseN5Button, cProjBrowse );
+
+ ctxt.gridy = 1;
+ panel.add( new JLabel( "Dataset:" ), ctxt );
+
+ gbcBar.gridy = 1;
+ n5DatasetTxt = new JTextField();
+ n5DatasetTxt.setPreferredSize( new Dimension( frameSizeX / 3, n5DatasetTxt.getPreferredSize().height ) );
+ n5DatasetTxt.setText( "dfield" );
+ n5DatasetTxt.getDocument().addDocumentListener( docListener );
+
+
+ panel.add( n5DatasetTxt, gbcBar );
+
+ ctxt.gridy = 2;
+ panel.add( new JLabel( "Block size:" ), ctxt );
+
+ gbcBar.gridy = 2;
+ n5BlockSizeTxt = new JTextField();
+ n5BlockSizeTxt.setPreferredSize( new Dimension( frameSizeX / 3, n5BlockSizeTxt.getPreferredSize().height ) );
+ n5BlockSizeTxt.setText( "64" );
+ panel.add( n5BlockSizeTxt, gbcBar );
+
+ ctxt.gridy = 3;
+ panel.add( new JLabel( "Compression" ), ctxt );
+
+ gbcBar.gridy = 3;
+ gbcBar.fill = GridBagConstraints.NONE;
+ gbcBar.anchor = GridBagConstraints.LINE_START;
+ n5CompressionDropdown = new JComboBox< String >( BigWarpToDeformationFieldPlugIn.compressionOptions );
+ panel.add( n5CompressionDropdown, gbcBar );
+
+ return panel;
+ }
+
+ private void addDefaultN5DatasetAction()
+ {
+ directionComboBox.addActionListener( e -> {
+ if( n5DatasetChanged ) {
+ return;
+ }
+
+ final String item = (String)directionComboBox.getSelectedItem();
+ if( item.equals( BigWarpToDeformationFieldPlugIn.INVERSE_OPTIONS.FORWARD.toString() ))
+ {
+ n5DatasetTxt.getDocument().removeDocumentListener( docListener );
+ n5DatasetTxt.setText( "dfield" );
+ n5DatasetTxt.getDocument().addDocumentListener( docListener );
+
+ SwingUtilities.invokeLater( () -> {
+ invPanel.setEnabled( false );
+ invPanel.setVisible( false );
+ contentPanel.revalidate();
+ contentPanel.repaint();
+ pack();
+ });
+ }
+ else if( item.equals( BigWarpToDeformationFieldPlugIn.INVERSE_OPTIONS.INVERSE.toString() ))
+ {
+ n5DatasetTxt.getDocument().removeDocumentListener( docListener );
+ n5DatasetTxt.setText( "invdfield" );
+ n5DatasetTxt.getDocument().addDocumentListener( docListener );
+ SwingUtilities.invokeLater( () -> {
+ invPanel.setEnabled( true );
+ invPanel.setVisible( true );
+ contentPanel.revalidate();
+ contentPanel.repaint();
+ pack();
+ });
+ }
+ else if( item.equals( BigWarpToDeformationFieldPlugIn.INVERSE_OPTIONS.BOTH.toString() ))
+ {
+ n5DatasetTxt.getDocument().removeDocumentListener( docListener );
+ n5DatasetTxt.setText( "transform" );
+ n5DatasetTxt.getDocument().addDocumentListener( docListener );
+ SwingUtilities.invokeLater( () -> {
+ invPanel.setEnabled( true );
+ invPanel.setVisible( true );
+ contentPanel.revalidate();
+ contentPanel.repaint();
+ pack();
+ });
+ }
+ });
+ }
+
+ private String browseLandmarksDialog()
+ {
+ final String s = browseDialogGeneral( JFileChooser.FILES_ONLY, new FileNameExtensionFilter( "csv file", "csv" ) );
+ landmarkPathTxt.setText( s );
+
+ return s;
+ }
+
+ private String browseN5Root()
+ {
+ final String s = browseDialogGeneral( JFileChooser.FILES_AND_DIRECTORIES, null );
+ n5RootTxt.setText( s );
+ return s;
+ }
+
+ private String browseDialogGeneral( final int mode, final FileFilter filefilter )
+ {
+
+ final JFileChooser fileChooser = new JFileChooser();
+ /*
+ * Need to allow files so h5 containers can be opened, and directories
+ * so that filesystem n5's and zarrs can be opened.
+ */
+ fileChooser.setFileSelectionMode( mode );
+ if( filefilter == null )
+ {
+ fileChooser.setFileFilter( filefilter );
+ }
+
+ if ( lastBrowsePath != null && !lastBrowsePath.isEmpty() )
+ fileChooser.setCurrentDirectory( new File( lastBrowsePath ) );
+ else if ( initialPath != null && !initialPath.isEmpty() )
+ fileChooser.setCurrentDirectory( new File( initialPath ) );
+ else if ( imageJOpen )
+ {
+ File f = null;
+
+ final String currDir = IJ.getDirectory( "current" );
+ final String homeDir = IJ.getDirectory( "home" );
+ if ( currDir != null )
+ f = new File( currDir );
+ else if ( homeDir != null )
+ f = new File( homeDir );
+
+ fileChooser.setCurrentDirectory( f );
+ }
+
+ final int ret = fileChooser.showOpenDialog( this );
+ if ( ret != JFileChooser.APPROVE_OPTION )
+ return null;
+
+ final String path = fileChooser.getSelectedFile().getAbsolutePath();
+ lastBrowsePath = path;
+
+ return path;
+ }
+
+ public DeformationFieldExportParameters getParams()
+ {
+ final String n5BlockSizeString = n5BlockSizeTxt.getText();
+ final int[] blockSize = n5BlockSizeString.isEmpty() ? null :
+ Arrays.stream( n5BlockSizeString.split( "," ) ).mapToInt( Integer::parseInt ).toArray();
+
+ return new DeformationFieldExportParameters(
+ landmarkPathTxt == null ? "" : landmarkPathTxt.getText(),
+ splitAffineCheckBox.isSelected(),
+ (String)typeComboBox.getSelectedItem(),
+ (String)directionComboBox.getSelectedItem(),
+ (Double)invToleranceSpinner.getValue(),
+ (Integer)invMaxIterationsSpinner.getValue(),
+ virtualCheckBox.isSelected(),
+ (Integer)nThreadsField.getValue(),
+ (String)formatComboBox.getSelectedItem(),
+ fovPanel == null ? null : fovPanel.getPixelSize(),
+ fovPanel == null ? null : fovPanel.getSpacing(),
+ fovPanel == null ? null : fovPanel.getMin(),
+ fovPanel == null ? null : fovPanel.getUnit(),
+ n5RootTxt.getText(),
+ n5DatasetTxt.getText(),
+ blockSize,
+ BigWarpToDeformationFieldPlugIn.getCompression( (String)n5CompressionDropdown.getSelectedItem() ) );
+ }
+
+ public void run()
+ {
+ BigWarpToDeformationFieldPlugIn.runFromParameters( getParams(), data, ltm, bwTransform );
+ }
+
+ public String macroRecord()
+ {
+ if( !Recorder.record )
+ return "";
+
+ Recorder.setCommand( commandName );
+ final String szString = Arrays.stream( fovPanel.getPixelSize() ).mapToObj( Long::toString ).collect( Collectors.joining( "," ) );
+ final String spacingString = Arrays.stream( fovPanel.getSpacing() ).mapToObj( Double::toString ).collect( Collectors.joining( "," ) );
+ final String minString = Arrays.stream( fovPanel.getMin() ).mapToObj( Double::toString ).collect( Collectors.joining( "," ) );
+
+ Recorder.resetCommandOptions();
+ Recorder.recordOption( landmarksKey, landmarkPathTxt.getText().trim() );
+ Recorder.recordOption( splitAffineKey );
+ Recorder.recordOption( virtualKey );
+ Recorder.recordOption( typeKey, ( String ) typeComboBox.getSelectedItem() );
+ Recorder.recordOption( threadsKey, Integer.toString( ( Integer ) nThreadsField.getValue() ) );
+ Recorder.recordOption( sizeKey, szString );
+ Recorder.recordOption( spacingKey, spacingString );
+ Recorder.recordOption( minKey, minString );
+ Recorder.recordOption( unitKey, fovPanel.getUnit() );
+
+ if( !n5RootTxt.getText().isEmpty() )
+ {
+ Recorder.recordOption( n5RootKey, n5RootTxt.getText().trim() );
+ Recorder.recordOption( n5DatasetKey, n5DatasetTxt.getText().trim() );
+ Recorder.recordOption( n5BlockSizeKey, n5BlockSizeTxt.getText().trim() );
+ Recorder.recordOption( n5CompressionKey, ( String ) n5CompressionDropdown.getSelectedItem() );
+ }
+
+ Recorder.saveCommand();
+ return Recorder.getCommandOptions();
+ }
+
+ public static void runMacro( String args )
+ {
+ final String landmarks = Macro.getValue( args, landmarksKey, "" );
+ final String type = Macro.getValue( args, typeKey, "" );
+ final String direction = Macro.getValue( args, directionKey, "" );
+ final double tolerance = Double.valueOf( Macro.getValue( args, inverseToleranceKey, "" ));
+ final int maxIters = Integer.valueOf( Macro.getValue( args, inverseMaxIterationsKey, "" ));
+
+ final boolean splitAffine = args.contains(" " + splitAffineKey );
+ final boolean openAsVirtual = args.contains(" " + virtualKey);
+ final int threads = Integer.valueOf( Macro.getValue( args, threadsKey, "1" ));
+ final String format = Macro.getValue( args, formatKey, FMT_NGFF );
+
+ final double[] min = Arrays.stream( Macro.getValue( args, minKey, "" ).split( "," ) ).mapToDouble( Double::valueOf ).toArray();
+ final double[] spacing = Arrays.stream( Macro.getValue( args, spacingKey, "" ).split( "," ) ).mapToDouble( Double::valueOf ).toArray();
+ final long[] pixSize = Arrays.stream( Macro.getValue( args, sizeKey, "" ).split( "," ) ).mapToLong( Long::valueOf ).toArray();
+ final String unit = Macro.getValue( args, typeKey, "pixel" );
+
+ final String n5Root = Macro.getValue( args, n5RootKey, "" );
+ final String n5Dataset = Macro.getValue( args, n5DatasetKey, "" );
+ final String n5BlockSizeString = Macro.getValue( args, n5BlockSizeKey, "" );
+ final String n5Compression = Macro.getValue( args, n5CompressionKey, "" );
+
+ final int[] blockSize = n5BlockSizeString.isEmpty() ? null :
+ Arrays.stream( n5BlockSizeString.split( "," ) ).mapToInt( Integer::parseInt ).toArray();
+
+ final DeformationFieldExportParameters params = new DeformationFieldExportParameters(
+ landmarks, splitAffine, type,
+ direction, tolerance, maxIters,
+ openAsVirtual, threads, format,
+ pixSize, spacing, min, unit,
+ n5Root,
+ n5Dataset,
+ blockSize,
+ BigWarpToDeformationFieldPlugIn.getCompression( n5Compression ) );
+
+ BigWarpToDeformationFieldPlugIn.runFromParameters( params, null, null, null );
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/bdv/gui/FieldOfViewPanel.java b/src/main/java/bdv/gui/FieldOfViewPanel.java
new file mode 100644
index 00000000..60e49e72
--- /dev/null
+++ b/src/main/java/bdv/gui/FieldOfViewPanel.java
@@ -0,0 +1,513 @@
+package bdv.gui;
+
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.SwingUtilities;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+
+import com.formdev.flatlaf.util.UIScale;
+
+import bdv.ij.ApplyBigwarpPlugin;
+import bigwarp.BigWarpData;
+import bigwarp.landmarks.LandmarkTableModel;
+import bigwarp.transforms.BigWarpTransform;
+import ij.IJ;
+import ij.ImagePlus;
+import ij.WindowManager;
+import net.imglib2.Interval;
+import net.imglib2.realtransform.BoundingBoxEstimation;
+
+public class FieldOfViewPanel extends JPanel
+{
+ private static final long serialVersionUID = 1719652204751351335L;
+
+ private static final int DEFAULT_OUTER_PAD = 8;
+ private static final int DEFAULT_BUTTON_PAD = 3;
+ private static final int DEFAULT_MID_PAD = 5;
+
+ private BigWarpData> data;
+ private LandmarkTableModel ltm;
+ private BigWarpTransform bwTransform;
+
+ private String unit;
+ private int ndims;
+ private int textFieldWidth;
+
+ private double[] initMin;
+ private double[] initSpacing;
+ private long[] initPixsize;
+
+ private double[] min;
+ private double[] spacing;
+ private double[] size;
+ private long[] pixSize;
+
+ private JComboBox referenceComboBox;
+ private JTextField unitField;
+ private JLabel sizeLabel;
+
+ private ImprovedFormattedTextField[] minFields;
+ private ImprovedFormattedTextField[] sizeFields;
+ private ImprovedFormattedTextField[] spacingFields;
+ private ImprovedFormattedTextField[] pixelFields;
+
+ public FieldOfViewPanel( final LandmarkTableModel ltm, final BigWarpTransform bwTransform, final String unit, final int textFieldWidth,
+ final double[] initMin, final double[] initSpacing, final long[] initPixsize )
+ {
+ this( null, ltm, bwTransform, unit, textFieldWidth, initMin, initSpacing, initPixsize );
+ }
+
+ public FieldOfViewPanel( final BigWarpData> data, final LandmarkTableModel ltm, final BigWarpTransform bwTransform, final String unit, final int textFieldWidth,
+ final double[] initMin, final double[] initSpacing, final long[] initPixsize )
+ {
+ super();
+
+ this.data = data;
+ this.ltm = ltm;
+ this.bwTransform = bwTransform;
+
+ this.ndims = ltm != null ? ltm.getNumdims() : 3;
+ this.unit = unit;
+ this.textFieldWidth = textFieldWidth;
+
+ this.initMin = initMin;
+ this.initSpacing = initSpacing;
+ this.initPixsize = initPixsize;
+
+ this.min = new double[ ndims ];
+ System.arraycopy( initMin, 0, min, 0, ndims );
+
+ this.spacing = new double[ ndims ];
+ System.arraycopy( initSpacing, 0, spacing, 0, ndims );
+
+ this.pixSize = new long[ ndims ];
+ System.arraycopy( initPixsize, 0, pixSize, 0, ndims );
+
+ this.size = new double[ ndims ];
+ for ( int i = 0; i < ndims; i++ )
+ size[ i ] = spacing[ i ] * pixSize[ i ];
+
+ create();
+ }
+
+ public double[] getMin()
+ {
+ return min;
+ }
+
+ /**
+ * Sets the minimum value, does not trigger callbacks.
+ *
+ * @param newMin the new min val
+ */
+ public void setMin( final double[] newMin )
+ {
+ final int N = newMin.length > min.length ? min.length : newMin.length;
+ for ( int i = 0; i < N; i++ )
+ {
+ minFields[ i ].setValue( new Double( newMin[ i ] ), false );
+ min[ i ] = newMin[ i ];
+ }
+ }
+
+ public double[] getSpacing()
+ {
+ return spacing;
+ }
+
+ /**
+ * Sets the spacing, does not trigger callbacks
+ *
+ * @param newSpacing the new spacing
+ */
+ public void setSpacing( final double[] newSpacing )
+ {
+ final int N = newSpacing.length > spacing.length ? spacing.length : newSpacing.length;
+ for ( int i = 0; i < N; i++ )
+ {
+ spacingFields[ i ].setValue( new Double( newSpacing[ i ] ), false );
+ spacing[ i ] = newSpacing[ i ];
+ }
+ }
+
+ public double[] getPhysicalSize()
+ {
+ return size;
+ }
+
+ public long[] getPixelSize()
+ {
+ return pixSize;
+ }
+
+ /**
+ * Sets the pixel size, does not trigger callbacks.
+ *
+ * @param newSize the new discrete image size (in pixels)
+ */
+ public void setPixelSize( final long[] newSize )
+ {
+ final int N = newSize.length > pixSize.length ? pixSize.length : newSize.length;
+ for ( int i = 0; i < N; i++ )
+ {
+ pixelFields[i].setValue( new Long(newSize[i]), false );
+ pixSize[ i ] = newSize[ i ];
+ }
+ }
+
+ public String getUnit()
+ {
+ return unitField.getText();
+ }
+
+ public void setUnit( String unit )
+ {
+ unitField.setText( unit );
+ updateSizeLabel();
+ }
+
+ private void updateSizeLabel()
+ {
+ sizeLabel.setText( String.format( "size (%s)", unitField.getText() ) );
+ }
+
+ public void create()
+ {
+ setLayout(new GridBagLayout());
+
+ final GridBagConstraints gbc = new GridBagConstraints();
+ gbc.gridx = 0;
+ gbc.gridy = 0;
+ gbc.gridwidth = 1;
+ gbc.gridheight = 1;
+ gbc.weightx = 0.0;
+ gbc.weighty = 0.0;
+ gbc.anchor = GridBagConstraints.CENTER;
+ gbc.fill = GridBagConstraints.NONE;
+ gbc.insets = new Insets( DEFAULT_OUTER_PAD, DEFAULT_OUTER_PAD, DEFAULT_MID_PAD, DEFAULT_BUTTON_PAD );
+
+ int j = 1;
+ if( data != null )
+ {
+ add( new JLabel( "reference:" ), gbc );
+
+ final String[] fovOpts = new String[]{
+ ApplyBigwarpPlugin.SPECIFIED,
+ ApplyBigwarpPlugin.TARGET,
+ ApplyBigwarpPlugin.MOVING_WARPED,
+ ApplyBigwarpPlugin.LANDMARK_POINTS };
+ referenceComboBox = new JComboBox<>( fovOpts );
+ referenceComboBox.setSelectedItem( ApplyBigwarpPlugin.MOVING_WARPED );
+ referenceComboBox.addActionListener( e -> {
+ updateFieldsFromReference();
+ });
+
+ gbc.gridx = 1;
+ add( referenceComboBox, gbc );
+
+ j = 2;
+ }
+ else if( IJ.getInstance() != null )
+ {
+ add( new JLabel( "reference:" ), gbc );
+
+ final String[] impTitles = getImagePlusTitles();
+ int numImp = 0;
+ if( impTitles != null )
+ numImp = impTitles.length;
+
+ final String[] fovOpts = new String[ numImp + 1 ];
+ fovOpts[ 0 ] = ApplyBigwarpPlugin.SPECIFIED;
+ for( int i = 0; i < numImp; i++ )
+ fovOpts[ i + 1 ] = impTitles[ i ];
+
+ referenceComboBox = new JComboBox<>( fovOpts );
+ if( impTitles.length > 0 )
+ referenceComboBox.setSelectedIndex( 1 );
+
+ referenceComboBox.addActionListener( e -> {
+ updateFieldsFromImageJReference();
+ });
+
+ gbc.gridx = 1;
+ add( referenceComboBox, gbc );
+
+ j = 2;
+ }
+
+ final JLabel unitLabel = new JLabel( "units:" );
+ gbc.gridx = 2;
+ gbc.anchor = GridBagConstraints.LINE_END;
+ add( unitLabel, gbc );
+
+ gbc.gridx = 3;
+ gbc.fill = GridBagConstraints.HORIZONTAL;
+ gbc.anchor = GridBagConstraints.CENTER;
+ unitField = new JTextField("pixel");
+ unitField.getDocument().addDocumentListener( new DocumentListener() {
+
+ @Override
+ public void changedUpdate( DocumentEvent e ) { }
+
+ @Override
+ public void insertUpdate( DocumentEvent e )
+ {
+ updateSizeLabel();
+ }
+
+ @Override
+ public void removeUpdate( DocumentEvent e )
+ {
+ updateSizeLabel();
+ }
+ });
+ add( unitField, gbc );
+
+ final JLabel minLabel = new JLabel( String.format("min (%s)", unit ));
+ sizeLabel = new JLabel( String.format( "size (%s)", unit ) );
+ final JLabel spacingLabel = new JLabel( String.format( "spacing (%s/px)", unit ));
+ final JLabel pixelLabel = new JLabel( "size (px)" );
+
+ gbc.gridy++;
+ gbc.gridx = 1;
+ add( minLabel, gbc );
+ gbc.gridx = 2;
+ add( sizeLabel, gbc );
+ gbc.gridx = 3;
+ add( spacingLabel, gbc );
+ gbc.gridx = 4;
+ add( pixelLabel, gbc );
+
+ final JLabel yLabel = new JLabel("y");
+
+ gbc.gridx = 0;
+ gbc.gridy++;
+ gbc.anchor = GridBagConstraints.LINE_END;
+
+ final JLabel xLabel = new JLabel("x");
+ add( xLabel, gbc );
+
+ gbc.gridy++;
+ add( yLabel, gbc );
+
+ if( ndims >= 3 )
+ {
+ gbc.gridy++;
+ final JLabel zLabel = new JLabel("z");
+ add( zLabel, gbc );
+ }
+
+ /*
+ * TODO investigate focus traversal, see:
+ * https://docs.oracle.com/javase/tutorial/uiswing/misc/focus.html#customFocusTraversal
+ */
+
+ minFields = new ImprovedFormattedTextField[ ndims ];
+ sizeFields = new ImprovedFormattedTextField[ ndims ];
+ spacingFields = new ImprovedFormattedTextField[ ndims ];
+ pixelFields = new ImprovedFormattedTextField[ ndims ];
+ gbc.fill = GridBagConstraints.HORIZONTAL;
+
+ final int textWidthScaled = UIScale.scale( textFieldWidth );
+ final int textHeight = UIScale.scale( 20 );
+ final Dimension textFieldSize = new Dimension( textWidthScaled, textHeight );
+ final DecimalFormat decimalFormat = new DecimalFormat();
+ decimalFormat.setMaximumFractionDigits( 8 );
+
+ // add fields
+ for( int i = 0; i < ndims; i++ )
+ {
+ gbc.gridy = i + j;
+
+ gbc.gridx = 1;
+ minFields[ i ] = new ImprovedFormattedTextField( decimalFormat );
+ minFields[ i ].setPreferredSize( textFieldSize );
+ minFields[ i ].setHorizontalAlignment( JTextField.RIGHT );
+ minFields[ i ].setValueNoCallback( new Double( initMin[i]) );
+ add( minFields[ i ], gbc );
+
+ gbc.gridx = 2;
+ sizeFields[ i ] = new ImprovedFormattedTextField( decimalFormat );
+ sizeFields[ i ].setPreferredSize( textFieldSize );
+ sizeFields[ i ].setHorizontalAlignment( JTextField.RIGHT );
+ sizeFields[ i ].setValueNoCallback( new Double( initSpacing[ i ] * initPixsize[ i ] ) );
+ add( sizeFields[ i ], gbc );
+
+ gbc.gridx = 3;
+ spacingFields[ i ] = new ImprovedFormattedTextField( decimalFormat );
+ spacingFields[ i ].setPreferredSize( textFieldSize );
+ spacingFields[ i ].setHorizontalAlignment( JTextField.RIGHT );
+ spacingFields[ i ].setValueNoCallback( new Double( initSpacing[ i ] ) );
+
+ add( spacingFields[ i ], gbc );
+
+ gbc.gridx = 4;
+ pixelFields[ i ] = new ImprovedFormattedTextField( NumberFormat.getIntegerInstance());
+ pixelFields[ i ].setPreferredSize( textFieldSize );
+ pixelFields[ i ].setHorizontalAlignment( JTextField.RIGHT );
+ pixelFields[ i ].setValueNoCallback( new Long( initPixsize[ i ] ) );
+ add( pixelFields[ i ], gbc );
+
+ // set callbacks for fields
+ // what fields update others when modified
+ final int idx = i;
+ minFields[ i ].setCallback( () -> {
+// System.out.println("min callback");
+ try {
+ min[ idx ] = Double.parseDouble( minFields[ idx ].getText() );
+ } catch (final NumberFormatException e) {}
+ });
+
+ sizeFields[ i ].setCallback( () -> {
+// System.out.println("size callback");
+ try {
+ size[ idx ] = Double.parseDouble( sizeFields[ idx ].getText() );
+ updatePixelsFromSize( idx );
+ } catch (final NumberFormatException e) {}
+ });
+
+ spacingFields[ i ].setCallback( () -> {
+// System.out.println("spacing callback");
+ try {
+ spacing[ idx ] = Double.parseDouble( spacingFields[ idx ].getText() );
+// updatePixelsFromSpacing( idx );
+ updateSize( idx );
+ } catch (final NumberFormatException e) {}
+ });
+
+ pixelFields[ i ].setCallback( () -> {
+// System.out.println("pix sz callback");
+ try {
+ pixSize[ idx ] = Long.parseLong( pixelFields[ idx ].getText() );
+ updateSize( idx );
+ } catch (final NumberFormatException e) {}
+// updateSpacingFromPixels( idx ); // an alternative update
+ });
+ }
+ }
+
+ protected void updatePixelsFromSize( int i )
+ {
+ pixSize[ i ] = ( long ) Math.ceil( size[ i ] / spacing[ i ] );
+ SwingUtilities.invokeLater( () -> { pixelFields[ i ].setValueNoCallback( new Double( pixSize[ i ] ) ); });
+ }
+
+ protected void updatePixelsFromSpacing( int i )
+ {
+ pixSize[ i ] = ( long ) Math.floor( size[ i ] / spacing[ i ] );
+ SwingUtilities.invokeLater( () -> { pixelFields[ i ].setValueNoCallback( new Long( pixSize[ i ] ) ); });
+ }
+
+ protected void updateSpacingFromPixels( int i )
+ {
+ spacing[ i ] = size[ i ] / pixSize[ i ];
+ SwingUtilities.invokeLater( () -> { spacingFields[ i ].setValueNoCallback( new Double( spacing[ i ] ) ); });
+ }
+
+ protected void updateSize( int i )
+ {
+ size[ i ] = spacing[ i ] * pixSize[ i ];
+ SwingUtilities.invokeLater( () -> { sizeFields[ i ].setValueNoCallback( new Double( size[ i ] ) ); });
+ }
+
+ protected void updateFieldsFromReference()
+ {
+ if ( data == null || bwTransform == null )
+ return;
+
+ final String referenceOption = ( String ) referenceComboBox.getSelectedItem();
+ if ( referenceOption.equals( ApplyBigwarpPlugin.SPECIFIED ) )
+ return;
+
+ final double[] res = ApplyBigwarpPlugin.getResolution( data, referenceOption, null );
+ if ( res != null )
+ setSpacing( res );
+
+ final List< Interval > itvl = ApplyBigwarpPlugin.getPixelInterval( data, ltm, bwTransform.getTransformation( false ),
+ referenceOption, "", new BoundingBoxEstimation(), null, null, getSpacing() );
+
+ final double[] offset = ApplyBigwarpPlugin.getPixelOffset( referenceOption, null, res, itvl.get( 0 ) );
+
+ setPixelSize( itvl.get( 0 ).dimensionsAsLongArray() );
+ setMin( offset );
+ setUnit( ApplyBigwarpPlugin.getUnit( data, referenceOption ));
+
+ for ( int i = 0; i < size.length; i++ )
+ updateSize( i );
+ }
+
+ protected String[] getImagePlusTitles()
+ {
+ if( IJ.getInstance() != null )
+ {
+ return WindowManager.getImageTitles();
+ }
+ else
+ return null;
+ }
+
+ protected void updateFieldsFromImageJReference()
+ {
+ final String referenceOption = (String)referenceComboBox.getSelectedItem();
+ if( referenceOption.equals( ApplyBigwarpPlugin.SPECIFIED ))
+ return;
+
+ if( IJ.getInstance() != null )
+ {
+ final ImagePlus refImp = WindowManager.getImage( (String)referenceComboBox.getSelectedItem() );
+ setSpacing( new double[] {
+ refImp.getCalibration().pixelWidth,
+ refImp.getCalibration().pixelHeight,
+ refImp.getCalibration().pixelDepth,
+ });
+
+ setMin( new double[] {
+ refImp.getCalibration().xOrigin,
+ refImp.getCalibration().yOrigin,
+ refImp.getCalibration().zOrigin,
+ });
+
+ setPixelSize( new long[] {
+ refImp.getWidth(),
+ refImp.getHeight(),
+ refImp.getNSlices()
+ });
+
+ setUnit( refImp.getCalibration().getUnit() );
+ }
+ }
+
+// public static void main( String[] args ) throws IOException, URISyntaxException, SpimDataException
+// {
+// final JFrame frame = new JFrame( "fov" );
+// BigWarpData data = new BigWarpData();
+//
+// int id = 0;
+// BigWarpInit.add( data, BigWarpInit.createSources( data, "/home/john/tmp/mri-stack_mm.tif", id++, true ) );
+// BigWarpInit.add( data, BigWarpInit.createSources( data, "/home/john/tmp/mri-stack_mm.tif", id++, false ) );
+//
+//// LandmarkTableModel ltm = LandmarkTableModel.loadFromCsv( new File("/home/john/tmp/mri-stack-landmarks.csv"), false );
+// LandmarkTableModel ltm = LandmarkTableModel.loadFromCsv( new File("/home/john/tmp/mri-stack-mm-landmarks.csv"), false );
+// BigWarpTransform bwTransform = new BigWarpTransform( ltm, BigWarpTransform.TPS );
+//
+// final FieldOfViewPanel panel = new FieldOfViewPanel( data, ltm, bwTransform, "mm", 150,
+// new double[] { 0, 0, 0 }, new double[] { 1, 1, 1 }, new long[] { 300, 200, 100 } );
+//
+// frame.add( panel );
+// frame.pack();
+// frame.setVisible( true );
+// }
+
+}
diff --git a/src/main/java/bdv/gui/ImprovedFormattedTextField.java b/src/main/java/bdv/gui/ImprovedFormattedTextField.java
new file mode 100644
index 00000000..790338ee
--- /dev/null
+++ b/src/main/java/bdv/gui/ImprovedFormattedTextField.java
@@ -0,0 +1,308 @@
+package bdv.gui;
+
+import javax.swing.JFormattedTextField;
+import javax.swing.JTextField;
+import javax.swing.KeyStroke;
+import javax.swing.SwingUtilities;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import java.awt.Color;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
+import java.awt.event.KeyEvent;
+import java.text.Format;
+import java.text.ParseException;
+import java.text.AttributedCharacterIterator;
+import java.text.FieldPosition;
+import java.text.ParsePosition;
+
+/**
+ *
+ * Extension of {@code JFormattedTextField} which solves some of the usability
+ * issues
+ *
+ *
+ *
+ * from
+ * https://stackoverflow.com/questions/1313390/is-there-any-way-to-accept-only-numeric-values-in-a-jtextfield?answertab=scoredesc#tab-top
+ *
+ */
+public class ImprovedFormattedTextField extends JFormattedTextField
+{
+ private static final Color ERROR_BACKGROUND_COLOR = new Color( 255, 215, 215 );
+
+ private static final Color ERROR_FOREGROUND_COLOR = null;
+
+ private Color fBackground, fForeground;
+
+ private Runnable updateCallback;
+
+ private boolean runCallback = true;
+
+ /**
+ * Create a new {@code ImprovedFormattedTextField} instance which will use
+ * {@code aFormat} for the validation of the user input.
+ *
+ * @param aFormat
+ * The format. May not be {@code null}
+ */
+ public ImprovedFormattedTextField( Format aFormat )
+ {
+ // use a ParseAllFormat as we do not want to accept user input which is
+ // partially valid
+ super( new ParseAllFormat( aFormat ) );
+ setFocusLostBehavior( JFormattedTextField.COMMIT_OR_REVERT );
+ updateBackgroundOnEachUpdate();
+ // improve the caret behavior
+ // see also
+ // http://tips4java.wordpress.com/2010/02/21/formatted-text-field-tips/
+ addFocusListener( new MousePositionCorrectorListener() );
+ }
+
+ /**
+ * Create a new {@code ImprovedFormattedTextField} instance which will use
+ * {@code aFormat} for the validation of the user input. The field will be
+ * initialized with {@code aValue}.
+ *
+ * @param aFormat
+ * The format. May not be {@code null}
+ * @param aValue
+ * The initial value
+ */
+ public ImprovedFormattedTextField( Format aFormat, Object aValue )
+ {
+ this( aFormat );
+ setValue( aValue );
+ }
+
+ public void setCallback(final Runnable updateCallback) {
+
+ this.updateCallback = updateCallback;
+ }
+
+ private void updateBackgroundOnEachUpdate()
+ {
+ getDocument().addDocumentListener( new DocumentListener()
+ {
+ @Override
+ public void insertUpdate( DocumentEvent e )
+ {
+ updateBackground();
+ if( runCallback && updateCallback != null )
+ updateCallback.run();
+ }
+
+ @Override
+ public void removeUpdate( DocumentEvent e )
+ {
+ updateBackground();
+ if( runCallback && updateCallback != null )
+ updateCallback.run();
+ }
+
+ @Override
+ public void changedUpdate( DocumentEvent e )
+ {
+ updateBackground();
+ if( runCallback && updateCallback != null )
+ updateCallback.run();
+ }
+ } );
+ }
+
+ /**
+ * Update the background color depending on the valid state of the current
+ * input. This provides visual feedback to the user
+ */
+ private void updateBackground()
+ {
+ final boolean valid = validContent();
+ if ( ERROR_BACKGROUND_COLOR != null )
+ {
+ setBackground( valid ? fBackground : ERROR_BACKGROUND_COLOR );
+ }
+ if ( ERROR_FOREGROUND_COLOR != null )
+ {
+ setForeground( valid ? fForeground : ERROR_FOREGROUND_COLOR );
+ }
+ }
+
+ @Override
+ public void updateUI()
+ {
+ super.updateUI();
+ fBackground = getBackground();
+ fForeground = getForeground();
+ }
+
+ private boolean validContent()
+ {
+ final AbstractFormatter formatter = getFormatter();
+ if ( formatter != null )
+ {
+ try
+ {
+ formatter.stringToValue( getText() );
+ return true;
+ }
+ catch ( final ParseException e )
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public void setValue( Object value, boolean callback )
+ {
+ boolean validValue = true;
+ // before setting the value, parse it by using the format
+ try
+ {
+ final AbstractFormatter formatter = getFormatter();
+ if ( formatter != null )
+ {
+ formatter.valueToString( value );
+ }
+ }
+ catch ( final ParseException e )
+ {
+ validValue = false;
+ updateBackground();
+ }
+ // only set the value when valid
+ if ( validValue )
+ {
+ final int old_caret_position = getCaretPosition();
+
+ // TODO synchronize?
+ final boolean before = runCallback;
+ runCallback = callback;
+ super.setValue( value );
+ runCallback = before;
+
+ setCaretPosition( Math.min( old_caret_position, getText().length() ) );
+ }
+ }
+
+ public void setValueNoCallback( Object value )
+ {
+ setValue( value, false );
+ }
+
+ @Override
+ public void setValue( Object value )
+ {
+ setValue( value, true );
+ }
+
+ @Override
+ protected boolean processKeyBinding( KeyStroke ks, KeyEvent e, int condition, boolean pressed )
+ {
+ // do not let the formatted text field consume the enters. This allows
+ // to trigger an OK button by
+ // pressing enter from within the formatted text field
+ if ( validContent() )
+ {
+ return super.processKeyBinding( ks, e, condition, pressed ) && ks != KeyStroke.getKeyStroke( KeyEvent.VK_ENTER, 0 );
+ }
+ else
+ {
+ return super.processKeyBinding( ks, e, condition, pressed );
+ }
+ }
+
+ private static class MousePositionCorrectorListener extends FocusAdapter
+ {
+ @Override
+ public void focusGained( FocusEvent e )
+ {
+ /*
+ * After a formatted text field gains focus, it replaces its text
+ * with its current value, formatted appropriately of course. It
+ * does this after any focus listeners are notified. We want to make
+ * sure that the caret is placed in the correct position rather than
+ * the dumb default that is before the 1st character !
+ */
+ final JTextField field = ( JTextField ) e.getSource();
+ final int dot = field.getCaret().getDot();
+ final int mark = field.getCaret().getMark();
+ if ( field.isEnabled() && field.isEditable() )
+ {
+ SwingUtilities.invokeLater( new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ // Only set the caret if the textfield hasn't got a
+ // selection on it
+ if ( dot == mark )
+ {
+ field.getCaret().setDot( dot );
+ }
+ }
+ } );
+ }
+ }
+ }
+
+ /**
+ *
+ * Decorator for a {@link Format Format} which only accepts values which can
+ * be completely parsed by the delegate format. If the value can only be
+ * partially parsed, the decorator will refuse to parse the value.
+ *
+ */
+ public static class ParseAllFormat extends Format
+ {
+ private final Format fDelegate;
+
+ /**
+ * Decorate aDelegate to make sure if parser everything or
+ * nothing
+ *
+ * @param aDelegate
+ * The delegate format
+ */
+ public ParseAllFormat( Format aDelegate )
+ {
+ fDelegate = aDelegate;
+ }
+
+ @Override
+ public StringBuffer format( Object obj, StringBuffer toAppendTo, FieldPosition pos )
+ {
+ return fDelegate.format( obj, toAppendTo, pos );
+ }
+
+ @Override
+ public AttributedCharacterIterator formatToCharacterIterator( Object obj )
+ {
+ return fDelegate.formatToCharacterIterator( obj );
+ }
+
+ @Override
+ public Object parseObject( String source, ParsePosition pos )
+ {
+ final int initialIndex = pos.getIndex();
+ final Object result = fDelegate.parseObject( source, pos );
+ if ( result != null && pos.getIndex() < source.length() )
+ {
+ final int errorIndex = pos.getIndex();
+ pos.setIndex( initialIndex );
+ pos.setErrorIndex( errorIndex );
+ return null;
+ }
+ return result;
+ }
+
+ @Override
+ public Object parseObject( String source ) throws ParseException
+ {
+ // no need to delegate the call, super will call the parseObject(
+ // source, pos ) method
+ return super.parseObject( source );
+ }
+ }
+
+}
diff --git a/src/main/java/bdv/gui/MaskOptionsPanel.java b/src/main/java/bdv/gui/MaskOptionsPanel.java
new file mode 100644
index 00000000..9117f2de
--- /dev/null
+++ b/src/main/java/bdv/gui/MaskOptionsPanel.java
@@ -0,0 +1,209 @@
+package bdv.gui;
+
+import bigwarp.BigWarp;
+import bigwarp.source.PlateauSphericalMaskRealRandomAccessible.FalloffShape;
+import bigwarp.source.PlateauSphericalMaskSource;
+import bigwarp.transforms.BigWarpTransform;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import javax.swing.BorderFactory;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import bdv.ui.convertersetupeditor.MaskBoundsRangePanel;
+
+public class MaskOptionsPanel extends JPanel
+{
+ private static final long serialVersionUID = -8614381106547838575L;
+
+ private static final String FALLOFF_HELP_TEXT = "Controls the shape of the mask in the transition region.";
+ private static final String AUTO_ESTIMATE_HELP_TEXT = "If selected, the mask location and size will be dynamically updated as you add landmarks.";
+ private static final String SHOW_MASK_OVERLAY_HELP_TEXT = "Toggles the visibility of the mask overlay";
+ private static final String INTERPOLATION_HELP_TEXT = "Controls whether a mask is applied to the transformation and how the transformation changes "
+ + "in the mask transition region.\n"
+ + "If your transformation has lots of rotation, try selecting \"ROTATION\" or \"SIMILARITY\".";
+
+ public static final String[] maskTypes = new String[] {
+ BigWarpTransform.NO_MASK_INTERP,
+ BigWarpTransform.MASK_INTERP,
+ BigWarpTransform.ROT_MASK_INTERP,
+ BigWarpTransform.SIM_MASK_INTERP,
+ };
+
+ private final BigWarp< ? > bw;
+
+ private final JCheckBox autoEstimateMaskButton;
+ private final JCheckBox showMaskOverlayButton;
+
+ private final JLabel falloffTypeLabel;
+ private final JComboBox< FalloffShape > falloffTypeDropdown;
+
+ private final JLabel maskTypeLabel;
+ private final JComboBox< String > maskTypeDropdown;
+
+ private final MaskBoundsRangePanel maskRangePanel;
+
+ private ActionListener falloffListener;
+
+ public MaskOptionsPanel( BigWarp> bw )
+ {
+ super( new GridBagLayout() );
+ this.bw = bw;
+
+ setBorder( BorderFactory.createCompoundBorder(
+ BorderFactory.createEmptyBorder( 4, 2, 4, 2 ),
+ BorderFactory.createCompoundBorder(
+ BorderFactory.createTitledBorder(
+ BorderFactory.createEtchedBorder(),
+ "Mask options" ),
+ BorderFactory.createEmptyBorder( 2, 2, 2, 2 ) ) ) );
+
+ autoEstimateMaskButton = new JCheckBox( "Auto-estimate mask", true );
+ autoEstimateMaskButton.setToolTipText( AUTO_ESTIMATE_HELP_TEXT );
+
+ showMaskOverlayButton = new JCheckBox( "Show mask overlay", true );
+ showMaskOverlayButton.setToolTipText( SHOW_MASK_OVERLAY_HELP_TEXT );
+
+
+ falloffTypeLabel = new JLabel( "Mask falloff");
+ falloffTypeLabel.setToolTipText( FALLOFF_HELP_TEXT );
+ falloffTypeDropdown = new JComboBox<>( FalloffShape.values() );
+ falloffTypeDropdown.setToolTipText( FALLOFF_HELP_TEXT );
+
+ maskTypeLabel = new JLabel( "Mask interpolation");
+ maskTypeLabel.setToolTipText( INTERPOLATION_HELP_TEXT );
+ maskTypeDropdown = new JComboBox<>( maskTypes );
+ maskTypeDropdown.setToolTipText( INTERPOLATION_HELP_TEXT );
+
+ maskRangePanel = new MaskBoundsRangePanel(bw);
+
+ // layout
+ final GridBagConstraints gbc = new GridBagConstraints();
+ gbc.gridx = 0;
+ gbc.gridy = 0;
+ gbc.gridwidth = 1;
+ gbc.gridheight = 1;
+ gbc.weightx = 0.0;
+ gbc.weighty = 0.0;
+ gbc.anchor = GridBagConstraints.LINE_END;
+ gbc.fill = GridBagConstraints.NONE;
+ gbc.insets = new Insets( 5, 5, 5, 5 );
+ add( maskTypeLabel, gbc );
+
+ gbc.gridx = 2;
+ gbc.weightx = 0.0;
+ gbc.anchor = GridBagConstraints.LINE_START;
+ add( maskTypeDropdown, gbc );
+
+ gbc.gridx = 0;
+ gbc.gridy = 1;
+ gbc.anchor = GridBagConstraints.LINE_END;
+ add( falloffTypeLabel, gbc );
+
+ gbc.gridx = 2;
+ gbc.anchor = GridBagConstraints.LINE_START;
+ add( falloffTypeDropdown, gbc );
+
+ gbc.gridx = 0;
+ gbc.gridy = 2;
+ gbc.anchor = GridBagConstraints.LINE_END;
+ add( autoEstimateMaskButton, gbc );
+
+ gbc.gridx = 2;
+ gbc.gridy = 2;
+ gbc.anchor = GridBagConstraints.LINE_START;
+ add( showMaskOverlayButton, gbc );
+
+ gbc.gridx = 0;
+ gbc.gridy = 3;
+ gbc.anchor = GridBagConstraints.LINE_START;
+ add( new JLabel("Imported mask intensity range:"), gbc );
+
+ gbc.gridy = 4;
+ gbc.gridwidth = 3;
+ gbc.fill = GridBagConstraints.HORIZONTAL;
+ gbc.anchor = GridBagConstraints.LINE_START;
+ add( maskRangePanel, gbc );
+ }
+
+ public void addActions()
+ {
+ autoEstimateMaskButton.addActionListener( e -> {
+ if ( autoEstimateMaskButton.isSelected() )
+ bw.autoEstimateMask();
+ } );
+
+ showMaskOverlayButton.addActionListener( e -> {
+ bw.setMaskOverlayVisibility( showMaskOverlayButton.isSelected() && isMask() );
+ } );
+
+ maskTypeDropdown.addActionListener( e -> {
+ bw.updateTransformMask();
+ } );
+ }
+
+ public void setMask( PlateauSphericalMaskSource maskSource )
+ {
+ if( falloffListener == null )
+ falloffTypeDropdown.removeActionListener( falloffListener );
+
+ falloffTypeDropdown.addActionListener( new ActionListener() {
+ @Override
+ public void actionPerformed( ActionEvent e )
+ {
+ maskSource.getRandomAccessible().setFalloffShape( (FalloffShape)falloffTypeDropdown.getSelectedItem() );
+ bw.getViewerFrameP().getViewerPanel().requestRepaint();
+ bw.getViewerFrameQ().getViewerPanel().requestRepaint();
+ }
+ });
+
+ }
+
+ public JCheckBox getAutoEstimateMaskButton()
+ {
+ return autoEstimateMaskButton;
+ }
+
+ public JCheckBox getShowMaskOverlayButton()
+ {
+ return showMaskOverlayButton;
+ }
+
+ public JComboBox< String > getMaskTypeDropdown()
+ {
+ return maskTypeDropdown;
+ }
+
+ public JComboBox< FalloffShape > getMaskFalloffTypeDropdown()
+ {
+ return falloffTypeDropdown;
+ }
+
+ public MaskBoundsRangePanel getMaskRangeSlider()
+ {
+ return maskRangePanel;
+ }
+
+ public String getType()
+ {
+ return ( String ) maskTypeDropdown.getSelectedItem();
+ }
+
+ /*
+ * @return true if a mask is applied to the transformation
+ */
+ public boolean isMask()
+ {
+ return maskTypeDropdown.getSelectedIndex() > 0;
+ }
+
+ public boolean showMaskOverlay()
+ {
+ return showMaskOverlayButton.isSelected();
+ }
+}
diff --git a/src/main/java/bdv/gui/MaskedSourceEditorMouseListener.java b/src/main/java/bdv/gui/MaskedSourceEditorMouseListener.java
new file mode 100644
index 00000000..dba48386
--- /dev/null
+++ b/src/main/java/bdv/gui/MaskedSourceEditorMouseListener.java
@@ -0,0 +1,213 @@
+package bdv.gui;
+
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.awt.event.MouseWheelEvent;
+import java.awt.event.MouseWheelListener;
+import java.util.ArrayList;
+import java.util.List;
+
+import bdv.util.Affine3DHelpers;
+import bdv.viewer.BigWarpViewerPanel;
+import bdv.viewer.overlay.BigWarpMaskSphereOverlay;
+import bigwarp.BigWarp;
+import bigwarp.source.PlateauSphericalMaskRealRandomAccessible;
+import bigwarp.transforms.AbstractTransformSolver;
+import bigwarp.transforms.MaskedSimRotTransformSolver;
+import net.imglib2.RealPoint;
+import net.imglib2.realtransform.AffineTransform3D;
+
+public class MaskedSourceEditorMouseListener implements MouseListener, MouseMotionListener, MouseWheelListener
+{
+ protected PlateauSphericalMaskRealRandomAccessible mask;
+ protected BigWarpViewerPanel viewer;
+ protected List overlays;
+
+ private boolean active;
+ private boolean dragged = false;
+
+ private RealPoint p;
+ private RealPoint c;
+ private RealPoint pressPt;
+
+ private BigWarp> bw;
+
+ private static final double fastSpeed = 10.0;
+ private static final double slowSpeed = 0.1;
+
+ public MaskedSourceEditorMouseListener( int nd, BigWarp> bw, BigWarpViewerPanel viewer )
+ {
+ this.bw = bw;
+ this.viewer = viewer;
+
+ this.viewer.getDisplay().addMouseListener( this );
+ this.viewer.getDisplay().addMouseWheelListener( this );
+ this.viewer.getDisplay().addMouseMotionListener( this );
+
+ overlays = new ArrayList();
+ overlays.add( bw.getViewerFrameP().getViewerPanel().getMaskOverlay() );
+ overlays.add( bw.getViewerFrameQ().getViewerPanel().getMaskOverlay() );
+
+ p = new RealPoint( 3 );
+ c = new RealPoint( 3 );
+ pressPt = new RealPoint( 3 );
+ active = false;
+ }
+
+ public void setActive( boolean active )
+ {
+ this.active = active;
+ bw.getViewerFrameP().setTransformEnabled( !active );
+ bw.getViewerFrameQ().setTransformEnabled( !active );
+
+ final String msg = active ? "Mask Edit On" : "Mask Edit Off";
+ bw.getViewerFrameP().getViewerPanel().showMessage( msg );
+ bw.getViewerFrameQ().getViewerPanel().showMessage( msg );
+ }
+
+ public void toggleActive( )
+ {
+ setActive( !active );
+ }
+
+ public void setMask( PlateauSphericalMaskRealRandomAccessible mask )
+ {
+ this.mask = mask;
+ }
+
+ public PlateauSphericalMaskRealRandomAccessible getMask()
+ {
+ return mask;
+ }
+
+ @Override
+ public void mouseClicked( MouseEvent e ) { }
+
+ @Override
+ public void mouseEntered( MouseEvent e ) { }
+
+ @Override
+ public void mouseExited( MouseEvent e ) { }
+
+ @Override
+ public void mousePressed( MouseEvent e )
+ {
+ if( !active )
+ return;
+
+ dragged = false;
+ }
+
+ @Override
+ public void mouseMoved( MouseEvent e ) { }
+
+ @Override
+ public void mouseDragged( MouseEvent e )
+ {
+ if( !active )
+ return;
+
+ // store starting center at start of drag
+ if( !dragged )
+ {
+// c.setPosition( mask.getCenter() );
+ mask.getCenter().localize( c );
+ viewer.getGlobalMouseCoordinates( pressPt );
+ dragged = true;
+ }
+
+ viewer.getGlobalMouseCoordinates( p );
+ bw.setAutoEstimateMask( false );
+
+ if( e.isControlDown() )
+ {
+ mask.getCenter().localize( c );
+ final double d = PlateauSphericalMaskRealRandomAccessible.squaredDistance( p, c );
+ synchronized ( mask )
+ {
+ mask.setSquaredRadius( d );
+ }
+ }
+ else if( e.isShiftDown() )
+ {
+ mask.getCenter().localize( c );
+ final double d = Math.sqrt( PlateauSphericalMaskRealRandomAccessible.squaredDistance( p, c ));
+ synchronized ( mask )
+ {
+ mask.setSigma( d - Math.sqrt( mask.getSquaredRadius()) );
+ }
+ }
+ else
+ {
+ // p - pressPt inside the setPosition is the delta
+ // c is the original center (before dragging started)
+ // the code below sets the mask center to (c + delta)
+ for( int i = 0; i < p.numDimensions(); i++ )
+ p.setPosition( c.getDoublePosition(i) + p.getDoublePosition(i) - pressPt.getDoublePosition(i), i);
+
+ synchronized ( mask )
+ {
+ mask.setCenter(p);
+ }
+
+ AbstractTransformSolver< ? > solver = bw.getBwTransform().getSolver();
+ if( solver instanceof MaskedSimRotTransformSolver )
+ {
+ ((MaskedSimRotTransformSolver)solver).setCenter( p );
+ }
+ }
+
+ bw.getViewerFrameP().getViewerPanel().requestRepaint();
+ bw.getViewerFrameQ().getViewerPanel().requestRepaint();
+ }
+
+ @Override
+ public void mouseReleased( MouseEvent e ) {
+
+ if( !active )
+ return;
+
+ if( e.isControlDown() || e.isShiftDown() )
+ return;
+
+ if( !dragged )
+ {
+ viewer.getGlobalMouseCoordinates( pressPt );
+ synchronized ( mask )
+ {
+ mask.setCenter( pressPt );
+ }
+
+ bw.setAutoEstimateMask( false );
+ bw.getViewerFrameP().getViewerPanel().requestRepaint();
+ bw.getViewerFrameQ().getViewerPanel().requestRepaint();
+ }
+ else
+ dragged = false;
+ }
+
+ @Override
+ public void mouseWheelMoved( MouseWheelEvent e )
+ {
+ if( !active )
+ return;
+
+ bw.setAutoEstimateMask( false );
+ final AffineTransform3D transform = viewer.state().getViewerTransform();
+ final double scale = (1.0 / (Affine3DHelpers.extractScale(transform, 0) + 1e-9 ) + 1e-6 );
+ final int sign = e.getWheelRotation();
+
+ if( e.isShiftDown() )
+ mask.incSquaredSigma( sign * scale * scale * fastSpeed * fastSpeed );
+ else if ( e.isControlDown() )
+ mask.incSquaredSigma( sign * scale * scale * slowSpeed * slowSpeed );
+ else
+ mask.incSquaredSigma( sign * scale * scale );
+
+ bw.getViewerFrameP().getViewerPanel().requestRepaint();
+ bw.getViewerFrameQ().getViewerPanel().requestRepaint();
+ }
+
+
+}
diff --git a/src/main/java/bdv/gui/TransformTypePanel.java b/src/main/java/bdv/gui/TransformTypePanel.java
new file mode 100644
index 00000000..93ce1160
--- /dev/null
+++ b/src/main/java/bdv/gui/TransformTypePanel.java
@@ -0,0 +1,111 @@
+package bdv.gui;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.BorderFactory;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import bigwarp.BigWarp;
+import bigwarp.transforms.BigWarpTransform;
+
+public class TransformTypePanel extends JPanel
+{
+ private static final long serialVersionUID = 3285885870885172257L;
+
+ private static final String TRANSFORM_TYPE_HELP_TEXT = "Select the type of transformation.";
+
+ public static final String[] TRANSFORM_TYPE_STRINGS = new String[] {
+ BigWarpTransform.TPS, BigWarpTransform.AFFINE,
+ BigWarpTransform.SIMILARITY, BigWarpTransform.ROTATION, BigWarpTransform.TRANSLATION };
+
+ private final BigWarp< ? > bw;
+
+ private final JLabel transformTypeLabel;
+ private final JComboBox< String > transformTypeDropdown;
+
+ private boolean active;
+
+ public TransformTypePanel( BigWarp> bw )
+ {
+ super( new GridBagLayout() );
+ this.bw = bw;
+ active = true;
+
+ setBorder( BorderFactory.createCompoundBorder(
+ BorderFactory.createEmptyBorder( 4, 2, 4, 2 ),
+ BorderFactory.createCompoundBorder(
+ BorderFactory.createTitledBorder(
+ BorderFactory.createEtchedBorder(),
+ "Transformation options" ),
+ BorderFactory.createEmptyBorder( 2, 2, 2, 2 ) ) ) );
+
+ transformTypeLabel = new JLabel( "Transform type");
+ transformTypeLabel.setToolTipText( TRANSFORM_TYPE_HELP_TEXT );
+ transformTypeDropdown = new JComboBox<>( TRANSFORM_TYPE_STRINGS );
+ getTransformTypeDropdown().setToolTipText( TRANSFORM_TYPE_HELP_TEXT );
+ getTransformTypeDropdown().addActionListener( new ActionListener() {
+ @Override
+ public void actionPerformed( ActionEvent e )
+ {
+ if( active )
+ {
+ final String type = (String)transformTypeDropdown.getSelectedItem();
+ bw.setTransformType( type );
+ bw.updateTransformTypeDialog( type );
+ }
+ }
+ });
+
+ // layout
+ final GridBagConstraints gbc = new GridBagConstraints();
+ gbc.gridx = 0;
+ gbc.gridy = 0;
+ gbc.gridwidth = 1;
+ gbc.gridheight = 1;
+ gbc.weightx = 0.0;
+ gbc.weighty = 0.0;
+ gbc.anchor = GridBagConstraints.LINE_END;
+ gbc.fill = GridBagConstraints.NONE;
+ gbc.insets = new Insets( 5, 5, 5, 5 );
+ add( transformTypeLabel, gbc );
+
+ gbc.gridx = 2;
+ gbc.weightx = 0.0;
+ gbc.anchor = GridBagConstraints.LINE_START;
+ add( getTransformTypeDropdown(), gbc );
+
+ }
+
+ public JComboBox< String > getTransformTypeDropdown()
+ {
+ return transformTypeDropdown;
+ }
+
+ public void setType( String type )
+ {
+ transformTypeDropdown.setSelectedItem( type );
+ }
+
+ /**
+ * After calling deactivate, updates to this panel won't affect Bigwarp.
+ */
+ public void deactivate()
+ {
+ active = false;
+ }
+
+ /**
+ * After calling activate, updates to this panel will affect Bigwarp.
+ */
+ public void activate()
+ {
+ active = true;
+ }
+
+}
diff --git a/src/main/java/bdv/gui/TransformTypeSelectDialog.java b/src/main/java/bdv/gui/TransformTypeSelectDialog.java
index fae8b02e..c662b450 100644
--- a/src/main/java/bdv/gui/TransformTypeSelectDialog.java
+++ b/src/main/java/bdv/gui/TransformTypeSelectDialog.java
@@ -8,12 +8,12 @@
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 2 of the
* License, or (at your option) any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* .
@@ -34,30 +34,43 @@
import javax.swing.JRadioButton;
import bigwarp.BigWarp;
+import bigwarp.transforms.BigWarpTransform;
public class TransformTypeSelectDialog extends JDialog
{
private static final long serialVersionUID = 1L;
-
+
+ @Deprecated
public static final String TPS = "Thin Plate Spline";
+ @Deprecated
+ public static final String MASKEDTPS = "Masked Thin Plate Spline";
+ @Deprecated
+ public static final String MASKEDSIMTPS = "Masked Similarity + Thin Plate Spline";
+ @Deprecated
public static final String AFFINE = "Affine";
+ @Deprecated
public static final String SIMILARITY = "Similarity";
+ @Deprecated
public static final String ROTATION = "Rotation";
+ @Deprecated
public static final String TRANSLATION = "Translation";
-
+
private final BigWarp< ? > bw;
private String transformType;
+ private final ButtonGroup group;
private final JRadioButton tpsButton;
private final JRadioButton affineButton;
private final JRadioButton similarityButton;
private final JRadioButton rotationButton;
private final JRadioButton translationButton;
+ private boolean active;
+
/**
* Instantiates and displays a JFrame that enables
* the selection of the transformation type.
- *
+ *
* @param owner the parent frame
* @param bw a bigwarp instance
*/
@@ -66,16 +79,17 @@ public TransformTypeSelectDialog( final Frame owner, final BigWarp< ? > bw )
super( owner, "Transform Type select", false );
this.bw = bw;
+ active = true;
this.setLayout( new BorderLayout() );
transformType = bw.getTransformType();
- tpsButton = new JRadioButton( TPS );
- affineButton = new JRadioButton( AFFINE );
- similarityButton = new JRadioButton( SIMILARITY );
- rotationButton = new JRadioButton( ROTATION );
- translationButton = new JRadioButton( TRANSLATION );
-
- ButtonGroup group = new ButtonGroup();
+ tpsButton = new JRadioButton( BigWarpTransform.TPS );
+ affineButton = new JRadioButton( BigWarpTransform.AFFINE );
+ similarityButton = new JRadioButton( BigWarpTransform.SIMILARITY );
+ rotationButton = new JRadioButton( BigWarpTransform.ROTATION );
+ translationButton = new JRadioButton( BigWarpTransform.TRANSLATION );
+
+ group = new ButtonGroup();
group.add( tpsButton );
group.add( affineButton );
group.add( similarityButton );
@@ -89,14 +103,14 @@ public TransformTypeSelectDialog( final Frame owner, final BigWarp< ? > bw )
addActionListender( similarityButton );
addActionListender( rotationButton );
addActionListender( translationButton );
-
- JPanel radioPanel = new JPanel( new GridLayout(0, 1));
+
+ final JPanel radioPanel = new JPanel( new GridLayout(0, 1));
radioPanel.add( tpsButton );
radioPanel.add( affineButton );
radioPanel.add( similarityButton );
radioPanel.add( rotationButton );
radioPanel.add( translationButton );
-
+
radioPanel.setBorder( BorderFactory.createCompoundBorder(
BorderFactory.createEmptyBorder( 4, 2, 4, 2 ),
BorderFactory.createCompoundBorder(
@@ -105,7 +119,8 @@ public TransformTypeSelectDialog( final Frame owner, final BigWarp< ? > bw )
"Transform type" ),
BorderFactory.createEmptyBorder( 2, 2, 2, 2 ) ) ) );
- add( radioPanel, BorderLayout.LINE_START );
+ add( radioPanel, BorderLayout.PAGE_START );
+
pack();
}
@@ -113,19 +128,19 @@ private void updateButtonGroup()
{
switch( transformType )
{
- case TPS:
+ case BigWarpTransform.TPS:
tpsButton.setSelected( true );
break;
- case AFFINE:
+ case BigWarpTransform.AFFINE:
affineButton.setSelected( true );
break;
- case SIMILARITY:
+ case BigWarpTransform.SIMILARITY:
similarityButton.setSelected( true );
break;
- case ROTATION:
+ case BigWarpTransform.ROTATION:
rotationButton.setSelected( true );
break;
- case TRANSLATION:
+ case BigWarpTransform.TRANSLATION:
translationButton.setSelected( true );
break;
}
@@ -136,11 +151,30 @@ public void addActionListender( final JRadioButton button )
button.addActionListener( new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
- bw.setTransformType( button.getText() );
+ if( active )
+ {
+ final String type = button.getText();
+ bw.setTransformType( type );
+ bw.updateTransformTypePanel( type );
+ }
}
});
}
+ public void addFalloffActionListender( final JRadioButton button )
+ {
+ button.addActionListener( new ActionListener()
+ {
+ @Override
+ public void actionPerformed( ActionEvent e )
+ {
+ bw.getTransformPlateauMaskSource().getRandomAccessible().setFalloffShape( button.getText() );
+ bw.getViewerFrameP().getViewerPanel().requestRepaint();
+ bw.getViewerFrameQ().getViewerPanel().requestRepaint();
+ }
+ } );
+ }
+
public void setTransformType( String transformType )
{
this.transformType = transformType;
@@ -148,4 +182,21 @@ public void setTransformType( String transformType )
this.validate();
this.repaint();
}
+
+ /**
+ * After calling deactivate, updates to this panel won't affect Bigwarp.
+ */
+ public void deactivate()
+ {
+ active = false;
+ }
+
+ /**
+ * After calling activate, updates to this panel will affect Bigwarp.
+ */
+ public void activate()
+ {
+ active = true;
+ }
+
}
diff --git a/src/main/java/bdv/gui/sourceList/BigWarpSourceListPanel.java b/src/main/java/bdv/gui/sourceList/BigWarpSourceListPanel.java
new file mode 100644
index 00000000..97b7bc11
--- /dev/null
+++ b/src/main/java/bdv/gui/sourceList/BigWarpSourceListPanel.java
@@ -0,0 +1,95 @@
+/*-
+ * #%L
+ * BigWarp plugin for Fiji.
+ * %%
+ * Copyright (C) 2015 - 2022 Howard Hughes Medical Institute.
+ * %%
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program. If not, see
+ * .
+ * #L%
+ */
+package bdv.gui.sourceList;
+
+import java.awt.Dimension;
+import java.awt.GridLayout;
+
+import javax.swing.JCheckBox;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import bdv.gui.BigWarpLandmarkPanel.JTableChecking;
+import bdv.gui.BigWarpLandmarkPanel.TextFieldCell;
+import bdv.gui.BigWarpLandmarkPanel.TextFieldCellEditor;
+import bdv.gui.sourceList.BigWarpSourceTableModel.ButtonEditor;
+import bdv.gui.sourceList.BigWarpSourceTableModel.ButtonRenderer;
+
+public class BigWarpSourceListPanel extends JPanel
+{
+ private static final long serialVersionUID = 2370565900502584680L;
+
+ protected BigWarpSourceTableModel tableModel;
+
+ protected JTableChecking table;
+
+ public final Logger logger = LoggerFactory.getLogger( BigWarpSourceListPanel.class );
+
+ public BigWarpSourceListPanel( BigWarpSourceTableModel tableModel )
+ {
+ super( new GridLayout( 1, 0 ) );
+
+ // set table model re-generates the table
+ setTableModel( tableModel );
+
+ final JScrollPane scrollPane = new JScrollPane( table );
+ add( scrollPane );
+ }
+
+ public BigWarpSourceTableModel getTableModel()
+ {
+ return tableModel;
+ }
+
+ public void genJTable()
+ {
+ table = new JTableChecking( tableModel );
+ table.setPreferredScrollableViewportSize( new Dimension( 500, 70 ) );
+ table.setFillsViewportHeight( true );
+ table.setShowVerticalLines( false );
+
+ table.setDefaultEditor( String.class,
+ new TextFieldCellEditor( new TextFieldCell(table), String.class ));
+
+ table.getColumn( " " ).setCellRenderer( new ButtonRenderer() );
+ table.getColumn( " " ).setCellEditor( new ButtonEditor( new JCheckBox(), tableModel ) );
+ table.getColumn( " " ).setPreferredWidth( 24 );
+ table.getColumn( " " ).setWidth( 24 );
+ }
+
+ public void setTableModel( BigWarpSourceTableModel tableModel )
+ {
+ this.tableModel = tableModel;
+ genJTable();
+ }
+
+ public JTable getJTable()
+ {
+ return table;
+ }
+
+
+}
diff --git a/src/main/java/bdv/gui/sourceList/BigWarpSourceTableModel.java b/src/main/java/bdv/gui/sourceList/BigWarpSourceTableModel.java
new file mode 100644
index 00000000..3890f08a
--- /dev/null
+++ b/src/main/java/bdv/gui/sourceList/BigWarpSourceTableModel.java
@@ -0,0 +1,373 @@
+package bdv.gui.sourceList;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import javax.swing.DefaultCellEditor;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JTable;
+import javax.swing.UIManager;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.TableCellRenderer;
+
+import bigwarp.transforms.NgffTransformations;
+import net.imglib2.realtransform.RealTransform;
+
+public class BigWarpSourceTableModel extends AbstractTableModel
+{
+
+ private static final long serialVersionUID = 5923947651732788341L;
+
+ public static enum SourceType { IMAGEPLUS, DATASET, URL };
+ protected final static String[] colNames = new String[] { "Name", "Moving", "Transform", " " };
+
+ protected final String[] columnNames;
+ protected final ArrayList sources;
+ protected final ArrayList rmRowButtons;
+
+ protected static int imageColIdx = 0;
+ protected static int movingColIdx = 1;
+ protected static int transformColIdx = 2;
+ protected static int removeColIdx = 3;
+
+ protected Function transformChangedCallback;
+
+ private Component container;
+
+ public BigWarpSourceTableModel()
+ {
+ this(null);
+ }
+
+ public BigWarpSourceTableModel(final Function transformChangedCallback )
+ {
+ super();
+ columnNames = colNames;
+ sources = new ArrayList<>();
+ rmRowButtons = new ArrayList<>();
+ this.transformChangedCallback = transformChangedCallback;
+ }
+
+ /**
+ * Set the {@link Component} to repaint when a row is removed.
+ *
+ * @param container the component containing this table
+ */
+ public void setContainer( Component container )
+ {
+ this.container = container;
+ }
+
+ public SourceRow get( int i )
+ {
+ return sources.get( i );
+ }
+
+ @Override
+ public String getColumnName( int col ){
+ return columnNames[col];
+ }
+
+ @Override
+ public int getColumnCount()
+ {
+ return columnNames.length;
+ }
+
+ @Override
+ public int getRowCount()
+ {
+ return sources.size();
+ }
+
+ @Override
+ public Object getValueAt( int r, int c )
+ {
+ if( c == 3 )
+ return rmRowButtons.get( r );
+ else
+ return sources.get( r ).get( c );
+ }
+
+ @Override
+ public Class> getColumnClass( int col ){
+ if ( col == 1 )
+ return Boolean.class;
+ else if ( col == 3 )
+ return JButton.class;
+ else
+ return String.class;
+ }
+
+ @Override
+ public boolean isCellEditable( int row, int col )
+ {
+ return true;
+ }
+
+ @Override
+ public void setValueAt(Object value, int row, int col)
+ {
+ if( col == movingColIdx )
+ sources.get( row ).moving = (Boolean)value;
+ else if( col == imageColIdx )
+ sources.get( row ).srcName = (String)value;
+ else if( col == transformColIdx )
+ setTransform( row, (String)value);
+ }
+
+ public void setTransform(final int row, final String value) {
+
+ if (transformChangedCallback != null) {
+ final String res = transformChangedCallback.apply(value);
+ if (res != null)
+ sources.get(row).transformUrl = res;
+ else
+ sources.get(row).transformUrl = value;
+ } else
+ sources.get(row).transformUrl = value;
+ }
+
+ public void add( String srcName, boolean moving, SourceType type )
+ {
+ final RemoveRowButton rmButton = new RemoveRowButton( sources.size() );
+ rmRowButtons.add( rmButton );
+ sources.add( new SourceRow( srcName, moving, "", type ));
+ }
+
+ public void add( String srcName, boolean moving )
+ {
+ add( srcName, moving, SourceType.URL );
+ }
+
+ public void add( String srcName )
+ {
+ add( srcName, false );
+ }
+
+ public void addImagePlus( String srcName )
+ {
+ addImagePlus( srcName, false );
+ }
+
+ public void addImagePlus( String srcName, boolean isMoving )
+ {
+ add( srcName, isMoving, SourceType.IMAGEPLUS );
+ }
+
+ public void addDataset( String srcName )
+ {
+ addImagePlus( srcName, false );
+ }
+
+ public void addDataset( String srcName, boolean isMoving )
+ {
+ add( srcName, isMoving, SourceType.DATASET );
+ }
+
+ public boolean remove( int i )
+ {
+ if( i >= sources.size() )
+ return false;
+
+ sources.remove( i );
+ rmRowButtons.remove( i );
+ updateRmButtonIndexes();
+
+ if( container != null )
+ container.repaint();
+
+ return true;
+ }
+
+ private void updateRmButtonIndexes()
+ {
+ for( int i = 0; i < rmRowButtons.size(); i++ )
+ rmRowButtons.get( i ).setRow( i );
+ }
+
+ public static class SourceRow
+ {
+ public String srcName;
+ public boolean moving;
+ public String transformUrl;
+
+ public SourceType type;
+
+ public SourceRow( String srcName, boolean moving, String transformUrl, SourceType type )
+ {
+ this.srcName = srcName;
+ this.moving = moving;
+ this.transformUrl = transformUrl;
+ this.type = type;
+ }
+
+ public SourceRow( String srcName, boolean moving, String transformName )
+ {
+ this( srcName, moving, transformName, SourceType.URL );
+ }
+
+ public Object get( int c )
+ {
+ if( c == 0 )
+ return srcName;
+ else if( c == 1 )
+ return moving;
+ else if ( c == 2 )
+ return transformUrl;
+ else
+ return null;
+ }
+
+ public RealTransform getTransform()
+ {
+ RealTransform transform = null;
+ if( transformUrl!= null && !transformUrl.isEmpty() )
+ transform = NgffTransformations.open( transformUrl );
+
+ return transform;
+ }
+
+ public Supplier getTransformUri()
+ {
+ if( transformUrl!= null && !transformUrl.isEmpty() )
+ return () -> transformUrl;
+
+ return null;
+ }
+ }
+
+ protected static class RemoveRowButton extends JButton {
+ private int row;
+ public RemoveRowButton( int row )
+ {
+ super( "remove" );
+ setRow( row );
+ }
+
+ public int getRow()
+ {
+ return row;
+ }
+
+ public void setRow(int row)
+ {
+ this.row = row;
+ }
+ }
+
+ /**
+ * From
+ * http://www.java2s.com/Code/Java/Swing-Components/ButtonTableExample.htm
+ */
+ protected static class ButtonRenderer extends JButton implements TableCellRenderer
+ {
+ public ButtonRenderer()
+ {
+ setOpaque( true );
+ }
+
+ @Override
+ public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column )
+ {
+ if ( isSelected )
+ {
+ setForeground( table.getSelectionForeground() );
+ setBackground( table.getSelectionBackground() );
+ }
+ else
+ {
+ setForeground( table.getForeground() );
+ setBackground( UIManager.getColor( "Button.background" ) );
+ }
+ setText( ( value == null ) ? "" : ((RemoveRowButton)value).getText());
+ return this;
+ }
+ }
+
+ /**
+ * From
+ * http://www.java2s.com/Code/Java/Swing-Components/ButtonTableExample.htm
+ */
+ protected static class ButtonEditor extends DefaultCellEditor
+ {
+ protected JButton button;
+
+ private String label;
+
+ private RemoveRowButton thisButton;
+
+ private BigWarpSourceTableModel model;
+
+ private boolean isPushed;
+
+ public ButtonEditor( JCheckBox checkBox, BigWarpSourceTableModel model )
+ {
+ super( checkBox );
+ checkBox.setText( "-" );
+ this.model = model;
+
+ button = new JButton();
+ button.setOpaque( true );
+ button.addActionListener( new ActionListener()
+ {
+ @Override
+ public void actionPerformed( ActionEvent e )
+ {
+ fireEditingStopped();
+ }
+ } );
+ }
+
+ @Override
+ public Component getTableCellEditorComponent( JTable table, Object value, boolean isSelected, int row, int column )
+ {
+ if ( isSelected )
+ {
+ button.setForeground( table.getSelectionForeground() );
+ button.setBackground( table.getSelectionBackground() );
+ }
+ else
+ {
+ button.setForeground( table.getForeground() );
+ button.setBackground( table.getBackground() );
+ }
+ thisButton = ((RemoveRowButton)value);
+ label = ( value == null ) ? "" : thisButton.getText();
+ button.setText( label );
+ isPushed = true;
+ return button;
+ }
+
+ @Override
+ public Object getCellEditorValue()
+ {
+ if ( isPushed )
+ {
+ model.remove( thisButton.getRow() );
+ }
+ isPushed = false;
+ return new String( label );
+ }
+
+ @Override
+ public boolean stopCellEditing()
+ {
+ isPushed = false;
+ return super.stopCellEditing();
+ }
+
+ @Override
+ protected void fireEditingStopped()
+ {
+ super.fireEditingStopped();
+ }
+ }
+
+}
diff --git a/src/main/java/bdv/ij/ApplyBigwarpPlugin.java b/src/main/java/bdv/ij/ApplyBigwarpPlugin.java
index c7eca5fd..99ab6bc3 100644
--- a/src/main/java/bdv/ij/ApplyBigwarpPlugin.java
+++ b/src/main/java/bdv/ij/ApplyBigwarpPlugin.java
@@ -46,14 +46,13 @@
import org.janelia.saalfeldlab.n5.universe.metadata.N5CosemMetadataParser;
import bdv.export.ProgressWriter;
-import bdv.gui.TransformTypeSelectDialog;
import bdv.ij.util.ProgressWriterIJ;
import bdv.img.WarpedSource;
import bdv.viewer.Interpolation;
import bdv.viewer.Source;
import bdv.viewer.SourceAndConverter;
import bigwarp.BigWarp;
-import bigwarp.BigWarp.BigWarpData;
+import bigwarp.BigWarpData;
import bigwarp.BigWarpExporter;
import bigwarp.BigWarpInit;
import bigwarp.landmarks.LandmarkTableModel;
@@ -167,17 +166,17 @@ public static String getUnit( final BigWarpData> bwData,
final String resolutionOption )
{
String unit = "pix";
- final boolean doTargetImagesExist = bwData.targetSourceIndices != null && bwData.targetSourceIndices.length > 0;
+ final boolean doTargetImagesExist = bwData.numTargetSources() > 0;
if( resolutionOption.equals( MOVING ) || resolutionOption.equals( MOVING_WARPED ) || !doTargetImagesExist )
{
- final VoxelDimensions mvgVoxDims = bwData.sources.get( bwData.movingSourceIndices[0] ).getSpimSource().getVoxelDimensions();
+ final VoxelDimensions mvgVoxDims = bwData.getMovingSource( 0 ).getSpimSource().getVoxelDimensions();
if( mvgVoxDims != null )
unit = mvgVoxDims.unit();
}
else
{
// use target units even if
- final VoxelDimensions tgtVoxDims = bwData.sources.get( bwData.targetSourceIndices[0] ).getSpimSource().getVoxelDimensions();
+ final VoxelDimensions tgtVoxDims = bwData.getTargetSource( 0 ).getSpimSource().getVoxelDimensions();
if( tgtVoxDims != null )
unit = tgtVoxDims.unit();
}
@@ -194,21 +193,18 @@ public static double[] getResolution(
if( resolutionOption.equals( TARGET ))
{
- if( bwData.targetSourceIndices.length <= 0 )
+ if( bwData.numTargetSources() <= 0 )
return null;
- final Source< ? > spimSource = bwData.sources.get(
- bwData.targetSourceIndices[ 0 ]).getSpimSource();
-
+ final Source< ? > spimSource = bwData.getTargetSource(0 ).getSpimSource();
return getResolution( spimSource, resolutionOption, resolutionSpec );
}
- else if( resolutionOption.equals( MOVING ))
+ else if( resolutionOption.equals( MOVING ) )
{
- if( bwData.targetSourceIndices.length <= 0 )
+ if( bwData.numTargetSources() <= 0 )
return null;
- final Source< ? > spimSource = bwData.sources.get(
- bwData.movingSourceIndices[ 0 ]).getSpimSource();
+ final Source< ? > spimSource = bwData.getMovingSource( 0 ).getSpimSource();
return getResolution( spimSource, resolutionOption, resolutionSpec );
}
else if( resolutionOption.equals( SPECIFIED ))
@@ -343,8 +339,9 @@ public static Interval getPixelInterval(
{
final double[] inputres = resolutionFromSource( source );
+ final int N = outputResolution.length <= rai.numDimensions() ? outputResolution.length : rai.numDimensions();
final long[] max = new long[ rai.numDimensions() ];
- for( int d = 0; d < rai.numDimensions(); d++ )
+ for( int d = 0; d < N; d++ )
{
max[ d ] = (long)Math.ceil( ( inputres[ d ] * rai.dimension( d )) / outputResolution[ d ]);
}
@@ -353,6 +350,13 @@ public static Interval getPixelInterval(
}
else if( fieldOfViewOption.equals( MOVING_WARPED ))
{
+ final FinalInterval interval = new FinalInterval(
+ Intervals.minAsLongArray( rai ),
+ Intervals.maxAsLongArray( rai ));
+
+ if( transform == null )
+ return interval;
+
final double[] movingRes = resolutionFromSource( source );
final int ndims = transform.numSourceDimensions();
final AffineTransform movingPixelToPhysical = new AffineTransform( ndims );
@@ -372,10 +376,6 @@ else if( fieldOfViewOption.equals( MOVING_WARPED ))
seq.add( transform.inverse() );
seq.add( outputResolution2Pixel.inverse() );
- final FinalInterval interval = new FinalInterval(
- Intervals.minAsLongArray( rai ),
- Intervals.maxAsLongArray( rai ));
-
return bboxEst.estimatePixelInterval(seq, interval);
// return BigWarpExporter.estimateBounds( seq, interval );
}
@@ -450,26 +450,26 @@ public static List getPixelInterval(
final double[] outputResolution) {
if (fieldOfViewOption.equals(TARGET)) {
- if (bwData.targetSourceIndices.length <= 0) {
+ if (bwData.numTargetSources() <= 0) {
System.err.println("Requested target fov but target image is missing.");
return null;
}
return Stream.of(
getPixelInterval(
- bwData.sources.get(bwData.targetSourceIndices[0]).getSpimSource(),
+ bwData.getTargetSource( 0 ).getSpimSource(),
landmarks, transform, fieldOfViewOption, bboxEst, outputResolution))
.collect(Collectors.toList());
} else if (fieldOfViewOption.equals(MOVING_WARPED)) {
return Stream.of(
getPixelInterval(
- bwData.sources.get(bwData.movingSourceIndices[0]).getSpimSource(),
+ bwData.getMovingSource( 0 ).getSpimSource(),
landmarks, transform, fieldOfViewOption, bboxEst, outputResolution))
.collect(Collectors.toList());
} else if (fieldOfViewOption.equals(UNION_TARGET_MOVING)) {
return Stream.of(
getPixelInterval(
- bwData.sources.get(bwData.movingSourceIndices[0]).getSpimSource(),
+ bwData.getMovingSource( 0 ).getSpimSource(),
landmarks, transform, fieldOfViewOption, bboxEst, outputResolution))
.collect(Collectors.toList());
} else if (fieldOfViewOption.equals(SPECIFIED_PIXEL)) {
@@ -820,18 +820,21 @@ public static List apply(
final boolean wait,
final WriteDestinationOptions writeOpts) {
- final int numChannels = bwData.movingSourceIndices.length;
- final int[] movingSourceIndexList = bwData.movingSourceIndices;
+// int numChannels = bwData.movingSourceIndexList.size();
+ final int numChannels = bwData.numMovingSources();
final List< SourceAndConverter< T >> sourcesxfm = BigWarp.wrapSourcesAsTransformed(
- bwData.sources,
+ bwData.sourceInfos,
landmarks.getNumdims(),
bwData );
final InvertibleRealTransform invXfm = new BigWarpTransform( landmarks, tranformTypeOption ).getTransformation();
for ( int i = 0; i < numChannels; i++ )
{
- ((WarpedSource< ? >) (sourcesxfm.get( movingSourceIndexList[ i ]).getSpimSource())).updateTransform( invXfm );
- ((WarpedSource< ? >) (sourcesxfm.get( movingSourceIndexList[ i ]).getSpimSource())).setIsTransformed( true );
+ final SourceAndConverter< T > originalMovingSource = bwData.getMovingSource( i );
+ final int originalIdx = bwData.sources.indexOf( originalMovingSource );
+
+ ((WarpedSource< ? >) (sourcesxfm.get( originalIdx).getSpimSource())).updateTransform( invXfm );
+ ((WarpedSource< ? >) (sourcesxfm.get( originalIdx).getSpimSource())).setIsTransformed( true );
}
final ProgressWriter progressWriter = new ProgressWriterIJ();
@@ -982,10 +985,11 @@ public static & NumericType> void runN5Export(
pixelRenderToPhysical.concatenate( offsetTransform );
// render and write
- final int N = data.movingSourceIndices.length;
+ final int N = data.numMovingSources();
for ( int i = 0; i < N; i++ )
{
- final int movingSourceIndex = data.movingSourceIndices[ i ];
+ final SourceAndConverter< S > originalMovingSource = data.getMovingSource( i );
+ final int movingSourceIndex = data.sources.indexOf( originalMovingSource );
@SuppressWarnings( "unchecked" )
final RealRandomAccessible< T > raiRaw = ( RealRandomAccessible< T > )sources.get( movingSourceIndex ).getSpimSource().getInterpolatedSource( 0, 0, interp );
@@ -994,7 +998,7 @@ public static & NumericType> void runN5Export(
final IntervalView< T > img = Views.interval( Views.raster( rai ),
Intervals.zeroMin( outputInterval ) );
- final String srcName = data.sources.get( data.movingSourceIndices[ i ]).getSpimSource().getName();
+ final String srcName = originalMovingSource.getSpimSource().getName();
String destDataset = dataset;
if( N > 1 )
@@ -1067,12 +1071,12 @@ public void run( final String arg )
gd.addChoice( "Transform type",
new String[] {
- TransformTypeSelectDialog.TPS,
- TransformTypeSelectDialog.AFFINE,
- TransformTypeSelectDialog.SIMILARITY,
- TransformTypeSelectDialog.ROTATION,
- TransformTypeSelectDialog.TRANSLATION },
- TransformTypeSelectDialog.TPS);
+ BigWarpTransform.TPS,
+ BigWarpTransform.AFFINE,
+ BigWarpTransform.SIMILARITY,
+ BigWarpTransform.ROTATION,
+ BigWarpTransform.TRANSLATION },
+ BigWarpTransform.TPS);
gd.addMessage( "Field of view and resolution:" );
gd.addChoice( "Resolution",
diff --git a/src/main/java/bdv/ij/BigWarpBdvCommand.java b/src/main/java/bdv/ij/BigWarpBdvCommand.java
index 34cd2c6e..ed6e406f 100755
--- a/src/main/java/bdv/ij/BigWarpBdvCommand.java
+++ b/src/main/java/bdv/ij/BigWarpBdvCommand.java
@@ -23,6 +23,7 @@
import bdv.ij.util.ProgressWriterIJ;
import bigwarp.BigWarp;
+import bigwarp.BigWarpData;
import bigwarp.BigWarpInit;
import mpicbg.spim.data.SpimData;
import mpicbg.spim.data.SpimDataException;
@@ -61,8 +62,8 @@ public void run()
final SpimData fixedSpimData = new XmlIoSpimData().load( fixedImageXml.getAbsolutePath() );
final SpimData movingSpimData = new XmlIoSpimData().load( movingImageXml.getAbsolutePath() );
new RepeatingReleasedEventsFixer().install();
- final BigWarp.BigWarpData< ? > bigWarpData = BigWarpInit.createBigWarpData( movingSpimData, fixedSpimData );
- bw = new BigWarp( bigWarpData, "Big Warp", new ProgressWriterIJ() );
+ final BigWarpData< ? > bigWarpData = BigWarpInit.createBigWarpData( movingSpimData, fixedSpimData );
+ bw = new BigWarp( bigWarpData, new ProgressWriterIJ() );
bw.getViewerFrameP().getViewerPanel().requestRepaint();
bw.getViewerFrameQ().getViewerPanel().requestRepaint();
bw.getLandmarkFrame().repaint();
diff --git a/src/main/java/bdv/ij/BigWarpCommand.java b/src/main/java/bdv/ij/BigWarpCommand.java
new file mode 100644
index 00000000..6fe1a074
--- /dev/null
+++ b/src/main/java/bdv/ij/BigWarpCommand.java
@@ -0,0 +1,100 @@
+package bdv.ij;
+
+import java.io.IOException;
+
+import org.scijava.command.Command;
+import org.scijava.plugin.Parameter;
+import org.scijava.plugin.Plugin;
+
+import bdv.gui.BigWarpInitDialog;
+import ij.Macro;
+import ij.plugin.PlugIn;
+import ij.plugin.frame.Recorder;
+import net.imagej.Dataset;
+import net.imagej.DatasetService;
+import net.imagej.ImageJ;
+
+@Plugin(type = Command.class, menuPath = "Plugins>BigDataViewer>Big Warp Command" )
+public class BigWarpCommand implements Command, PlugIn
+{
+ private boolean initialRecorderState;
+
+ @Parameter
+ private DatasetService datasetService;
+
+ public BigWarpCommand()
+ {
+ initialRecorderState = Recorder.record;
+ Recorder.record = false;
+ }
+
+ @Override
+ public void run( String args )
+ {
+ Recorder.record = initialRecorderState;
+ final String macroOptions = Macro.getOptions();
+ String options = args;
+ if ( options == null || options.isEmpty() )
+ options = macroOptions;
+
+ final boolean isMacro = (options != null && !options.isEmpty());
+ if ( isMacro )
+ BigWarpInitDialog.runMacro( macroOptions );
+ else
+ {
+// if( datasetService != null )
+// {
+// System.out.println( "dset service exists");
+// for( final Dataset d : datasetService.getDatasets() )
+// System.out.println( d.getName());
+// }
+
+ final BigWarpInitDialog dialog = BigWarpInitDialog.createAndShow( datasetService );
+ // dialog sets recorder to its initial state on cancel or execution
+ dialog.setInitialRecorderState( initialRecorderState );
+ }
+ }
+
+ @Override
+ public void run()
+ {
+ run(null);
+ }
+
+ public static void main( String[] a ) throws IOException
+ {
+// String options = "images=mri-stack.tif,mri-stack.tif moving=true,false transforms=,";
+//
+// String images = Macro.getValue(options, "images", "");
+// String moving = Macro.getValue(options, "moving", "");
+// String transforms = Macro.getValue(options, "transforms", "");
+//
+// System.out.println( images );
+// System.out.println( moving );
+// System.out.println( transforms );
+
+ final ImageJ ij2 = new ImageJ();
+ ij2.ui().showUI();
+
+// final Object im1 = ij2.io().open( "/home/john/tmp/mri-stack.tif" );
+// final Object im2 = ij2.io().open( "/home/john/tmp/t1-head.tif" );
+////
+//// Object im1 = ij2.io().open( "/groups/saalfeld/home/bogovicj/tmp/mri-stack.tif" );
+//// Object im2 = ij2.io().open( "/groups/saalfeld/home/bogovicj/tmp/t1-head.tif" );
+////
+// ij2.ui().show( im1 );
+// ij2.ui().show( im2 );
+
+
+ final Object im1 = ij2.io().open( "/home/john/tmp/boats.tif" );
+ ij2.ui().show( im1 );
+
+
+// String args = "images=[a, b, c], isMoving=[true, true, false], transforms=[,,]";
+//
+// String imagesList = null;
+// String isMovingList = null;
+// String transformsList = null;
+ }
+
+}
diff --git a/src/main/java/bdv/ij/BigWarpImagePlusPlugIn.java b/src/main/java/bdv/ij/BigWarpImagePlusPlugIn.java
index 2e804074..c8a0badd 100755
--- a/src/main/java/bdv/ij/BigWarpImagePlusPlugIn.java
+++ b/src/main/java/bdv/ij/BigWarpImagePlusPlugIn.java
@@ -27,7 +27,7 @@
import bdv.ij.util.ProgressWriterIJ;
import bigwarp.BigWarp;
-import bigwarp.BigWarp.BigWarpData;
+import bigwarp.BigWarpData;
import bigwarp.BigWarpInit;
import fiji.util.gui.GenericDialogPlus;
import ij.IJ;
@@ -170,7 +170,7 @@ public void run( final String arg )
try
{
new RepeatingReleasedEventsFixer().install();
- final BigWarp> bw = new BigWarp<>( bigwarpdata, "Big Warp", new ProgressWriterIJ() );
+ final BigWarp> bw = new BigWarp<>( bigwarpdata, new ProgressWriterIJ() );
if( landmarkPath != null && !landmarkPath.isEmpty())
{
diff --git a/src/main/java/bdv/ij/BigWarpToDeformationFieldPlugIn.java b/src/main/java/bdv/ij/BigWarpToDeformationFieldPlugIn.java
index a10a2256..1bceb401 100644
--- a/src/main/java/bdv/ij/BigWarpToDeformationFieldPlugIn.java
+++ b/src/main/java/bdv/ij/BigWarpToDeformationFieldPlugIn.java
@@ -26,16 +26,21 @@
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map.Entry;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import javax.swing.JFrame;
import org.janelia.saalfeldlab.n5.Compression;
import org.janelia.saalfeldlab.n5.GzipCompression;
import org.janelia.saalfeldlab.n5.Lz4Compression;
-import org.janelia.saalfeldlab.n5.N5Exception;
import org.janelia.saalfeldlab.n5.N5Writer;
import org.janelia.saalfeldlab.n5.RawCompression;
import org.janelia.saalfeldlab.n5.XzCompression;
@@ -43,36 +48,69 @@
import org.janelia.saalfeldlab.n5.ij.N5Exporter;
import org.janelia.saalfeldlab.n5.imglib2.N5DisplacementField;
import org.janelia.saalfeldlab.n5.universe.N5Factory;
-
-import bdv.gui.TransformTypeSelectDialog;
-import bdv.viewer.SourceAndConverter;
+import org.janelia.saalfeldlab.n5.universe.metadata.ome.ngff.v05.transformations.AffineCoordinateTransform;
+import org.janelia.saalfeldlab.n5.universe.metadata.ome.ngff.v05.transformations.CoordinateTransform;
+import org.janelia.saalfeldlab.n5.universe.metadata.ome.ngff.v05.transformations.DisplacementFieldCoordinateTransform;
+import org.janelia.saalfeldlab.n5.universe.metadata.ome.ngff.v05.transformations.ReferencedCoordinateTransform;
+import org.janelia.saalfeldlab.n5.universe.metadata.ome.ngff.v05.transformations.SequenceCoordinateTransform;
+import org.janelia.saalfeldlab.n5.universe.metadata.ome.ngff.v05.transformations.TranslationCoordinateTransform;
+
+import bdv.gui.ExportDisplacementFieldFrame;
+import bdv.img.WarpedSource;
+import bdv.viewer.Source;
+import bigwarp.BigWarp;
+import bigwarp.BigWarpData;
import bigwarp.BigWarpExporter;
import bigwarp.landmarks.LandmarkTableModel;
+import bigwarp.source.SourceInfo;
import bigwarp.transforms.BigWarpTransform;
+import bigwarp.transforms.NgffTransformations;
+import bigwarp.transforms.SlicerTransformations;
import fiji.util.gui.GenericDialogPlus;
import ij.IJ;
import ij.ImageJ;
import ij.ImagePlus;
+import ij.Macro;
import ij.WindowManager;
import ij.plugin.PlugIn;
+import ij.plugin.frame.Recorder;
import jitk.spline.ThinPlateR2LogRSplineKernelTransform;
-import mpicbg.spim.data.sequence.VoxelDimensions;
+import mpicbg.models.AffineModel2D;
+import mpicbg.models.AffineModel3D;
+import mpicbg.models.InvertibleCoordinateTransform;
+import mpicbg.models.RigidModel2D;
+import mpicbg.models.RigidModel3D;
+import mpicbg.models.SimilarityModel2D;
+import mpicbg.models.SimilarityModel3D;
+import mpicbg.models.TranslationModel2D;
+import mpicbg.models.TranslationModel3D;
import net.imglib2.Cursor;
import net.imglib2.FinalInterval;
import net.imglib2.Interval;
import net.imglib2.RandomAccessibleInterval;
import net.imglib2.RealPoint;
+import net.imglib2.converter.Converters;
+import net.imglib2.converter.RealFloatConverter;
+import net.imglib2.img.display.imagej.ImageJFunctions;
import net.imglib2.img.imageplus.FloatImagePlus;
import net.imglib2.img.imageplus.ImagePlusImgs;
import net.imglib2.iterator.IntervalIterator;
+import net.imglib2.loops.LoopBuilder;
+import net.imglib2.parallel.TaskExecutors;
import net.imglib2.realtransform.AffineGet;
import net.imglib2.realtransform.AffineTransform2D;
import net.imglib2.realtransform.AffineTransform3D;
+import net.imglib2.realtransform.DisplacementFieldTransform;
+import net.imglib2.realtransform.InvertibleRealTransform;
+import net.imglib2.realtransform.InvertibleRealTransformSequence;
+import net.imglib2.realtransform.InvertibleWrapped2DIntermediate3D;
import net.imglib2.realtransform.RealTransform;
-import net.imglib2.realtransform.Scale2D;
-import net.imglib2.realtransform.Scale3D;
+import net.imglib2.realtransform.RealTransformSequence;
+import net.imglib2.realtransform.ScaleAndTranslation;
import net.imglib2.realtransform.ThinplateSplineTransform;
+import net.imglib2.realtransform.inverse.WrappedIterativeInvertibleRealTransform;
import net.imglib2.type.numeric.RealType;
+import net.imglib2.type.numeric.real.DoubleType;
import net.imglib2.type.numeric.real.FloatType;
import net.imglib2.view.IntervalView;
import net.imglib2.view.Views;
@@ -80,7 +118,13 @@
import net.imglib2.view.composite.GenericComposite;
/**
- * ImageJ plugin to convert the thin plate spline to a deformation field.
+ * ImageJ plugin to convert the thin plate spline to a displacement field.
+ *
+ * If the ignoreAffine option is true, the resulting displacement field will
+ * not contain the affine part of the transformation. In this case, the total
+ * transformation is the displacement field first, followed by the affine part
+ * of the transformation.
+ *
*
* @author John Bogovic <bogovicj@janelia.hhmi.org>
* @author Tobias Pietzsch <tobias.pietzsch@gmail.com>
@@ -88,6 +132,8 @@
*/
public class BigWarpToDeformationFieldPlugIn implements PlugIn
{
+ public static enum INVERSE_OPTIONS { FORWARD, INVERSE, BOTH };
+
public static final String[] compressionOptions = new String[] {
N5Exporter.RAW_COMPRESSION,
N5Exporter.GZIP_COMPRESSION,
@@ -95,224 +141,731 @@ public class BigWarpToDeformationFieldPlugIn implements PlugIn
N5Exporter.XZ_COMPRESSION,
N5Exporter.BLOSC_COMPRESSION };
+ public static final String flattenOption = "Flat";
+ public static final String sequenceOption = "Sequence";
+
public static void main( final String[] args )
{
new ImageJ();
-// IJ.run("Boats (356K)");
- final ImagePlus imp = IJ.openImage( "/groups/saalfeld/home/bogovicj/tmp/mri-stack.tif" );
-// ImagePlus imp = IJ.openImage( "/groups/saalfeld/home/bogovicj/tmp/mri-stack_p2p2p4.tif" );
-// WindowManager.getActiveWindow();
+ new Recorder();
+ Recorder.record = true;
+
+// IJ.run("Boats (356K)");
+// ImagePlus imp = IJ.openImage( "/groups/saalfeld/home/bogovicj/tmp/mri-stack.tif" );
+ final ImagePlus imp = IJ.openImage( "/home/john/tmp/mri-stack.tif" );
+// ImagePlus imp = IJ.openImage( "/home/john/tmp/mri-stack_mm.tif" );
imp.show();
new BigWarpToDeformationFieldPlugIn().run( null );
}
- public void runFromBigWarpInstance(
- final LandmarkTableModel landmarkModel,
- final List> sources,
- final int[] targetSourceIndexList )
+ public void runFromBigWarpInstance( final BigWarp> bw )
{
- final ImageJ ij = IJ.getInstance();
- if ( ij == null )
- return;
-
-
- final DeformationFieldExportParameters params = DeformationFieldExportParameters.fromDialog( false, false );
- if( params == null )
- return;
+// ImageJ ij = IJ.getInstance();
+// if ( ij == null )
+// return;
+//
+ ExportDisplacementFieldFrame.createAndShow( bw );
+ }
- final RandomAccessibleInterval< ? > tgtInterval = sources.get( targetSourceIndexList[ 0 ] ).getSpimSource().getSource( 0, 0 );
+// /**
+// * @deprecated not necessary access thedesired source this way anymore, use {@link #runFromBigWarpInstance(LandmarkTableModel, SourceAndConverter)}
+// * on the result of {@link bigwarp.BigWarpData#getTargetSource(int)}
+// */
+// @Deprecated
+// public void runFromBigWarpInstanceOld(
+// final BigWarpData> data,
+// final LandmarkTableModel landmarkModel,
+// final List> sources,
+// final List targetSourceIndexList )
+// {
+// runFromBigWarpInstanceOld( data, landmarkModel, data.getTargetSource( 0 ) );
+// }
+
+// public void runFromBigWarpInstanceOld(
+// final BigWarpData> data, final LandmarkTableModel landmarkModel, final SourceAndConverter< T > sourceAndConverter )
+// {
+// ImageJ ij = IJ.getInstance();
+// if ( ij == null )
+// return;
+//
+// final DeformationFieldExportParameters params = DeformationFieldExportParameters.fromDialog( false, false );
+// if( params == null )
+// return;
+//
+// final RandomAccessibleInterval< ? > tgtInterval = sourceAndConverter.getSpimSource().getSource( 0, 0 );
+//
+// int ndims = landmarkModel.getNumdims();
+// long[] dims = tgtInterval.dimensionsAsLongArray();
+//
+// double[] spacing = new double[ 3 ];
+// double[] offset = new double[ 3 ];
+// String unit = "pix";
+// VoxelDimensions voxelDim = sourceAndConverter.getSpimSource().getVoxelDimensions();
+// voxelDim.dimensions( spacing );
+//
+// if( params.spacing != null )
+// spacing = params.spacing;
+//
+// if( params.offset != null )
+// offset = params.offset;
+//
+// if( params.size != null )
+// dims = params.size;
+//
+// if( params.n5Base.isEmpty() )
+// {
+//// toImagePlus( data, landmarkModel, null, params.ignoreAffine, params.flatten(), params.virtual, dims, spacing, params.nThreads );
+// if ( params.inverseOption.equals( INVERSE_OPTIONS.BOTH.toString() ) )
+// {
+// toImagePlus( data, landmarkModel, null, params.ignoreAffine, params.flatten(), false, params.virtual, params.size, params.spacing, params.nThreads );
+// toImagePlus( data, landmarkModel, null, params.ignoreAffine, params.flatten(), true, params.virtual, params.size, params.spacing, params.nThreads );
+// }
+// else
+// {
+// final boolean inverse = params.inverseOption.equals( INVERSE_OPTIONS.INVERSE.toString() );
+// toImagePlus( data, landmarkModel, null, params.ignoreAffine, params.flatten(), inverse, params.virtual, params.size, params.spacing, params.nThreads );
+// }
+// }
+// else
+// {
+// try
+// {
+// final boolean inverse = params.inverseOption.equals( INVERSE_OPTIONS.INVERSE.toString() );
+// writeN5( params.n5Base, params.n5Dataset, landmarkModel, null, data, dims, spacing, offset, unit, params.blockSize, params.compression, params.nThreads, params.ignoreAffine, params.flatten(), inverse );
+// }
+// catch ( IOException e )
+// {
+// e.printStackTrace();
+// }
+// }
+// }
+
+ public static void runFromParameters( final DeformationFieldExportParameters params, final BigWarpData> data, final LandmarkTableModel landmarkModel, final BigWarpTransform bwTransform )
+ {
+ final String unit = "pixel";
+ LandmarkTableModel ltm;
- final int ndims = landmarkModel.getNumdims();
- long[] dims;
- if ( ndims <= 2 )
+ if( landmarkModel == null )
{
- dims = new long[ 3 ];
- dims[ 0 ] = tgtInterval.dimension( 0 );
- dims[ 1 ] = tgtInterval.dimension( 1 );
- dims[ 2 ] = 2;
+ if( params.landmarkPath == null || params.landmarkPath.isEmpty() )
+ {
+ IJ.showMessage( "Must provide landmark file." ); // TODO message differently
+ return;
+ }
+
+ // load landmarks
+ try
+ {
+ ltm = LandmarkTableModel.loadFromCsv( new File( params.landmarkPath ), false );
+ }
+ catch ( final IOException e )
+ {
+ e.printStackTrace();
+ return;
+ }
}
else
- {
- dims = new long[ 4 ];
- dims[ 0 ] = tgtInterval.dimension( 0 );
- dims[ 1 ] = tgtInterval.dimension( 1 );
- dims[ 2 ] = 3;
- dims[ 3 ] = tgtInterval.dimension( 2 );
- }
+ ltm = landmarkModel;
- double[] spacing = new double[ 3 ];
- final VoxelDimensions voxelDim = sources.get( targetSourceIndexList[ 0 ] ).getSpimSource().getVoxelDimensions();
- voxelDim.dimensions( spacing );
+ final int ndims = ltm.getNumdims();
+ double[] spacing = new double[ ndims ];
+ double[] offset = new double[ ndims ];
+ long[] dims = new long[ ndims ];
+ if ( params.spacing != null )
- if( params.spacing != null )
spacing = params.spacing;
- if( params.size != null )
+ if ( params.offset != null )
+ offset = params.offset;
+
+ if ( params.size != null )
dims = params.size;
- if( params.n5Base.isEmpty() )
+ ImagePlus imp = null;
+ if ( params.n5Base.isEmpty() )
{
- toImagePlus( landmarkModel, params.ignoreAffine, dims, spacing, params.nThreads );
+ if( !bwTransform.isNonlinear() ) // is linear
+ {
+ IJ.showMessage("unsupported at the moment");
+ }
+ else if ( params.inverseOption.equals( INVERSE_OPTIONS.BOTH.toString() ) )
+ {
+ imp = toImagePlus( data, ltm, bwTransform, params.ignoreAffine, params.flatten(), false, params.virtual, params.size, params.spacing, params.nThreads );
+ imp = toImagePlus( data, ltm, bwTransform, params.ignoreAffine, params.flatten(), true, params.virtual, params.size, params.spacing, params.nThreads );
+ }
+ else
+ {
+ final boolean inverse = params.inverseOption.equals( INVERSE_OPTIONS.INVERSE.toString() );
+ imp = toImagePlus( data, ltm, bwTransform, params.ignoreAffine, params.flatten(), inverse, params.virtual, params.size, params.spacing, params.nThreads );
+ }
+ if( imp != null )
+ imp.show();
}
else
{
- try
+ if( !bwTransform.isNonlinear() ) // is linear
{
- writeN5( params.n5Base, landmarkModel, dims, spacing, params.blockSize, params.compression, params.nThreads );
+ writeAffineN5(params.n5Base, params.n5Dataset, data, bwTransform );
}
- catch ( final N5Exception e )
+ else if ( params.inverseOption.equals( INVERSE_OPTIONS.BOTH.toString() ) )
{
- e.printStackTrace();
+ writeN5( params.n5Base, params.n5Dataset + "/dfield", ltm, bwTransform, data, dims, spacing, offset, unit, params.blockSize, params.compression, params.nThreads, params.format, params.ignoreAffine, params.flatten(),
+ false, params.inverseTolerance, params.inverseMaxIterations );
+ writeN5( params.n5Base, params.n5Dataset + "/invdfield", ltm, bwTransform, data, dims, spacing, offset, unit, params.blockSize, params.compression, params.nThreads, params.format, params.ignoreAffine, params.flatten(),
+ true, params.inverseTolerance, params.inverseMaxIterations );
+ }
+ else
+ {
+ final boolean inverse = params.inverseOption.equals( INVERSE_OPTIONS.INVERSE.toString() );
+ writeN5( params.n5Base, params.n5Dataset, ltm, bwTransform, data, dims, spacing, offset, unit, params.blockSize, params.compression, params.nThreads, params.format, params.ignoreAffine, params.flatten(),
+ inverse, params.inverseTolerance, params.inverseMaxIterations );
}
}
}
@Override
- public void run( final String arg )
+ public void run( final String args )
{
if ( IJ.versionLessThan( "1.40" ) )
return;
- final DeformationFieldExportParameters params = DeformationFieldExportParameters.fromDialog( true, true );
- final int nd = params.size.length;
+ final String macroOptions = Macro.getOptions();
+ String options = args;
+ if( options == null || options.isEmpty())
+ options = macroOptions;
- // load
- final LandmarkTableModel ltm = new LandmarkTableModel( nd );
- try
- {
- ltm.load( new File( params.landmarkPath ) );
- }
- catch ( final IOException e )
- {
- e.printStackTrace();
- return;
- }
+ final boolean isMacro = (options != null && !options.isEmpty());
- if( params.n5Base.isEmpty() )
- {
- toImagePlus( ltm, params.ignoreAffine, params.size, params.spacing, params.nThreads );
- }
+ if( isMacro )
+ ExportDisplacementFieldFrame.runMacro( macroOptions );
else
- {
- try
- {
- writeN5( params.n5Base, params.n5Dataset, ltm, params.size, params.spacing, params.blockSize, params.compression, params.nThreads );
- }
- catch ( final N5Exception e )
- {
- e.printStackTrace();
- }
- }
+ ExportDisplacementFieldFrame.createAndShow();
}
public static ImagePlus toImagePlus(
+ final BigWarpData> data,
final LandmarkTableModel ltm,
- final boolean ignoreAffine,
+ final BigWarpTransform bwTransform,
+ final boolean splitAffine,
+ final boolean flatten,
+ final boolean inverse,
+ final boolean virtual,
final long[] dims,
final double[] spacing,
final int nThreads )
{
- final BigWarpTransform bwXfm = new BigWarpTransform( ltm, TransformTypeSelectDialog.TPS );
- final RealTransform tps = getTpsAffineToggle( bwXfm, ignoreAffine );
+ final double[] offset = new double[ spacing.length ];
+ return toImagePlus( data, ltm, bwTransform, splitAffine, flatten, inverse, virtual, dims, spacing, offset, nThreads );
+ }
- AffineGet pixelToPhysical = null;
- if( spacing.length == 2)
+ /**
+ *
+ * @param data the {@link BigWarpData}
+ * @param ltm the {@link LandmarkTableModel}
+ * @param bwTransform the {@link BigWarpTransform}
+ * @param splitAffine omit the affine part of the transformation
+ * @param flatten include any fixed transformation
+ * @param inverse output the inverse transformation
+ * @param virtual output of virtual image
+ * @param dims the dimensions of the output {@link ImagePlus}'s spatial dimensions
+ * @param spacing the pixel spacing of the output field
+ * @param offset the physical offset (origin) of the output field
+ * @param nThreads number of threads for copying
+ * @return the image plus representing the displacement field
+ */
+ public static ImagePlus toImagePlus(
+ final BigWarpData> data,
+ final LandmarkTableModel ltm,
+ final BigWarpTransform bwTransform,
+ final boolean splitAffine,
+ final boolean flatten,
+ final boolean inverse,
+ final boolean virtual,
+ final long[] dims,
+ final double[] spacing,
+ final double[] offset,
+ final int nThreads )
+ {
+ BigWarpTransform bwXfm;
+ if( bwTransform == null )
+ bwXfm = new BigWarpTransform( ltm, BigWarpTransform.TPS );
+ else
+ bwXfm = bwTransform;
+
+ final InvertibleRealTransform fwdTransform = getTransformation( data, bwXfm, flatten, splitAffine );
+ final InvertibleRealTransform startingTransform = inverse ? fwdTransform.inverse() : fwdTransform;
+
+ final InvertibleRealTransform transform;
+ final int nd = ltm.getNumdims();
+ long[] ipDims = null;
+ if ( nd == 2 )
{
- pixelToPhysical = new Scale2D( spacing );
+ ipDims = new long[ 4 ];
+ ipDims[ 0 ] = dims[ 0 ];
+ ipDims[ 1 ] = dims[ 1 ];
+ ipDims[ 2 ] = 2;
+ ipDims[ 3 ] = 1;
+
+ if (bwXfm.isMasked())
+ transform = new InvertibleWrapped2DIntermediate3D(startingTransform);
+ else
+ transform = startingTransform;
}
- else if( spacing.length == 3)
+ else if ( nd == 3 )
{
- pixelToPhysical = new Scale3D( spacing );
+ ipDims = new long[ 4 ];
+ ipDims[ 0 ] = dims[ 0 ];
+ ipDims[ 1 ] = dims[ 1 ];
+ ipDims[ 2 ] = 3;
+ ipDims[ 3 ] = dims[ 2 ];
+
+ transform = startingTransform;
}
else
- {
return null;
+
+ final RandomAccessibleInterval< DoubleType > dfieldVirt = DisplacementFieldTransform.createDisplacementField( transform, new FinalInterval( dims ), spacing, offset );
+
+ ImagePlus dfieldIp;
+ if( virtual )
+ {
+ final RealFloatConverter conv = new RealFloatConverter<>();
+ final RandomAccessibleInterval< FloatType > dfieldF = Views.moveAxis(
+ Converters.convert2( dfieldVirt, conv, FloatType::new ),
+ 0, 2 );
+ dfieldIp = ImageJFunctions.wrap( dfieldF, "" ); // title gets set below
}
+ else
+ {
+ final FloatImagePlus< FloatType > dfield = ImagePlusImgs.floats( ipDims );
- final FloatImagePlus< FloatType > dfield = convertToDeformationField( dims, tps, pixelToPhysical, nThreads );
+ // make the "vector" axis the first dimension
+ // 2d displacement fields will have an extraneous singleton z-dimension
+ final RandomAccessibleInterval< FloatType > dfieldImpPerm = Views.dropSingletonDimensions( Views.moveAxis( dfield, 2, 0 ));
+ LoopBuilder.setImages( dfieldVirt, dfieldImpPerm ).multiThreaded( TaskExecutors.fixedThreadPool( nThreads ) ).forEachPixel( (x,y) -> { y.setReal(x.get()); });
+ dfieldIp = dfield.getImagePlus();
+ }
String title = "bigwarp dfield";
- if ( ignoreAffine )
+ if ( splitAffine )
title += " (no affine)";
- final ImagePlus dfieldIp = dfield.getImagePlus();
dfieldIp.setTitle( title );
dfieldIp.getCalibration().pixelWidth = spacing[ 0 ];
dfieldIp.getCalibration().pixelHeight = spacing[ 1 ];
+ dfieldIp.getCalibration().xOrigin = offset[ 0 ];
+ dfieldIp.getCalibration().yOrigin = offset[ 1 ];
+
if( spacing.length > 2 )
+ {
dfieldIp.getCalibration().pixelDepth = spacing[ 2 ];
+ dfieldIp.getCalibration().zOrigin = offset[ 2 ];
+ }
- dfieldIp.show();
return dfieldIp;
}
+ /**
+ * Returns the transformation to be converted to a displacement field.
+ *
+ * @param data the bigwarp data storing the fixed transformations
+ * @param transform the current transformation
+ * @param concatPreTransforms concatenate the current with fixed transformation
+ * @param ignoreAffine whether the output should include the affine part of the transformation
+ * @return the transformation
+ */
+ public static InvertibleRealTransform getTransformation( final BigWarpData> data, final BigWarpTransform transform, final boolean concatPreTransforms,
+ final boolean ignoreAffine )
+ {
+ final InvertibleRealTransform tps = transform.getTransformation( false );
+
+ AffineGet affine = null;
+ if( ignoreAffine )
+ affine = transform.affinePartOfTps();
+
+ if ( !ignoreAffine && ( data == null || !concatPreTransforms ) )
+ {
+ return tps;
+ }
+
+ InvertibleRealTransform preTransform = null;
+ if( data != null && concatPreTransforms )
+ {
+ for ( final Entry< Integer, SourceInfo > entry : data.sourceInfos.entrySet() )
+ {
+ if ( entry.getValue().getTransform() != null )
+ {
+ final RealTransform tform = entry.getValue().getTransform();
+ if( tform instanceof InvertibleRealTransform )
+ {
+ preTransform = ( InvertibleRealTransform ) tform;
+ }
+ else
+ {
+ final WrappedIterativeInvertibleRealTransform< RealTransform > ixfm = new WrappedIterativeInvertibleRealTransform<>( tform );
+ // use same parameters as the passed transform, hopefully that works
+ // alternatively, I could wrap the sequence in the iterative invertible transform - there are tradeoffs here
+ // TODO consider the tradeoffs
+ ixfm.getOptimzer().setMaxIters( transform.getInverseMaxIterations() );
+ ixfm.getOptimzer().setTolerance( transform.getInverseTolerance() );
+ preTransform = ixfm;
+ }
+ break;
+ }
+ }
+ }
+
+ final InvertibleRealTransform startingTransform;
+ if ( preTransform != null || affine != null )
+ {
+ final InvertibleRealTransformSequence seq = new InvertibleRealTransformSequence();
+ seq.add( tps );
+
+ if( affine != null )
+ seq.add( affine.inverse() );
+
+ if( preTransform != null )
+ seq.add( preTransform );
+
+ startingTransform = seq;
+ }
+ else
+ startingTransform = tps;
+
+ return startingTransform;
+ }
+
+
+
+ public static void writeAffineN5(
+ final String n5BasePath,
+ final String n5Dataset,
+ final BigWarpData> data,
+ final BigWarpTransform bwTransform )
+ {
+ final String mvgSpaceName = data != null && data.numMovingSources() > 0 ? data.getMovingSource( 0 ).getSpimSource().getName() : "moving";
+ final String tgtSpaceName = data != null && data.numTargetSources() > 0 ? data.getTargetSource( 0 ).getSpimSource().getName() : "target";
+ final String input= mvgSpaceName;
+ final String output= tgtSpaceName;
+ final String name = input + " to " + output;
+
+ CoordinateTransform> ct = null;
+ final InvertibleCoordinateTransform tform = bwTransform.getCoordinateTransform();
+ switch( bwTransform.getTransformType()) {
+ case BigWarpTransform.TRANSLATION:
+ if (tform instanceof TranslationModel2D)
+ ct = new TranslationCoordinateTransform(name, input, output, ((TranslationModel2D)tform).getTranslation());
+ else if (tform instanceof TranslationModel3D)
+ ct = new TranslationCoordinateTransform(name, input, output, ((TranslationModel3D)tform).getTranslation());
+ break;
+ case BigWarpTransform.SIMILARITY:
+ double[] simparams;
+ if (tform instanceof SimilarityModel2D)
+ {
+ simparams = bwTransform.toImglib2((SimilarityModel2D)tform).getRowPackedCopy();
+ ct = new AffineCoordinateTransform(name, input, output, simparams);
+ }
+ else if (tform instanceof SimilarityModel3D)
+ {
+ simparams = bwTransform.toImglib2((SimilarityModel3D)tform).getRowPackedCopy();
+ ct = new AffineCoordinateTransform(name, input, output, simparams);
+ }
+ break;
+ case BigWarpTransform.ROTATION:
+ double[] rotparams;
+ if (tform instanceof RigidModel2D)
+ {
+ rotparams = bwTransform.toImglib2((RigidModel2D)tform).getRowPackedCopy();
+ ct = new AffineCoordinateTransform(name, input, output, rotparams);
+ }
+ else if (tform instanceof RigidModel3D)
+ {
+ rotparams = bwTransform.toImglib2((RigidModel3D)tform).getRowPackedCopy();
+ ct = new AffineCoordinateTransform(name, input, output, rotparams);
+ }
+ break;
+ case BigWarpTransform.AFFINE:
+ double[] affparams;
+ if (tform instanceof AffineModel2D)
+ {
+ affparams = bwTransform.toImglib2((AffineModel2D)tform).getRowPackedCopy();
+ ct = new AffineCoordinateTransform(name, input, output, affparams);
+ }
+ else if (tform instanceof AffineModel3D)
+ {
+ affparams = bwTransform.toImglib2((AffineModel3D)tform).getRowPackedCopy();
+ ct = new AffineCoordinateTransform(name, input, output, affparams);
+ }
+ break;
+ }
+
+ if( ct == null )
+ return;
+
+
+
+ final String dataset = (n5Dataset == null) ? "" : n5Dataset;
+ final N5Factory factory = new N5Factory().gsonBuilder( NgffTransformations.gsonBuilder() );
+ final N5Writer n5 = factory.openWriter( n5BasePath );
+ NgffTransformations.addCoordinateTransformations(n5, dataset, ct);
+
+ // also add to root
+ NgffTransformations.addCoordinateTransformations(n5, "", ct);
+ }
+
public static void writeN5(
final String n5BasePath,
final LandmarkTableModel ltm,
+ final BigWarpTransform bwTransform,
+ final BigWarpData> data,
final long[] dims,
final double[] spacing,
+ final double[] offset,
+ final String unit,
final int[] spatialBlockSize,
final Compression compression,
- final int nThreads )
+ final int nThreads,
+ final String format,
+ final boolean flatten,
+ final boolean inverse,
+ final double invTolerance,
+ final int invMaxIters ) throws IOException
{
- writeN5( n5BasePath, "dfield", ltm, dims, spacing, spatialBlockSize, compression, nThreads );
+ writeN5( n5BasePath, N5DisplacementField.FORWARD_ATTR, ltm, bwTransform, data, dims, spacing, offset, unit, spatialBlockSize, compression, nThreads, format, flatten, inverse, invTolerance, invMaxIters );
}
- public static void writeN5( final String n5BasePath, final String n5Dataset,
+ public static void writeN5(
+ final String n5BasePath,
+ final String n5Dataset,
final LandmarkTableModel ltm,
+ final BigWarpTransform bwTransform,
+ final BigWarpData> data,
final long[] dims,
final double[] spacing,
+ final double[] offset,
+ final String unit,
final int[] spatialBlockSize,
final Compression compression,
- final int nThreads )
+ final int nThreads,
+ final String format,
+ final boolean flatten,
+ final boolean inverse,
+ final double invTolerance,
+ final int invMaxIters ) throws IOException
{
- final BigWarpTransform bwXfm = new BigWarpTransform( ltm, TransformTypeSelectDialog.TPS );
- final RealTransform tpsTotal = getTpsAffineToggle( bwXfm, false );
+ writeN5( n5BasePath, n5Dataset, ltm, bwTransform, data, dims, spacing, offset, unit, spatialBlockSize, compression, nThreads, format, false, flatten, inverse, invTolerance, invMaxIters );
+ }
+
+ @SuppressWarnings("rawtypes")
+ public static void writeN5(
+ final String n5BasePath,
+ final String n5Dataset,
+ final LandmarkTableModel ltm,
+ final BigWarpTransform bwTransform,
+ final BigWarpData> data,
+ final long[] dims,
+ final double[] spacing,
+ final double[] offset,
+ final String unit,
+ final int[] spatialBlockSizeArg,
+ final Compression compression,
+ final int nThreads,
+ final String format,
+ final boolean splitAffine,
+ final boolean flatten,
+ final boolean inverse,
+ final double invTolerance,
+ final int invMaxIters )
+ {
+ final String dataset = ( n5Dataset == null || n5Dataset.isEmpty() ) ? N5DisplacementField.FORWARD_ATTR : n5Dataset;
+
+ final String mvgSpaceName = getMovingName(data);
+ final String tgtSpaceName = getTargetName(data);
+ final String inputSpace;
+ final String outputSpace;
+ if( inverse )
+ {
+ inputSpace = mvgSpaceName;
+ outputSpace = tgtSpaceName;
+ }
+ else
+ {
+ inputSpace = tgtSpaceName;
+ outputSpace = mvgSpaceName;
+ }
+
+ final BigWarpTransform bwXfm;
+ if( bwTransform == null )
+ bwXfm = new BigWarpTransform( ltm, BigWarpTransform.TPS );
+ else
+ bwXfm = bwTransform;
- AffineGet pixelToPhysical = null;
- if( spacing.length == 2 )
- pixelToPhysical = new Scale2D( spacing );
- else if( spacing.length == 3 )
- pixelToPhysical = new Scale3D( spacing );
+ bwXfm.setInverseMaxIterations( invMaxIters );
+ bwXfm.setInverseTolerance( invTolerance );
- final FloatImagePlus< FloatType > dfieldRaw = convertToDeformationField(
- dims, tpsTotal, pixelToPhysical, nThreads );
+ final InvertibleRealTransform fwdTransform = getTransformation( data, bwXfm, flatten, splitAffine );
+ final InvertibleRealTransform totalTransform = inverse ? fwdTransform.inverse() : fwdTransform;
+ final InvertibleRealTransform transform;
- // this works for both 2d and 3d, it turn out
- final RandomAccessibleInterval< FloatType > dfield =
- Views.permute(
- Views.permute( dfieldRaw,
- 0, 2 ),
- 1, 2 );
+ if (totalTransform.numSourceDimensions() == 2 && bwXfm.isMasked())
+ transform = new InvertibleWrapped2DIntermediate3D(totalTransform);
+ else
+ transform = totalTransform;
+ final int[] spatialBlockSize = fillBlockSize( spatialBlockSizeArg, ltm.getNumdims() );
final int[] blockSize = new int[ spatialBlockSize.length + 1 ];
blockSize[ 0 ] = spatialBlockSize.length;
- for( int i = 0; i < spatialBlockSize.length; i++ )
+ System.arraycopy( spatialBlockSize, 0, blockSize, 1, spatialBlockSize.length );
+
+ final N5Factory factory = new N5Factory().gsonBuilder( NgffTransformations.gsonBuilder() );
+ final N5Writer n5 = factory.openWriter( n5BasePath );
+
+
+ // TODO generalize
+ // get first transformUri for a moving source
+ ReferencedCoordinateTransform> refCt = null;
+ if( !flatten )
+ {
+ for ( final Entry e : data.sourceInfos.entrySet() )
+ {
+ final SourceInfo i = e.getValue();
+ if( i.isMoving() && i.getTransformUri() != null && !i.getTransformUri().isEmpty())
+ {
+ refCt = new ReferencedCoordinateTransform( i.getTransformUri() );
+ break;
+ }
+ }
+ }
+
+
+ final RandomAccessibleInterval< DoubleType > dfield;
+ if( splitAffine )
+ {
+ // the affine part
+ final AffineGet affine = bwXfm.affinePartOfTps();
+ final AffineCoordinateTransform ngffAffine = new AffineCoordinateTransform( affine.getRowPackedCopy() );
+
+ // the variable transform has the affine part removed here
+ dfield = DisplacementFieldTransform.createDisplacementField( transform, new FinalInterval( dims ), spacing, offset );
+
+ if( format.equals( ExportDisplacementFieldFrame.FMT_SLICER ))
+ {
+ final ThreadPoolExecutor exec = new ThreadPoolExecutor( nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue() );
+ SlicerTransformations.saveDisplacementField( n5, dataset, dfield, blockSize, compression, exec );
+ SlicerTransformations.saveAffine( n5, dataset, affine );
+ }
+ else
+ {
+ final DisplacementFieldCoordinateTransform> dfieldTform = NgffTransformations.save( n5, dataset, dfield, inputSpace, outputSpace, spacing, offset, unit, blockSize, compression, nThreads );
+
+ // the transform sequence needs to have a reference to whatever transform was imported, if requested
+ final CoordinateTransform[] ctList = refCt == null ? new CoordinateTransform[]{ dfieldTform, ngffAffine } : new CoordinateTransform[]{ dfieldTform, ngffAffine, refCt };
+
+ // the total transform
+ final SequenceCoordinateTransform totalTform = new SequenceCoordinateTransform( inputSpace, outputSpace, ctList );
+
+ NgffTransformations.addCoordinateTransformations( n5, "/", totalTform );
+ }
+ }
+ else
{
- blockSize[ i + 1 ] = spatialBlockSize[ i ];
+ dfield = DisplacementFieldTransform.createDisplacementField( transform, new FinalInterval( dims ), spacing, offset );
+
+ if( format.equals( ExportDisplacementFieldFrame.FMT_SLICER ))
+ {
+ final ThreadPoolExecutor exec = new ThreadPoolExecutor( nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue() );
+ SlicerTransformations.saveDisplacementField( n5, dataset, dfield, blockSize, compression, exec );
+
+ // for slicer, this affine represents the pixel to physical transformation
+ SlicerTransformations.saveAffine( n5, dataset, new ScaleAndTranslation( spacing, offset ) );
+ }
+ else
+ {
+ final DisplacementFieldCoordinateTransform> dfieldTform = NgffTransformations.save( n5, dataset, dfield, inputSpace, outputSpace, spacing, offset, unit, blockSize, compression, nThreads );
+
+ final CoordinateTransform> ngffTform;
+ if( refCt == null )
+ ngffTform = dfieldTform;
+ else
+ ngffTform = new SequenceCoordinateTransform( refCt.getInput(), dfieldTform.getOutput(), new CoordinateTransform[]{ dfieldTform, refCt });
+
+
+ NgffTransformations.addCoordinateTransformations( n5, "/", ngffTform );
+ }
}
- final N5Writer n5 = new N5Factory().openWriter( n5BasePath );
- N5DisplacementField.save( n5, n5Dataset, null, dfield, spacing, blockSize, compression );
n5.close();
}
- private static RealTransform getTpsAffineToggle( final BigWarpTransform bwXfm, final boolean ignoreAffine )
+ private static String getMovingName( final BigWarpData data ) {
+ if( data != null && data.numMovingSources() > 0 )
+ {
+ final Source src = data.getMovingSource( 0 ).getSpimSource();
+ if( src instanceof WarpedSource )
+ return ((WarpedSource)src).getOriginalName();
+ else
+ return src.getName();
+ }
+ return "moving";
+ }
+
+ private static String getTargetName( final BigWarpData data ) {
+ if( data != null && data.numTargetSources() > 0 )
+ {
+ final Source src = data.getTargetSource( 0 ).getSpimSource();
+ if( src instanceof WarpedSource )
+ return ((WarpedSource)src).getOriginalName();
+ else
+ return src.getName();
+ }
+ return "target";
+ }
+
+ private static int[] fillBlockSize(final int[] blockSize, final int N)
{
- if( ignoreAffine )
+ if( blockSize.length >= N )
+ return blockSize;
+ else
+ {
+ final int[] out = new int[ N ];
+ final int j = blockSize.length - 1;
+ for ( int i = 0; i < N; i++ )
+ {
+ if ( i < blockSize.length )
+ out[ i ] = blockSize[ i ];
+ else
+ out[ i ] = blockSize[ j ];
+ }
+ return out;
+ }
+ }
+
+ private static RealTransform getTpsAffineToggle( final BigWarpTransform bwXfm, final boolean splitAffine )
+ {
+ if ( splitAffine )
{
final ThinPlateR2LogRSplineKernelTransform tps = bwXfm.getTpsBase();
return new ThinplateSplineTransform(
- new ThinPlateR2LogRSplineKernelTransform( tps.getSourceLandmarks(), null, null, tps.getKnotWeights() ));
+ new ThinPlateR2LogRSplineKernelTransform( tps.getSourceLandmarks(), null, null, tps.getKnotWeights() ) );
}
else
- return bwXfm.getTransformation();
+ return bwXfm.getTransformation( false );
}
+ /**
+ * Get the affine part of a thin-plate spline transform. The affine will be 2D (3D)
+ * if the transformation is 2D (3D).
+ *
+ * @param tps the {@link ThinPlateR2LogRSplineKernelTransform} instance
+ * @return the {@link AffineGet} instance.
+ *
+ * @deprecated Use {@link BigWarpTransform} method instead
+ */
+ @Deprecated()
public static AffineGet toAffine( final ThinPlateR2LogRSplineKernelTransform tps )
{
final double[] affineFlat = toFlatAffine( tps );
@@ -379,22 +932,21 @@ public static long[] dimensionsFromImagePlus( final ImagePlus ref_imp )
long[] dims;
if( ref_imp.getNSlices() < 2 )
{
- dims = new long[ 3 ];
+ dims = new long[ 2 ];
dims[ 0 ] = ref_imp.getWidth();
dims[ 1 ] = ref_imp.getHeight();
- dims[ 2 ] = 2;
}
else
{
- dims = new long[ 4 ];
+ dims = new long[ 3 ];
dims[ 0 ] = ref_imp.getWidth();
dims[ 1 ] = ref_imp.getHeight();
- dims[ 2 ] = 3;
- dims[ 3 ] = ref_imp.getNSlices();
+ dims[ 2 ] = ref_imp.getNSlices();
}
return dims;
}
+ @Deprecated
public static FloatImagePlus< FloatType > convertToDeformationField(
final long[] dims,
final RealTransform transform,
@@ -458,6 +1010,7 @@ public static boolean areTransformsTheSame( final RealTransform xfm1, final Real
* the {@link RandomAccessibleInterval} into which the
* displacement field will be written
*/
+ @Deprecated
public static < T extends RealType< T > > void fromRealTransform(
final RealTransform transform,
final AffineGet pixelToPhysical,
@@ -509,6 +1062,7 @@ public static < T extends RealType< T > > void fromRealTransform(
* @param nThreads
* the number of threads
*/
+ @Deprecated
public static < T extends RealType< T > > void fromRealTransform( final RealTransform transform,
final AffineGet pixelToPhysical,
final RandomAccessibleInterval< T > deformationField,
@@ -532,7 +1086,7 @@ public static < T extends RealType< T > > void fromRealTransform( final RealTran
dim2split = 2;
}
- final long del = ( long )( N / nThreads );
+ final long del = ( N / nThreads );
splitPoints[ 0 ] = 0;
splitPoints[ nThreads ] = deformationField.dimension( dim2split );
for( int i = 1; i < nThreads; i++ )
@@ -606,7 +1160,7 @@ public Boolean call()
}
}
- private static Compression getCompression( final String compressionArg )
+ public static Compression getCompression( final String compressionArg )
{
switch (compressionArg) {
case N5Exporter.GZIP_COMPRESSION:
@@ -629,26 +1183,44 @@ private static Compression getCompression( final String compressionArg )
* and can prompt the user for these parameters.
*
*/
- private static class DeformationFieldExportParameters
+ public static class DeformationFieldExportParameters
{
public final String landmarkPath;
public final boolean ignoreAffine;
+ public final String option;
+ public final String inverseOption;
+ public final boolean virtual;
public final int nThreads;
+ public final String format;
+
public final long[] size;
public final double[] spacing;
+ public final double[] offset;
+ public final String unit;
public final String n5Base;
public final String n5Dataset;
public final Compression compression;
public final int[] blockSize;
+ public final double inverseTolerance;
+ public final int inverseMaxIterations;
+
public DeformationFieldExportParameters(
final String landmarkPath,
final boolean ignoreAffine,
+ final String option,
+ final String inverseOption,
+ final double inverseTolerance,
+ final int inverseMaxIterations,
+ final boolean virtual,
final int nThreads,
+ final String format,
final long[] size,
final double[] spacing,
+ final double[] offset,
+ final String unit,
final String n5Base,
final String n5Dataset,
final int[] blockSize,
@@ -656,10 +1228,20 @@ public DeformationFieldExportParameters(
{
this.landmarkPath = landmarkPath;
this.ignoreAffine = ignoreAffine;
+ this.option = option;
+
+ this.inverseOption = inverseOption;
+ this.inverseTolerance = inverseTolerance;
+ this.inverseMaxIterations = inverseMaxIterations;
+
+ this.virtual = virtual;
this.nThreads = nThreads;
+ this.format = format;
this.size = size;
this.spacing = spacing;
+ this.offset = offset;
+ this.unit = unit;
this.n5Base = n5Base;
this.n5Dataset = n5Dataset;
@@ -667,6 +1249,11 @@ public DeformationFieldExportParameters(
this.compression = compression;
}
+ public boolean flatten()
+ {
+ return option.equals( flattenOption );
+ }
+
public static DeformationFieldExportParameters fromDialog(
final boolean promptLandmarks,
final boolean promptReference )
@@ -678,8 +1265,17 @@ public static DeformationFieldExportParameters fromDialog(
gd.addFileField( "landmarks_image_file", "" );
}
- gd.addCheckbox( "Ignore affine part", false );
+ gd.addCheckbox( "Split affine part", false );
+
+ final String[] choices = new String[] { flattenOption, sequenceOption };
+ gd.addChoice( "type", choices, flattenOption );
+
+ final String[] invChoices = new String[] { INVERSE_OPTIONS.FORWARD.toString(), INVERSE_OPTIONS.INVERSE.toString(), INVERSE_OPTIONS.BOTH.toString() };
+ gd.addChoice( "direction", invChoices, INVERSE_OPTIONS.FORWARD.toString() );
+
+ gd.addCheckbox( "virtual", false );
gd.addNumericField( "threads", 1, 0 );
+ gd.addStringField( "format", ExportDisplacementFieldFrame.FMT_NGFF );
gd.addMessage( "Size and spacing" );
final int[] ids = WindowManager.getIDList();
@@ -697,10 +1293,12 @@ public static DeformationFieldExportParameters fromDialog(
}
gd.addStringField( "output size", "");
gd.addStringField( "output spacing", "");
+ gd.addStringField( "output offset", "");
+ gd.addStringField( "output unit", "pixel");
gd.addMessage( "Leave n5 path empty to export as ImagePlus" );
gd.addDirectoryOrFileField( "n5 root path", "" );
- gd.addStringField( "n5 dataset", "");
+ gd.addStringField( "n5 dataset", N5DisplacementField.FORWARD_ATTR );
gd.addStringField( "n5 block size", "32,32,32");
gd.addChoice( "n5 compression", compressionOptions, N5Exporter.GZIP_COMPRESSION );
gd.showDialog();
@@ -713,7 +1311,11 @@ public static DeformationFieldExportParameters fromDialog(
landmarkPath = gd.getNextString();
final boolean ignoreAffine = gd.getNextBoolean();
+ final String option = gd.getNextChoice();
+ final String direction = gd.getNextChoice();
+ final boolean virtual = gd.getNextBoolean();
final int nThreads = ( int ) gd.getNextNumber();
+ final String format = gd.getNextString();
ImagePlus ref_imp = null;
if( promptReference )
@@ -725,6 +1327,8 @@ public static DeformationFieldExportParameters fromDialog(
final String sizeString = gd.getNextString();
final String spacingString = gd.getNextString();
+ final String offsetString = gd.getNextString();
+ final String unitString = gd.getNextString();
final String n5Base = gd.getNextString();
final String n5Dataset = gd.getNextString();
@@ -737,6 +1341,8 @@ public static DeformationFieldExportParameters fromDialog(
final long[] size;
final double[] spacing;
+ final double[] offset;
+ String unit = "pixel";
if( ref_imp == null )
{
if( !sizeString.isEmpty())
@@ -748,6 +1354,14 @@ public static DeformationFieldExportParameters fromDialog(
spacing = Arrays.stream( spacingString.split( "," ) ).mapToDouble( Double::parseDouble ).toArray();
else
spacing = null;
+
+ if( !offsetString.isEmpty() )
+ offset = Arrays.stream( offsetString.split( "," ) ).mapToDouble( Double::parseDouble ).toArray();
+ else
+ offset = null;
+
+ if( !unitString.isEmpty() )
+ unit = unitString;
}
else
{
@@ -757,11 +1371,18 @@ public static DeformationFieldExportParameters fromDialog(
// account for physical units of reference image
spacing = new double[ nd ];
+ offset = new double[ nd ];
spacing[ 0 ] = ref_imp.getCalibration().pixelWidth;
spacing[ 1 ] = ref_imp.getCalibration().pixelHeight;
+ offset[ 0 ] = ref_imp.getCalibration().xOrigin;
+ offset[ 1 ] = ref_imp.getCalibration().yOrigin;
+
if ( nd > 2 )
+ {
spacing[ 2 ] = ref_imp.getCalibration().pixelDepth;
+ offset[ 2 ] = ref_imp.getCalibration().zOrigin;
+ }
size = BigWarpToDeformationFieldPlugIn.dimensionsFromImagePlus( ref_imp );
}
@@ -769,14 +1390,27 @@ public static DeformationFieldExportParameters fromDialog(
return new DeformationFieldExportParameters(
landmarkPath,
ignoreAffine,
+ option,
+ direction, 0.5, 200,
+ virtual,
nThreads,
+ format,
size,
spacing,
+ offset,
+ unit,
n5Base,
n5Dataset,
blockSize,
compression );
}
+
+ public JFrame makeDialog()
+ {
+ final ExportDisplacementFieldFrame frame = new ExportDisplacementFieldFrame( null );
+ return frame;
+ }
+
}
}
diff --git a/src/main/java/bdv/ij/BigWarpTransformExportPlugin.java b/src/main/java/bdv/ij/BigWarpTransformExportPlugin.java
new file mode 100644
index 00000000..157808ec
--- /dev/null
+++ b/src/main/java/bdv/ij/BigWarpTransformExportPlugin.java
@@ -0,0 +1,101 @@
+package bdv.ij;
+
+import java.io.File;
+import java.io.IOException;
+
+import bigwarp.BigWarp;
+import bigwarp.landmarks.LandmarkTableModel;
+import bigwarp.transforms.BigWarpTransform;
+import bigwarp.transforms.NgffTransformations;
+import bigwarp.transforms.WrappedCoordinateTransform;
+import fiji.util.gui.GenericDialogPlus;
+import ij.ImageJ;
+import ij.plugin.PlugIn;
+import mpicbg.models.Model;
+import net.imglib2.realtransform.AffineGet;
+
+public class BigWarpTransformExportPlugin implements PlugIn
+{
+ private boolean promptLandmarks = true;
+
+ private BigWarpTransform bwTransform;
+
+ public static void main( final String[] args )
+ {
+ new ImageJ();
+ new BigWarpTransformExportPlugin().run( null );
+ }
+
+ public void runFromBigWarpInstance( final BigWarp< ? > bw )
+ {
+ promptLandmarks = false;
+ bwTransform = bw.getBwTransform();
+ run( null );
+ }
+
+ @Override
+ public void run( final String arg )
+ {
+ // TODO deal with macro recordability
+
+ final GenericDialogPlus gd = new GenericDialogPlus( "Export BigWarp Transformation" );
+ gd.addMessage( "Transformation export:" );
+ if ( promptLandmarks )
+ {
+ gd.addFileField( "landmarks_file", "" );
+ }
+
+ gd.addChoice( "Transform type",
+ new String[] {
+ BigWarpTransform.AFFINE,
+ BigWarpTransform.SIMILARITY,
+ BigWarpTransform.ROTATION,
+ BigWarpTransform.TRANSLATION },
+ BigWarpTransform.AFFINE );
+
+ gd.addFileField( "Output json file or n5", "" );
+ gd.addStringField( "N5 dataset", "" );
+ gd.addStringField( "N5 attribute name", "" );
+
+ gd.showDialog();
+ if ( gd.wasCanceled() )
+ return;
+
+ String landmarksPath = null;
+ if ( promptLandmarks )
+ {
+ landmarksPath = gd.getNextString();
+ }
+
+ final String transformTypeOption = gd.getNextChoice();
+ final String fileOrN5Root = gd.getNextString();
+ final String n5Dataset = gd.getNextString();
+ final String n5Attr = gd.getNextString();
+
+ if ( bwTransform == null )
+ {
+ try
+ {
+ final LandmarkTableModel ltm = LandmarkTableModel.loadFromCsv( new File( landmarksPath ), false );
+ bwTransform = new BigWarpTransform( ltm, transformTypeOption );
+ }
+ catch ( final IOException e )
+ {
+ e.printStackTrace();
+ return;
+ }
+ }
+
+ String url = fileOrN5Root;
+ if ( !n5Dataset.isEmpty() )
+ url = url + "?" + n5Dataset;
+
+ if ( !n5Attr.isEmpty() )
+ url = url + "#" + n5Attr;
+
+ final WrappedCoordinateTransform wct = ( WrappedCoordinateTransform ) bwTransform.getTransformation( false );
+ final AffineGet affine = bwTransform.toImglib2( ( Model< ? > ) wct.getTransform() );
+ NgffTransformations.save( url, NgffTransformations.createAffine( affine ) );
+ }
+
+}
diff --git a/src/main/java/bdv/img/RealTransformedSource.java b/src/main/java/bdv/img/RealTransformedSource.java
new file mode 100644
index 00000000..1701f84c
--- /dev/null
+++ b/src/main/java/bdv/img/RealTransformedSource.java
@@ -0,0 +1,266 @@
+/*-
+ * #%L
+ * BigWarp plugin for Fiji.
+ * %%
+ * Copyright (C) 2015 - 2022 Howard Hughes Medical Institute.
+ * %%
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program. If not, see
+ * .
+ * #L%
+ */
+package bdv.img;
+
+import java.util.function.Supplier;
+
+import bdv.viewer.Interpolation;
+import bdv.viewer.Source;
+import bdv.viewer.SourceAndConverter;
+import bdv.viewer.render.DefaultMipmapOrdering;
+import bdv.viewer.render.MipmapOrdering;
+import mpicbg.spim.data.sequence.VoxelDimensions;
+import net.imglib2.Interval;
+import net.imglib2.RandomAccessibleInterval;
+import net.imglib2.RealRandomAccessible;
+import net.imglib2.realtransform.AffineTransform3D;
+import net.imglib2.realtransform.BoundingBoxEstimation;
+import net.imglib2.realtransform.InvertibleRealTransform;
+import net.imglib2.realtransform.RealTransform;
+import net.imglib2.realtransform.RealTransformRealRandomAccessible;
+import net.imglib2.realtransform.RealTransformSequence;
+import net.imglib2.realtransform.inverse.WrappedIterativeInvertibleRealTransform;
+import net.imglib2.view.Views;
+
+public class RealTransformedSource < T > implements Source< T >, MipmapOrdering
+{
+
+ public static < T > SourceAndConverter< T > wrap( final SourceAndConverter< T > wrap, final String name, int ndims )
+ {
+ return new SourceAndConverter< T >(
+ new RealTransformedSource< T >( wrap.getSpimSource(), name ),
+ wrap.getConverter(),
+ wrap.asVolatile() == null ? null : wrap( wrap.asVolatile(), name, ndims ) );
+ }
+
+ /**
+ * The wrapped {@link Source}.
+ */
+ private final Source< T > source;
+
+ private final String name;
+
+ /**
+ * This is either the {@link #source} itself, if it implements
+ * {@link MipmapOrdering}, or a {@link DefaultMipmapOrdering}.
+ */
+ private final MipmapOrdering sourceMipmapOrdering;
+
+ private InvertibleRealTransform xfm;
+
+ private Interval[] boundingIntervalsPerLevel;
+
+ private boolean isTransformed;
+
+ private final Supplier< Boolean > boundingBoxCullingSupplier;
+
+ private BoundingBoxEstimation bboxEst;
+
+ public RealTransformedSource( final Source< T > source, final String name )
+ {
+ this( source, name, null );
+ }
+
+ public RealTransformedSource( final Source< T > source, final String name,
+ final Supplier< Boolean > doBoundingBoxCulling )
+ {
+ this.source = source;
+ this.name = name;
+ this.isTransformed = false;
+ this.boundingBoxCullingSupplier = doBoundingBoxCulling;
+ this.xfm = null;
+
+ bboxEst = new BoundingBoxEstimation( BoundingBoxEstimation.Method.FACES, 5 );
+ boundingIntervalsPerLevel = new Interval[source.getNumMipmapLevels()];
+
+ sourceMipmapOrdering = MipmapOrdering.class.isInstance( source ) ?
+ ( MipmapOrdering ) source : new DefaultMipmapOrdering( source );
+ }
+
+ @Override
+ public boolean isPresent( final int t )
+ {
+ return source.isPresent( t );
+ }
+
+ @Override
+ public boolean doBoundingBoxCulling()
+ {
+ if( boundingBoxCullingSupplier != null )
+ return boundingBoxCullingSupplier.get();
+ else
+ return ( !isTransformed ) && ( source.doBoundingBoxCulling() );
+ }
+
+ public void updateTransform( RealTransform xfm )
+ {
+ if( xfm instanceof InvertibleRealTransform )
+ this.xfm = (InvertibleRealTransform)xfm;
+ else
+ this.xfm = new WrappedIterativeInvertibleRealTransform<>(xfm);
+
+ updateBoundingIntervals();
+ }
+
+ protected void updateBoundingIntervals()
+ {
+ for( int i = 0; i < getNumMipmapLevels(); i++ )
+ {
+ boundingIntervalsPerLevel[i] = estimateBoundingInterval(0, i);
+ }
+ }
+
+ public void setIsTransformed( boolean isTransformed )
+ {
+ this.isTransformed = isTransformed;
+ }
+
+ public void setBoundingBoxEstimator( final BoundingBoxEstimation bboxEst )
+ {
+ this.bboxEst = bboxEst;
+ }
+
+ public boolean isTransformed( )
+ {
+ return isTransformed;
+ }
+
+ public Source< T > getWrappedSource()
+ {
+ return source;
+ }
+
+ @Override
+ public RandomAccessibleInterval< T > getSource( final int t, final int level )
+ {
+// if( isTransformed )
+// {
+// return Views.interval(
+// Views.raster( getInterpolatedSource( t, level, Interpolation.NEARESTNEIGHBOR ) ),
+// boundingIntervalsPerLevel[level] );
+// }
+// return source.getSource( t, level );
+
+ if( isTransformed )
+ {
+ final RealTransformRealRandomAccessible interpSrc = (RealTransformRealRandomAccessible)getInterpolatedSource( t, level, Interpolation.NEARESTNEIGHBOR );
+
+ final AffineTransform3D transform = new AffineTransform3D();
+ source.getSourceTransform( t, level, transform );
+ final RealTransformSequence totalInverseTransform = new RealTransformSequence();
+ totalInverseTransform.add( transform.inverse() );
+ totalInverseTransform.add( transform.inverse() );
+ totalInverseTransform.add( transform );
+
+ return Views.interval( Views.raster(interpSrc), boundingIntervalsPerLevel[level] );
+ }
+ else
+ return source.getSource( t, level );
+ }
+
+ private Interval estimateBoundingInterval( final int t, final int level )
+ {
+ if( xfm == null )
+ {
+ return source.getSource( t, level );
+ }
+ else
+ {
+ // getSource can be called by multiple threads, so need ensure application of
+ // the transform is thread safe here by copying
+ return bboxEst.estimatePixelInterval( xfm.copy().inverse(), source.getSource( t, level ) );
+ }
+ }
+
+ @Override
+ public RealRandomAccessible< T > getInterpolatedSource( final int t, final int level, final Interpolation method )
+ {
+
+ final RealRandomAccessible realSrc = source.getInterpolatedSource( t, level, method );
+ if( isTransformed && xfm != null )
+ {
+ final AffineTransform3D transform = new AffineTransform3D();
+ source.getSourceTransform( t, level, transform );
+
+ final RealTransformSequence totalTransform = new RealTransformSequence();
+ totalTransform.add( transform.inverse() );
+ totalTransform.add( xfm );
+ totalTransform.add( transform );
+
+ return new RealTransformRealRandomAccessible< T, RealTransform >( realSrc, xfm );
+ }
+ else
+ {
+ return realSrc;
+ }
+ }
+
+ @Override
+ public synchronized void getSourceTransform( final int t, final int level, final AffineTransform3D transform )
+ {
+ if( isTransformed )
+ transform.identity();
+ else
+ source.getSourceTransform( t, level, transform );
+ }
+
+ public RealTransform getTransform()
+ {
+ return xfm;
+ }
+
+ @Override
+ public T getType()
+ {
+ return source.getType();
+ }
+
+ @Override
+ public String getName()
+ {
+ return source.getName() + "_" + name;
+ }
+
+ public String getOriginalName()
+ {
+ return getWrappedSource().getName();
+ }
+
+ @Override
+ public VoxelDimensions getVoxelDimensions()
+ {
+ return source.getVoxelDimensions();
+ }
+
+ @Override
+ public int getNumMipmapLevels()
+ {
+ return source.getNumMipmapLevels();
+ }
+
+ @Override
+ public synchronized MipmapHints getMipmapHints( final AffineTransform3D screenTransform, final int timepoint, final int previousTimepoint )
+ {
+ return sourceMipmapOrdering.getMipmapHints( screenTransform, timepoint, previousTimepoint );
+ }
+
+}
diff --git a/src/main/java/bdv/img/WarpedSource.java b/src/main/java/bdv/img/WarpedSource.java
index bd70dd1c..296d804d 100644
--- a/src/main/java/bdv/img/WarpedSource.java
+++ b/src/main/java/bdv/img/WarpedSource.java
@@ -8,12 +8,12 @@
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 2 of the
* License, or (at your option) any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* .
@@ -28,16 +28,18 @@
import bdv.viewer.SourceAndConverter;
import bdv.viewer.render.DefaultMipmapOrdering;
import bdv.viewer.render.MipmapOrdering;
-import bigwarp.BigWarpExporter;
import mpicbg.spim.data.sequence.VoxelDimensions;
import net.imglib2.Interval;
import net.imglib2.RandomAccessibleInterval;
import net.imglib2.RealRandomAccessible;
import net.imglib2.realtransform.AffineTransform3D;
import net.imglib2.realtransform.BoundingBoxEstimation;
+import net.imglib2.realtransform.InvertibleRealTransform;
import net.imglib2.realtransform.RealTransform;
import net.imglib2.realtransform.RealTransformRealRandomAccessible;
+import net.imglib2.realtransform.RealTransformSequence;
import net.imglib2.realtransform.RealViews;
+import net.imglib2.realtransform.inverse.WrappedIterativeInvertibleRealTransform;
import net.imglib2.view.Views;
public class WarpedSource < T > implements Source< T >, MipmapOrdering
@@ -64,10 +66,12 @@ public static < T > SourceAndConverter< T > wrap( final SourceAndConverter< T >
*/
private final MipmapOrdering sourceMipmapOrdering;
- private RealTransform xfm;
+ private InvertibleRealTransform xfm;
+
+ private Interval[] boundingIntervalsPerLevel;
private boolean isTransformed;
-
+
private final Supplier< Boolean > boundingBoxCullingSupplier;
private BoundingBoxEstimation bboxEst;
@@ -84,9 +88,11 @@ public WarpedSource( final Source< T > source, final String name,
this.name = name;
this.isTransformed = false;
this.boundingBoxCullingSupplier = doBoundingBoxCulling;
-
this.xfm = null;
+ bboxEst = new BoundingBoxEstimation( BoundingBoxEstimation.Method.FACES, 5 );
+ boundingIntervalsPerLevel = new Interval[source.getNumMipmapLevels()];
+
sourceMipmapOrdering = MipmapOrdering.class.isInstance( source ) ?
( MipmapOrdering ) source : new DefaultMipmapOrdering( source );
}
@@ -96,7 +102,7 @@ public boolean isPresent( final int t )
{
return source.isPresent( t );
}
-
+
@Override
public boolean doBoundingBoxCulling()
{
@@ -108,14 +114,27 @@ public boolean doBoundingBoxCulling()
public void updateTransform( RealTransform xfm )
{
- this.xfm = xfm;
+ if( xfm instanceof InvertibleRealTransform )
+ this.xfm = (InvertibleRealTransform)xfm;
+ else
+ this.xfm = new WrappedIterativeInvertibleRealTransform<>(xfm);
+
+ updateBoundingIntervals();
+ }
+
+ protected void updateBoundingIntervals()
+ {
+ for( int i = 0; i < getNumMipmapLevels(); i++ )
+ {
+ boundingIntervalsPerLevel[i] = estimateBoundingInterval(0, i);
+ }
}
-
+
public void setIsTransformed( boolean isTransformed )
{
this.isTransformed = isTransformed;
}
-
+
public void setBoundingBoxEstimator( final BoundingBoxEstimation bboxEst )
{
this.bboxEst = bboxEst;
@@ -138,44 +157,32 @@ public RandomAccessibleInterval< T > getSource( final int t, final int level )
{
return Views.interval(
Views.raster( getInterpolatedSource( t, level, Interpolation.NEARESTNEIGHBOR ) ),
- estimateBoundingInterval( t, level ));
+ boundingIntervalsPerLevel[level] );
+
}
return source.getSource( t, level );
}
private Interval estimateBoundingInterval( final int t, final int level )
{
- return bboxEst.estimatePixelInterval( xfm, source.getSource( t, level ) );
+ if( xfm == null )
+ {
+ return source.getSource( t, level );
+ }
+ else
+ {
+ // getSource can be called by multiple threads, so need ensure application of
+ // the transform is thread safe here by copying
+ return bboxEst.estimatePixelInterval( xfm.copy().inverse(), source.getSource( t, level ) );
+ }
}
@Override
public RealRandomAccessible< T > getInterpolatedSource( final int t, final int level, final Interpolation method )
{
-// RealRandomAccessible realSrc = source.getInterpolatedSource( t, level, method );
-// if( isTransformed && xfm != null )
-// {
-// final AffineTransform3D transform = new AffineTransform3D();
-// source.getSourceTransform( t, level, transform );
-//
-// RealTransformSequence totalTransform = new RealTransformSequence();
-//// totalTransform.add( transform );
-//// totalTransform.add( xfm );
-//// totalTransform.add( transform.inverse() );
-//
-// totalTransform.add( transform.inverse() );
-// totalTransform.add( xfm );
-// totalTransform.add( transform );
-//
-// return new RealTransformRealRandomAccessible< T, RealTransform >( realSrc, xfm );
-// }
-// else
-// {
-// return realSrc;
-// }
-
- final RealRandomAccessible< T > sourceRealAccessible = source.getInterpolatedSource( t, level, method );
- if( isTransformed )
+ final RealRandomAccessible realSrc = source.getInterpolatedSource( t, level, method );
+ if( isTransformed && xfm != null )
{
final AffineTransform3D transform = new AffineTransform3D();
source.getSourceTransform( t, level, transform );
@@ -184,11 +191,11 @@ public RealRandomAccessible< T > getInterpolatedSource( final int t, final int l
if( xfm == null )
return srcRaTransformed;
else
- return new RealTransformRealRandomAccessible< T, RealTransform >( srcRaTransformed, xfm );
+ return new RealTransformRealRandomAccessible< T, RealTransform >( srcRaTransformed, xfm);
}
else
{
- return sourceRealAccessible;
+ return realSrc;
}
}
@@ -218,6 +225,11 @@ public String getName()
return source.getName() + "_" + name;
}
+ public String getOriginalName()
+ {
+ return getWrappedSource().getName();
+ }
+
@Override
public VoxelDimensions getVoxelDimensions()
{
diff --git a/src/main/java/bdv/ui/convertersetupeditor/MaskBoundsRangePanel.java b/src/main/java/bdv/ui/convertersetupeditor/MaskBoundsRangePanel.java
new file mode 100644
index 00000000..e617fa48
--- /dev/null
+++ b/src/main/java/bdv/ui/convertersetupeditor/MaskBoundsRangePanel.java
@@ -0,0 +1,29 @@
+package bdv.ui.convertersetupeditor;
+
+import bdv.util.BoundedRange;
+import bigwarp.BigWarp;
+
+public class MaskBoundsRangePanel extends BoundedRangePanel {
+
+ private static final long serialVersionUID = -4065636040399818543L;
+
+ public MaskBoundsRangePanel(BigWarp> bw ) {
+ this(bw, new BoundedRange(0, 1, 0, 1));
+ }
+
+ public MaskBoundsRangePanel( final BigWarp> bw, final BoundedRange range ) {
+ super( range );
+ setConsistent( true );
+ setup( bw );
+ }
+
+ protected void setup( BigWarp> bw ) {
+ changeListeners().add(new ChangeListener() {
+ @Override
+ public void boundedRangeChanged() {
+ bw.getBwTransform().setMaskIntensityBounds( getRange().getMin(), getRange().getMax());
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/bdv/viewer/BigWarpDragOverlay.java b/src/main/java/bdv/viewer/BigWarpDragOverlay.java
index 5af16790..b6979a07 100644
--- a/src/main/java/bdv/viewer/BigWarpDragOverlay.java
+++ b/src/main/java/bdv/viewer/BigWarpDragOverlay.java
@@ -81,13 +81,11 @@ public void paint( final Graphics2D g )
{
arad = viewer.getSettings().getSpotSize();
baseColor = viewer.getSettings().getSpotColor();
-
- // System.out.println("BigWarpDragOverlay - PAINT" );
+
if( inProgress )
{
viewer.state().getViewerTransform( transform );
- //System.out.println("BigWarpDragOverlay - PAINT IN PROGRESS" );
transform.apply( movingPoint, movingPointScreen );
transform.apply( targetPoint, targetPointScreen );
diff --git a/src/main/java/bdv/viewer/BigWarpLandmarkFrame.java b/src/main/java/bdv/viewer/BigWarpLandmarkFrame.java
index 52116094..b54e90b9 100644
--- a/src/main/java/bdv/viewer/BigWarpLandmarkFrame.java
+++ b/src/main/java/bdv/viewer/BigWarpLandmarkFrame.java
@@ -31,9 +31,11 @@
import javax.swing.WindowConstants;
import org.scijava.ui.behaviour.util.InputActionBindings;
+import org.scijava.ui.behaviour.util.TriggerBehaviourBindings;
import bdv.gui.BigWarpLandmarkPanel;
import bigwarp.BigWarp;
+import bigwarp.ui.keymap.KeymapManager;
public class BigWarpLandmarkFrame extends JFrame {
@@ -43,16 +45,21 @@ public class BigWarpLandmarkFrame extends JFrame {
private BigWarpLandmarkPanel lmPanel;
+ private final KeymapManager keymapManager;
+
private final InputActionBindings keybindings;
- public BigWarpLandmarkFrame( String name, BigWarpLandmarkPanel panel, BigWarp bw )
+ private final TriggerBehaviourBindings triggerbindings;
+
+ public BigWarpLandmarkFrame( String name, BigWarpLandmarkPanel panel, BigWarp< ? > bw, KeymapManager keymapManager )
{
super( name, AWTUtils.getSuitableGraphicsConfiguration( AWTUtils.RGB_COLOR_MODEL ) );
this.bw = bw;
+ this.keymapManager = keymapManager;
setLandmarkPanel( panel );
keybindings = new InputActionBindings();
-
+ triggerbindings = new TriggerBehaviourBindings();
// do nothing because the closeAll method in bigWarp is responsible for calling dispose and cleaning up
setDefaultCloseOperation( WindowConstants.DO_NOTHING_ON_CLOSE );
@@ -63,11 +70,11 @@ public void windowClosing( final WindowEvent e )
BigWarpLandmarkFrame.this.bw.closeAll();
}
} );
-
+
SwingUtilities.replaceUIActionMap( lmPanel.getJTable(), keybindings.getConcatenatedActionMap() );
- SwingUtilities.replaceUIInputMap( lmPanel.getJTable(), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, keybindings.getConcatenatedInputMap() );
+ SwingUtilities.replaceUIInputMap( lmPanel.getJTable(), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, keybindings.getConcatenatedInputMap() );
}
-
+
public void setLandmarkPanel( BigWarpLandmarkPanel panel )
{
this.lmPanel = panel;
@@ -81,5 +88,4 @@ public InputActionBindings getKeybindings()
return keybindings;
}
-
}
diff --git a/src/main/java/bdv/viewer/BigWarpOverlay.java b/src/main/java/bdv/viewer/BigWarpOverlay.java
index 23ce4368..2ab164aa 100644
--- a/src/main/java/bdv/viewer/BigWarpOverlay.java
+++ b/src/main/java/bdv/viewer/BigWarpOverlay.java
@@ -8,12 +8,12 @@
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 2 of the
* License, or (at your option) any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* .
@@ -40,70 +40,51 @@
public class BigWarpOverlay {
private BigWarpViewerPanel viewer;
-
- protected JTable table;
- protected LandmarkTableModel landmarkModel;
+ private BigWarpLandmarkPanel landmarkPanel;
protected RealTransform estimatedXfm;
-
+
protected boolean isTransformed = false;
private int hoveredIndex;
protected final boolean isMoving;
- protected final boolean is3d;
-
+
protected final double[] spot;
- protected final double[] viewerCoords;
+ protected final double[] viewerCoords;
/** The transform for the viewer current viewpoint. */
private final AffineTransform3D transform = new AffineTransform3D();
-
- public BigWarpOverlay( final BigWarpViewerPanel viewer, BigWarpLandmarkPanel landmarkpanel )
- {
- this.viewer = viewer;
- this.table = landmarkpanel.getJTable();
- this.landmarkModel = landmarkpanel.getTableModel();
- if( landmarkModel.getNumdims() == 3 )
- is3d = true;
- else
- is3d = false;
+ public BigWarpOverlay( final BigWarpViewerPanel viewer, BigWarpLandmarkPanel landmarkPanel )
+ {
+ this.viewer = viewer;
+ setLandmarkPanel(landmarkPanel);
isMoving = viewer.getIsMoving();
spot = new double[ 3 ];
viewerCoords = new double[ 3 ];
}
-
public int getHoveredIndex()
{
return hoveredIndex;
}
-
public void setHoveredIndex( int hoveredIndex )
{
this.hoveredIndex = hoveredIndex;
}
+ public void setLandmarkPanel( final BigWarpLandmarkPanel landmarkPanel )
+ {
+ this.landmarkPanel = landmarkPanel;
+ }
public void paint( final Graphics2D g )
{
- // Save graphic device original settings
- final Composite originalComposite = g.getComposite();
- final Stroke originalStroke = g.getStroke();
- final Color originalColor = g.getColor();
-
- // get selected points
- int[] selectedRows = table.getSelectedRows();
- Arrays.sort( selectedRows );
- boolean[] isSelected = new boolean[ landmarkModel.getRowCount() ];
- for( int i : selectedRows )
- isSelected[ i ] = true;
-
/*
* Draw spots.
@@ -111,17 +92,34 @@ public void paint( final Graphics2D g )
if ( viewer.getSettings().areLandmarksVisible() )
{
+ final LandmarkTableModel landmarkModel = landmarkPanel.getTableModel();
+ final JTable table = landmarkPanel.getJTable();
+ final boolean is3d = landmarkPanel.numDimensions() == 3;
+
+ // Save graphic device original settings
+ final Composite originalComposite = g.getComposite();
+ final Stroke originalStroke = g.getStroke();
+ final Color originalColor = g.getColor();
+
+ // get selected points
+ final int[] selectedRows = table.getSelectedRows();
+ Arrays.sort( selectedRows );
+ final boolean[] isSelected = new boolean[ landmarkModel.getRowCount() ];
+ for( final int i : selectedRows )
+ isSelected[ i ] = true;
+
+
final BasicStroke hlStroke = new BasicStroke( (int)viewer.getSettings().strokeWeight );
- final BasicStroke selStroke = new BasicStroke( (int)viewer.getSettings().strokeWeight / 2 );
+ // final BasicStroke selStroke = new BasicStroke( (int)viewer.getSettings().strokeWeight / 2 );
- final double radiusRatio = ( Double ) viewer.getSettings().get(
+ final double radiusRatio = ( Double ) viewer.getSettings().get(
BigWarpViewerSettings.KEY_SPOT_RADIUS_RATIO );
-
+
final double radius = viewer.getSettings().getSpotSize();
Stroke stroke;
stroke = BigWarpViewerSettings.NORMAL_STROKE;
-
+
FontMetrics fm = null;
Font font = null;
int fonthgt = 0;
@@ -143,7 +141,7 @@ public void paint( final Graphics2D g )
textBoxColorHl = new Color( color.getRed(), color.getGreen(), color.getBlue(), 255 );
textBoxColor = new Color( color.getRed(), color.getGreen(), color.getBlue(), 128 );
- } final Color desaturatedColor = null;
+ }
final int nRows = landmarkModel.getRowCount();
for( int index = 0; index < nRows; index++ )
@@ -195,13 +193,13 @@ public void paint( final Graphics2D g )
else
arad = rad;
- double srad = arad + strokeW;
+ final double srad = arad + strokeW;
// vary size
- g.fillOval( ( int ) ( viewerCoords[ 0 ] - arad ),
- ( int ) ( viewerCoords[ 1 ] - arad ),
+ g.fillOval( ( int ) ( viewerCoords[ 0 ] - arad ),
+ ( int ) ( viewerCoords[ 1 ] - arad ),
( int ) ( 2 * arad + 1 ), ( int ) ( 2 * arad + 1) );
-
+
if( isSelected[ index ] )
{
g.setStroke( hlStroke );
@@ -228,31 +226,31 @@ public void paint( final Graphics2D g )
{
final int tx = ( int ) ( viewerCoords[ 0 ] + arad + 5 );
final int ty = ( int ) viewerCoords[ 1 ];
-
- String name = landmarkModel.getNames().get(index);
- int strwidth = fm.stringWidth( name );
-
+
+ final String name = landmarkModel.getNames().get(index);
+ final int strwidth = fm.stringWidth( name );
+
if( hoveredIndex == index )
g.setColor( textBoxColorHl );
else
g.setColor( textBoxColor );
-
+
g.fillRect( tx - 1, ty - fonthgt + 2, strwidth + 2, fonthgt);
-
+
g.setColor( Color.BLACK );
g.setFont( font );
g.drawString( name, tx, ty );
-
+
}
}
-
}
+
+ // Restore graphic device original settings
+ g.setComposite( originalComposite );
+ g.setStroke( originalStroke );
+ g.setColor( originalColor );
}
- // Restore graphic device original settings
- g.setComposite( originalComposite );
- g.setStroke( originalStroke );
- g.setColor( originalColor );
}
@@ -267,17 +265,17 @@ public void setViewerState( final ViewerState state )
*/
state.getViewerTransform( transform );
}
-
+
public void setEstimatedTransform( final RealTransform estimatedXfm )
{
this.estimatedXfm = estimatedXfm;
}
-
+
public boolean getIsTransformed()
{
return isTransformed;
}
-
+
public void setIsTransformed( boolean isTransformed )
{
this.isTransformed = isTransformed;
diff --git a/src/main/java/bdv/viewer/BigWarpViewerPanel.java b/src/main/java/bdv/viewer/BigWarpViewerPanel.java
index 1bb9b2cf..de353058 100644
--- a/src/main/java/bdv/viewer/BigWarpViewerPanel.java
+++ b/src/main/java/bdv/viewer/BigWarpViewerPanel.java
@@ -8,12 +8,12 @@
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 2 of the
* License, or (at your option) any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* .
@@ -28,6 +28,10 @@
import bdv.util.Prefs;
import bdv.viewer.animate.RotationAnimator;
import bdv.viewer.animate.SimilarityTransformAnimator3D;
+import bdv.viewer.overlay.BigWarpMaskSphereOverlay;
+import bigwarp.BigWarp;
+import bigwarp.BigWarpData;
+import bigwarp.source.SourceInfo;
import bigwarp.util.Rotation2DHelpers;
import java.awt.Graphics;
import java.awt.Graphics2D;
@@ -45,12 +49,16 @@ public class BigWarpViewerPanel extends ViewerPanel
public static final int TARGET_GROUP_INDEX = 1;
+ protected final BigWarpData> bwData;
+
protected BigWarpViewerSettings viewerSettings;
protected BigWarpOverlay overlay;
protected BigWarpDragOverlay dragOverlay;
+ protected BigWarpMaskSphereOverlay maskOverlay;
+
protected boolean isMoving;
protected boolean updateOnDrag;
@@ -59,10 +67,6 @@ public class BigWarpViewerPanel extends ViewerPanel
protected int ndims;
- final protected int[] movingSourceIndexList;
-
- final protected int[] targetSourceIndexList;
-
protected boolean boxOverlayVisible = true;
protected boolean textOverlayVisible = true;
@@ -74,35 +78,33 @@ public class BigWarpViewerPanel extends ViewerPanel
ViewerOptions options;
- public BigWarpViewerPanel( final List< SourceAndConverter< ? > > sources, final BigWarpViewerSettings viewerSettings, final CacheControl cache, boolean isMoving,
- int[] movingSourceIndexList, int[] targetSourceIndexList )
+ @SuppressWarnings("rawtypes")
+ public BigWarpViewerPanel( final BigWarpData bwData , final BigWarpViewerSettings viewerSettings, final CacheControl cache, boolean isMoving )
{
- this( sources, viewerSettings, cache, BigWarpViewerOptions.options(), isMoving, movingSourceIndexList, targetSourceIndexList );
+ this( bwData, viewerSettings, cache, BigWarpViewerOptions.options(), isMoving );
}
- public BigWarpViewerPanel( final List< SourceAndConverter< ? > > sources, final BigWarpViewerSettings viewerSettings, final CacheControl cache, final BigWarpViewerOptions optional, boolean isMoving,
- int[] movingSourceIndexList, int[] targetSourceIndexList )
+ @SuppressWarnings("unchecked")
+ public BigWarpViewerPanel( final BigWarpData bwData, final BigWarpViewerSettings viewerSettings, final CacheControl cache, final BigWarpViewerOptions optional, boolean isMoving )
{
- super( sources, 1, cache, optional.getViewerOptions( isMoving ) );
+ // TODO compiler complains if the first argument is 'final BigWarpData> bwData'
+ super( bwData.sources, 1, cache, optional.getViewerOptions( isMoving ) );
+ this.bwData = bwData;
this.viewerSettings = viewerSettings;
this.isMoving = isMoving;
this.updateOnDrag = !isMoving; // update on drag only for the fixed
// image by default
- this.movingSourceIndexList = movingSourceIndexList;
- this.targetSourceIndexList = targetSourceIndexList;
-
getDisplay().overlays().add( g -> {
if ( null != overlay ) {
overlay.setViewerState( state() );
overlay.paint( ( Graphics2D ) g );
}
if ( dragOverlay != null ) {
- //dragOverlay.setViewerState( state );
dragOverlay.paint( ( Graphics2D ) g );
}
} );
- updateGrouping();
+ //updateGrouping();
}
@Override
@@ -114,29 +116,29 @@ public ViewerOptions.Values getOptionValues()
public void precomputeRotations2d( final AffineTransform3D initialViewTransform )
{
orthoTransforms = new ArrayList<>();
- AffineTransform3D rot = new AffineTransform3D();
+ final AffineTransform3D rot = new AffineTransform3D();
rot.rotate( 2, -Math.PI / 2 );
AffineTransform3D xfm = initialViewTransform;
orthoTransforms.add( xfm );
for( int i = 1; i < 4; i++ )
{
- AffineTransform3D newXfm = xfm.copy();
+ final AffineTransform3D newXfm = xfm.copy();
newXfm.rotate( 2, -Math.PI/2);
orthoTransforms.add( newXfm );
xfm = newXfm;
}
}
- public void toggleTextOverlayVisible()
- {
- textOverlayVisible = !textOverlayVisible;
- }
-
- public void toggleBoxOverlayVisible()
- {
- boxOverlayVisible = !boxOverlayVisible;
- }
+// public void toggleTextOverlayVisible()
+// {
+// textOverlayVisible = !textOverlayVisible;
+// }
+//
+// public void toggleBoxOverlayVisible()
+// {
+// boxOverlayVisible = !boxOverlayVisible;
+// }
public void setHoveredIndex( int index )
{
@@ -154,23 +156,30 @@ public void setHoveredIndex( int index )
/**
* Makes the first group contain all the moving images and the second group
* contain all the fixed images
+ *
+ * @deprecated use {@link BigWarp#createMovingTargetGroups}
*
* @return the number sources in the moving group
*/
+ @Deprecated
public int updateGrouping()
{
final SynchronizedViewerState state = state();
synchronized ( state )
{
- // TODO: work backwards to find out whether movingSourceIndexList
- // and targetSourceIndexList are required, or whether a
- // List> can be used directly
final List< SourceAndConverter< ? > > moving = new ArrayList<>();
- for ( int i : movingSourceIndexList )
- moving.add( state.getSources().get( i ) );
final List< SourceAndConverter< ? > > target = new ArrayList<>();
- for ( int i : targetSourceIndexList )
- target.add( state.getSources().get( i ) );
+
+ int idx = 0;
+ for ( final SourceInfo sourceInfo : bwData.sourceInfos.values() )
+ {
+ if (sourceInfo.isMoving()) {
+ moving.add( state.getSources().get( idx ) );
+ } else {
+ target.add( state.getSources().get( idx ) );
+ }
+ idx++;
+ }
state.clearGroups();
@@ -202,7 +211,12 @@ public int updateGrouping()
public boolean isInFixedImageSpace()
{
- return !isMoving || ( ( WarpedSource< ? > ) ( state().getSources().get( movingSourceIndexList[ 0 ] ).getSpimSource() ) ).isTransformed();
+ if( bwData.numMovingSources() < 1 )
+ return true;
+ else
+ {
+ return !isMoving || ( ( WarpedSource< ? > ) ( ( bwData.getMovingSource( 0 )).getSpimSource() ) ).isTransformed();
+ }
}
public boolean doUpdateOnDrag()
@@ -236,10 +250,26 @@ public void addDragOverlay( BigWarpDragOverlay dragOverlay ){
this.dragOverlay = dragOverlay;
}
+ public void addOverlay( OverlayRenderer overlay )
+ {
+ super.getDisplay().overlays().add( overlay );
+ }
+
public BigWarpDragOverlay getDragOverlay(){
return dragOverlay;
}
+ public BigWarpMaskSphereOverlay getMaskOverlay()
+ {
+ return maskOverlay;
+ }
+
+ public void setMaskOverlay( final BigWarpMaskSphereOverlay maskOverlay )
+ {
+ this.maskOverlay = maskOverlay;
+ addOverlay( maskOverlay );
+ }
+
public boolean getIsMoving()
{
return isMoving;
@@ -288,7 +318,7 @@ public synchronized void rotateView2d( boolean isClockwise )
newTransform = Rotation2DHelpers.targetViewerTransform2d( transform , isClockwise );
break;
}
- catch(Exception e)
+ catch(final Exception e)
{
if( isClockwise )
transform.rotate( 2, -0.1 );
@@ -297,7 +327,7 @@ public synchronized void rotateView2d( boolean isClockwise )
}
}
- double[] qNew = new double[ 4 ];
+ final double[] qNew = new double[ 4 ];
Affine3DHelpers.extractRotation( newTransform, qNew );
setTransformAnimator( new RotationAnimator(transform, centerX, centerY, qNew, 300 ) );
}
@@ -369,12 +399,12 @@ public void drawOverlays( final Graphics g )
final boolean prefsShowTextOverlay = Prefs.showTextOverlay();
final boolean prefsShowMultibox = Prefs.showMultibox();
- Prefs.showTextOverlay( textOverlayVisible );
- Prefs.showMultibox( boxOverlayVisible );
+// Prefs.showTextOverlay( textOverlayVisible );
+// Prefs.showMultibox( boxOverlayVisible );
super.drawOverlays( g );
- // restore Prefs settings
- Prefs.showTextOverlay( prefsShowTextOverlay );
- Prefs.showMultibox( prefsShowMultibox );
+// // restore Prefs settings
+// Prefs.showTextOverlay( prefsShowTextOverlay );
+// Prefs.showMultibox( prefsShowMultibox );
}
}
diff --git a/src/main/java/bdv/viewer/BigWarpViewerSettings.java b/src/main/java/bdv/viewer/BigWarpViewerSettings.java
index 3413a7c2..99f077b3 100644
--- a/src/main/java/bdv/viewer/BigWarpViewerSettings.java
+++ b/src/main/java/bdv/viewer/BigWarpViewerSettings.java
@@ -120,8 +120,6 @@ public Boolean areLandmarksVisible(){
public void togglePointsVisible(){
displaySettings.put( KEY_SPOTS_VISIBLE,
!((Boolean)displaySettings.get( KEY_SPOTS_VISIBLE )).booleanValue());
-
- //System.out.println(((Boolean)displaySettings.get( KEY_DISPLAY_SPOT_NAMES )));
}
public void setSpotColor( Color c )
diff --git a/src/main/java/bdv/viewer/LandmarkPointMenu.java b/src/main/java/bdv/viewer/LandmarkPointMenu.java
index 8ca7b5e3..49965081 100644
--- a/src/main/java/bdv/viewer/LandmarkPointMenu.java
+++ b/src/main/java/bdv/viewer/LandmarkPointMenu.java
@@ -23,10 +23,10 @@
import java.awt.Point;
import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
+import java.util.Arrays;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
@@ -35,6 +35,7 @@
import bdv.gui.BigWarpLandmarkPanel;
import bigwarp.BigWarp;
+import bigwarp.BigWarpActions;
import bigwarp.landmarks.LandmarkTableModel;
public class LandmarkPointMenu extends JPopupMenu
@@ -43,46 +44,45 @@ public class LandmarkPointMenu extends JPopupMenu
public static final boolean MOVING = true;
public static final boolean FIXED = false;
- public static final String CLEAR_MOVING = "table clear moving";
- public static final String CLEAR_FIXED = "table clear fixed";
- public static final String CLEAR_SELECTED_MOVING = "table clear selected moving";
- public static final String CLEAR_SELECTED_FIXED = "table clear selected fixed";
-
- public static final String DELETE = "table delete";
- public static final String DELETE_SELECTED = "table delete selected ";
-
- public static final String ACTIVATE = "table activate";
- public static final String ACTIVATE_SELECTED = "table activate selected ";
+// public static final String CLEAR_MOVING = "table clear moving";
+// public static final String CLEAR_FIXED = "table clear fixed";
+// public static final String CLEAR_SELECTED_MOVING = "table clear selected moving";
+// public static final String CLEAR_SELECTED_FIXED = "table clear selected fixed";
+//
+// public static final String DELETE = "table delete";
+// public static final String DELETE_SELECTED = "table delete selected ";
+//
+// public static final String ACTIVATE_SELECTED = "table activate selected";
+// public static final String DEACTIVATE_SELECTED = "table deactivate selected ";
private static final long serialVersionUID = -3676180390835767585L;
protected BigWarpLandmarkPanel landmarkPanel;
- protected BigWarp bw;
-
-// public ClearHandler clearMoving;
-// public ClearHandler clearFixed;
+ protected BigWarp< ? > bw;
+
public ClearSelectedHandler clearSelectedMoving;
public ClearSelectedHandler clearSelectedFixed;
-// public DeleteHandler deleteHandler;
public DeleteSelectedHandler deleteSelectedHandler;
- public ActivateSelectedHandler activateAllHandler;
- public DeactivateSelectedHandler deactivateAllHandler;
-
+ public ActivateSelectedHandler activateSelectedHandler;
+ public DeactivateSelectedHandler deactivateSelectedHandler;
+
+ public AddToSelection addAboveHandler;
+ public AddToSelection addAllAboveHandler;
+ public AddToSelection addBelowHandler;
+ public AddToSelection addAllBelowHandler;
+
protected MouseListener popupListener;
-// protected JMenuItem deleteSingleItem;
protected JMenuItem deleteAllItem;
protected JMenuItem activateAllItem;
protected JMenuItem deactivateAllItem;
-// protected JMenuItem clearMovingItem;
-// protected JMenuItem clearFixedItem;
protected JMenuItem clearSelectedMovingItem;
protected JMenuItem clearSelectedFixedItem;
private Point clickPt;
- public LandmarkPointMenu( BigWarp bw )
+ public LandmarkPointMenu( BigWarp< ? > bw )
{
this( bw.getLandmarkPanel() );
this.bw = bw;
@@ -92,30 +92,23 @@ public LandmarkPointMenu( BigWarpLandmarkPanel landmarkPanel )
{
this.landmarkPanel = landmarkPanel;
-// deleteHandler = new DeleteHandler( DELETE );
- deleteSelectedHandler = new DeleteSelectedHandler( DELETE_SELECTED );
- activateAllHandler = new ActivateSelectedHandler( ACTIVATE );
- deactivateAllHandler = new DeactivateSelectedHandler( ACTIVATE_SELECTED );
+ deleteSelectedHandler = new DeleteSelectedHandler( BigWarpActions.DELETE_SELECTED );
+ activateSelectedHandler = new ActivateSelectedHandler( BigWarpActions.ACTIVATE_SELECTED );
+ deactivateSelectedHandler = new DeactivateSelectedHandler( BigWarpActions.DEACTIVATE_SELECTED );
-// clearMoving = new ClearHandler( CLEAR_MOVING, MOVING );
-// clearFixed = new ClearHandler( CLEAR_FIXED, FIXED );
- clearSelectedMoving = new ClearSelectedHandler( CLEAR_SELECTED_MOVING, MOVING );
- clearSelectedFixed = new ClearSelectedHandler( CLEAR_SELECTED_FIXED, FIXED );
+ clearSelectedMoving = new ClearSelectedHandler( BigWarpActions.CLEAR_SELECTED_MOVING, MOVING );
+ clearSelectedFixed = new ClearSelectedHandler( BigWarpActions.CLEAR_SELECTED_FIXED, FIXED );
- popupListener = new PopupListener();
+ addAboveHandler = new AddToSelection( BigWarpActions.LANDMARK_SELECT_ABOVE, true, false );
+ addAllAboveHandler = new AddToSelection( BigWarpActions.LANDMARK_SELECT_ALL_ABOVE, true, true );
+ addBelowHandler = new AddToSelection( BigWarpActions.LANDMARK_SELECT_BELOW, false, false );
+ addAllBelowHandler = new AddToSelection( BigWarpActions.LANDMARK_SELECT_ALL_BELOW, false, true );
-// deleteSingleItem = new JMenuItem( "Delete" );
-// deleteSingleItem.addActionListener( deleteHandler );
+ popupListener = new PopupListener();
deleteAllItem = new JMenuItem( "Delete" );
deleteAllItem.addActionListener( deleteSelectedHandler );
-// clearMovingItem = new JMenuItem( "Clear moving point" );
-// clearMovingItem.addActionListener( clearMoving );
-//
-// clearFixedItem = new JMenuItem( "Clear fixed point" );
-// clearFixedItem.addActionListener( clearFixed );
-
clearSelectedMovingItem = new JMenuItem( "Clear moving" );
clearSelectedMovingItem.addActionListener( clearSelectedMoving );
@@ -123,17 +116,14 @@ public LandmarkPointMenu( BigWarpLandmarkPanel landmarkPanel )
clearSelectedFixedItem.addActionListener( clearSelectedFixed );
activateAllItem = new JMenuItem( "Activate" );
- activateAllItem.addActionListener( activateAllHandler );
+ activateAllItem.addActionListener( activateSelectedHandler );
deactivateAllItem = new JMenuItem( "Deactivate" );
- deactivateAllItem.addActionListener( deactivateAllHandler );
+ deactivateAllItem.addActionListener( deactivateSelectedHandler );
-// this.add( deleteSingleItem );
this.add( deleteAllItem );
this.addSeparator();
-// this.add( clearMovingItem );
-// this.add( clearFixedItem );
this.add( clearSelectedMovingItem );
this.add( clearSelectedFixedItem );
@@ -305,7 +295,8 @@ public ActivateSelectedHandler( String name )
@Override
public void actionPerformed(ActionEvent e)
{
- int[] selectedRows = landmarkPanel.getJTable().getSelectedRows();
+ final int[] selectedRows = landmarkPanel.getJTable().getSelectedRows();
+ Arrays.sort( selectedRows );
// do in reverse order so that the index
for( int i = selectedRows.length - 1; i >= 0; i-- )
@@ -333,7 +324,8 @@ public DeactivateSelectedHandler( String name )
@Override
public void actionPerformed(ActionEvent e)
{
- int[] selectedRows = landmarkPanel.getJTable().getSelectedRows();
+ final int[] selectedRows = landmarkPanel.getJTable().getSelectedRows();
+ Arrays.sort( selectedRows );
// do in reverse order so that the index
for( int i = selectedRows.length - 1; i >= 0; i-- )
@@ -348,4 +340,48 @@ public void actionPerformed(ActionEvent e)
landmarkPanel.repaint();
}
}
+
+ public class AddToSelection extends AbstractNamedAction
+ {
+ private static final long serialVersionUID = -904756750247052099L;
+
+ private final boolean before;
+ private final boolean all;
+
+ public AddToSelection( final String name, final boolean before, final boolean all )
+ {
+ super( name );
+ this.before = before;
+ this.all = all;
+ }
+
+ @Override
+ public void actionPerformed( ActionEvent e )
+ {
+ final int[] selectedRows = landmarkPanel.getJTable().getSelectedRows();
+ if( selectedRows.length == 0 )
+ return;
+
+ Arrays.sort( selectedRows );
+
+ int i;
+ int j;
+ if ( before )
+ {
+ j = selectedRows[ 0 ];
+ i = all ? 0 : j - 1;
+ } else
+ {
+ i = selectedRows[ selectedRows.length - 1 ];
+ j = all ? landmarkPanel.getJTable().getRowCount() - 1 : i + 1;
+ }
+ landmarkPanel.getJTable().getSelectionModel().addSelectionInterval( i, j );
+
+ if ( bw != null )
+ bw.restimateTransformation();
+
+ landmarkPanel.repaint();
+ }
+ }
+
}
diff --git a/src/main/java/bdv/viewer/overlay/BigWarpMaskSphereOverlay.java b/src/main/java/bdv/viewer/overlay/BigWarpMaskSphereOverlay.java
new file mode 100644
index 00000000..0776abd7
--- /dev/null
+++ b/src/main/java/bdv/viewer/overlay/BigWarpMaskSphereOverlay.java
@@ -0,0 +1,171 @@
+package bdv.viewer.overlay;
+
+import java.awt.AlphaComposite;
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+
+import bdv.util.Affine3DHelpers;
+import bdv.viewer.BigWarpViewerPanel;
+import bdv.viewer.OverlayRenderer;
+import net.imglib2.RealLocalizable;
+import net.imglib2.RealPoint;
+import net.imglib2.realtransform.AffineTransform3D;
+
+public class BigWarpMaskSphereOverlay implements OverlayRenderer
+{
+
+ final static protected BasicStroke stroke = new BasicStroke( 1 );
+ final protected BigWarpViewerPanel viewer;
+
+ protected double[] radii;
+ protected final double[] viewerCoords;
+ protected double minWidth = 0.1;
+
+ protected final Color[] colors;
+ final protected AffineTransform3D viewerTransform;
+
+ protected double[] center;
+ protected int width, height;
+ protected boolean is3d;
+ protected boolean visible = false;
+
+ public BigWarpMaskSphereOverlay( final BigWarpViewerPanel viewer, boolean is3d )
+ {
+ this( viewer, new double[]{0,0}, new Color[]{ Color.ORANGE, Color.YELLOW }, is3d );
+ }
+
+ public BigWarpMaskSphereOverlay( final BigWarpViewerPanel viewer, final Color[] colors, boolean is3d )
+ {
+ this( viewer, new double[]{0, 0}, colors, is3d );
+ }
+
+ public BigWarpMaskSphereOverlay( final BigWarpViewerPanel viewer, final double[] radii, final Color[] colors, boolean is3d )
+ {
+ this.viewer = viewer;
+ this.radii = radii;
+ this.colors = colors;
+ viewerCoords = new double[ 3 ];
+ center = new double[ 3 ];
+ this.is3d = is3d;
+ viewerTransform = new AffineTransform3D();
+ }
+
+ public double[] getCenter()
+ {
+ return center;
+ }
+
+ public double[] getRadii()
+ {
+ return radii;
+ }
+
+ public void setCenter( final double[] center )
+ {
+ this.center = center;
+ }
+
+ public void setCenter( final RealLocalizable c )
+ {
+ c.localize( center );
+ }
+
+ public void setCenter( final RealPoint center )
+ {
+ center.localize( this.center );
+ }
+
+ public void setRadii( double[] radii )
+ {
+ this.radii = radii;
+ }
+
+ public void setInnerRadius( double inner )
+ {
+ final double del = radii[1] - radii[0];
+ radii[0] = inner;
+ radii[1] = inner + del;
+ }
+
+ public void setOuterRadiusDelta( double outerDelta )
+ {
+ radii[1] = radii[0] + outerDelta;
+ }
+
+ public void setColor( final Color color, final int i )
+ {
+ colors[ i ] = color;
+ }
+
+ public void setColors( final Color[] colors )
+ {
+ System.arraycopy( colors, 0, this.colors, 0, Math.min( colors.length, this.colors.length ) );
+ }
+
+ public void setVisible( final boolean visible )
+ {
+ this.visible = visible;
+ viewer.requestRepaint();
+ }
+
+ public void toggleVisible()
+ {
+ setVisible( !visible );
+ }
+
+ @Override
+ public void drawOverlays( final Graphics g )
+ {
+ if ( visible )
+ {
+ final Graphics2D g2d = (Graphics2D) g;
+ g2d.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
+ g2d.setComposite( AlphaComposite.SrcOver );
+
+ viewer.state().getViewerTransform( viewerTransform ); // synchronized
+ final double scale = Affine3DHelpers.extractScale( viewerTransform, 0 );
+ viewerTransform.apply( center, viewerCoords );
+
+ final double zv;
+ if( is3d )
+ zv = viewerCoords[ 2 ];
+ else
+ zv = 0;
+
+ final double dz2 = zv * zv;
+ for ( int i = 0; i < radii.length; ++i )
+ {
+ final double rad = radii[i];
+ final double scaledRadius = scale * rad;
+
+ if ( viewerCoords[0] + scaledRadius > 0 && viewerCoords[0] - scaledRadius < width &&
+ viewerCoords[1] + scaledRadius > 0 && viewerCoords[1] - scaledRadius < height )
+ {
+ final double arad;
+ if( is3d )
+ arad = Math.sqrt( scaledRadius * scaledRadius - dz2 );
+ else
+ arad = scaledRadius;
+
+ final int rarad = (int)Math.round( arad );
+
+ g2d.setColor( colors[ i ] );
+ g2d.setStroke( stroke );
+ g2d.drawOval( (int)viewerCoords[0] - rarad, (int)viewerCoords[1] - rarad,
+ (2 * rarad + 1), 2 * rarad + 1 );
+ }
+ }
+ }
+ }
+
+ @Override
+ public void setCanvasSize( final int width, final int height )
+ {
+ this.width = width;
+ this.height = height;
+ }
+
+}
diff --git a/src/main/java/bdv/viewer/overlay/BigWarpSourceOverlayRenderer.java b/src/main/java/bdv/viewer/overlay/BigWarpSourceOverlayRenderer.java
index a94157e4..8971569f 100644
--- a/src/main/java/bdv/viewer/overlay/BigWarpSourceOverlayRenderer.java
+++ b/src/main/java/bdv/viewer/overlay/BigWarpSourceOverlayRenderer.java
@@ -24,23 +24,60 @@
import java.awt.Font;
import java.awt.Graphics2D;
+import bdv.img.WarpedSource;
+import bdv.viewer.Source;
+import bdv.viewer.SourceAndConverter;
+import bdv.viewer.ViewerState;
+
public class BigWarpSourceOverlayRenderer extends SourceInfoOverlayRenderer
{
+ private boolean indicateTransformed = true;
+
+ // are any visible source in this viewer transformed
+ private boolean anyTransformed = false;
@Override
public synchronized void paint( final Graphics2D g )
{
- g.setFont( new Font( "Monospaced", Font.PLAIN, 12 ) );
+ super.paint( g );
+ if( indicateTransformed && anyTransformed )
+ {
+ g.setFont( new Font( "Monospaced", Font.BOLD, 16 ) );
+ int tformedWidth = g.getFontMetrics().stringWidth( "TRANSFORMED" );
+ g.drawString( "TRANSFORMED",
+ ( int ) ( g.getClipBounds().getWidth() - tformedWidth ) / 2,
+ ( int ) g.getClipBounds().getHeight() - 16 );
+ }
+ }
- int actual_width = g.getFontMetrics().stringWidth( sourceName );
- g.drawString( sourceName, ( int ) g.getClipBounds().getWidth() - actual_width - 10, 12 );
+ /**
+ * Update data to show in the overlay.
+ *
+ * Checks whether any sources in this viewer are transformed in order to indicate that fact.
+ */
+ @Override
+ public synchronized void setViewerState( final ViewerState state )
+ {
+ super.setViewerState( state );
- if( !groupName.isEmpty() )
+ anyTransformed = false;
+ for( SourceAndConverter> vs : state.getVisibleSources())
{
- String groupStringBracket = "[ " + groupName + " ]";
- int actual_width_group = g.getFontMetrics().stringWidth( groupStringBracket );
- g.drawString( groupStringBracket,
- ( int ) g.getClipBounds().getWidth() - actual_width - actual_width_group - 20, 12 );
+ Source< ? > src = vs.getSpimSource();
+ if( src instanceof WarpedSource )
+ {
+ WarpedSource> ws = (WarpedSource>)src;
+ if( ws.isTransformed() )
+ {
+ anyTransformed = true;
+ break;
+ }
+ }
}
}
+
+ public void setIndicateTransformed( final boolean indicateTransformed )
+ {
+ this.indicateTransformed = indicateTransformed;
+ }
}
diff --git a/src/main/java/bigwarp/BigWarp.java b/src/main/java/bigwarp/BigWarp.java
index 52e80812..e80ed662 100755
--- a/src/main/java/bigwarp/BigWarp.java
+++ b/src/main/java/bigwarp/BigWarp.java
@@ -8,12 +8,12 @@
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 2 of the
* License, or (at your option) any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* .
@@ -21,13 +21,11 @@
*/
package bigwarp;
-import bdv.viewer.ConverterSetups;
-import bdv.viewer.DisplayMode;
-import bdv.viewer.TransformListener;
-import bdv.viewer.ViewerState;
+import bdv.TransformState;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
+import java.awt.Dimension;
import java.awt.FileDialog;
import java.awt.KeyEventPostProcessor;
import java.awt.KeyboardFocusManager;
@@ -37,14 +35,16 @@
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.File;
+import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URISyntaxException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -66,15 +66,14 @@
import javax.swing.Timer;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
+import javax.swing.filechooser.FileFilter;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellEditor;
-import mpicbg.spim.data.SpimData;
-import mpicbg.spim.data.XmlIoSpimData;
-import mpicbg.spim.data.registration.ViewTransformAffine;
-
import org.janelia.saalfeldlab.n5.Compression;
import org.janelia.saalfeldlab.n5.ij.N5Exporter;
+import org.janelia.utility.geom.BoundingSphereRitter;
+import org.janelia.utility.geom.Sphere;
import org.janelia.utility.ui.RepeatingReleasedEventsFixer;
import org.jdom2.Document;
import org.jdom2.Element;
@@ -83,11 +82,14 @@
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
import org.scijava.ui.behaviour.io.InputTriggerConfig;
+import org.scijava.ui.behaviour.util.Actions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.formdev.flatlaf.util.UIScale;
+import com.google.gson.stream.JsonReader;
+
import bdv.BigDataViewer;
-import bdv.cache.CacheControl;
import bdv.export.ProgressWriter;
import bdv.export.ProgressWriterConsole;
import bdv.gui.BigWarpLandmarkPanel;
@@ -95,7 +97,10 @@
import bdv.gui.BigWarpViewerFrame;
import bdv.gui.BigWarpViewerOptions;
import bdv.gui.BigwarpLandmarkSelectionPanel;
+import bdv.gui.ExportDisplacementFieldFrame;
import bdv.gui.LandmarkKeyboardProcessor;
+import bdv.gui.MaskOptionsPanel;
+import bdv.gui.MaskedSourceEditorMouseListener;
import bdv.gui.TransformTypeSelectDialog;
import bdv.ij.ApplyBigwarpPlugin;
import bdv.ij.ApplyBigwarpPlugin.WriteDestinationOptions;
@@ -103,44 +108,65 @@
import bdv.ij.util.ProgressWriterIJ;
import bdv.img.WarpedSource;
import bdv.tools.InitializeViewerState;
+import bdv.tools.PreferencesDialog;
import bdv.tools.VisibilityAndGroupingDialog;
import bdv.tools.bookmarks.Bookmarks;
import bdv.tools.bookmarks.BookmarksEditor;
-import bdv.tools.brightness.BrightnessDialog;
import bdv.tools.brightness.ConverterSetup;
import bdv.tools.brightness.SetupAssignments;
+import bdv.ui.appearance.AppearanceManager;
+import bdv.ui.appearance.AppearanceSettingsPage;
+import bdv.ui.keymap.Keymap;
+import bdv.ui.keymap.KeymapSettingsPage;
+import bdv.util.BoundedRange;
import bdv.util.Bounds;
+import bdv.viewer.AbstractViewerPanel.AlignPlane;
import bdv.viewer.BigWarpDragOverlay;
import bdv.viewer.BigWarpLandmarkFrame;
import bdv.viewer.BigWarpOverlay;
import bdv.viewer.BigWarpViewerPanel;
import bdv.viewer.BigWarpViewerSettings;
+import bdv.viewer.ConverterSetups;
+import bdv.viewer.DisplayMode;
import bdv.viewer.Interpolation;
import bdv.viewer.LandmarkPointMenu;
import bdv.viewer.MultiBoxOverlay2d;
import bdv.viewer.Source;
import bdv.viewer.SourceAndConverter;
+import bdv.viewer.SourceGroup;
import bdv.viewer.SynchronizedViewerState;
+import bdv.viewer.TransformListener;
import bdv.viewer.ViewerPanel;
-import bdv.viewer.VisibilityAndGrouping;
+import bdv.viewer.ViewerState;
import bdv.viewer.WarpNavigationActions;
import bdv.viewer.animate.SimilarityModel3D;
import bdv.viewer.animate.TranslationAnimator;
+import bdv.viewer.overlay.BigWarpMaskSphereOverlay;
import bdv.viewer.overlay.BigWarpSourceOverlayRenderer;
import bdv.viewer.overlay.MultiBoxOverlayRenderer;
import bigwarp.landmarks.LandmarkTableModel;
-import bigwarp.loader.ImagePlusLoader;
-import bigwarp.loader.ImagePlusLoader.ColorSettings;
import bigwarp.source.GridSource;
import bigwarp.source.JacobianDeterminantSource;
+import bigwarp.source.PlateauSphericalMaskRealRandomAccessible;
+import bigwarp.source.PlateauSphericalMaskRealRandomAccessible.FalloffShape;
+import bigwarp.source.PlateauSphericalMaskSource;
+import bigwarp.source.SourceInfo;
import bigwarp.source.WarpMagnitudeSource;
+import bigwarp.transforms.AbstractTransformSolver;
import bigwarp.transforms.BigWarpTransform;
+import bigwarp.transforms.MaskedSimRotTransformSolver;
import bigwarp.transforms.WrappedCoordinateTransform;
+import bigwarp.transforms.io.TransformWriterJson;
+import bigwarp.ui.keymap.KeymapManager;
+import bigwarp.ui.keymap.NavigationKeys;
import bigwarp.util.BigWarpUtils;
+import dev.dirs.ProjectDirectories;
import fiji.util.gui.GenericDialogPlus;
import ij.IJ;
import ij.ImageJ;
import ij.ImagePlus;
+
+import java.util.LinkedHashMap;
import jitk.spline.ThinPlateR2LogRSplineKernelTransform;
import jitk.spline.XfmUtils;
import mpicbg.models.AbstractModel;
@@ -155,7 +181,10 @@
import mpicbg.models.SimilarityModel2D;
import mpicbg.models.TranslationModel2D;
import mpicbg.models.TranslationModel3D;
+import mpicbg.spim.data.SpimData;
import mpicbg.spim.data.SpimDataException;
+import mpicbg.spim.data.XmlIoSpimData;
+import mpicbg.spim.data.registration.ViewTransformAffine;
import net.imglib2.Interval;
import net.imglib2.RandomAccessibleInterval;
import net.imglib2.RealPoint;
@@ -163,18 +192,23 @@
import net.imglib2.realtransform.AffineTransform3D;
import net.imglib2.realtransform.BoundingBoxEstimation;
import net.imglib2.realtransform.InvertibleRealTransform;
+import net.imglib2.realtransform.InvertibleWrapped2DTransformAs3D;
+import net.imglib2.realtransform.RealTransform;
import net.imglib2.realtransform.ThinplateSplineTransform;
-import net.imglib2.realtransform.Wrapped2DTransformAs3D;
+import net.imglib2.realtransform.inverse.RealTransformFiniteDerivatives;
import net.imglib2.realtransform.inverse.WrappedIterativeInvertibleRealTransform;
import net.imglib2.type.numeric.ARGBType;
+import net.imglib2.type.numeric.RealType;
+import net.imglib2.type.numeric.real.DoubleType;
import net.imglib2.type.numeric.real.FloatType;
public class BigWarp< T >
{
+ public static String configDir = ProjectDirectories.from( "sc", "fiji", "bigwarp" ).configDir;
- protected static final int DEFAULT_WIDTH = 600;
+ protected static final int DEFAULT_WIDTH = 400;
- protected static final int DEFAULT_HEIGHT = 400;
+ protected static final int DEFAULT_HEIGHT = 300;
public static final int GRID_SOURCE_ID = 1696993146;
@@ -182,34 +216,32 @@ public class BigWarp< T >
public static final int JACDET_SOURCE_ID = 1006827158;
+ public static final int TRANSFORM_MASK_SOURCE_ID = 33872301;
+
protected BigWarpViewerOptions options;
protected BigWarpData< T > data;
- // descriptive names for indexing sources
- protected int[] movingSourceIndexList;
-
- protected int[] targetSourceIndexList;
-
- protected List< SourceAndConverter< T > > sources;
-
protected final SetupAssignments setupAssignments;
-
- protected final BrightnessDialog brightnessDialog;
-
protected final WarpVisFrame warpVisDialog;
protected final HelpDialog helpDialog;
+ private final KeymapManager keymapManager;
+
+ private final AppearanceManager appearanceManager;
+
protected final SourceInfoDialog sourceInfoDialog;
protected final VisibilityAndGroupingDialog activeSourcesDialogP;
protected final VisibilityAndGroupingDialog activeSourcesDialogQ;
+ protected final PreferencesDialog preferencesDialog;
+
final AffineTransform3D fixedViewXfm;
- private final Bookmarks bookmarks;
+ private Bookmarks bookmarks;
protected final BookmarksEditor bookmarkEditorP;
@@ -223,9 +255,9 @@ public class BigWarp< T >
protected final BigWarpViewerPanel viewerQ;
- protected final AffineTransform3D initialViewP;
+ protected AffineTransform3D initialViewP;
- protected final AffineTransform3D initialViewQ;
+ protected AffineTransform3D initialViewQ;
private JMenuItem toggleAlwaysWarpMenuP;
@@ -235,7 +267,7 @@ public class BigWarp< T >
protected final LandmarkPointMenu landmarkPopupMenu;
- protected final BigWarpLandmarkFrame landmarkFrame;
+ protected BigWarpLandmarkFrame landmarkFrame;
protected final BigWarpViewerSettings viewerSettings;
@@ -263,17 +295,27 @@ public class BigWarp< T >
protected MouseLandmarkTableListener landmarkTableListener;
+ protected MaskedSourceEditorMouseListener maskSourceMouseListenerP;
+
+ protected MaskedSourceEditorMouseListener maskSourceMouseListenerQ;
+
protected BigWarpMessageAnimator message;
protected final Set< KeyEventPostProcessor > keyEventPostProcessorSet = new HashSet< KeyEventPostProcessor >();
private final RepeatingReleasedEventsFixer repeatedKeyEventsFixer;
- protected final SourceAndConverter< FloatType > gridSource;
+ protected GridSource gridSource;
- protected final SourceAndConverter< FloatType > warpMagSource;
+ protected WarpMagnitudeSource warpMagSource;
- protected final SourceAndConverter< FloatType > jacDetSource;
+ protected JacobianDeterminantSource jacDetSource;
+
+ protected SourceAndConverter< ? extends RealType>> transformMaskSource;
+
+ protected Source< ? extends RealType>> transformMask;
+
+ protected PlateauSphericalMaskSource plateauTransformMask;
protected final AbstractModel< ? >[] baseXfmList;
@@ -283,10 +325,12 @@ public class BigWarp< T >
private BigWarpTransform bwTransform;
+ protected SourceGroup mvgGrp, tgtGrp;
+
private BoundingBoxEstimation bboxOptions;
private long keyClickMaxLength = 250;
-
+
protected TransformTypeSelectDialog transformSelector;
protected AffineTransform3D tmpTransform = new AffineTransform3D();
@@ -300,9 +344,11 @@ public class BigWarp< T >
protected int baselineModelIndex;
// file selection
- final JFrame fileFrame;
+ protected JFrame fileFrame;
- final FileDialog fileDialog;
+ protected FileDialog fileDialog;
+
+ final JFileChooser fileChooser;
protected File autoSaveDirectory;
@@ -324,21 +370,42 @@ public class BigWarp< T >
protected static Logger logger = LoggerFactory.getLogger( BigWarp.class );
+ //TODO Caleb: John, can this be replaced by info from BigWarpData/SourceInfo url?
private SpimData movingSpimData;
private File movingImageXml;
private CopyOnWriteArrayList< TransformListener< InvertibleRealTransform > > transformListeners = new CopyOnWriteArrayList<>( );
- final int ndims;
+ int ndims;
- public BigWarp( final BigWarpData data, final String windowTitle, final ProgressWriter progressWriter ) throws SpimDataException
+ @Deprecated
+ public BigWarp( final BigWarpData< T > data, final String windowTitle, final ProgressWriter progressWriter ) throws SpimDataException
{
- this( data, windowTitle, BigWarpViewerOptions.options().is2D( detectNumDims( data.sources ) == 2 ), progressWriter );
+ this( data, BigWarpViewerOptions.options().is2D( detectNumDims( data.sources ) == 2 ), progressWriter );
}
- public BigWarp( final BigWarpData data, final String windowTitle, BigWarpViewerOptions options, final ProgressWriter progressWriter ) throws SpimDataException
+ @Deprecated
+ public BigWarp( final BigWarpData< T > data, final String windowTitle, BigWarpViewerOptions options, final ProgressWriter progressWriter ) throws SpimDataException {
+ this(data, options, progressWriter );
+ }
+
+ public BigWarp( final BigWarpData< T > data, final ProgressWriter progressWriter ) throws SpimDataException {
+ this( data, BigWarpViewerOptions.options().is2D( detectNumDims( data.sources ) == 2 ), progressWriter );
+ }
+
+ public BigWarp( final BigWarpData data, BigWarpViewerOptions options, final ProgressWriter progressWriter ) throws SpimDataException
{
+ final KeymapManager optionsKeymapManager = options.getValues().getKeymapManager();
+ final AppearanceManager optionsAppearanceManager = options.values.getAppearanceManager();
+ keymapManager = optionsKeymapManager != null ? optionsKeymapManager : new KeymapManager( configDir );
+ appearanceManager = optionsAppearanceManager != null ? optionsAppearanceManager : new AppearanceManager( configDir );
+
+ InputTriggerConfig inputTriggerConfig = options.values.getInputTriggerConfig();
+ final Keymap keymap = this.keymapManager.getForwardSelectedKeymap();
+ if ( inputTriggerConfig == null )
+ inputTriggerConfig = keymap.getConfig();
+
repeatedKeyEventsFixer = RepeatingReleasedEventsFixer.installAnyTime();
ij = IJ.getInstance();
@@ -361,58 +428,34 @@ public BigWarp( final BigWarpData data, final String windowTitle, BigWarpVie
* Set up LandmarkTableModel, holds the data and interfaces with the
* LandmarkPanel
*/
- landmarkModel = new LandmarkTableModel( ndims );
- landmarkModellistener = new LandmarkTableListener();
- landmarkModel.addTableModelListener( landmarkModellistener );
- addTransformListener( landmarkModel );
/* Set up landmark panel */
- landmarkPanel = new BigWarpLandmarkPanel( landmarkModel );
- landmarkPanel.setOpaque( true );
- landmarkTable = landmarkPanel.getJTable();
- landmarkTable.setDefaultRenderer( Object.class, new WarningTableCellRenderer() );
- addDefaultTableMouseListener();
-
- landmarkFrame = new BigWarpLandmarkFrame( "Landmarks", landmarkPanel, this );
+ setupLandmarkFrame();
baseXfmList = new AbstractModel< ? >[ 3 ];
setupWarpMagBaselineOptions( baseXfmList, ndims );
fixedViewXfm = new AffineTransform3D();
- //sources.get( targetSourceIndexList[ 0 ] ).getSpimSource().getSourceTransform( 0, 0, fixedViewXfm );
- data.sources.get( data.targetSourceIndices[ 0 ] ).getSpimSource().getSourceTransform( 0, 0, fixedViewXfm );
-
- baselineModelIndex = 0;
- warpMagSource = addWarpMagnitudeSource( data, ndims == 2, "WarpMagnitudeSource" );
- jacDetSource = addJacobianDeterminantSource( data, "JacobianDeterminantSource" );
- gridSource = addGridSource( data, "GridSource" );
-
- this.sources = this.data.sources;
- final List< ConverterSetup > converterSetups = data.converterSetups;
- this.movingSourceIndexList = data.movingSourceIndices;
- this.targetSourceIndexList = data.targetSourceIndices;
- Arrays.sort( movingSourceIndexList );
- Arrays.sort( targetSourceIndexList );
-
- sources = wrapSourcesAsTransformed( data.sources, ndims, data );
-
- setGridType( GridSource.GRID_TYPE.LINE );
-
viewerSettings = new BigWarpViewerSettings();
// key properties
final InputTriggerConfig keyProperties = BigDataViewer.getInputTriggerConfig( options );
options = options.inputTriggerConfig( keyProperties );
+ final int width = UIScale.scale( DEFAULT_WIDTH );
+ final int height = UIScale.scale( DEFAULT_HEIGHT );
+
+ final List> srcs = (List)data.sources;
+
// Viewer frame for the moving image
- viewerFrameP = new BigWarpViewerFrame( this, DEFAULT_WIDTH, DEFAULT_HEIGHT, (List)sources, converterSetups, viewerSettings,
- data.cache, options, "Bigwarp moving image", true, movingSourceIndexList, targetSourceIndexList );
+ viewerFrameP = new BigWarpViewerFrame(this, width, height, srcs, data.converterSetups,
+ viewerSettings, data.cache, keymapManager, appearanceManager, options, "BigWarp moving image", true);
viewerP = getViewerFrameP().getViewerPanel();
// Viewer frame for the fixed image
- viewerFrameQ = new BigWarpViewerFrame( this, DEFAULT_WIDTH, DEFAULT_HEIGHT, (List)sources, converterSetups, viewerSettings,
- data.cache, options, "Bigwarp fixed image", false, movingSourceIndexList, targetSourceIndexList );
+ viewerFrameQ = new BigWarpViewerFrame(this, width, height, srcs, data.converterSetups,
+ viewerSettings, data.cache, keymapManager, appearanceManager, options, "BigWarp fixed image", false);
viewerQ = getViewerFrameQ().getViewerPanel();
@@ -434,7 +477,7 @@ public BigWarp( final BigWarpData data, final String windowTitle, BigWarpVie
final MultiBoxOverlayRenderer overlayRenderP = new MultiBoxOverlayRenderer( DEFAULT_WIDTH, DEFAULT_HEIGHT );
final MultiBoxOverlayRenderer overlayRenderQ = new MultiBoxOverlayRenderer( DEFAULT_WIDTH, DEFAULT_HEIGHT );
-
+
// TODO hopefully I won't' need reflection any more
final Field boxField = overlayRenderP.getClass().getDeclaredField( "box" );
boxField.setAccessible( true );
@@ -476,22 +519,6 @@ public BigWarp( final BigWarpData data, final String windowTitle, BigWarpVie
activeSourcesDialogQ = new VisibilityAndGroupingDialog( viewerFrameQ, viewerQ.getVisibilityAndGrouping() );
activeSourcesDialogQ.setTitle( "visibility and grouping ( fixed )" );
- final ARGBType white = new ARGBType( ARGBType.rgba( 255, 255, 255, 255 ));
- // set warp mag source to inactive at the start
- viewerP.state().setSourceActive( warpMagSource, false );
- viewerQ.state().setSourceActive( warpMagSource, false );
- data.sourceColorSettings.put( warpMagSource, new ImagePlusLoader.ColorSettings( -1, 0, 15, white ));
-
- // set warp grid source to inactive at the start
- viewerP.state().setSourceActive( gridSource, false );
- viewerQ.state().setSourceActive( gridSource, false );
- data.sourceColorSettings.put( gridSource, new ImagePlusLoader.ColorSettings( -1, 0, 255, white ));
-
- // set jacobian determinant source to inactive at the start
- viewerP.state().setSourceActive( jacDetSource, false );
- viewerQ.state().setSourceActive( jacDetSource, false );
- data.sourceColorSettings.put( jacDetSource, new ImagePlusLoader.ColorSettings( -1, 0.0, 1.0, white ));
-
overlayP = new BigWarpOverlay( viewerP, landmarkPanel );
overlayQ = new BigWarpOverlay( viewerQ, landmarkPanel );
viewerP.addOverlay( overlayP );
@@ -504,7 +531,6 @@ public BigWarp( final BigWarpData data, final String windowTitle, BigWarpVie
solverThread.start();
bboxOptions = new BoundingBoxEstimation( BoundingBoxEstimation.Method.FACES, 5 );
- updateSourceBoundingBoxEstimators();
dragOverlayP = new BigWarpDragOverlay( this, viewerP, solverThread );
dragOverlayQ = new BigWarpDragOverlay( this, viewerQ, solverThread );
@@ -514,24 +540,76 @@ public BigWarp( final BigWarpData data, final String windowTitle, BigWarpVie
landmarkPopupMenu = new LandmarkPointMenu( this );
landmarkPopupMenu.setupListeners();
- setupAssignments = new SetupAssignments( new ArrayList<>( converterSetups ), 0, 65535 );
+ setupAssignments = new SetupAssignments( new ArrayList<>( data.converterSetups ), 0, 65535 );
- brightnessDialog = new BrightnessDialog( landmarkFrame, setupAssignments );
helpDialog = new HelpDialog( landmarkFrame );
sourceInfoDialog = new SourceInfoDialog( landmarkFrame, data );
transformSelector = new TransformTypeSelectDialog( landmarkFrame, this );
// dialogs have to be constructed before action maps are made
- warpVisDialog = new WarpVisFrame( viewerFrameQ, this );
+ warpVisDialog = new WarpVisFrame( viewerFrameQ, this );
+// warpVisDialog.maskOptionsPanel.setMask( transformMask );
- WarpNavigationActions.installActionBindings( getViewerFrameP().getKeybindings(), viewerFrameP, keyProperties, ( ndims == 2 ) );
- BigWarpActions.installActionBindings( getViewerFrameP().getKeybindings(), this, keyProperties );
+ preferencesDialog = new PreferencesDialog( landmarkFrame, keymap, new String[] { "bigwarp", "navigation", "bw-table" } );
+ preferencesDialog.addPage( new AppearanceSettingsPage( "Appearance", appearanceManager ) );
+ preferencesDialog.addPage( new KeymapSettingsPage( "Keymap", this.keymapManager, new KeymapManager(), this.keymapManager.getCommandDescriptions() ) );
- WarpNavigationActions.installActionBindings( getViewerFrameQ().getKeybindings(), viewerFrameQ, keyProperties, ( ndims == 2 ) );
- BigWarpActions.installActionBindings( getViewerFrameQ().getKeybindings(), this, keyProperties );
+ fileChooser = new JFileChooser();
+ fileChooser.setFileFilter( new FileFilter()
+ {
+ @Override
+ public String getDescription()
+ {
+ return "xml files";
+ }
- BigWarpActions.installLandmarkPanelActionBindings( landmarkFrame.getKeybindings(), this, landmarkTable, keyProperties );
+ @Override
+ public boolean accept( final File f )
+ {
+ if ( f.isDirectory() )
+ return true;
+ if ( f.isFile() )
+ {
+ final String s = f.getName();
+ final int i = s.lastIndexOf( '.' );
+ if ( i > 0 && i < s.length() - 1 )
+ {
+ final String ext = s.substring( i + 1 ).toLowerCase();
+ return ext.equals( "xml" );
+ }
+ }
+ return false;
+ }
+ } );
+
+ appearanceManager.appearance().updateListeners().add( viewerFrameP::repaint );
+ appearanceManager.appearance().updateListeners().add( viewerFrameQ::repaint );
+ appearanceManager.appearance().updateListeners().add( landmarkFrame::repaint );
+ appearanceManager.addLafComponent( fileChooser );
+ SwingUtilities.invokeLater(() -> appearanceManager.updateLookAndFeel());
+
+ final Actions navigationActions = new Actions( inputTriggerConfig, "navigation" );
+ navigationActions.install( getViewerFrameP().getKeybindings(), "navigation" );
+ NavigationKeys.install( navigationActions, getViewerFrameP().getViewerPanel(), options.values.is2D() );
+ navigationActions.install( getViewerFrameQ().getKeybindings(), "navigation" );
+ NavigationKeys.install( navigationActions, getViewerFrameQ().getViewerPanel(), options.values.is2D() );
+
+ final BigWarpActions bwActions = new BigWarpActions( inputTriggerConfig, "bigwarp" );
+ BigWarpActions.installViewerActions( bwActions, getViewerFrameP(), this );
+ BigWarpActions.installViewerActions( bwActions, getViewerFrameQ(), this );
+ final BigWarpActions tableActions = new BigWarpActions( inputTriggerConfig, "bw-table" );
+ BigWarpActions.installTableActions( tableActions, getLandmarkFrame().getKeybindings(), this );
+// UnmappedNavigationActions.install( tableActions, options.values.is2D() );
+
+ keymap.updateListeners().add( () -> {
+
+ bwActions.updateKeyConfig( keymap.getConfig() );
+ tableActions.updateKeyConfig( keymap.getConfig() );
+
+ viewerFrameP.getTransformBehaviours().updateKeyConfig( keymap.getConfig() );
+ viewerFrameQ.getTransformBehaviours().updateKeyConfig( keymap.getConfig() );
+ } );
// this call has to come after the actions are set
warpVisDialog.setActions();
@@ -542,11 +620,11 @@ public BigWarp( final BigWarpData data, final String windowTitle, BigWarpVie
setUpLandmarkMenus();
/* Set the locations of frames */
- final Point viewerFramePloc = getViewerFrameP().getLocation();
- viewerFramePloc.setLocation( viewerFramePloc.x + DEFAULT_WIDTH, viewerFramePloc.y );
- getViewerFrameQ().setLocation( viewerFramePloc );
- viewerFramePloc.setLocation( viewerFramePloc.x + DEFAULT_WIDTH, viewerFramePloc.y );
- landmarkFrame.setLocation( viewerFramePloc );
+ viewerFrameP.setLocation( 0, 0 );
+ viewerFrameP.setSize( width, height );
+ viewerFrameQ.setLocation( width, 0 );
+ viewerFrameQ.setSize( width, height );
+ landmarkFrame.setLocation( 2 * width, 0 );
landmarkClickListenerP = new MouseLandmarkListener( this.viewerP );
landmarkClickListenerQ = new MouseLandmarkListener( this.viewerQ );
@@ -562,40 +640,13 @@ public BigWarp( final BigWarpData data, final String windowTitle, BigWarpVie
viewerP.state().getViewerTransform( initialViewP );
viewerQ.state().getViewerTransform( initialViewQ );
- viewerFrameP.setVisible( true );
- viewerFrameQ.setVisible( true );
-
- landmarkFrame.pack();
- landmarkFrame.setVisible( true );
-
- SwingUtilities.invokeLater( new Runnable()
- {
- public void run()
- {
- data.transferChannelSettings( viewerFrameP );
- data.transferChannelSettings( viewerFrameQ );
-
- }
- } );
-
- // set initial transforms so data are visible
- InitializeViewerState.initTransform( viewerP );
- InitializeViewerState.initTransform( viewerQ );
-
checkBoxInputMaps();
- // file selection
- fileFrame = new JFrame( "Select File" );
- fileDialog = new FileDialog( fileFrame );
-
if ( ij == null || (IJ.getDirectory( "current" ) == null) )
lastDirectory = new File( System.getProperty( "user.home" ));
else
lastDirectory = new File( IJ.getDirectory( "current" ));
- // default to linear interpolation
- fileFrame.setVisible( false );
-
// add focus listener
//new BigwarpFocusListener( this );
@@ -605,6 +656,324 @@ public void run()
// add landmark mode listener
//addKeyEventPostProcessor( new LandmarkModeListener() );
+
+ baselineModelIndex = 0;
+
+ if( data.sources.size() > 0 )
+ initialize();
+
+ createMovingTargetGroups();
+ viewerP.state().setCurrentGroup( mvgGrp );
+ viewerP.state().setCurrentGroup( tgtGrp );
+
+ SwingUtilities.invokeLater( () -> {
+ viewerFrameP.setVisible( true );
+ viewerFrameQ.setVisible( true );
+ landmarkFrame.setVisible( true );
+
+ fileFrame = new JFrame( "Select File" );
+ fileDialog = new FileDialog( fileFrame );
+ fileFrame.setVisible( false );
+ });
+ }
+
+ public void changeDimensionality(boolean is2D) {
+
+ if (options.values.is2D() == is2D)
+ return;
+
+ options.is2D( is2D );
+ if( options.values.is2D() )
+ ndims = 2;
+ else
+ ndims = 3;
+
+ /* update landmark model with new dimensionality */
+ landmarkModel = new LandmarkTableModel( ndims );
+ landmarkModel.addTableModelListener( landmarkModellistener );
+ addTransformListener( landmarkModel );
+ landmarkModel.setMessage( message );
+
+ landmarkPanel.setTableModel(landmarkModel);
+ setupLandmarkFrame();
+
+ setupWarpMagBaselineOptions( baseXfmList, ndims );
+
+ final Class< ViewerPanel > c_vp = ViewerPanel.class;
+ try
+ {
+ final Field transformEventHandlerField = c_vp.getDeclaredField( "transformEventHandler" );
+ transformEventHandlerField.setAccessible( true );
+ transformEventHandlerField.set( viewerP, options.values.getTransformEventHandlerFactory().create( TransformState.from( viewerP.state()::getViewerTransform, viewerP.state()::setViewerTransform ) ) );
+ transformEventHandlerField.set( viewerQ, options.values.getTransformEventHandlerFactory().create( TransformState.from( viewerQ.state()::getViewerTransform, viewerQ.state()::setViewerTransform ) ) );
+ transformEventHandlerField.setAccessible( false );
+ }
+ catch ( final Exception e )
+ {
+ e.printStackTrace();
+ }
+
+ viewerFrameP.updateTransformBehaviors( options );
+ viewerFrameQ.updateTransformBehaviors( options );
+
+ // If the images are 2d, use a transform handler that limits
+ // transformations to rotations and scalings of the 2d plane ( z = 0 )
+ if ( options.values.is2D() )
+ {
+
+ // final Class< ViewerPanel > c_vp = ViewerPanel.class;
+ try
+ {
+ final Field overlayRendererField = c_vp.getDeclaredField( "multiBoxOverlayRenderer" );
+ overlayRendererField.setAccessible( true );
+
+ final MultiBoxOverlayRenderer overlayRenderP = new MultiBoxOverlayRenderer( DEFAULT_WIDTH, DEFAULT_HEIGHT );
+ final MultiBoxOverlayRenderer overlayRenderQ = new MultiBoxOverlayRenderer( DEFAULT_WIDTH, DEFAULT_HEIGHT );
+
+ // TODO hopefully I won't' need reflection any more
+ final Field boxField = overlayRenderP.getClass().getDeclaredField( "box" );
+ boxField.setAccessible( true );
+ boxField.set( overlayRenderP, new MultiBoxOverlay2d() );
+ boxField.set( overlayRenderQ, new MultiBoxOverlay2d() );
+ boxField.setAccessible( false );
+
+ overlayRendererField.set( viewerP, overlayRenderP );
+ overlayRendererField.set( viewerQ, overlayRenderQ );
+ overlayRendererField.setAccessible( false );
+
+ }
+ catch ( final Exception e )
+ {
+ e.printStackTrace();
+ }
+ }
+
+ viewerP.setNumDim( ndims );
+ viewerQ.setNumDim( ndims );
+
+// overlayP.is2D( options.values.is2D() );
+// overlayQ.is2D( options.values.is2D() );
+
+ bwTransform = new BigWarpTransform( landmarkModel );
+ bwTransform.initializeInverseParameters(data);
+
+ transformSelector = new TransformTypeSelectDialog( landmarkFrame, this );
+
+ final InputTriggerConfig keyProperties = BigDataViewer.getInputTriggerConfig( options );
+ WarpNavigationActions.installActionBindings( getViewerFrameP().getKeybindings(), viewerFrameP, keyProperties, ( ndims == 2 ) );
+ BigWarpActions.installActionBindings( getViewerFrameP().getKeybindings(), this, keyProperties );
+
+ WarpNavigationActions.installActionBindings( getViewerFrameQ().getKeybindings(), viewerFrameQ, keyProperties, ( ndims == 2 ) );
+ BigWarpActions.installActionBindings( getViewerFrameQ().getKeybindings(), this, keyProperties );
+
+ BigWarpActions.installLandmarkPanelActionBindings( landmarkFrame.getKeybindings(), this, landmarkTable, keyProperties );
+
+ warpVisDialog.toleranceSpinner.setValue( bwTransform.getInverseTolerance() );
+
+ SwingUtilities.invokeLater( () -> {
+ landmarkFrame.setVisible( true );
+ });
+ }
+
+ public void initialize()
+ {
+ wrapMovingSources( ndims, data );
+
+ // starting view
+ if( data.numTargetSources() > 0 )
+ data.getTargetSource( 0 ).getSpimSource().getSourceTransform( 0, 0, fixedViewXfm );
+
+//TODO Expose adding these sources via UI
+
+// final ARGBType white = new ARGBType( ARGBType.rgba( 255, 255, 255, 255 ));
+//
+// warpMagSource = addWarpMagnitudeSource( data, ndims == 2, "Warp magnitude" );
+// jacDetSource = addJacobianDeterminantSource( data, "Jacobian determinant" );
+// gridSource = addGridSource( data, "GridSource" );
+// setGridType( GridSource.GRID_TYPE.LINE );
+//
+// transformMaskSource = addTransformMaskSource( data, ndims, "Transform mask" );
+// bwTransform.setLambda( transformMask.getRandomAccessible() );
+// addMaskMouseListener();
+//
+// // set warp mag source to inactive at the start
+// viewerP.state().setSourceActive( warpMagSource, false );
+// viewerQ.state().setSourceActive( warpMagSource, false );
+// data.sourceColorSettings.put( warpMagSource, new ImagePlusLoader.ColorSettings( -1, 0, 15, white ));
+//
+// // set warp grid source to inactive at the start
+// viewerP.state().setSourceActive( gridSource, false );
+// viewerQ.state().setSourceActive( gridSource, false );
+// data.sourceColorSettings.put( gridSource, new ImagePlusLoader.ColorSettings( -1, 0, 255, white ));
+
+ // set jacobian determinant source to inactive at the start
+// viewerP.state().setSourceActive( jacDetSource, false );
+// viewerQ.state().setSourceActive( jacDetSource, false );
+// data.sourceColorSettings.put( jacDetSource, new ImagePlusLoader.ColorSettings( -1, 0.0, 1.0, white ));
+
+ synchronizeSources();
+
+ data.transferChannelSettings( viewerFrameP );
+ data.transferChannelSettings( viewerFrameQ );
+
+ updateSourceBoundingBoxEstimators();
+
+ createMovingTargetGroups();
+ viewerP.state().setCurrentGroup( mvgGrp );
+ viewerP.state().setCurrentGroup( tgtGrp );
+
+ // set initial transforms so data are visible
+// SwingUtilities.invokeLater( () -> {
+
+ // show moving sources in the moving viewer
+ if( data.numMovingSources() == 1 )
+ viewerP.state().setCurrentSource( data.getMovingSource( 0 ) );
+ else
+ {
+ viewerP.state().setDisplayMode( DisplayMode.GROUP );
+ viewerP.state().setCurrentGroup( mvgGrp );
+ }
+
+ // show fixed sources in the fixed viewer
+ if( data.numTargetSources() == 1 )
+ viewerQ.state().setCurrentSource( data.getTargetSource( 0 ) );
+ else
+ {
+ viewerQ.state().setDisplayMode( DisplayMode.GROUP );
+ viewerQ.state().setCurrentGroup( tgtGrp );
+ }
+
+ InitializeViewerState.initTransform( viewerP );
+ InitializeViewerState.initTransform( viewerQ );
+
+ // save the initial viewer transforms
+ initialViewP = new AffineTransform3D();
+ initialViewQ = new AffineTransform3D();
+ viewerP.state().getViewerTransform( initialViewP );
+ viewerQ.state().getViewerTransform( initialViewQ );
+// } );
+ }
+
+ protected void setupLandmarkFrame()
+ {
+ Point loc = null;
+ Dimension sz = null;
+ if ( landmarkFrame != null )
+ {
+ loc = landmarkFrame.getLocation();
+ sz = landmarkFrame.getSize();
+
+ landmarkModel = null;
+ landmarkFrame.setVisible( false );
+ landmarkFrame.dispose();
+ landmarkFrame = null;
+ landmarkPanel = null;
+
+ }
+
+ landmarkModel = new LandmarkTableModel( ndims );
+ landmarkModellistener = new LandmarkTableListener();
+ landmarkModel.addTableModelListener( landmarkModellistener );
+ addTransformListener( landmarkModel );
+
+ /* Set up landmark panel */
+ landmarkPanel = new BigWarpLandmarkPanel( landmarkModel );
+ landmarkPanel.setOpaque( true );
+ landmarkTable = landmarkPanel.getJTable();
+ landmarkTable.setDefaultRenderer( Object.class, new WarningTableCellRenderer() );
+ addDefaultTableMouseListener();
+ landmarkFrame = new BigWarpLandmarkFrame( "Landmarks", landmarkPanel, this, keymapManager );
+
+ if( overlayP != null )
+ overlayP.setLandmarkPanel(landmarkPanel);
+
+ if( overlayQ != null )
+ overlayQ.setLandmarkPanel(landmarkPanel);
+
+ if ( loc != null )
+ landmarkFrame.setLocation( loc );
+
+ if ( sz != null )
+ landmarkFrame.setSize( sz );
+
+ SwingUtilities.invokeLater( () -> {
+ setUpLandmarkMenus();
+ landmarkFrame.pack();
+ });
+ }
+
+ public void synchronizeSources()
+ {
+ final SynchronizedViewerState pState = viewerP.state();
+ final SynchronizedViewerState qState = viewerQ.state();
+
+ final Set> activeSourcesP = new HashSet<>(pState.getActiveSources());
+ final Set> activeSourcesQ = new HashSet<>(qState.getActiveSources());
+
+ pState.clearSources();
+ qState.clearSources();
+
+ final ArrayList converterSetupsToRemove = new ArrayList<>(setupAssignments.getConverterSetups());
+ converterSetupsToRemove.forEach( setupAssignments::removeSetup );
+
+ for ( int i = 0; i < data.sources.size(); i++ )
+ {
+ final SourceAndConverter< T > sac = data.sources.get( i );
+ pState.addSource( sac );
+ if (activeSourcesP.contains(sac)) {
+ pState.setSourceActive(sac, true);
+ }
+ qState.addSource( sac );
+ if (activeSourcesQ.contains(sac)) {
+ qState.setSourceActive(sac, true);
+ }
+
+ // update the viewer converter setups too
+ final ConverterSetup setup = data.converterSetups.get( i );
+
+ viewerFrameP.getConverterSetups().put( sac, setup );
+ viewerFrameQ.getConverterSetups().put( sac, setup );
+ setupAssignments.addSetup( setup );
+ }
+ }
+
+ /**
+ * Create two source groups - one for moving images,
+ * and the other for target images, for both viewer frames.
+ *
+ * Ensure sources are synchronized with {@link #synchronizeSources()}
+ * before calling this method.
+ */
+ public void createMovingTargetGroups()
+ {
+ mvgGrp = new SourceGroup();
+ tgtGrp = new SourceGroup();
+
+ final SynchronizedViewerState pState = viewerP.state();
+ pState.addGroup( mvgGrp );
+ pState.addGroup( tgtGrp );
+ pState.setGroupName( mvgGrp, "moving images" );
+ pState.setGroupName( tgtGrp, "target images" );
+
+ final SynchronizedViewerState qState = viewerQ.state();
+ qState.addGroup( mvgGrp );
+ qState.addGroup( tgtGrp );
+ qState.setGroupName( mvgGrp, "moving images" );
+ qState.setGroupName( tgtGrp, "target images" );
+
+ for ( final SourceAndConverter< ? > sac : data.sources )
+ {
+ if ( data.isMoving( sac ) )
+ {
+ viewerP.state().addSourceToGroup( sac, mvgGrp );
+ viewerQ.state().addSourceToGroup( sac, mvgGrp );
+ }
+ else
+ {
+ viewerP.state().addSourceToGroup( sac, tgtGrp );
+ viewerQ.state().addSourceToGroup( sac, tgtGrp );
+ }
+ }
}
public int numDimensions()
@@ -615,7 +984,7 @@ public int numDimensions()
/**
* TODO Make a PR that updates this method in InitializeViewerState in bdv-core
* @deprecated Use {@link InitializeViewerState} method instead.
- *
+ *
* @param cumulativeMinCutoff the min image intensity
* @param cumulativeMaxCutoff the max image intensity
* @param state the viewer state
@@ -624,14 +993,48 @@ public int numDimensions()
@Deprecated
public static void initBrightness( final double cumulativeMinCutoff, final double cumulativeMaxCutoff, final ViewerState state, final ConverterSetups converterSetups )
{
- final SourceAndConverter< ? > current = state.getCurrentSource();
- if ( current == null )
- return;
- final Source< ? > source = current.getSpimSource();
- final int timepoint = state.getCurrentTimepoint();
- final Bounds bounds = InitializeViewerState.estimateSourceRange( source, timepoint, cumulativeMinCutoff, cumulativeMaxCutoff );
- final ConverterSetup setup = converterSetups.getConverterSetup( current );
- setup.setDisplayRange( bounds.getMinBound(), bounds.getMaxBound() );
+ final SourceAndConverter< ? > current = state.getCurrentSource();
+ if ( current == null )
+ return;
+ final Source< ? > source = current.getSpimSource();
+ final int timepoint = state.getCurrentTimepoint();
+ final Bounds bounds = InitializeViewerState.estimateSourceRange( source, timepoint, cumulativeMinCutoff, cumulativeMaxCutoff );
+ final ConverterSetup setup = converterSetups.getConverterSetup( current );
+ setup.setDisplayRange( bounds.getMinBound(), bounds.getMaxBound() );
+ }
+
+ /**
+ * @param sources to add; typically the output of a {#{@link BigWarpInit#createSources(BigWarpData, String, int, boolean)}} call
+ */
+ public void addSources( LinkedHashMap