Accuracy of subpixel localization in LoG detector

I’m having difficulties getting an accurate z localisation using the LoG detector in TrackMate. We are following spots over time to measure their diffusion, but when you look at the plots of x,y,z vs. time, the spots seem to be jumping a whole pixel/plane distance at times.

Sub pixel localisation seems to be fine in general, I was just wondering if the (seemingly quantized) steps are due to a bug in the localisation or if they can entirely be attributed to noise? I would have expected a more random variation when it’s just noise…

@tinevez Maybe you have any comments? I can upload a sample stack if necessary.

This is an example stack containing two spots:

And here are the plots (x vs t, y vs t, z vs t) for this stack:



1 Like

Hi @imagejan.
It seems like you encountered a same possible bug that Leanna.
Look here:


and here:
http://fiji.sc/bugzilla/show_bug.cgi?id=1165

I still don’t know how to tackle this one. It comes from the SubPixelLocalization scheme I stole from ImgLib2 (yet-to-be-ported) multi-scale stuff. The class involved in the bug is the following:

It seems like one direction is privileged. I still have no idea how to fix this and need some more time on this.

Hey, @imagejan can you help me with anything? Could you check whether the bug also appears for 2D?

1 Like

Yes, it seems to work better in 2D:


2 Likes

Hi all.
Check this:

Hopefully the bug is fixed, but you still need to have a new release of imglib2-algorithm to get the fix.

1 Like

Thanks @tinevez,

apparently there are still some issue with the z localization, here’s the z vs t plot of two spots that show discrete “jumps”:

I’ll investigate more with some synthetic images (and using imglib2-algorithm-0.3.3-SNAPSHOT.jar of course) …

Groumph
Let me send you the code that helped.

Here it is. You can find it in the test package of the TrackMate repo:

Hi @imagejan
I could not reproduce the bug. When I ran the test I could not find a bias in Z.

However, it took me several trials to actually download the RIGHT version of imglib2-algorithm-0.3.3-SNAPSHOT. Because I had my local repo of it, Eclipse was relying on this one, which did not contain the fix. After doing a proper mvn -U, the test passed.

I think I will make a JUnit test for the localization accuracy. A tolerance of even 0.1 would allow us to detect whether this bug appears again.

And done:

Alright, after verifying that the previous bug was fixed indeed using an imglib2-algorithm-0.3.3-SNAPSHOT.jar built locally from the master branch, I investigated a bit more:

Using the Python script below, I created an image containing a single bead moving from [10.01, 10.01, 10.01] to [11.01, 11.01, 11.01] in steps of 0.10 in each dimension.
(If you wonder about the strange decimals: position 10.50 was giving me 8 spots in the LoG detector, because there were 8 pixels of exact same intensity; so I added 0.01 to all positions)

# @DisplayService display
# @OpService ops

from ij import IJ
from ij.plugin import HyperStackConverter

pos = [10.01, 10.11, 10.21, 10.31, 10.41, 10.51, 10.61, 10.71, 10.81, 10.91, 11.01]

sizeX = 20
sizeY = 20
sizeZ = 20

sigmaXY = 2
sigmaZ = 2


for p in pos:
  posX = p
  posY = p
  posZ = p
  outputData = ops.create().img([sizeX, sizeY, sizeZ])
  formula = "( Math.exp( -0.5*((p[0]-" + str(posX) + ")*(p[0]-" + str(posX) + ")/" + str(sigmaXY) + "+(p[1]-" + str(posY) + ")*(p[1]-" + str(posY) + ")/" + str(sigmaXY) + "+(p[2]-" + str(posZ) + ")*(p[2]-" + str(posZ) + ")/" + str(sigmaZ) + " )) )"
  test1 = ops.image().equation(outputData, formula)

  display.createDisplay("image" + str(p), test1)

IJ.run("Concatenate...", "  title=BeadTrack image1=image10.01 image2=image10.11 image3=image10.21 image4=image10.31 image5=image10.41 image6=image10.51 image7=image10.61 image8=image10.71 image9=image10.81 image10=image10.91 image11=image11.01 image12=[-- None --]")
imp = IJ.getImage()
IJ.run(imp, "Stack to Hyperstack...", "order=xyczt(default) channels=1 slices=20 frames=11 display=Color")
IJ.setMinAndMax(imp, 0.0, 1.0)

