Trackmate with manual detection and automatic tracking in jython script

Dear All,

I know that it is possible to use manually Trackmate as it is explained here:https://imagej.net/Scripting_TrackMate#Export_spot.2C_edge_and_track_numerical_features_after_tracking

but in my case I have already detected all the spots in a 2d+T stack and i would like to track them. Do i have to just skip the DetectorFactory in settings by inserting my own detected spot as written in the scripting Trackmate page and configure only the trackerFactory ?
Could you send me an example how to proceed if it is different of what I am suggesting…
Thanks by advance

regards
Philippe

Hi @philgi

I am not sure how you can do that from within the GUI.
Where do the spots come from?
If they come from a CSV file, you can use the TrackMate CSV importer.

Otherwise, I would use a Jython script.
Add the spots to the SpotCollection and track them with TrackMate. Just adapt the Jython script https://imagej.net/Scripting_TrackMate#A_full_example

Hi @tinevez

sorry maybe my explanations were not clear…
I would like to track the mouvement of cells but I cannot use the spot Detector of Trackmate because only the cell membrane is labeled. However I have managed to analyse them and to segment each cell separately.
Now I can measure the center of mass of each segmented region (a segmented region corresponds to one cell) in order to create a SpotCollection of each of these center of mass and to track them with Trackmate.
I am using the Particle Analyzer to get the center of mass (array of Xm and Ym) of the corresponding frame (array of Tm) and I create the SpotCollection:

spots = SpotCollection()
for j in range(len(Tm)):
   spots.add(Spot(Xm[j], Ym[j], 0, 1, -1), Tm[j])

After in the ExampleScript_3.py that you have provided on GitHub, I just have to skip the 2 lines

settings.detectorFactory = ...
settings.detectorSettings = ...

and to replace them by

model.setSpots(spots,False)

That’s right ???

regards
Philippe

This is a great idea!

I have never done it before, and I would be glad to work on a script that does exactly this. Would you mind posting the data somewhere, as so that I can run what you did on my side?

I would create a Jython plugin that does the tracking part and add it to the TrackMate wiki pages, if that is ok.

I received the stack. Thanks! It looks great!

Could you post here the draft of your script? I will start from that,

Hi @tinevez
with the data I sent you, only the tracking part is missing, I have try the following code but it does not work

    def process_im(imp_):
	rt = ResultsTable()
	rt.reset
	p = PA(PA.SHOW_RESULTS, PA.STACK_POSITION+PA.CENTER_OF_MASS, rt, 10, MAXSIZE)
	for i in range(imp_.getStackSize()):
		imp_.setSliceWithoutUpdate(i+1)
		p.analyze(imp_)
	Tm = rt.getColumnAsDoubles(rt.getColumnIndex("Slice"))
	Xm = rt.getColumnAsDoubles(rt.getColumnIndex("XM"))
	Ym = rt.getColumnAsDoubles(rt.getColumnIndex("YM"))
	spots_ = SpotCollection()
	for j in range(len(Tm)):
		spots_.add(Spot(Xm[j], Ym[j], 0, 1, -1), Double.intValue(Tm[j]))
	return spots_
    #------------------------------
    imp = WindowManager.getCurrentImage()
    spots = SpotCollection()
    spots = process_im(imp)
    #  TrackMate
    model = Model()
    model.setLogger(Logger.IJ_LOGGER)
    settings = Settings()
    settings.setFrom(imp)
     
    # Configure SpotCollection
    model.setSpots(spots,False)
    # Configure tracker
    settings.trackerFactory = SparseLAPTrackerFactory()
    settings.trackerSettings = LAPUtils.getDefaultLAPSettingsMap()
    settings.trackerSettings['LINKING_MAX_DISTANCE'] = 10.0
    settings.trackerSettings['GAP_CLOSING_MAX_DISTANCE']=10.0
    settings.trackerSettings['MAX_FRAME_GAP']= 3

    settings.addTrackAnalyzer(TrackSpeedStatisticsAnalyzer())
  
    settings.initialSpotFilterValue = 1
  
    print(str(settings))
    trackmate = TrackMate(model, settings)
    ok = trackmate.checkInput()
    if not ok :
	sys.exit(str(trackmate.getErrorMessage()))
    
    ok = trackmate.process()
    if not ok:
	sys.exit(str(trackmate.getErrorMessage()))
    
    model.getLogger().log('Found ' + str(model.getTrackModel().nTracks(True)) + ' tracks.')
   
    fm = model.getFeatureModel()


    rt_exist = WindowManager.getWindow("TrackMate Results")
    if rt_exist==None or not isinstance(rt_exist, TextWindow):
         table= ResultsTable()
    else:
         table = rt_exist.getTextPanel().getOrCreateResultsTable()


    table.reset

    for id in model.getTrackModel().trackIDs(True):
	v = fm.getTrackFeature(id, 'TRACK_MEAN_SPEED')
	model.getLogger().log('')
	model.getLogger().log('Track ' + str(id) + ': mean velocity = ' + str(v) + ' ' + model.getSpaceUnits() + '/' + model.getTimeUnits())
	track = model.getTrackModel().trackSpots(id)
	
	for spot in track:
		sid = spot.ID()
		# Fetch spot features directly from spot. 
		x=spot.getFeature('POSITION_X')
		y=spot.getFeature('POSITION_Y')
		t=spot.getFeature('FRAME')
		q=spot.getFeature('QUALITY')
		snr=spot.getFeature('SNR')
		mean=spot.getFeature('MEAN_INTENSITY')
		model.getLogger().log('\tspot ID = ' + str(sid) + ': x='+str(x)+', y='+str(y)+', t='+str(t)+', q='+str(q) + ', snr='+str(snr) + ', mean = ' + str(mean))
		table.incrementCounter()
		table.addValue("TRACK ID", id)
		table.addValue("SPOT ID", sid)
		table.addValue("POSITION_X", x)
		table.addValue("POSITION_Y", y)
		table.addValue("FRAME", t)
		table.addValue("QUALITY", q)
		table.addValue("SNR", snr)
		table.addValue("MEAN_INTENSITY", mean)

    table.show("TrackMate Results")     

