Help with Groovy script for Trackmate using track filters

I am writing a groovy script to batch process the detection of particle trajectories in multiple avi videos using TrackMate.

The script and analysis works well except when I want to add filters on the tracks. I am new to groovy and somehow not getting the syntax right, which is based on a Jython script (I could not find examples for groovy online).

Specifically, my code up to adding the filters is:

//Input variables
#@ File    (label = "Input directory", style = "directory") srcFile
#@ File    (label = "Output directory", style = "directory") dstFile
#@ Boolean (label = "Keep directory structure when saving", value = true) keepDirectories
#@ String  (label = "File extension", value=".avi") ext
#@ double (label = "Spot radius", stepSize=0.1) radius
#@ double (label = "Quality threshold") threshold
#@ int (label = "Max frame gap") frameGap
#@ double (label = "Linking max distance") linkingMax
#@ double (label = "Gap-closing max distance") closingMax
#@ double (label = "min N spots") nSpotMin

import ij.IJ
import ij.ImagePlus
import ij.process.ImageProcessor
import ij.process.ShortProcessor
import ij.ImageStack;
import ij.plugin.AVI_Reader;
import fiji.plugin.trackmate.providers.SpotAnalyzerProvider
import fiji.plugin.trackmate.providers.EdgeAnalyzerProvider
import fiji.plugin.trackmate.providers.TrackAnalyzerProvider
import fiji.plugin.trackmate.features.FeatureFilter
import fiji.plugin.trackmate.Model
import fiji.plugin.trackmate.Settings
import fiji.plugin.trackmate.TrackMate
import fiji.plugin.trackmate.detection.LogDetectorFactory
import fiji.plugin.trackmate.tracking.LAPUtils
import fiji.plugin.trackmate.tracking.sparselap.SparseLAPTrackerFactory
import fiji.plugin.trackmate.action.ExportTracksToXML

def main() {
	srcFile.eachFileRecurse {
		name = it.getName()
		if (name.endsWith(ext))  {
			process(it, srcFile, dstFile, keepDirectories)
		}
	}
}

def process(file, src, dst, keep) {
	println "Processing $file"

	// Opening the video

read = new AVI_Reader();
stack = read.makeStack(file.getAbsolutePath(), 1, 0, false, true, true)
	imp = new ImagePlus("avi", stack)
	imp.getStack()
	imp.show()

// Swap Z and T dimensions if T=1
dims = imp.getDimensions() // default order: XYCZT
if (dims[4] == 1) {
	imp.setDimensions( dims[2,4,3] )
}

	// Setup settings for TrackMate
settings = new Settings()
settings.setFrom(imp)
settings.dt = 1.0

settings.detectorFactory = new LogDetectorFactory()
settings.detectorSettings = settings.detectorFactory.getDefaultSettings()

settings.detectorSettings['RADIUS'] = radius
settings.detectorSettings['THRESHOLD'] = threshold
settings.detectorSettings['DO_SUBPIXEL_LOCALIZATION']= true
println settings.detectorSettings

filter1= FeatureFilter('NUMBER_SPOTS', nSpotMin, true)
settings.addTrackFilter(filter1)

filter2 = FeatureFilter('TRACK_DISPLACEMENT', 15, True)
settings.addTrackFilter(filter2)


As soon as I add these filters, I get the error “groovy.lang.MissingMethodException: No signature of method: org.scijava.plugins.scripting.groovy.GroovyScriptEngine.TrackBranchingAnalyzer() is applicable for argument types: (String, Double, Boolean) values: [NUMBER_SPOTS, 10.0, true]”

If I remove the filters, everything goes smoothly, so I cut the code off at this point in this example.

I tried various ways to formulate the line to add filters differently but have not had any luck.
Does anyone know the correct syntax?

Hi @Janna_Nawroth and welcome to the forum!

You probably copied/adapted these lines from some Python examples?

Two differences between Python/Jython and other Java-based scripting languages are of importance here:

  • constructing new instances of classes (like FeatureFilter) requires the keyword new in Groovy:
filter1 = new FeatureFilter('NUMBER_SPOTS', nSpotMin, true)
  • boolean values are all lower-case in Groovy and all other Java-based languages (so True won’t work in your second filter constructor):
filter2 = new FeatureFilter('TRACK_DISPLACEMENT', 15, true)

Hope that helps.

Thank you very much, that’s super helpful. Indeed, I was able to create a working spot filter using the syntax you mentioned:

//Configure spot filters
def filter3 = new FeatureFilter( 'QUALITY', spotQual, true )
settings.addSpotFilter( filter3 )

where spotQual is a double I set during initiation.

However, when I use the same approach for a track filter, I get java.lang.NullPointerException, no matter what type of filter I try:

// configure track filters
def filter1 = new FeatureFilter('TRACK_DISPLACEMENT', mindisplacement, true)
settings.addTrackFilter( filter1 )

where mindisplacement is a double during initiation (I tried integer as well, no difference).

Any idea what I am still doing wrong?

Can you post the full stack trace? It should tell us where the NullPointerException occurred.

Hi Jan, thank you for your help! Here is the full error output (I assume that’s what you meant):

java.lang.NullPointerException
	at fiji.plugin.trackmate.FeatureModel.getTrackFeature(FeatureModel.java:429)
	at fiji.plugin.trackmate.TrackMate.execTrackFiltering(TrackMate.java:539)
	at fiji.plugin.trackmate.TrackMate.process(TrackMate.java:622)
	at net.imglib2.algorithm.Algorithm$process$0.call(Unknown Source)
	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:130)
	at Script4.process(Script4.groovy:133)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:107)
	at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:323)
	at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:351)
	at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:61)
	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:51)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:171)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:212)
	at Script4$_main_closure1.doCall(Script4.groovy:70)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:107)
	at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:323)
	at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:263)
	at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1029)
	at groovy.lang.Closure.call(Closure.java:412)
	at groovy.lang.Closure.call(Closure.java:428)
	at org.codehaus.groovy.runtime.ResourceGroovyMethods.eachFileRecurse(ResourceGroovyMethods.java:1248)
	at org.codehaus.groovy.runtime.ResourceGroovyMethods.eachFileRecurse(ResourceGroovyMethods.java:1523)
	at org.codehaus.groovy.runtime.dgm$1018.invoke(Unknown Source)
	at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:247)
	at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:56)
	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:139)
	at Script4.main(Script4.groovy:67)
	at Script4.run(Script4.groovy:167)
	at org.scijava.plugins.scripting.groovy.GroovyScriptEngine.eval(GroovyScriptEngine.java:303)
	at org.scijava.plugins.scripting.groovy.GroovyScriptEngine.eval(GroovyScriptEngine.java:122)
	at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264)
	at org.scijava.script.ScriptModule.run(ScriptModule.java:157)
	at org.scijava.module.ModuleRunner.run(ModuleRunner.java:165)
	at org.scijava.module.ModuleRunner.call(ModuleRunner.java:124)
	at org.scijava.module.ModuleRunner.call(ModuleRunner.java:63)
	at org.scijava.thread.DefaultThreadService.lambda$wrap$2(DefaultThreadService.java:225)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)