morphonets / SNT

The ImageJ framework for quantification of neuronal anatomy
https://imagej.net/plugins/snt
GNU General Public License v3.0
42 stars 18 forks source link

Cable Length Mismatch Between GUI and SNT Scripting #148

Closed tferr closed 1 year ago

tferr commented 1 year ago

Submitted by Jeffrey Kopsick:

We noticed a mismatch between the SNT GUI and two SNT scripting interfaces (both Python based: pyimagej and SNT’s Python scripting interface used in the SNT GUI) for computed cable lengths of MouseLight projection neurons using SNT. For example, we noticed the following using the AA0091 reconstruction (a Dentate Gyrus Granule Cell):

Scripting interfaces (using getCableLength via TreeStatistics class) compute the axonal length as ~33.34 microns within the dentate gyrus granule layer (CCF Compartment DG-sg; computed using AllenUtils.getCompartment), whereas the GUI reports the axonal length within DG-sg as ~124.89 microns. The GUI does not properly load the breakdown by CCF compartment when called for the dendritic length, so we could not compare between scripting interfaces and GUI. The total axonal cable length irrespective of CCF Compartment in the scripting interfaces were ~4849.19 microns, whereas the GUI reported ~4835.57 microns. The total axonal cable length irrespective of CCF Compartment in the scripting interfaces were ~4849.19 microns, whereas the total axonal cable length using the CCF Compartment “Whole Brain” (via AllenUtils.getCompartment) was ~4682.27 microns. However, total cable length (axonal + dendritic) irrespective of CCF compartment is the same when computed in either the GUI or the scripting interfaces, with a total cable length of ~4849.19 (axonal) + ~3091.55 (dendritic) = ~7940.74 microns; this computation for the total cable length is ~2.27 microns off the L-measure computation for this same reconstruction in NeuroMorpho (~7938.47 microns).

Digging through the SNT source code on GitHub, we believe that the GUI and scripting interfaces should be using the same compartment meshes to make the cable length computation, so in theory these should be the same. So we are uncertain as to what is leading to this discrepancy and wanted to bring it to both of your attention. There could be a difference in the cable length computation that is different between scripting interface and GUI, or the use of different meshes, yet based on the above we would assume it is not the cable length computation given that the total length irrespective of mesh matches in scripting interfaces and GUI, and the estimate is very similar to what is reported in L-measure. Any help in resolving this matter would be greatly appreciated!

Thanks and best,

tferr commented 1 year ago

I did not have the time to look at this carefully, but just wanted to lay down the 4 (sub) issues (likely related) in this report by @jkopsick:

  1. Mismatch between TreeStatistics#getCableLength() in GUI and API calls
  2. Mistmatch between TreeStatistics#getCableLength(AllenCompartment) in GUI and API calls
  3. Mismatch between TreeStatistics#getCableLength() and TreeStatistics#getCableLength(AllenCompartment#root)
    • Isn't this expected if there are non-annotated nodes? Either way, we should investigate
  4. Mistmatch between TreeStatistics#getCableLength() and L-measure
    • Are we sure the mistmatch is above floating point differences!?

Useful jython snippet for initial troubleshooting:

from sc.fiji.snt.io import MouseLightLoader
from sc.fiji.snt.analysis import TreeStatistics
from sc.fiji.snt.gui import MeasureUI

# Non-GUI
loader = MouseLightLoader("AA0091")
axn = loader.getTree("axon")
stats = TreeStatistics(axn)
print(stats.getCableLength()) 

# GUI
mui = MeasureUI([axn])
mui.setVisible(True)
tferr commented 1 year ago

@jkopsick, an update/explanation on this:

On the getCableLength() mistmatch: In the early days, we were computing lengths in compartments by manual iteration. When @carshadi implemented graph support we refactored things to adopt the more efficient graph parsing. But - it seems- the refactoring was defective and there was still a method in TreeAnalyzer - that the GUI uses because that option already existed in the GUI early on- that was mixing things up. I think we never noticed it before because that method was excluded from the Junit tests. This should now be fixed and v4.1.15 released yesterday has this fix. It Would be great if you could confirm on your end.

Unfortunately, I did not have yet time to look at the other issues...

tferr commented 1 year ago

Apologies for the long delay on this. the differences in cable length are caused by modifications in the CNG version of the SWC file. The following groovy script demonstrates this:

import sc.fiji.snt.*
import sc.fiji.snt.analysis.*
import sc.fiji.snt.io.*

cellId = "AA0091"
loader = new NeuroMorphoLoader()
println("# Comparing Source/CNG versions for neuron " + cellId)
loader.enableSourceVersion(true)
tree1 = loader.getTree(cellId)
tree1.setLabel("Source version")
loader.enableSourceVersion(false)
tree2 = loader.getTree(cellId)
tree2.setLabel("CNG version")

for (tree in [tree1, tree2]) {
    stats = new TreeStatistics(tree)
    println(tree.getLabel())
    println("  Total cable length: " + stats.getCableLength())
    for (type in tree.getSWCTypes()) {
        stats.restrictToSWCType(type)
        println("    sub-compartment " + Path.getSWCtypeName(type, false))
        println("      cable length: " + stats.getCableLength())
        println("      no. nodes: " + stats.getNNodes())
        stats.resetRestrictions()
    }
}

result:

# Comparing Source/CNG versions for neuron AA0091
Source version
  Total cable length: 7940.740260123613
    sub-compartment soma
      cable length: 0.0
      no. nodes: 1
    sub-compartment axon
      cable length: 4835.573615659712
      no. nodes: 156
    sub-compartment (basal) dendrite
      cable length: 3004.0677608769874
      no. nodes: 93
CNG version
  Total cable length: 7940.417323688646
    sub-compartment soma
      cable length: 1.9426785632214094
      no. nodes: 3
    sub-compartment axon
      cable length: 4835.355050388459
      no. nodes: 156
    sub-compartment (basal) dendrite
      cable length: 3002.13776844564
      no. nodes: 94

In the CNG version soma adopts 3 nodes, as opposed to the single node of the original file. @jkopsick, it would be useful to know why NeuroMorpho performs such modifications. Because the issue does not seem to pertain to SNT, I'm closing this for now.