For the segmented analysis it is a little complicated because the code is not robust and have to manually work on it
thank you for your help

regards
Philippe

Hi @philgi

OK I got it!

I am sorry, I had to start again from scratch. But here is what the script allows to do.

You have to start from a 2D+T image (nothing else) and a results table that contains at least the center of mass XM, YM, the slice and the Area for the cells in the movie. The results table is typically generated from the ROI manager, that would contain the results of the particle analyzer.

So an ideal starting situation would like this:

this script will generate the following tracks:

Cool no? The output can be controlled via a TrackMate GUI that will be shown upon running the script. Showing the GUI might not be desirable in batch mode, but from the GUI you can save your data, export to IJ tables and save to CSV, export a movie etc.

The script also offers to color the ROIs by track ID, if you have the ROI manager that was used to create the results table. It looks like this:


Also cool no?

So, here is the script. It is about 270 lines long.
@philgi please tell me whether it works!

import sys
from math import pi
from math import sqrt
from random import shuffle

from java.awt import Color

from ij import WindowManager
from ij.measure import ResultsTable
from ij.plugin.frame import RoiManager

from fiji.plugin.trackmate import Logger
from fiji.plugin.trackmate import Model
from fiji.plugin.trackmate import SelectionModel
from fiji.plugin.trackmate import Settings
from fiji.plugin.trackmate import Spot
from fiji.plugin.trackmate import SpotCollection
from fiji.plugin.trackmate import TrackMate
from fiji.plugin.trackmate.detection import ManualDetectorFactory
from fiji.plugin.trackmate.tracking import LAPUtils
from fiji.plugin.trackmate.providers import SpotAnalyzerProvider
from fiji.plugin.trackmate.providers import EdgeAnalyzerProvider
from fiji.plugin.trackmate.providers import TrackAnalyzerProvider
from fiji.plugin.trackmate.tracking.sparselap import SparseLAPTrackerFactory
from fiji.plugin.trackmate.visualization.hyperstack import HyperStackDisplayer
from fiji.plugin.trackmate.gui import TrackMateGUIController
from org.jfree.chart.renderer.InterpolatePaintScale import Jet






