TrackMate scripting principle
TrackMate can also be used without the GUI, using a scripting language that allows making calls to Java. The most simple way to get started is to use the Script Editor, which takes care of the difficult & boring part for you (such as path). The examples proposed on this page all use Jython, but can be adapted to anything.
Since we are calling the internals of TrackMate, we must get to know a bit of its guts. I have tried to come up with a rational design; though not always successfully. There is 3 main classes to interact with in a script:
- Model (
fiji.plugin.trackmate.Model) is the class in charge of storing the data. It cannot do anything to create it. It can help you follow manual modifications you would made in the manual editing mode, interrogate it, … but it is conceptually just a data recipient.
- Settings (
fiji.plugin.trackmate.Settings) is the class storing the fields that will configure TrackMate and pilot how the data is created. This is where you specify what is the source image, what are the detector and tracking algorithms to use, what are the filters to use, etc…
IMPORTANT! You will see in the example below that all detectors and trackers are configured in the
Settings object using a
Map (e.g. in Java) or a
dict (e.g. in Python). You need to know the keys of the dictionnary for each detector and tracker, and the type of values they accept.
All the keys of the known detectors and trackers are documented in this page.
- TrackMate ([https://fiji.sc/javadoc/fiji/plugin/trackmate/plugins/trackmate.html>
fiji.plugin.trackmate.TrackMate]) is the guy that does the actual work. In scripts, we use it to actually perform the analysis tasks</u, such as generating spots from images, linking them into track, etc... It reads configuration information in the Settings object mentioned above and put the resulting data in the model.
So getting a working script is all about configuring a proper
Settings object and calling
exec* methods on a
TrackMate object. Then we read the results in the
A full example
Here is an example of full tracking process, using the easy image found in the first tutorial. The following (Jython) script works as following:
- It fetches the image from the web
- It configures settings for segmentation and tracking
- The model is instantiated, with the settings and imp objects
- The TrackMate class is instantiated with the model object
- Then the TrackMate object performs all the steps needed.
- The final results is displayed as an overlay.
Loading and reading from a saved TrackMate XML file
Scripting is a good way to interrogate and play non-interactively with tracking results. The example below shows how to load a XML TrackMate file and rebuild a full working model from it.
That way you could for instance redo a full tracking process by only changing one parameter with respect to the saved one. You might also want to check results without relying on the GUI. Etc…
For the example below to work for you, you will have to edit line 25 and put the actual path to your TrackMate file.
Export spot, edge and track numerical features after tracking
This example shows how to extract numerical features from tracking results.
TrackMate computes and stores three kind of numerical features:
- Spot features, such as a spot location (X, Y, Z), its mean intensity, radius etc…
- Edge or link features: An edge is a link between two spots. Its feature typically stores the velocity and displacement, which are defined only for two consecutive spots in the same track.
- Track features: numerical features that apply to a whole track, such as the number of spots it contains.
By default, TrackMate only computes a very limited number of features. The GUI forces TrackMate to compute them all, but if you do scripting, you will have to explicitly configures TrackMate to compute the features you desire. This is done by adding feature analyzers to the settings object.
There are some gotchas: some feature analyzers require other numerical features to be already calculated. If something does not work, it is a good idea to directly check the preamble in the source code of the analyzers (TrackMate feature logic).
Finally, depending on their type, numerical features are not stored at the same place:
- Spot features are simply conveyed by the spot object, and you can access them through
- Edge and track features are stored in a sub-component of the model object called the FeatureModel (FeatureModel.java).
Check the script below to see a working example.
Manually creating a model
TrackMate aims at combining automatic and manual tracking facilities. This is also the case when scripting: a part of the API offers to a edit a model extensively. A few code patterns must be followed.
First, every edit must happen between a call to
model.beginUpdate() # ... do whatever you want to the model here. model.endUpdate()
The reason for this is that TrackMate caches each modification made to its model. This is required because we can deal with a rather complex content. For instance: imagine you have a single track that splits in two branches at some point. If you decide to remove the spot at the fork, a complex series of events will happen:
- First, three edges will be removed: the ones that were connected to the spot you just removed.
- Then the spot will actually be removed from the model.
- But then you need to recompute the tracks, because now, you have 3 tracks instead of 1.
- But also: all the numerical features of the tracks are now invalid, and you need to recompute them.
- And what happens to the track name? What track, amongst the 3 new ones, will receive the old name?
Well, TrackMate does that for you automatically, but for the chain of events to happen timely, you must make your edits within this
model.beginUpdate() / model.endUpdate() code block.
This script just shows you how to use this construct to build and populate a model from scratch. Appending content to a model is done by, sequentially:
- Creating spot objects. You have to provide their x, y, z location, as well as a radius and a quality value for each. At this stage, you don’t provide at what frame (or time) they belong.
- This is done by adding the spot to the model, using
model.addSpotTo(Spot, frame), frame being a positive integer number.
- Then you create a link, or an edge as it is called in TrackMate, between two spots. You have to provide the link cost:
model.addEdge(Spot1, Spot2, cost).
Spot quality and link cost are typically useful to quantify automatic spot detection and linking. We typically use negative values for these two numbers when doing manual edits.
The script below does this:
Making TrackMate macro recordable with a 64-line script
Contributed by Jan Eglinger during a NEUBIAS course. Quoting from Jan:
“The macro language is too limited to work with such awesome things as TrackMate, but that you can do everything with a more powerful scripting language. So when using a 64-line script to call it, it actually is macro recordable.”
Add 3D maximas in the ROI Manager using TrackMate
Using the 3D spots finder of TrackMate, it is possible to add the maximas to the ROI Manager with a simple Jython code:
# @ImagePlus imp # Imports from fiji.plugin.trackmate.detection import LogDetector from net.imglib2.img.display.imagej import ImageJFunctions from ij.plugin.frame import RoiManager from ij.gui import PointRoi # Set the parameters for LogDetector img = ImageJFunctions.wrap(imp) interval = img cal = imp.getCalibration() # Get the calibration from the metadata if exists calibration = [cal.pixelWidth, cal.pixelHeight, cal.pixelDepth] # Values to enter based on the TrackMate GUI radius = 5 # the radius is half the diameter threshold = 1050 doSubpixel = True doMedian = True # Setup spot detector (see http://javadoc.imagej.net/Fiji/fiji/plugin/trackmate/detection/LogDetector.html) # # public LogDetector(RandomAccessible<T> img, # Interval interval, # double calibration, # double radius, # double threshold, # boolean doSubPixelLocalization, # boolean doMedianFilter) detector = LogDetector(img, interval, calibration, radius, threshold, doSubpixel, doMedian) # Start processing and display the results if detector.process(): # Get the list of peaks found peaks = detector.getResult() print str(len(peaks)), "peaks were found." # Add points to ROI manager rm = RoiManager.getInstance() if not rm: rm = RoiManager() # Loop through all the peak that were found for peak in peaks: # Print the current coordinates print peak.getDoublePosition(0), peak.getDoublePosition(1), peak.getDoublePosition(2) # Add the current peak to the Roi manager roi = PointRoi(peak.getDoublePosition(0) / cal.pixelWidth, peak.getDoublePosition(1) / cal.pixelHeight) # Set the Z position of the peak otherwise the peaks are all set on the same slice roi.setPosition(int(round(peak.getDoublePosition(2) / cal.pixelDepth))+1) rm.addRoi(roi) # Show all ROIs on the image rm.runCommand(imp, "Show All") else: print "The detector could not process the data."