Fit paths with simple neurite tracer API

@tferr I am having some trouble figuring out how to use the Fit Paths option in simple neurite tracer from a script.

I have a set of images and associated .traces files (100+) from a while back and I would like to apply the ‘Fit Paths’ option on the already traced paths and save the results.
The script would:

  1. open the image and associated traces file
  2. select all paths
  3. run Fit Paths… with diameter = X, optimization options 1) and 2) and replace nodes option.
  4. save traces

I am looking through the traces javadocs and I assume PathFitter (https://javadoc.scijava.org/Fiji/tracing/PathFitter.html) is the class I am looking for, but I am getting lost when trying to use it from a python script (for example, how to set the diameter and optimization options before running the .call() method).

Any guidance or tips would be much appreciated!

These are great questions! There are two ways of doing so:

Option 1: Call the Path Manager’s command

# @SNTService snt_service

from sc.fiji.snt import PathManagerUI
pm = snt_service.getUI().getPathManager()
pm.runCommand("Fit Paths...")

Option 2: Call PathFitter directly on every path:

# @SNTService snt_service

from sc.fiji.snt import PathFitter
snt = snt_service.getPlugin()
for path in snt_service.getPaths():
    pf = PathFitter(snt, path)
    pf.setScope(PathFitter.RADII_AND_MIDPOINTS)
    pf.setMaxRadius(PathFitter.DEFAULT_MAX_RADIUS)
    #pf.setReplaceNodes(False)
    pf.call()
    print("Fitting ", str(path), ": ", str(pf.getSucceeded()))

if snt_service.getUI():
    snt_service.getUI().getPathManager().update()

Option 1) is effectively the same as running the command manually. Option 2) is more flexible, and suitable for running headless. As is it will be slower because fits are performed in sequence: you would need to split the loop across threads. (NB: the reason you need to instantiate a PathFitter for every Path is parallelization, so that each instance can run on a separated thread).

It is not your fault. the javadocs on the scijava portal are awfully outdated. This is because they are built against the old code of Simple Neurite Tracer, not the new version served by the NeuroAnatomy update site. We’ll solve all this, as soon as we release SNT.

2 Likes

@tferr this is a big help, thank you for getting back to me so quickly. I just had a chance to play with this a bit more over the weekend and I made some progress by looking at your tips and referring directly to the SNT source code (the one on the Fiji Github repo) but I have a few more questions.

I am planning to do this headless-ly without opening the UI and associated dialogues at all. So I used your latter example and have the following to load the traces file and image and fit the paths:

from ij import ImagePlus
from sc.fiji.snt import SNT
from sc.fiji.snt import PathAndFillManager
from sc.fiji.snt import PathFitter

def fit_path(img, path_id):
    print("called with path {}".format(path_id))
    pf = PathFitter(img, path_id)
    pf.setScope(4)
    pf.setMaxRadius(10)
    pf.call()
    print(pf.getSucceeded())
    # return the PathFitter instance in case I need it to update the pfm path passed in.
    return pf 

img = ImagePlus(image_test) # some image I already traced
pfm = PathAndFillManager()
pfm.setHeadless(True)
pfm.load(traces_file_test) # load the associated traces file associated with that image

for path in pfm.getPaths():
    pm_temp = fit_path(img, path)
    #.... do something here to update the path on the main list...
#finally, save the csv properties file for all the traces (pfm.exportToCSV())
# and save the new traces file with the fit paths
  1. This seems to be fitting the paths without errors, but is it updating the paths in the PathAndFillManager() instance in place? In your example you used snt_service.getUI().getPathManager().update() after looping through all the paths, which seems to update everything at the end, is that correct? If so, how do you do it without opening the UI version of the plugin?
  2. Do I need to assign spatial settings headlessly or are they read from the traces file? I tried to pass the ImagePlus to .assignSpatialSettings() but it is protected and I can’t see it being called anywhere in PathAndFillManager.
  3. Once I have updated and processed the paths, how do i use the protected method PathAndFillManager.writeXML() to save a new traces file? I haven’t tried it yet, but it looks like PathAndFillManager.exportToCSV() is public so I shouldn’t have a problem accessing that.

Thanks again for your help, I appreciate it.