def spots_from_results_table( results_table, frame_interval ):
	""" 
	Creates a spot collection from a results table in ImageJ.
	Requires the current results table, in which the results from 
	particle analysis should be. We need at least the center
	of mass, the area and the slice to be specified there.
	We also query the frame interval to properly generate the 
	POSITION_T spot feature.
	"""
	
	frames = results_table.getColumnAsDoubles( results_table.getColumnIndex( 'Slice' ) )
	xs = results_table.getColumnAsDoubles( results_table.getColumnIndex( 'XM' ) )
	ys = results_table.getColumnAsDoubles( results_table.getColumnIndex( 'YM' ) )
	z = 0.
	# Get radiuses from area.
	areas = results_table.getColumnAsDoubles( results_table.getColumnIndex( 'Area' ) )
	spots = SpotCollection()

	for i in range( len( xs ) ):
		x = xs[ i ]
		y = ys[ i ]
		frame = frames[ i ]
		area = areas[ i ]
		t = ( frame - 1 ) * frame_interval
		radius = sqrt( area / pi )
		quality = i # Store the line index, to later retrieve the ROI.
		spot = Spot( x, y, z, radius, quality )
		spot.putFeature( 'POSITION_T', t )
		spots.add( spot, int( frame ) )
		
	return spots


def create_trackmate( imp, results_table ):
	"""
	Creates a TrackMate instance configured to operated on the specified
	ImagePlus imp with cell analysis stored in the specified ResultsTable
	results_table.
	"""
	
	cal = imp.getCalibration()
	
	# TrackMate.
	
	# Model.
	model = Model()
	model.setLogger( Logger.IJ_LOGGER )
	model.setPhysicalUnits( cal.getUnit(), cal.getTimeUnit() )
	
	# Settings.
	settings = Settings()
	settings.setFrom( imp )
	
	# Create the TrackMate instance.
	trackmate = TrackMate( model, settings )
	
	# Add ALL the feature analyzers known to TrackMate, via
	# providers. 
	# They offer automatic analyzer detection, so all the 
	# available feature analyzers will be added. 
	# Some won't make sense on the binary image (e.g. contrast)
	# but nevermind.
	
	spotAnalyzerProvider = SpotAnalyzerProvider()
	for key in spotAnalyzerProvider.getKeys():
		print( key )
		settings.addSpotAnalyzerFactory( spotAnalyzerProvider.getFactory( key ) )
	
	edgeAnalyzerProvider = EdgeAnalyzerProvider()
	for  key in edgeAnalyzerProvider.getKeys():
		print( key )
		settings.addEdgeAnalyzer( edgeAnalyzerProvider.getFactory( key ) )
	
	trackAnalyzerProvider = TrackAnalyzerProvider()
	for key in trackAnalyzerProvider.getKeys():
		print( key )
		settings.addTrackAnalyzer( trackAnalyzerProvider.getFactory( key ) )
	
	trackmate.getModel().getLogger().log( settings.toStringFeatureAnalyzersInfo() )
	trackmate.computeSpotFeatures( True )
	trackmate.computeEdgeFeatures( True )
	trackmate.computeTrackFeatures( True )
	
	# Skip detection and get spots from results table.
	spots = spots_from_results_table( results_table, cal.frameInterval )
	model.setSpots( spots, False )
	
	# Configure detector. We put nothing here, since we already have the spots 
	# from previous step.
	settings.detectorFactory = ManualDetectorFactory()
	settings.detectorSettings = {}
	settings.detectorSettings[ 'RADIUS' ] = 1.
	
	# Configure tracker
	settings.trackerFactory = SparseLAPTrackerFactory()
	settings.trackerSettings = LAPUtils.getDefaultLAPSettingsMap()
	settings.trackerSettings[ 'LINKING_MAX_DISTANCE' ] 		= 10.0
	settings.trackerSettings[ 'GAP_CLOSING_MAX_DISTANCE' ]	= 10.0
	settings.trackerSettings[ 'MAX_FRAME_GAP' ]				= 3
	
	settings.initialSpotFilterValue = -1.

	return trackmate



def process( trackmate ):
	"""
	Execute the full process BUT for the detection step.
	"""
	# Check settings.
	ok = trackmate.checkInput()
	# Initial filtering
	print( 'Spot initial filtering' )
	ok = ok and trackmate.execInitialSpotFiltering()
	# Compute spot features.
	print( 'Computing spot features' )
	ok = ok and trackmate.computeSpotFeatures( True ) 
	# Filter spots.
	print( 'Filtering spots' )
	ok = ok and trackmate.execSpotFiltering( True )
	# Track spots.
	print( 'Tracking' )
	ok = ok and trackmate.execTracking()
	# Compute track features.
	print( 'Computing track features' )
	ok = ok and trackmate.computeTrackFeatures( True )
	# Filter tracks.
	print( 'Filtering tracks' )
	ok = ok and trackmate.execTrackFiltering( True )
	# Compute edge features.
	print( 'Computing link features' )
	ok = ok and trackmate.computeEdgeFeatures( True )

	return ok


