Skip to content

Commit

Permalink
Merge pull request #11 from trackmate-sc/compute-metrics-plugin
Browse files Browse the repository at this point in the history
Tool to compute tracking metrics
  • Loading branch information
tinevez authored Apr 4, 2024
2 parents 6f7c066 + 4c999ab commit 80dbf07
Show file tree
Hide file tree
Showing 16 changed files with 1,052 additions and 244 deletions.
4 changes: 2 additions & 2 deletions src/main/java/fiji/plugin/trackmate/helper/HelperRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 )
Expand Down Expand Up @@ -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
{
Expand Down
68 changes: 59 additions & 9 deletions src/main/java/fiji/plugin/trackmate/helper/MetricsRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 )
{
Expand Down Expand Up @@ -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 );
Expand Down Expand Up @@ -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,
Expand All @@ -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 );
Expand Down Expand Up @@ -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 );
Expand Down Expand Up @@ -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();
Expand All @@ -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();
Expand All @@ -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;

}
}
Original file line number Diff line number Diff line change
@@ -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
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #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 );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
*/
package fiji.plugin.trackmate.helper;

import javax.swing.SwingUtilities;

import fiji.plugin.trackmate.helper.ui.HelperLauncherController;
import ij.plugin.PlugIn;

Expand All @@ -30,6 +32,6 @@ public class TrackMateParameterSweepPlugin implements PlugIn
@Override
public void run( final String arg )
{
new HelperLauncherController();
SwingUtilities.invokeLater( () -> new HelperLauncherController() );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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 );
Expand All @@ -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
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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: <root> 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 <b>given in physical units</b>.
Expand All @@ -72,17 +74,20 @@ public class SPTTrackingMetricsType extends TrackingMetricsType
*
* @param maxDist
* the max pairing distance.
* @param units
* the physical units in which <code>maxDist</code> 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
Expand Down
Loading

0 comments on commit 80dbf07

Please sign in to comment.