diff --git a/src/main/java/fiji/plugin/trackmate/helper/HelperRunner.java b/src/main/java/fiji/plugin/trackmate/helper/HelperRunner.java index 01759c8..d034b49 100644 --- a/src/main/java/fiji/plugin/trackmate/helper/HelperRunner.java +++ b/src/main/java/fiji/plugin/trackmate/helper/HelperRunner.java @@ -303,7 +303,7 @@ public void run() final double trackingTiming = runner.execTracking( trackmate ); // Perform and save metrics measurements. - runner.performMetricsMeasurements( trackmate, detectionTiming, trackingTiming ); + runner.performAndSaveMetricsMeasurements( trackmate, detectionTiming, trackingTiming ); // Save TrackMate file if required. if ( saveTrackMateFiles ) @@ -625,7 +625,7 @@ else if ( typeStr.equals( "SPT" ) ) str.append( "Max pairing distance for SPT metrics has not been set.\n" ); ok = false; } - this.type = new SPTTrackingMetricsType( maxDist ); + this.type = new SPTTrackingMetricsType( maxDist, "image units" ); } else { diff --git a/src/main/java/fiji/plugin/trackmate/helper/MetricsRunner.java b/src/main/java/fiji/plugin/trackmate/helper/MetricsRunner.java index 05eac8e..d1f5799 100644 --- a/src/main/java/fiji/plugin/trackmate/helper/MetricsRunner.java +++ b/src/main/java/fiji/plugin/trackmate/helper/MetricsRunner.java @@ -75,7 +75,47 @@ public MetricsRunner( final Path resultsRootPath, final TrackingMetricsType type this.nameGenWithID = ( imName, i ) -> String.format( "%s_" + type.csvSuffix() + "_%02d.csv", imName, i ); } - public abstract void performMetricsMeasurements( TrackMate trackmate, double detectionTiming, double trackingTiming ); + /** + * Performs the tracking metrics measurements for the tracks in the model in + * the specified TrackMate instance, against the ground truth given at + * construction. Metric values are returned as a {@link TrackingMetrics}. + * + * @param trackmate + * the tracks on which to measure tracking metrics. + * @return the metric values. + */ + public abstract TrackingMetrics performMetricsMeasurements( final TrackMate trackmate ) throws MetricsComputationErrorException; + + /** + * Performs the tracking metrics measurements for the tracks in the model in + * the specified TrackMate instance, against the ground truth given at + * construction, and save the results in an adequate CSV file. + * + * @param trackmate + * the tracks on which to measure tracking metrics. + * @param detectionTiming + * the metric measuring the detection time. + * @param trackingTiming + * the metric measuring the tracking time. + */ + public void performAndSaveMetricsMeasurements( final TrackMate trackmate, final double detectionTiming, final double trackingTiming ) + { + final Settings settings = trackmate.getSettings(); + final File csvFile = findSuitableCSVFile( settings ); + final String[] csvHeader1 = toCSVHeader( settings ); + + try + { + final TrackingMetrics metrics = performMetricsMeasurements( trackmate ); + batchLogger.log( "SPT metrics:\n" ); + batchLogger.log( metrics.toString() + '\n' ); + writeResults( csvFile, metrics, detectionTiming, trackingTiming, settings, csvHeader1 ); + } + catch ( final MetricsComputationErrorException e ) + { + writeFailedResults( csvFile, settings, csvHeader1 ); + } + } public ValuePair< TrackMate, Double > execDetection( final Settings settings ) { @@ -154,9 +194,14 @@ public double execTracking( final TrackMate trackmate ) return trackingTiming; } - protected File findSuitableCSVFile( final Settings settings ) + private File findSuitableCSVFile( final Settings settings ) { - final String imFileName = settings.imp.getShortTitle(); + final String imFileName; + if ( settings.imp == null ) + imFileName = ""; + else + imFileName = settings.imp.getShortTitle(); + // Prepare CSV headers. final String[] csvHeader1 = toCSVHeader( settings ); final String[] csvHeader = type.concatWithHeader( csvHeader1 ); @@ -220,7 +265,7 @@ protected File findSuitableCSVFile( final Settings settings ) * @param csvHeader * the header name for each setting value. */ - protected void writeResults( + private void writeResults( final File csvFile, final TrackingMetrics metrics, final double detectionTiming, @@ -232,8 +277,6 @@ protected void writeResults( metrics.set( TrackingMetricsType.TIM, detectionTiming + trackingTiming ); metrics.set( TrackingMetricsType.DETECTION_TIME, detectionTiming ); metrics.set( TrackingMetricsType.TRACKING_TIME, trackingTiming ); - batchLogger.log( "SPT metrics:\n" ); - batchLogger.log( metrics.toString() + '\n' ); // Write to CSV. final String[] line1 = toCSVLine( settings, csvHeader ); @@ -267,7 +310,7 @@ protected void writeResults( * @param csvHeader * the header name for each setting value. */ - protected void writeFailedResults( final File csvFile, final Settings settings, final String[] csvHeader ) + private void writeFailedResults( final File csvFile, final Settings settings, final String[] csvHeader ) { // Write default values to CSV. final String[] settingsValueColumns = toCSVLine( settings, csvHeader ); @@ -324,7 +367,7 @@ private final File getCSVFile( final String resultsRootPath, final String imageN return csvFilePath.toFile(); } - protected static final String[] toCSVHeader( final Settings settings ) + private static final String[] toCSVHeader( final Settings settings ) { final int nDetectorParams = settings.detectorSettings.size(); final int nTrackerParams = settings.trackerSettings.size(); @@ -341,7 +384,7 @@ protected static final String[] toCSVHeader( final Settings settings ) return out; } - protected static final String[] toCSVLine( final Settings settings, final String[] csvHeader ) + private static final String[] toCSVLine( final Settings settings, final String[] csvHeader ) { final int nDetectorParams = settings.detectorSettings.size(); final int nTrackerParams = settings.trackerSettings.size(); @@ -363,4 +406,11 @@ protected static final String[] toCSVLine( final Settings settings, final String } return out; } + + public static class MetricsComputationErrorException extends Exception + { + + private static final long serialVersionUID = 1L; + + } } diff --git a/src/main/java/fiji/plugin/trackmate/helper/TrackMateComputeMetricsPlugin.java b/src/main/java/fiji/plugin/trackmate/helper/TrackMateComputeMetricsPlugin.java new file mode 100644 index 0000000..12ea9fa --- /dev/null +++ b/src/main/java/fiji/plugin/trackmate/helper/TrackMateComputeMetricsPlugin.java @@ -0,0 +1,45 @@ +/*- + * #%L + * TrackMate: your buddy for everyday tracking. + * %% + * Copyright (C) 2021 - 2024 TrackMate developers. + * %% + * 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 3 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 fiji.plugin.trackmate.helper; + +import javax.swing.SwingUtilities; + +import fiji.plugin.trackmate.helper.ui.MetricsLauncherController; +import ij.plugin.PlugIn; +import net.imagej.ImageJ; + +public class TrackMateComputeMetricsPlugin implements PlugIn +{ + + @Override + public void run( final String arg ) + { + SwingUtilities.invokeLater( () -> new MetricsLauncherController() ); + } + + public static void main( final String[] args ) + { + final ImageJ ij = new ImageJ(); + ij.launch( args ); + new TrackMateComputeMetricsPlugin().run( null ); + } +} diff --git a/src/main/java/fiji/plugin/trackmate/helper/TrackMateParameterSweepPlugin.java b/src/main/java/fiji/plugin/trackmate/helper/TrackMateParameterSweepPlugin.java index c78980b..a18d620 100644 --- a/src/main/java/fiji/plugin/trackmate/helper/TrackMateParameterSweepPlugin.java +++ b/src/main/java/fiji/plugin/trackmate/helper/TrackMateParameterSweepPlugin.java @@ -21,6 +21,8 @@ */ package fiji.plugin.trackmate.helper; +import javax.swing.SwingUtilities; + import fiji.plugin.trackmate.helper.ui.HelperLauncherController; import ij.plugin.PlugIn; @@ -30,6 +32,6 @@ public class TrackMateParameterSweepPlugin implements PlugIn @Override public void run( final String arg ) { - new HelperLauncherController(); + SwingUtilities.invokeLater( () -> new HelperLauncherController() ); } } diff --git a/src/main/java/fiji/plugin/trackmate/helper/TrackMateParameterSweepResultsPlugin.java b/src/main/java/fiji/plugin/trackmate/helper/TrackMateParameterSweepResultsPlugin.java index 54ce077..2ccf322 100644 --- a/src/main/java/fiji/plugin/trackmate/helper/TrackMateParameterSweepResultsPlugin.java +++ b/src/main/java/fiji/plugin/trackmate/helper/TrackMateParameterSweepResultsPlugin.java @@ -89,7 +89,7 @@ public void run( final String arg ) else if ( typeStr.equals( "SPT" ) ) { // SPT max distance does not matter for inspection. - type = new SPTTrackingMetricsType( 1. ); + type = new SPTTrackingMetricsType( 1., "image units" ); } else { diff --git a/src/main/java/fiji/plugin/trackmate/helper/ctc/CTCMetricsRunner.java b/src/main/java/fiji/plugin/trackmate/helper/ctc/CTCMetricsRunner.java index 9ab1083..9405ac4 100644 --- a/src/main/java/fiji/plugin/trackmate/helper/ctc/CTCMetricsRunner.java +++ b/src/main/java/fiji/plugin/trackmate/helper/ctc/CTCMetricsRunner.java @@ -21,7 +21,6 @@ */ package fiji.plugin.trackmate.helper.ctc; -import java.io.File; import java.io.IOException; import java.nio.file.FileVisitResult; import java.nio.file.Files; @@ -32,7 +31,6 @@ import org.scijava.Context; -import fiji.plugin.trackmate.Settings; import fiji.plugin.trackmate.TrackMate; import fiji.plugin.trackmate.action.CTCExporter; import fiji.plugin.trackmate.action.CTCExporter.ExportType; @@ -67,12 +65,9 @@ public CTCMetricsRunner( final String gtPath, final String saveFolder, final Con } @Override - public void performMetricsMeasurements( final TrackMate trackmate, final double detectionTiming, final double trackingTiming ) + public TrackingMetrics performMetricsMeasurements( final TrackMate trackmate ) throws MetricsComputationErrorException { batchLogger.log( "Exporting as CTC results.\n" ); - final Settings settings = trackmate.getSettings(); - final File csvFile = findSuitableCSVFile( settings ); - final String[] csvHeader1 = toCSVHeader( settings ); final int id = CTCExporter.getAvailableDatasetID( resultsRootPath.toString() ); final String resultsFolder = CTCExporter.getExportTrackingDataPath( resultsRootPath.toString(), id, ExportType.RESULTS, trackmate ); @@ -84,13 +79,12 @@ public void performMetricsMeasurements( final TrackMate trackmate, final double // Perform CTC measurements. batchLogger.log( "Performing CTC metrics measurements.\n" ); final TrackingMetrics metrics = ctc.process( gtPath, resultsFolder ); - - writeResults( csvFile, metrics, detectionTiming, trackingTiming, settings, csvHeader1 ); + return metrics; } catch ( final IOException | IllegalArgumentException e ) { batchLogger.error( "Could not export tracking data to CTC files:\n" + e.getMessage() + '\n' ); - writeFailedResults( csvFile, settings, csvHeader1 ); + throw new MetricsComputationErrorException(); } finally { diff --git a/src/main/java/fiji/plugin/trackmate/helper/spt/SPTMetricsRunner.java b/src/main/java/fiji/plugin/trackmate/helper/spt/SPTMetricsRunner.java index 9a60081..d80c8f6 100644 --- a/src/main/java/fiji/plugin/trackmate/helper/spt/SPTMetricsRunner.java +++ b/src/main/java/fiji/plugin/trackmate/helper/spt/SPTMetricsRunner.java @@ -23,16 +23,21 @@ import java.io.File; import java.nio.file.Paths; +import java.util.ArrayList; import java.util.List; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + import fiji.plugin.trackmate.Model; -import fiji.plugin.trackmate.Settings; import fiji.plugin.trackmate.TrackMate; import fiji.plugin.trackmate.helper.MetricsRunner; import fiji.plugin.trackmate.helper.TrackingMetrics; import fiji.plugin.trackmate.helper.spt.importer.SPTFormatImporter; +import fiji.plugin.trackmate.helper.spt.importer.XMLUtil; import fiji.plugin.trackmate.helper.spt.measure.DistanceTypes; import fiji.plugin.trackmate.helper.spt.measure.TrackSegment; +import fiji.plugin.trackmate.io.TmXmlReader; public class SPTMetricsRunner extends MetricsRunner { @@ -41,32 +46,60 @@ public class SPTMetricsRunner extends MetricsRunner private final double maxDist; - public SPTMetricsRunner( final String gtPath, final String saveFolder, final double maxDist ) + private final String units; + + public SPTMetricsRunner( final String gtPath, final String saveFolder, final double maxDist, final String units ) { - super( Paths.get( saveFolder ), new SPTTrackingMetricsType( maxDist ) ); + super( Paths.get( saveFolder ), new SPTTrackingMetricsType( maxDist, units ) ); this.maxDist = maxDist; - this.referenceTracks = SPTFormatImporter.fromXML( new File( gtPath ) ); + this.units = units; + + // Is the GT a TrackMate or a ISBI challenge file? + final File gtFile = new File( gtPath ); + final Document document = XMLUtil.loadDocument( gtFile ); + final Element root = XMLUtil.getRootElement( document ); + if ( root == null ) + throw new IllegalArgumentException( "can't find: tag." ); + + final ArrayList< Element > rootEls = XMLUtil.getElements( root, "TrackContestISBI2012" ); + if ( rootEls.size() == 0 ) + { + // Not an ISBI challenge file. + final TmXmlReader reader = new TmXmlReader( gtFile ); + if ( !reader.isReadingOk() ) + throw new IllegalArgumentException( "Ground-truth file is neither a TrackMate file nor a ISBI SPT challenge file." ); + + final Model model = reader.getModel(); + try + { + this.referenceTracks = SPTFormatImporter.fromTrackMate( model ); + } + catch ( final Exception iae ) + { + throw new IllegalArgumentException( "TrackMate ground-truth file cannot be used with SPT metrics:\n" + iae.getMessage() ); + } + } + else + { + // ISBI challenge file. + this.referenceTracks = SPTFormatImporter.fromXML( gtFile ); + } } @Override - public void performMetricsMeasurements( final TrackMate trackmate, final double detectionTiming, final double trackingTiming ) + public TrackingMetrics performMetricsMeasurements( final TrackMate trackmate ) { - final Settings settings = trackmate.getSettings(); final Model model = trackmate.getModel(); - final File csvFile = findSuitableCSVFile( settings ); - final String[] csvHeader1 = toCSVHeader( settings ); - final List< TrackSegment > candidateTracks = SPTFormatImporter.fromTrackMate( model ); - // Perform SPT measurements. batchLogger.log( String.format( "Performing SPT metrics measurements with max pairing dist = %.2f %s\n", - maxDist, settings.imp.getCalibration().getUnits() ) ); + maxDist, units ) ); final double[] score = ISBIScoring.score( referenceTracks, candidateTracks, maxDist, DistanceTypes.DISTANCE_EUCLIDIAN ); final TrackingMetrics metrics = new TrackingMetrics( type ); for ( int i = 0; i < score.length; i++ ) metrics.set( i, score[ i ] ); - writeResults( csvFile, metrics, detectionTiming, trackingTiming, settings, csvHeader1 ); + return metrics; } } diff --git a/src/main/java/fiji/plugin/trackmate/helper/spt/SPTTrackingMetricsType.java b/src/main/java/fiji/plugin/trackmate/helper/spt/SPTTrackingMetricsType.java index fca75da..3596bc8 100644 --- a/src/main/java/fiji/plugin/trackmate/helper/spt/SPTTrackingMetricsType.java +++ b/src/main/java/fiji/plugin/trackmate/helper/spt/SPTTrackingMetricsType.java @@ -60,6 +60,8 @@ public class SPTTrackingMetricsType extends TrackingMetricsType private final double maxDist; + private final String units; + /** * Builds a new metrics type based on the SPT challenge, with the specified * max pairing distance given in physical units. @@ -72,17 +74,20 @@ public class SPTTrackingMetricsType extends TrackingMetricsType * * @param maxDist * the max pairing distance. + * @param units + * the physical units in which maxDist is specified. */ - public SPTTrackingMetricsType( final double maxDist ) + public SPTTrackingMetricsType( final double maxDist, final String units ) { super( KEYS ); this.maxDist = maxDist; + this.units = units; } @Override public MetricsRunner runner( final String gtPath, final String saveFolder ) { - return new SPTMetricsRunner( gtPath, saveFolder, maxDist ); + return new SPTMetricsRunner( gtPath, saveFolder, maxDist, units ); } @Override diff --git a/src/main/java/fiji/plugin/trackmate/helper/spt/importer/SPTFormatImporter.java b/src/main/java/fiji/plugin/trackmate/helper/spt/importer/SPTFormatImporter.java index 444f1a7..4af319e 100644 --- a/src/main/java/fiji/plugin/trackmate/helper/spt/importer/SPTFormatImporter.java +++ b/src/main/java/fiji/plugin/trackmate/helper/spt/importer/SPTFormatImporter.java @@ -83,12 +83,13 @@ public static List< TrackSegment > fromXML( final File inputFile ) throws Illega final Document document = XMLUtil.loadDocument( inputFile ); final Element root = XMLUtil.getRootElement( document ); if ( root == null ) - { throw new IllegalArgumentException( "can't find: tag." ); } - final Element trackingSet = XMLUtil.getElements( root, "TrackContestISBI2012" ).get( 0 ); + throw new IllegalArgumentException( "can't find: tag." ); - if ( trackingSet == null ) + final ArrayList< Element > trackingSets = XMLUtil.getElements( root, "TrackContestISBI2012" ); + if ( trackingSets.size() == 0 ) throw new IllegalArgumentException( "can't find: tag." ); - + + final Element trackingSet = trackingSets.get( 0 ); final List< Element > particleElementArrayList = XMLUtil.getElements( trackingSet, "particle" ); for ( final Element particleElement : particleElementArrayList ) diff --git a/src/main/java/fiji/plugin/trackmate/helper/ui/HelperLauncherController.java b/src/main/java/fiji/plugin/trackmate/helper/ui/HelperLauncherController.java index e41236a..2b31467 100644 --- a/src/main/java/fiji/plugin/trackmate/helper/ui/HelperLauncherController.java +++ b/src/main/java/fiji/plugin/trackmate/helper/ui/HelperLauncherController.java @@ -53,9 +53,10 @@ public HelperLauncherController() gui.btnOK.addActionListener( e -> { final ImagePlus imp; final boolean impOpen = WindowManager.getImageCount() > 0; - final boolean ctcSelected = gui.rdbtnCTC.isSelected(); + final boolean ctcSelected = gui.isCTCSelected(); final String gtPath = gui.tfGTPath.getText(); + String units; if ( impOpen ) { final String imName = ( String ) gui.cmbboxImp.getSelectedItem(); @@ -65,6 +66,7 @@ public HelperLauncherController() IJ.error( "TrackMate-Helper", "Could not find opened image with name " + imName ); return; } + units = imp.getCalibration().getUnit(); } else { @@ -76,12 +78,13 @@ public HelperLauncherController() return; } imp.show(); + units = imp.getCalibration().getUnit(); } frame.dispose(); final TrackingMetricsType type = ctcSelected ? new CTCTrackingMetricsType() - : new SPTTrackingMetricsType( ( ( Number ) gui.ftfMaxDist.getValue() ).doubleValue() ); + : new SPTTrackingMetricsType( gui.getSPTMaxPairingDistance(), units ); final File modelFile = ParameterSweepModelIO.makeSettingsFileForGTPath( gtPath ); if ( !modelFile.exists() ) diff --git a/src/main/java/fiji/plugin/trackmate/helper/ui/HelperLauncherPanel.java b/src/main/java/fiji/plugin/trackmate/helper/ui/HelperLauncherPanel.java index ff52c26..b4ad987 100644 --- a/src/main/java/fiji/plugin/trackmate/helper/ui/HelperLauncherPanel.java +++ b/src/main/java/fiji/plugin/trackmate/helper/ui/HelperLauncherPanel.java @@ -25,11 +25,6 @@ import static fiji.plugin.trackmate.gui.Fonts.FONT; import static fiji.plugin.trackmate.gui.Fonts.SMALL_FONT; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Desktop; -import java.awt.Dimension; -import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Image; @@ -39,28 +34,17 @@ import java.awt.dnd.DropTarget; import java.awt.dnd.DropTargetDropEvent; import java.awt.event.FocusAdapter; -import java.awt.event.ItemListener; -import java.awt.event.MouseAdapter; import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.text.DecimalFormat; import java.util.List; import java.util.stream.Collectors; import javax.swing.BorderFactory; -import javax.swing.Box; -import javax.swing.BoxLayout; -import javax.swing.ButtonGroup; import javax.swing.DefaultComboBoxModel; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComboBox; -import javax.swing.JFormattedTextField; import javax.swing.JLabel; import javax.swing.JPanel; -import javax.swing.JRadioButton; import javax.swing.JSeparator; import javax.swing.JTextField; @@ -68,8 +52,7 @@ import org.scijava.util.VersionUtils; import fiji.plugin.trackmate.gui.Icons; -import fiji.plugin.trackmate.helper.ctc.CTCTrackingMetricsType; -import fiji.plugin.trackmate.helper.spt.SPTTrackingMetricsType; +import fiji.plugin.trackmate.helper.ui.components.MetricsChooserPanel; import fiji.plugin.trackmate.util.FileChooser; import fiji.plugin.trackmate.util.FileChooser.DialogType; import fiji.plugin.trackmate.util.FileChooser.SelectionMode; @@ -90,11 +73,9 @@ public class HelperLauncherPanel extends JPanel final JButton btnCancel; - final JRadioButton rdbtnCTC; - final JComboBox< String > cmbboxImp; - final JFormattedTextField ftfMaxDist; + private MetricsChooserPanel metricsChooserPanel; public HelperLauncherPanel() { @@ -102,9 +83,9 @@ public HelperLauncherPanel() final GridBagLayout gridBagLayout = new GridBagLayout(); gridBagLayout.columnWidths = new int[] { 0, 0, 0 }; - gridBagLayout.rowHeights = new int[] { 0, 0, 24, 0, 0, 0, 100, 0, 13, 31, 24, 0, 0, 0, 0, 24, 0, 0, 0, 0 }; + gridBagLayout.rowHeights = new int[] { 0, 0, 24, 31, 24, 0, 0, 0, 0, 24, 0, 0, 0, 0 }; gridBagLayout.columnWeights = new double[] { 1.0, 0.0, Double.MIN_VALUE }; - gridBagLayout.rowWeights = new double[] { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE }; + gridBagLayout.rowWeights = new double[] { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE }; setLayout( gridBagLayout ); final Image im = Icons.TRACKMATE_ICON.getImage(); @@ -139,83 +120,21 @@ public HelperLauncherPanel() gbcSeparator.gridy = 2; add( new JSeparator(), gbcSeparator ); - final JLabel lblChooseMetrics = new JLabel( "Metrics to use:" ); - lblChooseMetrics.setFont( FONT ); - final GridBagConstraints gbcLblChooseMetrics = new GridBagConstraints(); - gbcLblChooseMetrics.gridwidth = 2; - gbcLblChooseMetrics.anchor = GridBagConstraints.WEST; - gbcLblChooseMetrics.insets = new Insets( 0, 0, 5, 0 ); - gbcLblChooseMetrics.gridx = 0; - gbcLblChooseMetrics.gridy = 3; - add( lblChooseMetrics, gbcLblChooseMetrics ); - - rdbtnCTC = new JRadioButton( "Cell-Tracking challenge (CTC)" ); - rdbtnCTC.setFont( SMALL_FONT ); - final GridBagConstraints gbcRdbtnCTC = new GridBagConstraints(); - gbcRdbtnCTC.gridwidth = 2; - gbcRdbtnCTC.anchor = GridBagConstraints.WEST; - gbcRdbtnCTC.insets = new Insets( 0, 0, 5, 0 ); - gbcRdbtnCTC.gridx = 0; - gbcRdbtnCTC.gridy = 4; - add( rdbtnCTC, gbcRdbtnCTC ); - - final JRadioButton rdbtnSPT = new JRadioButton( "Single-Particle Tracking challenge (SPT)" ); - rdbtnSPT.setFont( SMALL_FONT ); - final GridBagConstraints gbcRdbtnSPT = new GridBagConstraints(); - gbcRdbtnSPT.gridwidth = 2; - gbcRdbtnSPT.insets = new Insets( 0, 0, 5, 0 ); - gbcRdbtnSPT.anchor = GridBagConstraints.WEST; - gbcRdbtnSPT.gridx = 0; - gbcRdbtnSPT.gridy = 5; - add( rdbtnSPT, gbcRdbtnSPT ); - - final JLabel lblMetricsDescription = new JLabel(); - lblMetricsDescription.setFont( SMALL_FONT.deriveFont( Font.ITALIC ) ); - final GridBagConstraints gbcLblMetricsDescription = new GridBagConstraints(); - gbcLblMetricsDescription.anchor = GridBagConstraints.SOUTH; - gbcLblMetricsDescription.fill = GridBagConstraints.HORIZONTAL; - gbcLblMetricsDescription.gridwidth = 2; - gbcLblMetricsDescription.insets = new Insets( 0, 0, 5, 0 ); - gbcLblMetricsDescription.gridx = 0; - gbcLblMetricsDescription.gridy = 6; - add( lblMetricsDescription, gbcLblMetricsDescription ); - - final JLabel lblUrl = new JLabel( " " ); - lblUrl.setFont( SMALL_FONT ); - lblUrl.setForeground( Color.BLUE ); - final GridBagConstraints gbcLblUrl = new GridBagConstraints(); - gbcLblUrl.anchor = GridBagConstraints.NORTH; - gbcLblUrl.gridwidth = 2; - gbcLblUrl.fill = GridBagConstraints.HORIZONTAL; - gbcLblUrl.insets = new Insets( 0, 0, 5, 0 ); - gbcLblUrl.gridx = 0; - gbcLblUrl.gridy = 7; - add( lblUrl, gbcLblUrl ); - - final GridBagConstraints gbcSeparator2 = new GridBagConstraints(); - gbcSeparator2.fill = GridBagConstraints.BOTH; - gbcSeparator2.gridwidth = 2; - gbcSeparator2.insets = new Insets( 0, 0, 5, 5 ); - gbcSeparator2.gridx = 0; - gbcSeparator2.gridy = 8; - add( new JSeparator(), gbcSeparator2 ); - - final JPanel panelConfigParams = new JPanel( new BorderLayout() ); - final GridBagConstraints gbcPanelConfigParams = new GridBagConstraints(); - gbcPanelConfigParams.anchor = GridBagConstraints.NORTH; - gbcPanelConfigParams.gridwidth = 2; - gbcPanelConfigParams.insets = new Insets( 0, 0, 5, 0 ); - gbcPanelConfigParams.fill = GridBagConstraints.BOTH; - gbcPanelConfigParams.gridx = 0; - gbcPanelConfigParams.gridy = 9; - add( panelConfigParams, gbcPanelConfigParams ); + this.metricsChooserPanel = new MetricsChooserPanel(); + final GridBagConstraints gbcMetricsPanel = new GridBagConstraints(); + gbcMetricsPanel.gridwidth = 2; + gbcMetricsPanel.insets = new Insets( 0, 0, 5, 0 ); + gbcMetricsPanel.fill = GridBagConstraints.BOTH; + gbcMetricsPanel.gridx = 0; + gbcMetricsPanel.gridy = 3; + add( metricsChooserPanel, gbcMetricsPanel ); final GridBagConstraints gbcSeparator1 = new GridBagConstraints(); gbcSeparator1.gridwidth = 2; gbcSeparator1.insets = new Insets( 0, 0, 5, 0 ); gbcSeparator1.fill = GridBagConstraints.BOTH; gbcSeparator1.gridx = 0; - gbcSeparator1.gridy = 10; + gbcSeparator1.gridy = 4; add( new JSeparator(), gbcSeparator1 ); final JLabel lblPleaseSelectImage = new JLabel( "Please select an image to run the tracking on." ); @@ -225,7 +144,7 @@ public HelperLauncherPanel() gbcLblPleaseSelectImage.insets = new Insets( 0, 0, 5, 0 ); gbcLblPleaseSelectImage.anchor = GridBagConstraints.WEST; gbcLblPleaseSelectImage.gridx = 0; - gbcLblPleaseSelectImage.gridy = 11; + gbcLblPleaseSelectImage.gridy = 5; add( lblPleaseSelectImage, gbcLblPleaseSelectImage ); final JLabel lblImagePath = new JLabel( "Path to the source image:" ); @@ -235,7 +154,7 @@ public HelperLauncherPanel() gbcLblImagePath.insets = new Insets( 0, 0, 5, 0 ); gbcLblImagePath.anchor = GridBagConstraints.WEST; gbcLblImagePath.gridx = 0; - gbcLblImagePath.gridy = 12; + gbcLblImagePath.gridy = 6; add( lblImagePath, gbcLblImagePath ); tfImagePath = new JTextField(); @@ -244,7 +163,7 @@ public HelperLauncherPanel() gbcTfImagePath.insets = new Insets( 0, 0, 5, 5 ); gbcTfImagePath.fill = GridBagConstraints.HORIZONTAL; gbcTfImagePath.gridx = 0; - gbcTfImagePath.gridy = 13; + gbcTfImagePath.gridy = 7; add( tfImagePath, gbcTfImagePath ); tfImagePath.setColumns( 10 ); @@ -253,7 +172,7 @@ public HelperLauncherPanel() final GridBagConstraints gbcBtnBrowseImage = new GridBagConstraints(); gbcBtnBrowseImage.insets = new Insets( 0, 0, 5, 0 ); gbcBtnBrowseImage.gridx = 1; - gbcBtnBrowseImage.gridy = 13; + gbcBtnBrowseImage.gridy = 7; add( btnBrowseImage, gbcBtnBrowseImage ); cmbboxImp = new JComboBox<>(); @@ -263,7 +182,7 @@ public HelperLauncherPanel() gbcCmbboxImp.gridwidth = 2; gbcCmbboxImp.fill = GridBagConstraints.HORIZONTAL; gbcCmbboxImp.gridx = 0; - gbcCmbboxImp.gridy = 14; + gbcCmbboxImp.gridy = 8; add( cmbboxImp, gbcCmbboxImp ); final GridBagConstraints gbcSeparator3 = new GridBagConstraints(); @@ -271,7 +190,7 @@ public HelperLauncherPanel() gbcSeparator3.gridwidth = 2; gbcSeparator3.fill = GridBagConstraints.BOTH; gbcSeparator3.gridx = 0; - gbcSeparator3.gridy = 15; + gbcSeparator3.gridy = 9; add( new JSeparator(), gbcSeparator3 ); final JLabel lblBrowseGroundTruth = new JLabel( "Please browse to the ground truth file or folder:" ); @@ -281,7 +200,7 @@ public HelperLauncherPanel() gbcLblBrowseGroundTruth.anchor = GridBagConstraints.WEST; gbcLblBrowseGroundTruth.gridwidth = 2; gbcLblBrowseGroundTruth.gridx = 0; - gbcLblBrowseGroundTruth.gridy = 16; + gbcLblBrowseGroundTruth.gridy = 10; add( lblBrowseGroundTruth, gbcLblBrowseGroundTruth ); tfGTPath = new JTextField(); @@ -290,7 +209,7 @@ public HelperLauncherPanel() gbcTfGTPath.insets = new Insets( 0, 0, 5, 5 ); gbcTfGTPath.fill = GridBagConstraints.HORIZONTAL; gbcTfGTPath.gridx = 0; - gbcTfGTPath.gridy = 17; + gbcTfGTPath.gridy = 11; add( tfGTPath, gbcTfGTPath ); tfGTPath.setColumns( 10 ); @@ -299,7 +218,7 @@ public HelperLauncherPanel() final GridBagConstraints gbcBtnBrowseGT = new GridBagConstraints(); gbcBtnBrowseGT.insets = new Insets( 0, 0, 5, 0 ); gbcBtnBrowseGT.gridx = 1; - gbcBtnBrowseGT.gridy = 17; + gbcBtnBrowseGT.gridy = 11; add( btnBrowseGT, gbcBtnBrowseGT ); final JPanel panelButtons = new JPanel(); @@ -307,7 +226,7 @@ public HelperLauncherPanel() gbcPanelButtons.anchor = GridBagConstraints.SOUTHEAST; gbcPanelButtons.gridwidth = 2; gbcPanelButtons.gridx = 0; - gbcPanelButtons.gridy = 18; + gbcPanelButtons.gridy = 12; add( panelButtons, gbcPanelButtons ); btnCancel = new JButton( "Cancel" ); @@ -342,83 +261,12 @@ public HelperLauncherPanel() tfImagePath.setVisible( !impOpen ); btnBrowseImage.setVisible( !impOpen ); - /* - * Config panel. - */ - - final JPanel ctcParamPanel = new JPanel(); // Empty - final JPanel sptParamPanel = new JPanel(); - sptParamPanel.setLayout( new BoxLayout( sptParamPanel, BoxLayout.LINE_AXIS ) ); - final JLabel lblMaxDist = new JLabel( "Max distance for pairing:" ); - lblMaxDist.setFont( SMALL_FONT ); - sptParamPanel.add( lblMaxDist ); - sptParamPanel.add( Box.createHorizontalStrut( 5 ) ); - ftfMaxDist = new JFormattedTextField( new DecimalFormat( "0.00" ) ); - ftfMaxDist.setColumns( 9 ); - ftfMaxDist.setFont( SMALL_FONT ); - ftfMaxDist.setHorizontalAlignment( JTextField.RIGHT ); - ftfMaxDist.setMaximumSize( new Dimension( 100, 40 ) ); - sptParamPanel.add( ftfMaxDist ); - sptParamPanel.add( Box.createHorizontalStrut( 5 ) ); - final JLabel lblUnits = new JLabel( "image units" ); - lblUnits.setFont( SMALL_FONT ); - sptParamPanel.add( lblUnits ); - sptParamPanel.add( Box.createHorizontalGlue() ); - /* * Listeners & co. */ - final ButtonGroup buttonGroup = new ButtonGroup(); - buttonGroup.add( rdbtnSPT ); - buttonGroup.add( rdbtnCTC ); - - final ItemListener changeMetrics = e -> { - // Help and selection. - final String doc = rdbtnCTC.isSelected() ? DOC_CTC : DOC_SPT; - lblMetricsDescription.setText( doc ); - final String url = rdbtnCTC.isSelected() ? URL_CTC : URL_SPT; - lblUrl.setText( url ); - - // Config panel. - String units = "image units"; - if ( cmbboxImp.isVisible() ) - { - final String imName = ( String ) cmbboxImp.getSelectedItem(); - final ImagePlus imp = WindowManager.getImage( imName ); - if ( imp != null ) - units = imp.getCalibration().getUnits(); - } - lblUnits.setText( units ); - - final JPanel paramPanel = rdbtnCTC.isSelected() ? ctcParamPanel : sptParamPanel; - panelConfigParams.removeAll(); - panelConfigParams.add( paramPanel, BorderLayout.CENTER ); - - prefService.put( getClass(), METRICS_TYPE_KEY, rdbtnCTC.isSelected() ); - }; - - rdbtnCTC.addItemListener( changeMetrics ); - lblUrl.addMouseListener( new MouseAdapter() - { - @Override - public void mouseClicked( final java.awt.event.MouseEvent e ) - { - try - { - final String link = rdbtnCTC.isSelected() ? LINK_CTC : LINK_SPT; - Desktop.getDesktop().browse( new URI( link ) ); - } - catch ( URISyntaxException | IOException ex ) - { - ex.printStackTrace(); - } - } - } ); - fiji.plugin.trackmate.gui.GuiUtils.selectAllOnFocus( tfImagePath ); fiji.plugin.trackmate.gui.GuiUtils.selectAllOnFocus( tfGTPath ); - fiji.plugin.trackmate.gui.GuiUtils.selectAllOnFocus( ftfMaxDist ); final Runnable storeGtPath = () -> prefService.put( getClass(), GT_PATH_KEY, tfGTPath.getText() ); final Runnable storeImagePath = () -> prefService.put( getClass(), IMAGE_PATH_KEY, tfImagePath.getText() ); @@ -456,10 +304,10 @@ public void focusLost( final java.awt.event.FocusEvent e ) } ); btnBrowseGT.addActionListener( e -> { - final String dialogTitle = rdbtnCTC.isSelected() - ? "Select a CTC ground-ruth folder." + final String dialogTitle = metricsChooserPanel.isCTCSelected() + ? "Select a CTC ground-truth folder." : "Select a SPT ground-truth XML file."; - final SelectionMode selectionMode = rdbtnCTC.isSelected() + final SelectionMode selectionMode = metricsChooserPanel.isCTCSelected() ? SelectionMode.DIRECTORIES_ONLY : SelectionMode.FILES_ONLY; final File file = FileChooser.chooseFile( this, tfGTPath.getText(), null, dialogTitle, DialogType.LOAD, selectionMode ); @@ -482,16 +330,20 @@ public void focusLost( final java.awt.event.FocusEvent e ) final String lastUsedGtPath = prefService.get( getClass(), GT_PATH_KEY, System.getProperty( "user.home" ) ); tfGTPath.setText( lastUsedGtPath ); + } - final boolean ctcSelected = prefService.getBoolean( getClass(), METRICS_TYPE_KEY, true ); - rdbtnCTC.setSelected( ctcSelected ); - rdbtnSPT.setSelected( !ctcSelected ); - changeMetrics.itemStateChanged( null ); - - ftfMaxDist.setValue( Double.valueOf( 1.0 ) ); + /** + * Returns true if the CTC metrics are selected. If + * false, the SPT metrics are selected. + * + * @return true if the CTC metrics are selected + */ + public boolean isCTCSelected() + { + return metricsChooserPanel.isCTCSelected(); } - private static class SetFileDropTarget extends DropTarget + static class SetFileDropTarget extends DropTarget { private final JTextField tf; @@ -528,25 +380,14 @@ public synchronized void drop( final DropTargetDropEvent evt ) } } - private static final String GT_PATH_KEY = "GT_FOLDER"; + static final String GT_PATH_KEY = "GT_FOLDER"; private static final String IMAGE_PATH_KEY = "IMAGE_PATH"; - private static final String METRICS_TYPE_KEY = "METRICS_TYPE"; - - private static final String DOC_CTC = CTCTrackingMetricsType.INFO; - - private static final String DOC_SPT = SPTTrackingMetricsType.INFO; - - protected static final String LINK_CTC = CTCTrackingMetricsType.URL; - - protected static final String LINK_SPT = SPTTrackingMetricsType.URL; + public double getSPTMaxPairingDistance() + { + return metricsChooserPanel.getSPTMaxPairingDistance(); + } - private static final String URL_CTC = "Ulman, Maška, et al. 2017"; - private static final String URL_SPT = "Chenouard, Smal, de Chaumont, Maška, et al. 2014"; } diff --git a/src/main/java/fiji/plugin/trackmate/helper/ui/MetricsLauncherController.java b/src/main/java/fiji/plugin/trackmate/helper/ui/MetricsLauncherController.java new file mode 100644 index 0000000..b1a2655 --- /dev/null +++ b/src/main/java/fiji/plugin/trackmate/helper/ui/MetricsLauncherController.java @@ -0,0 +1,248 @@ +/*- + * #%L + * TrackMate: your buddy for everyday tracking. + * %% + * Copyright (C) 2021 - 2024 TrackMate developers. + * %% + * 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 3 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 fiji.plugin.trackmate.helper.ui; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +import javax.swing.JFrame; +import javax.swing.JLabel; + +import org.apache.commons.io.FilenameUtils; +import org.scijava.Cancelable; + +import com.opencsv.CSVWriter; + +import fiji.plugin.trackmate.Logger; +import fiji.plugin.trackmate.Model; +import fiji.plugin.trackmate.Settings; +import fiji.plugin.trackmate.TrackMate; +import fiji.plugin.trackmate.gui.Icons; +import fiji.plugin.trackmate.helper.MetricsRunner; +import fiji.plugin.trackmate.helper.TrackingMetrics; +import fiji.plugin.trackmate.helper.TrackingMetricsType; +import fiji.plugin.trackmate.helper.ctc.CTCTrackingMetricsType; +import fiji.plugin.trackmate.helper.spt.SPTTrackingMetricsType; +import fiji.plugin.trackmate.io.TmXmlReader; +import fiji.plugin.trackmate.util.EverythingDisablerAndReenabler; +import ij.ImagePlus; + +public class MetricsLauncherController implements Cancelable +{ + + private final Logger logger = Logger.IJ_LOGGER; + + private boolean isCanceled; + + private String cancelReason; + + public MetricsLauncherController() + { + final MetricsLauncherPanel gui = new MetricsLauncherPanel(); + final JFrame frame = new JFrame( "TrackMate tracking metrics" ); + frame.setIconImage( Icons.TRACKMATE_ICON.getImage() ); + frame.getContentPane().add( gui ); + frame.setSize( 350, 550 ); + frame.setLocationRelativeTo( null ); + + gui.btnCancel.addActionListener( e -> cancel( "User pressed the cancel button" ) ); + gui.btnOK.addActionListener( e -> { + + final EverythingDisablerAndReenabler disabler = new EverythingDisablerAndReenabler( gui, new Class[] { JLabel.class } ); + disabler.disable(); + new Thread( "TrackMate tracking metrics computation thread" ) + { + @Override + public void run() + { + try + { + final boolean ctcSelected = gui.isCTCSelected(); + final String gtPath = gui.tfGTPath.getText(); + final String inputPath = gui.tfInputPath.getText(); + final double maxDist = gui.getSPTMaxPairingDistance(); + final String units = gui.getUnits(); + computeMetrics( ctcSelected, gtPath, inputPath, maxDist, units ); + } + finally + { + disabler.reenable(); + } + } + }.start(); + } ); + frame.setVisible( true ); + } + + private void computeMetrics( final boolean ctcSelected, final String gtPath, final String inputPath, final double maxDist, final String units ) + { + isCanceled = false; + + // Save folder. + final String saveFolder; + final File input = new File( inputPath ); + if ( input.isDirectory() ) + saveFolder = input.getAbsolutePath(); + else + saveFolder = input.getParent(); + + // Prepare the runner. + final TrackingMetricsType type = ctcSelected + ? new CTCTrackingMetricsType() + : new SPTTrackingMetricsType( maxDist, units ); + + // Prepare the CSV file. + final String gtName = FilenameUtils.removeExtension( new File( gtPath ).getName() ); + final String csvFileName = type.csvSuffix() + "_" + gtName + ".csv"; + final File csvFile = new File( saveFolder, csvFileName ); + + // Echo info + logger.log( "Performing tracking metrics measurements.\n" + + " - Tracking metrics type: " + ( ctcSelected ? "Cell Tracking Challenge" : "Single-Particle Tracking Challenge" ) + "\n" + + " - Ground-truth file: " + gtPath +"\n" + + " - Saving metrics to CSV file: " + csvFile + "\n" ); + final StringBuilder str = new StringBuilder(); + for ( int i = 0; i < type.metrics().size(); i++ ) + str.append( " - " + type.metrics().get( i ).key + ": " + type.metrics().get( i ).description + '\n' ); + logger.log( str.toString() ); + + // Save CSV header. + final String[] csvHeader = new String[ 1 + type.metrics().size() ]; + csvHeader[ 0 ] = "File"; + for ( int i = 0; i < type.metrics().size(); i++ ) + csvHeader[ i + 1 ] = type.metrics().get( i ).key; + + try (CSVWriter csvWriter = new CSVWriter( new FileWriter( csvFile, false ), + CSVWriter.DEFAULT_SEPARATOR, + CSVWriter.NO_QUOTE_CHARACTER, + CSVWriter.DEFAULT_ESCAPE_CHARACTER, + CSVWriter.DEFAULT_LINE_END )) + { + csvWriter.writeNext( csvHeader ); + } + catch ( final IOException e ) + { + logger.error( "Error saving results to CSV file " + csvFile + ":\n" + e.getMessage() ); + return; + } + + // Process input. + try + { + final MetricsRunner runner = type.runner( gtPath, saveFolder ); + runner.setBatchLogger( logger ); + // Process candidate files. + if ( input.isDirectory() ) + { + // Loop over the TrackMate files it contains. + final File[] xmlFiles = input.listFiles( ( dir, name ) -> name.toLowerCase().endsWith( ".xml" ) ); + for ( final File xmlFile : xmlFiles ) + { + if ( isCanceled ) + { + logger.log( "Canceled" ); + return; + } + process( xmlFile, runner, csvFile ); + } + } + else + { + process( input, runner, csvFile ); + } + logger.log( "____________________________________\nDone.\n" ); + } + catch ( final Exception e ) + { + logger.error( "The ground-path file " + gtPath + " cannot be used:\n" + e.getMessage() ); + } + } + + private void process( final File xmlFile, final MetricsRunner runner, final File csvFile ) + { + logger.log( "____________________________________\nProcessing file " + xmlFile + "\n" ); + try + { + final TmXmlReader reader = new TmXmlReader( xmlFile ); + final Model model = reader.getModel(); + final ImagePlus imp = reader.readImage(); + final Settings settings = reader.readSettings( imp ); + final TrackMate trackmate = new TrackMate( model, settings ); + final TrackingMetrics metrics = runner.performMetricsMeasurements( trackmate ); + + logger.log( metrics.toString() ); + + final double[] values = metrics.toArray(); + final String[] line = new String[ 1 + values.length ]; + line[ 0 ] = xmlFile.getName(); + for ( int i = 0; i < values.length; i++ ) + line[ i + 1 ] = "" + values[ i ]; + + try (CSVWriter csvWriter = new CSVWriter( new FileWriter( csvFile, true ), + CSVWriter.DEFAULT_SEPARATOR, + CSVWriter.NO_QUOTE_CHARACTER, + CSVWriter.DEFAULT_ESCAPE_CHARACTER, + CSVWriter.DEFAULT_LINE_END )) + { + csvWriter.writeNext( line ); + } + catch ( final IOException e ) + { + logger.error( "Error saving results to CSV file " + csvFile + ":\n" + e.getMessage() ); + } + + // Loop + if ( imp != null ) + { + imp.changes = false; + imp.close(); + } + } + catch ( final Exception ex ) + { + logger.error( "File " + xmlFile + " is not a TrackMate file. Skipping.\n" ); + ex.printStackTrace(); + } + } + + // --- org.scijava.Cancelable methods --- + + @Override + public boolean isCanceled() + { + return isCanceled; + } + + @Override + public void cancel( final String reason ) + { + isCanceled = true; + cancelReason = reason; + } + + @Override + public String getCancelReason() + { + return cancelReason; + } +} diff --git a/src/main/java/fiji/plugin/trackmate/helper/ui/MetricsLauncherPanel.java b/src/main/java/fiji/plugin/trackmate/helper/ui/MetricsLauncherPanel.java new file mode 100644 index 0000000..8dda0dd --- /dev/null +++ b/src/main/java/fiji/plugin/trackmate/helper/ui/MetricsLauncherPanel.java @@ -0,0 +1,331 @@ +package fiji.plugin.trackmate.helper.ui; + +import static fiji.plugin.trackmate.gui.Fonts.BIG_FONT; +import static fiji.plugin.trackmate.gui.Fonts.FONT; +import static fiji.plugin.trackmate.gui.Fonts.SMALL_FONT; +import static fiji.plugin.trackmate.helper.ui.HelperLauncherPanel.GT_PATH_KEY; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Image; +import java.awt.Insets; +import java.awt.event.FocusAdapter; +import java.io.File; + +import javax.swing.BorderFactory; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSeparator; +import javax.swing.JTextField; + +import org.scijava.prefs.PrefService; +import org.scijava.util.VersionUtils; + +import fiji.plugin.trackmate.Logger; +import fiji.plugin.trackmate.Model; +import fiji.plugin.trackmate.gui.Icons; +import fiji.plugin.trackmate.helper.ui.HelperLauncherPanel.SetFileDropTarget; +import fiji.plugin.trackmate.helper.ui.components.MetricsChooserPanel; +import fiji.plugin.trackmate.io.TmXmlReader; +import fiji.plugin.trackmate.util.FileChooser; +import fiji.plugin.trackmate.util.FileChooser.DialogType; +import fiji.plugin.trackmate.util.FileChooser.SelectionMode; +import fiji.plugin.trackmate.util.JLabelLogger; +import fiji.plugin.trackmate.util.TMUtils; + +public class MetricsLauncherPanel extends JPanel +{ + + private static final long serialVersionUID = 1L; + + final JTextField tfInputPath; + + final JTextField tfGTPath; + + final JButton btnCancel; + + final JButton btnOK; + + private final MetricsChooserPanel metricsChooserPanel; + + private Logger logger; + + public MetricsLauncherPanel() + { + setBorder( BorderFactory.createEmptyBorder( 5, 5, 5, 5 ) ); + + final GridBagLayout gridBagLayout = new GridBagLayout(); + gridBagLayout.rowWeights = new double[] { 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1. }; + gridBagLayout.rowHeights = new int[] { 0, 0, 15, 200, 15 }; + gridBagLayout.columnWeights = new double[] { 1., 0. }; + setLayout( gridBagLayout ); + + final Image im = Icons.TRACKMATE_ICON.getImage(); + final Image newimg = im.getScaledInstance( 32, 32, java.awt.Image.SCALE_SMOOTH ); + final ImageIcon icon = new ImageIcon( newimg ); + + final JLabel lblTitle = new JLabel( "TrackMate tracking metrics", icon, JLabel.LEADING ); + lblTitle.setFont( BIG_FONT ); + final GridBagConstraints gbcLblTitle = new GridBagConstraints(); + gbcLblTitle.gridwidth = 2; + gbcLblTitle.insets = new Insets( 0, 0, 5, 0 ); + gbcLblTitle.fill = GridBagConstraints.HORIZONTAL; + gbcLblTitle.gridx = 0; + gbcLblTitle.gridy = 0; + add( lblTitle, gbcLblTitle ); + + final JLabel lblVersion = new JLabel( "v" + VersionUtils.getVersion( getClass() ) ); + lblVersion.setFont( SMALL_FONT ); + final GridBagConstraints gbcLblVersion = new GridBagConstraints(); + gbcLblVersion.anchor = GridBagConstraints.WEST; + gbcLblVersion.gridwidth = 2; + gbcLblVersion.insets = new Insets( 0, 0, 5, 0 ); + gbcLblVersion.gridx = 0; + gbcLblVersion.gridy = 1; + add( lblVersion, gbcLblVersion ); + + final GridBagConstraints gbcSeparator = new GridBagConstraints(); + gbcSeparator.gridwidth = 2; + gbcSeparator.insets = new Insets( 0, 0, 5, 0 ); + gbcSeparator.fill = GridBagConstraints.BOTH; + gbcSeparator.gridx = 0; + gbcSeparator.gridy = 2; + add( new JSeparator(), gbcSeparator ); + + metricsChooserPanel = new MetricsChooserPanel(); + final GridBagConstraints gbcMetricsPanel = new GridBagConstraints(); + gbcMetricsPanel.gridwidth = 2; + gbcMetricsPanel.insets = new Insets( 0, 0, 5, 0 ); + gbcMetricsPanel.fill = GridBagConstraints.BOTH; + gbcMetricsPanel.gridx = 0; + gbcMetricsPanel.gridy = 3; + add( metricsChooserPanel, gbcMetricsPanel ); + + final GridBagConstraints gbcSeparator1 = new GridBagConstraints(); + gbcSeparator1.gridwidth = 2; + gbcSeparator1.insets = new Insets( 0, 0, 5, 0 ); + gbcSeparator1.fill = GridBagConstraints.BOTH; + gbcSeparator1.gridx = 0; + gbcSeparator1.gridy = 4; + add( new JSeparator(), gbcSeparator1 ); + + final JLabel lblPleaseSelectTestFile = new JLabel( "" + + "Please select the TrackMate file or folder containing " + + "the files on which the metrics will be measured." + + "" ); + lblPleaseSelectTestFile.setFont( FONT ); + final GridBagConstraints gbcLblPleaseSelectTestFile = new GridBagConstraints(); + gbcLblPleaseSelectTestFile.fill = GridBagConstraints.BOTH; + gbcLblPleaseSelectTestFile.gridwidth = 2; + gbcLblPleaseSelectTestFile.insets = new Insets( 0, 0, 5, 0 ); + gbcLblPleaseSelectTestFile.anchor = GridBagConstraints.WEST; + gbcLblPleaseSelectTestFile.gridx = 0; + gbcLblPleaseSelectTestFile.gridy = 5; + add( lblPleaseSelectTestFile, gbcLblPleaseSelectTestFile ); + + final JLabel lblInputPath = new JLabel( "Input path:" ); + lblInputPath.setFont( SMALL_FONT ); + final GridBagConstraints gbcLblInputPath = new GridBagConstraints(); + gbcLblInputPath.gridwidth = 2; + gbcLblInputPath.insets = new Insets( 0, 0, 5, 0 ); + gbcLblInputPath.anchor = GridBagConstraints.WEST; + gbcLblInputPath.gridx = 0; + gbcLblInputPath.gridy = 6; + add( lblInputPath, gbcLblInputPath ); + + tfInputPath = new JTextField(); + tfInputPath.setFont( SMALL_FONT ); + final GridBagConstraints gbcTfImagePath = new GridBagConstraints(); + gbcTfImagePath.insets = new Insets( 0, 0, 5, 5 ); + gbcTfImagePath.fill = GridBagConstraints.HORIZONTAL; + gbcTfImagePath.gridx = 0; + gbcTfImagePath.gridy = 7; + add( tfInputPath, gbcTfImagePath ); + tfInputPath.setColumns( 10 ); + + final JButton btnBrowseInput = new JButton( "Browse" ); + btnBrowseInput.setFont( SMALL_FONT ); + final GridBagConstraints gbcBtnBrowseInput = new GridBagConstraints(); + gbcBtnBrowseInput.insets = new Insets( 0, 0, 5, 0 ); + gbcBtnBrowseInput.gridx = 1; + gbcBtnBrowseInput.gridy = 7; + add( btnBrowseInput, gbcBtnBrowseInput ); + + final GridBagConstraints gbcSeparator3 = new GridBagConstraints(); + gbcSeparator3.insets = new Insets( 0, 0, 5, 0 ); + gbcSeparator3.gridwidth = 2; + gbcSeparator3.fill = GridBagConstraints.BOTH; + gbcSeparator3.gridx = 0; + gbcSeparator3.gridy = 9; + add( new JSeparator(), gbcSeparator3 ); + + final JLabel lblBrowseGroundTruth = new JLabel( "Please browse to the ground truth file or folder:" ); + lblBrowseGroundTruth.setFont( FONT ); + final GridBagConstraints gbcLblBrowseGroundTruth = new GridBagConstraints(); + gbcLblBrowseGroundTruth.insets = new Insets( 0, 0, 5, 0 ); + gbcLblBrowseGroundTruth.anchor = GridBagConstraints.WEST; + gbcLblBrowseGroundTruth.gridwidth = 2; + gbcLblBrowseGroundTruth.gridx = 0; + gbcLblBrowseGroundTruth.gridy = 10; + add( lblBrowseGroundTruth, gbcLblBrowseGroundTruth ); + + tfGTPath = new JTextField(); + tfGTPath.setFont( SMALL_FONT ); + final GridBagConstraints gbcTfGTPath = new GridBagConstraints(); + gbcTfGTPath.insets = new Insets( 0, 0, 5, 5 ); + gbcTfGTPath.fill = GridBagConstraints.HORIZONTAL; + gbcTfGTPath.gridx = 0; + gbcTfGTPath.gridy = 11; + add( tfGTPath, gbcTfGTPath ); + tfGTPath.setColumns( 10 ); + + final JButton btnBrowseGT = new JButton( "Browse" ); + btnBrowseGT.setFont( SMALL_FONT ); + final GridBagConstraints gbcBtnBrowseGT = new GridBagConstraints(); + gbcBtnBrowseGT.insets = new Insets( 0, 0, 5, 0 ); + gbcBtnBrowseGT.gridx = 1; + gbcBtnBrowseGT.gridy = 11; + add( btnBrowseGT, gbcBtnBrowseGT ); + + final JLabelLogger labelLogger = new JLabelLogger(); + labelLogger.setFont( SMALL_FONT ); + final GridBagConstraints gbcLogger = new GridBagConstraints(); + gbcLogger.anchor = GridBagConstraints.SOUTHEAST; + gbcLogger.fill = GridBagConstraints.BOTH; + gbcLogger.gridwidth = 2; + gbcLogger.gridx = 0; + gbcLogger.gridy = 12; + add( labelLogger, gbcLogger ); + + final JPanel panelButtons = new JPanel(); + final GridBagConstraints gbcPanelButtons = new GridBagConstraints(); + gbcPanelButtons.anchor = GridBagConstraints.SOUTHEAST; + gbcPanelButtons.gridwidth = 2; + gbcPanelButtons.gridx = 0; + gbcPanelButtons.gridy = 13; + add( panelButtons, gbcPanelButtons ); + + btnCancel = new JButton( "Cancel" ); + btnCancel.setFont( SMALL_FONT ); + panelButtons.add( btnCancel ); + + btnOK = new JButton( "OK" ); + btnOK.setFont( SMALL_FONT ); + panelButtons.add( btnOK ); + + /* + * Listeners & co. + */ + + this.logger = labelLogger.getLogger(); + final PrefService prefService = TMUtils.getContext().getService( PrefService.class ); + + fiji.plugin.trackmate.gui.GuiUtils.selectAllOnFocus( tfInputPath ); + fiji.plugin.trackmate.gui.GuiUtils.selectAllOnFocus( tfGTPath ); + + final Runnable storeGtPath = () -> prefService.put( getClass(), GT_PATH_KEY, tfGTPath.getText() ); + final Runnable storeInputPath = () -> prefService.put( getClass(), INPUT_PATH_KEY, tfInputPath.getText() ); + + tfInputPath.addActionListener( e -> storeInputPath.run() ); + final FocusAdapter faIm = new FocusAdapter() + { + @Override + public void focusLost( final java.awt.event.FocusEvent e ) + { + storeInputPath.run(); + } + }; + tfInputPath.addFocusListener( faIm ); + + tfGTPath.addActionListener( e -> storeGtPath.run() ); + final FocusAdapter faGt = new FocusAdapter() + { + @Override + public void focusLost( final java.awt.event.FocusEvent e ) + { + storeGtPath.run(); + readPixelUnits(); + } + }; + tfGTPath.addFocusListener( faGt ); + + btnBrowseInput.addActionListener( e -> { + final File file = FileChooser.chooseFile( this, tfInputPath.getText(), null, + "Select an input file or folder", DialogType.LOAD, SelectionMode.FILES_AND_DIRECTORIES ); + if ( file == null ) + return; + + tfInputPath.setText( file.getAbsolutePath() ); + storeInputPath.run(); + } ); + + btnBrowseGT.addActionListener( e -> { + final String dialogTitle = metricsChooserPanel.isCTCSelected() + ? "Select a CTC ground-truth folder." + : "Select a SPT ground-truth XML file."; + final SelectionMode selectionMode = metricsChooserPanel.isCTCSelected() + ? SelectionMode.DIRECTORIES_ONLY + : SelectionMode.FILES_ONLY; + final File file = FileChooser.chooseFile( this, tfGTPath.getText(), null, dialogTitle, DialogType.LOAD, selectionMode ); + if ( file == null ) + return; + + tfGTPath.setText( file.getAbsolutePath() ); + storeGtPath.run(); + readPixelUnits(); + } ); + + tfInputPath.setDropTarget( new SetFileDropTarget( tfInputPath, storeInputPath ) ); + tfGTPath.setDropTarget( new SetFileDropTarget( tfGTPath, storeGtPath ) ); + + /* + * Default values. + */ + + final String lastUsedImagePathFolder = prefService.get( getClass(), INPUT_PATH_KEY, System.getProperty( "user.home" ) ); + tfInputPath.setText( lastUsedImagePathFolder ); + + final String lastUsedGtPath = prefService.get( getClass(), GT_PATH_KEY, System.getProperty( "user.home" ) ); + tfGTPath.setText( lastUsedGtPath ); + readPixelUnits(); + } + + private void readPixelUnits() + { + logger.log( "" ); + if ( !isCTCSelected() ) + { + metricsChooserPanel.setUnits( "image units" ); + // Try to read the pixel units. + final TmXmlReader reader = new TmXmlReader( new File( tfGTPath.getText() ) ); + try + { + final Model model = reader.getModel(); + if ( model != null ) + metricsChooserPanel.setUnits( model.getSpaceUnits() ); + } + catch ( final Exception ex ) + {} + } + } + + public boolean isCTCSelected() + { + return metricsChooserPanel.isCTCSelected(); + } + + public double getSPTMaxPairingDistance() + { + return metricsChooserPanel.getSPTMaxPairingDistance(); + } + + public String getUnits() + { + return metricsChooserPanel.getUnits(); + } + + private static final String INPUT_PATH_KEY = "INPUT_PATH"; + +} diff --git a/src/main/java/fiji/plugin/trackmate/helper/ui/components/MetricsChooserPanel.java b/src/main/java/fiji/plugin/trackmate/helper/ui/components/MetricsChooserPanel.java new file mode 100644 index 0000000..87a6c29 --- /dev/null +++ b/src/main/java/fiji/plugin/trackmate/helper/ui/components/MetricsChooserPanel.java @@ -0,0 +1,253 @@ +package fiji.plugin.trackmate.helper.ui.components; + +import static fiji.plugin.trackmate.gui.Fonts.FONT; +import static fiji.plugin.trackmate.gui.Fonts.SMALL_FONT; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Desktop; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ItemListener; +import java.awt.event.MouseAdapter; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.text.DecimalFormat; + +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; +import javax.swing.JFormattedTextField; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JTextField; + +import org.scijava.prefs.PrefService; + +import fiji.plugin.trackmate.helper.ctc.CTCTrackingMetricsType; +import fiji.plugin.trackmate.helper.spt.SPTTrackingMetricsType; +import fiji.plugin.trackmate.util.TMUtils; +import net.imagej.ImageJ; + +public class MetricsChooserPanel extends JPanel +{ + + private static final long serialVersionUID = 1L; + + private final JRadioButton rdbtnCTC; + + private final JFormattedTextField ftfMaxDist; + + private final JLabel lblUnits; + + public MetricsChooserPanel() + { + final GridBagLayout gridBagLayout = new GridBagLayout(); + gridBagLayout.columnWeights = new double[] { 1.0, 0.0 }; + setLayout( gridBagLayout ); + + final JLabel lblChooseMetrics = new JLabel( "Metrics to use:" ); + lblChooseMetrics.setFont( FONT ); + final GridBagConstraints gbcLblChooseMetrics = new GridBagConstraints(); + gbcLblChooseMetrics.gridwidth = 2; + gbcLblChooseMetrics.anchor = GridBagConstraints.WEST; + gbcLblChooseMetrics.insets = new Insets( 0, 0, 5, 0 ); + gbcLblChooseMetrics.gridx = 0; + gbcLblChooseMetrics.gridy = 0; + add( lblChooseMetrics, gbcLblChooseMetrics ); + + rdbtnCTC = new JRadioButton( "Cell-Tracking challenge (CTC)" ); + rdbtnCTC.setFont( SMALL_FONT ); + final GridBagConstraints gbcRdbtnCTC = new GridBagConstraints(); + gbcRdbtnCTC.gridwidth = 2; + gbcRdbtnCTC.anchor = GridBagConstraints.WEST; + gbcRdbtnCTC.insets = new Insets( 0, 0, 5, 0 ); + gbcRdbtnCTC.gridx = 0; + gbcRdbtnCTC.gridy = 1; + add( rdbtnCTC, gbcRdbtnCTC ); + + final JRadioButton rdbtnSPT = new JRadioButton( "Single-Particle Tracking challenge (SPT)" ); + rdbtnSPT.setFont( SMALL_FONT ); + final GridBagConstraints gbcRdbtnSPT = new GridBagConstraints(); + gbcRdbtnSPT.gridwidth = 2; + gbcRdbtnSPT.insets = new Insets( 0, 0, 5, 0 ); + gbcRdbtnSPT.anchor = GridBagConstraints.WEST; + gbcRdbtnSPT.gridx = 0; + gbcRdbtnSPT.gridy = 2; + add( rdbtnSPT, gbcRdbtnSPT ); + + final JLabel lblMetricsDescription = new JLabel(); + lblMetricsDescription.setFont( SMALL_FONT.deriveFont( Font.ITALIC ) ); + final GridBagConstraints gbcLblMetricsDescription = new GridBagConstraints(); + gbcLblMetricsDescription.anchor = GridBagConstraints.SOUTH; + gbcLblMetricsDescription.fill = GridBagConstraints.HORIZONTAL; + gbcLblMetricsDescription.gridwidth = 2; + gbcLblMetricsDescription.insets = new Insets( 0, 0, 5, 0 ); + gbcLblMetricsDescription.gridx = 0; + gbcLblMetricsDescription.gridy = 3; + add( lblMetricsDescription, gbcLblMetricsDescription ); + + final JLabel lblUrl = new JLabel( " " ); + lblUrl.setFont( SMALL_FONT ); + lblUrl.setForeground( Color.BLUE ); + final GridBagConstraints gbcLblUrl = new GridBagConstraints(); + gbcLblUrl.anchor = GridBagConstraints.NORTH; + gbcLblUrl.gridwidth = 2; + gbcLblUrl.fill = GridBagConstraints.HORIZONTAL; + gbcLblUrl.insets = new Insets( 0, 0, 5, 0 ); + gbcLblUrl.gridx = 0; + gbcLblUrl.gridy = 4; + add( lblUrl, gbcLblUrl ); + + final JPanel panelConfigParams = new JPanel( new BorderLayout() ); + final GridBagConstraints gbcPanelConfigParams = new GridBagConstraints(); + gbcPanelConfigParams.anchor = GridBagConstraints.NORTH; + gbcPanelConfigParams.gridwidth = 2; + gbcPanelConfigParams.insets = new Insets( 0, 0, 5, 0 ); + gbcPanelConfigParams.fill = GridBagConstraints.BOTH; + gbcPanelConfigParams.gridx = 0; + gbcPanelConfigParams.gridy = 5; + add( panelConfigParams, gbcPanelConfigParams ); + + /* + * Config panel. + */ + + final JPanel ctcParamPanel = new JPanel(); // Empty + final JPanel sptParamPanel = new JPanel(); + sptParamPanel.setLayout( new BoxLayout( sptParamPanel, BoxLayout.LINE_AXIS ) ); + final JLabel lblMaxDist = new JLabel( "Max distance for pairing:" ); + lblMaxDist.setFont( SMALL_FONT ); + sptParamPanel.add( lblMaxDist ); + sptParamPanel.add( Box.createHorizontalStrut( 5 ) ); + ftfMaxDist = new JFormattedTextField( new DecimalFormat( "0.00" ) ); + ftfMaxDist.setColumns( 9 ); + ftfMaxDist.setFont( SMALL_FONT ); + ftfMaxDist.setHorizontalAlignment( JTextField.RIGHT ); + ftfMaxDist.setMaximumSize( new Dimension( 100, 40 ) ); + sptParamPanel.add( ftfMaxDist ); + sptParamPanel.add( Box.createHorizontalStrut( 5 ) ); + lblUnits = new JLabel( "image units" ); + lblUnits.setFont( SMALL_FONT ); + sptParamPanel.add( lblUnits ); + sptParamPanel.add( Box.createHorizontalGlue() ); + + /* + * Listeners & co. + */ + + final PrefService prefService = TMUtils.getContext().getService( PrefService.class ); + + final ButtonGroup buttonGroup = new ButtonGroup(); + buttonGroup.add( rdbtnSPT ); + buttonGroup.add( rdbtnCTC ); + + final ItemListener changeMetrics = e -> { + // Help and selection. + final String doc = rdbtnCTC.isSelected() ? DOC_CTC : DOC_SPT; + lblMetricsDescription.setText( doc ); + final String url = rdbtnCTC.isSelected() ? URL_CTC : URL_SPT; + lblUrl.setText( url ); + + // Config panel. + final JPanel paramPanel = rdbtnCTC.isSelected() ? ctcParamPanel : sptParamPanel; + panelConfigParams.removeAll(); + panelConfigParams.add( paramPanel, BorderLayout.CENTER ); + + prefService.put( getClass(), METRICS_TYPE_KEY, rdbtnCTC.isSelected() ); + }; + + rdbtnCTC.addItemListener( changeMetrics ); + lblUrl.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( final java.awt.event.MouseEvent e ) + { + try + { + final String link = rdbtnCTC.isSelected() ? LINK_CTC : LINK_SPT; + Desktop.getDesktop().browse( new URI( link ) ); + } + catch ( URISyntaxException | IOException ex ) + { + ex.printStackTrace(); + } + } + } ); + fiji.plugin.trackmate.gui.GuiUtils.selectAllOnFocus( ftfMaxDist ); + + final boolean ctcSelected = prefService.getBoolean( getClass(), METRICS_TYPE_KEY, true ); + rdbtnCTC.setSelected( ctcSelected ); + rdbtnSPT.setSelected( !ctcSelected ); + changeMetrics.itemStateChanged( null ); + + ftfMaxDist.setValue( Double.valueOf( 1.0 ) ); + } + + public void setUnits( final String units ) + { + lblUnits.setText( units ); + } + + /** + * Returns the value of the max pairing distance specified in this panel, + * used for the SPT metrics. + * + * @return the value of the max pairing distance + */ + public double getSPTMaxPairingDistance() + { + return ( ( Number ) ftfMaxDist.getValue() ).doubleValue(); + } + + public String getUnits() + { + return lblUnits.getText(); + } + + /** + * Returns true if the CTC metrics are selected. If + * false, the SPT metrics are selected. + * + * @return true if the CTC metrics are selected + */ + public boolean isCTCSelected() + { + return rdbtnCTC.isSelected(); + } + + public static final void main( final String... args ) + { + final ImageJ ij = new ImageJ(); + ij.launch( args ); + final JFrame frame = new JFrame(); + frame.add( new MetricsChooserPanel() ); + frame.pack(); + frame.setLocationRelativeTo( null ); + frame.setVisible( true ); + } + + private static final String METRICS_TYPE_KEY = "METRICS_TYPE"; + + private static final String DOC_CTC = CTCTrackingMetricsType.INFO; + + private static final String DOC_SPT = SPTTrackingMetricsType.INFO; + + protected static final String LINK_CTC = CTCTrackingMetricsType.URL; + + protected static final String LINK_SPT = SPTTrackingMetricsType.URL; + + private static final String URL_CTC = "Ulman, Maška, et al. 2017"; + + private static final String URL_SPT = "Chenouard, Smal, de Chaumont, Maška, et al. 2014"; +} diff --git a/src/main/resources/plugins.config b/src/main/resources/plugins.config index eff4e03..2dfe0bd 100644 --- a/src/main/resources/plugins.config +++ b/src/main/resources/plugins.config @@ -28,3 +28,4 @@ Plugins>Tracking, "TrackMate Batcher", fiji.plugin.trackmate.batcher.TrackMateBatcherPlugin Plugins>Tracking, "TrackMate Helper", fiji.plugin.trackmate.helper.TrackMateParameterSweepPlugin Plugins>Tracking, "TrackMate Helper results inspector", fiji.plugin.trackmate.helper.TrackMateParameterSweepResultsPlugin +Plugins>Tracking, "TrackMate Metrics computation", fiji.plugin.trackmate.helper.TrackMateComputeMetricsPlugin diff --git a/src/test/java/fiji/plugin/trackmate/ctc/CLITestDrive.java b/src/test/java/fiji/plugin/trackmate/ctc/CLITestDrive.java index 1ad0898..d3b22d1 100644 --- a/src/test/java/fiji/plugin/trackmate/ctc/CLITestDrive.java +++ b/src/test/java/fiji/plugin/trackmate/ctc/CLITestDrive.java @@ -57,7 +57,8 @@ public static void main( final String[] args ) throws ClassNotFoundException, In final ImagePlus imp = IJ.openImage( sourceImagePath ); final double maxDist = 1.; - final TrackingMetricsType type = new SPTTrackingMetricsType( maxDist ); + final String units = "image units"; + final TrackingMetricsType type = new SPTTrackingMetricsType( maxDist, units ); final Builder builder = HelperRunner.create(); final HelperRunner runner = builder