def display_results_in_GUI( trackmate ):
	"""
	Creates and show a TrackMate GUI to configure the display 
	of the results. 

	This might not always be desriable in e.g. batch mode, but 
	this allows to save the data, export statistics in IJ tables then
	save them to CSV, export results to AVI etc...
	"""
	
	gui = TrackMateGUIController( trackmate )

	# Link displayer and GUI.
	
	model = trackmate.getModel()
	selectionModel = SelectionModel( model)
	displayer = HyperStackDisplayer( model, selectionModel, imp )
	gui.getGuimodel().addView( displayer )
	displaySettings = gui.getGuimodel().getDisplaySettings()
	for key in displaySettings.keySet():
		displayer.setDisplaySettings( key, displaySettings.get( key ) )
	displayer.render()
	displayer.refresh()
	
	gui.setGUIStateString( 'ConfigureViews' )



def color_rois_by_track( trackmate, rm ):
	"""
	Colors the ROIs stored in the specified ROIManager rm using a color
	determined by the track ID they have.
	
	We retrieve the IJ ROI that matches the TrackMate Spot because in the
	latter we stored the index of the spot in the quality feature. This
	is a hack of course. On top of that, it supposes that the index of the 
	ROI in the ROIManager corresponds to the line in the ResultsTable it 
	generated. So any changes to the ROIManager or the ResultsTable is 
	likely to break things.
	"""
	model = trackmate.getModel()
	track_colors = {}
	track_indices = [] 
	for i in model.getTrackModel().trackIDs( True ):
		track_indices.append( i )
	shuffle( track_indices )
	
	index = 0
	for track_id in track_indices:
		color = Jet.getPaint( float(index) / ( len( track_indices) - 1 ) )
		track_colors[ track_id ] = color
		index = index + 1
	
	spots = model.getSpots()
	for spot in spots.iterable( True ):
		q = spot.getFeature( 'QUALITY' ) # Stored the ROI id.
		roi_id = int( q )
		roi = rm.getRoi( roi_id )
	
		# Get track id.
		track_id = model.getTrackModel().trackIDOf( spot )
		if track_id is None:
			color = Color.GRAY
		else:
			color = track_colors[ track_id ] 
			
		roi.setFillColor( color )



#------------------------------
# 			MAIN 
#------------------------------

# Get current image.
imp = WindowManager.getCurrentImage()

# Remove overlay if any.
imp.setOverlay( None )

# Get results table.
results_table = ResultsTable.getResultsTable()

# Create TrackMate instance.
trackmate = create_trackmate( imp, results_table )

#-----------------------
# Process.
#-----------------------

ok = process( trackmate )
if not ok:
	sys.exit(str(trackmate.getErrorMessage()))

#-----------------------
# Display results.
#-----------------------

# Create the GUI and let it control display of results.
display_results_in_GUI( trackmate )

# Color ROI by track ID!
rm = RoiManager.getInstance()
color_rois_by_track( trackmate, rm )


4 Likes

Hi @tinevez
Sorry for the delay,
It is perfect and exactly what I was looking for… I have already tried on my mac and it is working… I am still adjusting some parameter.
For the GUI i have to think about that because the plan is to do the analysis in a batch of data.
For the moment I am playing with your code… When everything will run perfectly I will post my code…
Thank you again for your help.

regards
Philippe

1 Like

This is one of the most useful features of TrackMate that I have seen. Would it be possible to implement it directly as a plug-in or as feature of TrackMate???

Hello everybody,

I’ve modified @tinevez script a bit, to make extraction of tracks and finding track’s ROIs easier:

  • added track ID/# to the Results table after tracking;
  • ROIs in RoiManager are renamed in a following way: “tr3_f_2_0302-3232”, where tr3 = track ID #3
    and f_2 = frame 2.

Here is link to updated script.

Maybe it would be useful to somebody.

P.S. In general, I think it would be maybe nice to ask for linking parameters (LINKING_MAX_DISTANCE, etc) in the beginning of the script, so it is a bit more flexible.

Cheers,
Eugene

2 Likes

Cool!
Can I put it on the wiki instead of my version?

1 Like

Yes, of course!

////

2 Likes