Trackmate- Help with extending TrackMate with feature analyzers

Hello everyone,
Recently I’ve been trying to extend TrackMate so that I can pull start and stop X and Y positions of the tracks in the track analysis. I have been following the tutorials on the ImageJ website: http://imagej.net/How_to_write_your_own_track_feature_analyzer_algorithm_for_TrackMate#Declaring_features
But I cannot seem to get the features I want to show up in the table that TrackMate produces. I’m a Java neophyte so I just copied and pasted the feature analyzer example code (https://github.com/fiji/TrackMate-examples/blob/master/src/main/java/plugin/trackmate/examples/trackanalyzer/TrackStartSpotAnalyzer.java) into Eclipse and compiled it. Then dragged and dropped the jar file into the plugin folder of Fiji. I used the install command in Fiji and then ran TrackMate on the Tracks for Trackmate sample. But when the data tables come up for the tracks I do not see the features that I want. Has anyone had any success in extending TrackMate that can help me figure out what I am doing wrong? Or alternatively, direct me to a different tracking program that will be able to give me start and stop X and Y positions innately. Thanks!

1 Like

Can you somehow show us the code?
I will be able to look into it in details next week.

Certainly:

package plugin.trackmate.examples.trackanalyzer;

import ij.ImageJ;
import ij.ImagePlus;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.swing.ImageIcon;

import org.scijava.plugin.Plugin;

import fiji.plugin.trackmate.Dimension;
import fiji.plugin.trackmate.FeatureModel;
import fiji.plugin.trackmate.Model;
import fiji.plugin.trackmate.Spot;
import fiji.plugin.trackmate.TrackMatePlugIn_;
import fiji.plugin.trackmate.features.track.TrackAnalyzer;

@Plugin( type = TrackAnalyzer.class )
public class TrackStartSpotAnalyzer implements TrackAnalyzer
{

	private static final String KEY = "TRACK_START_SPOT_ANALYZER";

	public static final String TRACK_START_X = "TRACK_START_X";

	public static final String TRACK_START_Y = "TRACK_START_Y";

	public static final String TRACK_START_Z = "TRACK_START_Z";

	public static final String TRACK_STOP_X = "TRACK_STOP_X";

	public static final String TRACK_STOP_Y = "TRACK_STOP_Y";

	public static final String TRACK_STOP_Z = "TRACK_STOP_Z";

	private static final List< String > FEATURES = new ArrayList< String >( 6 );

	private static final Map< String, Boolean > IS_INT = new HashMap< String, Boolean >( 6 );

	private static final Map< String, String > FEATURE_SHORT_NAMES = new HashMap< String, String >( 6 );

	private static final Map< String, String > FEATURE_NAMES = new HashMap< String, String >( 6 );

	private static final Map< String, Dimension > FEATURE_DIMENSIONS = new HashMap< String, Dimension >( 6 );

	static
	{
		FEATURES.add( TRACK_START_X );
		FEATURES.add( TRACK_START_Y );
		FEATURES.add( TRACK_START_Z );
		FEATURES.add( TRACK_STOP_X );
		FEATURES.add( TRACK_STOP_Y );
		FEATURES.add( TRACK_STOP_Z );

		IS_INT.put( TRACK_START_X, false );
		IS_INT.put( TRACK_START_Y, false );
		IS_INT.put( TRACK_START_Z, false );
		IS_INT.put( TRACK_STOP_X, false );
		IS_INT.put( TRACK_STOP_Y, false );
		IS_INT.put( TRACK_STOP_Z, false );

		FEATURE_NAMES.put( TRACK_START_X, "Track start X" );
		FEATURE_NAMES.put( TRACK_START_Y, "Track start Y" );
		FEATURE_NAMES.put( TRACK_START_Z, "Track start Z" );
		FEATURE_NAMES.put( TRACK_STOP_X, "Track stop X" );
		FEATURE_NAMES.put( TRACK_STOP_Y, "Track stop Y" );
		FEATURE_NAMES.put( TRACK_STOP_Z, "Track stop Z" );

		FEATURE_SHORT_NAMES.put( TRACK_START_X, "X start" );
		FEATURE_SHORT_NAMES.put( TRACK_START_Y, "Y start" );
		FEATURE_SHORT_NAMES.put( TRACK_START_Z, "Z start" );
		FEATURE_SHORT_NAMES.put( TRACK_STOP_X, "X stop" );
		FEATURE_SHORT_NAMES.put( TRACK_STOP_Y, "Y stop" );
		FEATURE_SHORT_NAMES.put( TRACK_STOP_Z, "Z stop" );

		FEATURE_DIMENSIONS.put( TRACK_START_X, Dimension.POSITION );
		FEATURE_DIMENSIONS.put( TRACK_START_Y, Dimension.POSITION );
		FEATURE_DIMENSIONS.put( TRACK_START_Z, Dimension.POSITION );
		FEATURE_DIMENSIONS.put( TRACK_STOP_X, Dimension.POSITION );
		FEATURE_DIMENSIONS.put( TRACK_STOP_Y, Dimension.POSITION );
		FEATURE_DIMENSIONS.put( TRACK_STOP_Z, Dimension.POSITION );
	}

	private long processingTime;

	/*
	 * TRACKMODULE METHODS
	 */

	@Override
	public String getKey()
	{
		return KEY;
	}

	@Override
	public String getName()
	{
		return "Track starting and end points position";
	}

	// We do not use info texts for any feature actually.
	@Override
	public String getInfoText()
	{
		return "";
	}

	// The same: we don't use icons for features.
	@Override
	public ImageIcon getIcon()
	{
		return null;
	}

	/*
	 * FEATUREANALYZER METHODS
	 */

	@Override
	public List< String > getFeatures()
	{
		return FEATURES;
	}

	@Override
	public Map< String, String > getFeatureShortNames()
	{
		return FEATURE_SHORT_NAMES;
	}

	@Override
	public Map< String, String > getFeatureNames()
	{
		return FEATURE_NAMES;
	}

	@Override
	public Map< String, Dimension > getFeatureDimensions()
	{
		return FEATURE_DIMENSIONS;
	}

	/*
	 * MULTITHREADED METHODS
	 */

	@Override
	public void setNumThreads()
	{
		// We ignore multithreading for this tutorial.
	}

	@Override
	public void setNumThreads( final int numThreads )
	{
		// We ignore multithreading for this tutorial.
	}

	@Override
	public int getNumThreads()
	{
		// We ignore multithreading for this tutorial.
		return 1;
	}

	/*
	 * BENCHMARK METHOD
	 */

	@Override
	public long getProcessingTime()
	{
		return processingTime;
	}

	/*
	 * TRACKANALYZER METHODS
	 */

	@Override
	public void process( final Collection< Integer > trackIDs, final Model model )
	{
		// The feature model where we store the feature values:
		final FeatureModel fm = model.getFeatureModel();

		// Loop over all the tracks we have to process.
		for ( final Integer trackID : trackIDs )
		{
			// The tracks are indexed by their ID. Here is how to get their
			// content:
			final Set< Spot > spots = model.getTrackModel().trackSpots( trackID );
			// Or .trackEdges( trackID ) if you want the edges.

			// This set is NOT ordered. If we want the first one and last one we
			// have to sort them:
			final Comparator< Spot > comparator = Spot.frameComparator;
			final List< Spot > sorted = new ArrayList< Spot >( spots );
			Collections.sort( sorted, comparator );

			// Extract and store feature values.
			final Spot first = sorted.get( 0 );
			fm.putTrackFeature( trackID, TRACK_START_X, Double.valueOf( first.getDoublePosition( 0 ) ) );
			fm.putTrackFeature( trackID, TRACK_START_Y, Double.valueOf( first.getDoublePosition( 1 ) ) );
			fm.putTrackFeature( trackID, TRACK_START_Z, Double.valueOf( first.getDoublePosition( 2 ) ) );

			final Spot last = sorted.get( sorted.size() - 1 );
			fm.putTrackFeature( trackID, TRACK_STOP_X, Double.valueOf( last.getDoublePosition( 0 ) ) );
			fm.putTrackFeature( trackID, TRACK_STOP_Y, Double.valueOf( last.getDoublePosition( 1 ) ) );
			fm.putTrackFeature( trackID, TRACK_STOP_Z, Double.valueOf( last.getDoublePosition( 2 ) ) );

			// Et voilà!
		}
	}

	@Override
	public boolean isLocal()
	{
		return true;
	}

	/*
	 * MAIN METHOD
	 */

	public static void main( final String[] args )
	{
		ImageJ.main( args );
		new ImagePlus( "../fiji/samples/FakeTracks.tif" ).show();
		new TrackMatePlugIn_().run( "" );
	}
	

	@Override
	public Map<String, Boolean> getIsIntFeature() {
		return Collections.unmodifiableMap( IS_INT );
	}

	@Override
	public boolean isManualFeature() {
		return false;
	}
}

Thank you so much for any help!

This looks ok.
I will have to look at this next week.

I tried to use Fiji’s compiler instead of Eclipse to see if I get a different result but what I got this error:

/Applications/Fiji.app/plugins/TracksEx2.java:27: class TrackStartSpotAnalyzer is public, should be declared in a file named TrackStartSpotAnalyzer.java
public class TrackStartSpotAnalyzer implements TrackAnalyzer
       ^
/Applications/Fiji.app/plugins/TracksEx2.java:3: package ij does not exist
import ij.ImageJ;
         ^
/Applications/Fiji.app/plugins/TracksEx2.java:4: package ij does not exist
import ij.ImagePlus;
         ^
warning: /Applications/Fiji.app/java/macosx/jdk1.8.0_66/jre/Contents/Home/lib/rt.jar(java/util/ArrayList.class): major version 52 is newer than 51, the highest major version supported by this compiler.
It is recommended that the compiler be upgraded.
warning: /Applications/Fiji.app/java/macosx/jdk1.8.0_66/jre/Contents/Home/lib/rt.jar(java/util/Collection.class): major version 52 is newer than 51, the highest major version supported by this compiler.
It is recommended that the compiler be upgraded.
warning: /Applications/Fiji.app/java/macosx/jdk1.8.0_66/jre/Contents/Home/lib/rt.jar(java/util/Collections.class): major version 52 is newer than 51, the highest major version supported by this compiler.
It is recommended that the compiler be upgraded.
warning: /Applications/Fiji.app/java/macosx/jdk1.8.0_66/jre/Contents/Home/lib/rt.jar(java/util/Comparator.class): major version 52 is newer than 51, the highest major version supported by this compiler.
It is recommended that the compiler be upgraded.
/Applications/Fiji.app/plugins/TracksEx2.java:9: cannot access java.util.Comparator
bad class file: /Applications/Fiji.app/java/macosx/jdk1.8.0_66/jre/Contents/Home/lib/rt.jar(java/util/Comparator.class)
bad constant pool tag: 18 at 20
Please remove or make sure it appears in the correct subdirectory of the classpath.
import java.util.Comparator;
                ^

I renamed the file and tried to install it again and have received this error now:

/Applications/Fiji.app/plugins/TrackStartSpotAnalyzer.java:3: package ij does not exist
import ij.ImageJ;
         ^
/Applications/Fiji.app/plugins/TrackStartSpotAnalyzer.java:4: package ij does not exist
import ij.ImagePlus;
         ^
warning: /Applications/Fiji.app/java/macosx/jdk1.8.0_66/jre/Contents/Home/lib/rt.jar(java/util/ArrayList.class): major version 52 is newer than 51, the highest major version supported by this compiler.
It is recommended that the compiler be upgraded.
warning: /Applications/Fiji.app/java/macosx/jdk1.8.0_66/jre/Contents/Home/lib/rt.jar(java/util/Collection.class): major version 52 is newer than 51, the highest major version supported by this compiler.
It is recommended that the compiler be upgraded.
warning: /Applications/Fiji.app/java/macosx/jdk1.8.0_66/jre/Contents/Home/lib/rt.jar(java/util/Collections.class): major version 52 is newer than 51, the highest major version supported by this compiler.
It is recommended that the compiler be upgraded.
warning: /Applications/Fiji.app/java/macosx/jdk1.8.0_66/jre/Contents/Home/lib/rt.jar(java/util/Comparator.class): major version 52 is newer than 51, the highest major version supported by this compiler.
It is recommended that the compiler be upgraded.
/Applications/Fiji.app/plugins/TrackStartSpotAnalyzer.java:9: cannot access java.util.Comparator
bad class file: /Applications/Fiji.app/java/macosx/jdk1.8.0_66/jre/Contents/Home/lib/rt.jar(java/util/Comparator.class)
bad constant pool tag: 18 at 20
Please remove or make sure it appears in the correct subdirectory of the classpath.
import java.util.Comparator;
                ^

This is because ij.jar cannot be found in the Java classpath.


It is recommended to use a dependency management tool like Maven to build your own plugins.

After creating a pom.xml file like the one found on the minimal-ij1-plugin example project, and putting your TrackStartSpotAnalyzer.java file in a path corresponding to your package hierarchy (./src/main/java/plugin/trackmate/examples/trackanalyzer), building the plugin gets as easy as running:

mvn

from the command line.

In Eclipse, you can use File > Import… > Existing Maven Projects to import the minimal-ij1-plugin project and adjust the pom.xml / paste your Java code as necessary.

1 Like

What do you mean by “Fiji’s compiler”? What command did you run? Why not just use mvn as @imagejan suggests?