Running TrackMate on this image (using 5.0 pixels as spot size and a threshold of 0.01), I get the following pattern in all three dimensions:

And here are the coordinates:

@tinevez Could you please check if you get the same?

1 Like

Without the fix, I get this:

With the fix I get this:

which is pretty good no?

Apart from the spot at 0.5, which has a problem probably caused by the interpolator jumping from one pixel to another to localize this one, and giving up in this noiseless situation.

2 Likes

Thanks again @tinevez I definitely see the improvement here :+1:

I was just puzzled by the asymmetry of the mismatch: the spot at t=4 has a greater deviation than the spot at t=6.

To complete the picture, I tested once again with added (synthetic) noise (thanks to imagej-ops!):

# @DisplayService display
# @OpService ops

from ij import IJ
from ij.plugin import HyperStackConverter
from net.imglib2.type.numeric.real import DoubleType

pos = [10.01, 10.11, 10.21, 10.31, 10.41, 10.51, 10.61, 10.71, 10.81, 10.91, 11.01]

sizeX = 20
sizeY = 20
sizeZ = 20

sigmaXY = 2
sigmaZ = 2

noiseOp = ops.op("filter.addNoise", DoubleType, DoubleType, 0.0, 1.0, 0.05)

for p in pos:
  posX = p
  posY = p
  posZ = p
  outputData = ops.create().img([sizeX, sizeY, sizeZ])
  formula = "( Math.exp( -0.5*((p[0]-" + str(posX) + ")*(p[0]-" + str(posX) + ")/" + str(sigmaXY) + "+(p[1]-" + str(posY) + ")*(p[1]-" + str(posY) + ")/" + str(sigmaXY) + "+(p[2]-" + str(posZ) + ")*(p[2]-" + str(posZ) + ")/" + str(sigmaZ) + " )) )"
  test1 = ops.image().equation(outputData, formula)
  ops.run("map", test1, test1, noiseOp)
  display.createDisplay("image" + str(p), test1)

IJ.run("Concatenate...", "  title=BeadTrack image1=image10.01 image2=image10.11 image3=image10.21 image4=image10.31 image5=image10.41 image6=image10.51 image7=image10.61 image8=image10.71 image9=image10.81 image10=image10.91 image11=image11.01 image12=[-- None --]")
imp = IJ.getImage()
IJ.run(imp, "Stack to Hyperstack...", "order=xyczt(default) channels=1 slices=20 frames=11 display=Color")
IJ.setMinAndMax(imp, 0.0, 1.0)

While I get accurate results most of the times, I still sometimes see mismatches when the spot is in between two adjacent pixels:


but this might be unavoidable I guess…

Anyways, great job for fixing the bug in the first place, thanks again.

1 Like

@imagejan This might be avoidable I think.
The SubpixelLocalization class specially deals with localization between two pixels. Check these lines:

I did not pay attention when I was using it, and probably configured it in a way that lead it to oscillate between the two pixels forever until it gives up. Indeed, if you check how I use it in TrackMate:

1 Like

Hi @tinevez ,

I think there is still a problem in the subpixel localization routine. However, it only occurs when using the find maxima detector and not when using LoG detector. Here is a snapshot release with a special log output for tracing the bug:
https://dl.dropboxusercontent.com/u/560426/TrackMate_FindMaxima-1.0.2-SNAPSHOT.jar

It happens in frame 12 of the attached video: prob.zip (2,6 KB)

Here is how to reproduce:

  1. Start trackmate
  2. Select frame 12 in the video file
  3. Select the find maxima detector (diameter 3, tolerance 20)
  4. You will get the following output:

The original position detected by the find max algorithm was (7,8) and is refined to (9.33,10.33) which is obviously wrong.

As this does not happen when using the LoG detector I was wondering if there is any problem with my code?

Cheers,
THorsten

1 Like

Hi Thorsten.

I guess it is linked to the image not being smooth.

The quadratic fit that fosters the subpixel refinement assumes that the pixel intensity resembles a parabole.

The LoG detector filters the image by a Laplacian of Gaussian prior to detection and subpixel localization, and therefore the processed image has this desired quality.

If this hypothesis is right, you should see defects in subpixel localization for non-smooth peaks often.

3 Likes

Thanks @tinevez ! When I apply a mean filter with radius 0.5 / 1 prior tracking the problem is vanishing.

2 Likes