Skip to content

Add support for polydisperse features#807

Open
putnokiabel wants to merge 22 commits into
soft-matter:masterfrom
putnokiabel:polydisperse
Open

Add support for polydisperse features#807
putnokiabel wants to merge 22 commits into
soft-matter:masterfrom
putnokiabel:polydisperse

Conversation

@putnokiabel

@putnokiabel putnokiabel commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Currently, trackpy only allows setting a single diameter (monodisperse features) for detection. This works if all features are the same size, but does not work if an image/video has features of multiple sizes (polydisperse) which is still a common setup that is currently not supported.
The workarounds at the moment are:

  • track everything with the diameter of the largest feature (works only if the size differences between features are minimal and the distance between features is large), or
  • track the image multiple times with different diameters and combine the results by deduplicating features that are close to each other (takes significantly more compute time, deduplication is not trivial, accuracy is mediocre).

This PR introduces true support for polydisperse features by allowing the user to set diameter=Polydisperse(min_diameter, max_diameter) and making changes to tracking to facilitate this range of diameters at every step (preprocessing, refining, deduplication, spiff, etc).
The existing tracking for monodisperse particles (i.e. if you do not set diameter=Polydisperse) remains completely unchanged.

Benchmark:

I added a benchmark (run using python benchmarks/polydisperse_grid.py) that tests a combination of different scenarios:

  • size differences (3x, 5x, 10x)
  • feature density (low, med, high)
  • noise level (low, med, high)

and compares the baseline (tracking everything with the max diameter) vs polydisperse (spiff enabled for both).
We compare:

  • Recall (the proportion of features that get identified at all, within a tolerance of 1.5px distance)
  • Error (RMS of distance between actual and estimated feature positions, for the features that were identified correctly)
  • Tracking time

Results

range dens  noise    N | baseline      poly         
--------------------------------------------------------------
x3    low   low    125 | r1.00 e0.020  r1.00 e0.021
x3    low   med    120 | r1.00 e0.038  r1.00 e0.046
x3    low   high   121 | r1.00 e0.087  r1.00 e0.086
x3    med   low    300 | r1.00 e0.022  r1.00 e0.029
x3    med   med    300 | r1.00 e0.035  r1.00 e0.042
x3    med   high   300 | r1.00 e0.082  r1.00 e0.085
x3    high  low    300 | r0.82 e0.020  r1.00 e0.034
x3    high  med    300 | r0.82 e0.034  r1.00 e0.048
x3    high  high   300 | r0.83 e0.077  r1.00 e0.086
x5    low   low    102 | r1.00 e0.018  r1.00 e0.022
x5    low   med    105 | r1.00 e0.052  r1.00 e0.085
x5    low   high   113 | r1.00 e0.161  r0.99 e0.381
x5    med   low    270 | r0.54 e0.027  r1.00 e0.037
x5    med   med    266 | r0.59 e0.039  r1.00 e0.046
x5    med   high   266 | r0.59 e0.113  r1.00 e0.098
x5    high  low    300 | r0.44 e0.065  r1.00 e0.072
x5    high  med    300 | r0.47 e0.074  r1.00 e0.080
x5    high  high   300 | r0.40 e0.097  r1.00 e0.105
x10   low   low     82 | r0.49 e0.009  r1.00 e0.031
x10   low   med     91 | r0.46 e0.046  r1.00 e0.055
x10   low   high    87 | r0.45 e0.238  r1.00 e0.090
x10   med   low    186 | r0.18 e0.080  r1.00 e0.071
x10   med   med    186 | r0.17 e0.195  r1.00 e0.084
x10   med   high   186 | r0.20 e0.216  r1.00 e0.112
x10   high  low    300 | r0.07 e0.327  r1.00 e0.118
x10   high  med    300 | r0.08 e0.349  r1.00 e0.119
x10   high  high   300 | r0.04 e0.304  r1.00 e0.130
--------------------------------------------------------------
Total grid time:  baseline=1.46s  poly=2.53s  (poly/baseline=1.73x)

As you can see, baseline does OK in the 3x range when the features aren't too dense, but fails completely in other scenarios (recall drops dramatically), whereas Polydisperse has (near) perfect recall across every simulated scenario.
Note that Polydisperse has slightly higher tracking errors than baseline, however it's still the same order of magnitude and still gives good subpixel accuracy.

Polydisperse tracking takes ~73% longer than baseline (despite polydisperse finding many more features). Worth the tradeoff as tracking is significantly better.

Current scope:

  • Support in locate, batch and helper methods (like refine and spiff). This PR does not include linking (which could be significantly improved if we use particle size for linking, assuming features don't change in size over time).
  • Support for isotropic features. This PR does not include support for anisotropic features.
  • Support up to 10x range in feature size.

Future work:

  • Use particle size in linking (see also Assigning sizes to each particle. #694)
  • Support anisotropic features
  • Add multiprocessing (some parts of the updating tracking process could benefit from additional parallellization/multiprocessing)
  • Test for scenarios where there is more than a 10x range in feature sizes, and diagnose bottlenecks

@putnokiabel putnokiabel marked this pull request as ready for review July 9, 2026 15:36
Comment thread trackpy/find.py
Comment on lines +147 to +153
collapse_flat : boolean, optional
When True, collapse each connected region of equal-valued maxima to a
single representative (its centroid). A flat or plateaued peak -- common
on large or saturated features, especially after integer coercion --
otherwise reports every pixel of the plateau as a separate maximum. Two
distinct features never merge, as they are separated by a lower-valued
gap. Default False.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note:
Currently I only enabled this in polydisperse mode. However, I think this could lead to a performance improvement also in the existing, monodisperse setup.
By doing this piece of filtering upfront, we can skip wasteful refine cycles for multiple peaks that are touching (and thus clearly part of the same feature).

Comment thread trackpy/feature.py
Comment on lines +300 to +302
In polydisperse mode (a ``Polydisperse`` ``diameter``) an additional
``diameter`` column reports the refinement diameter assigned to each
feature from its size.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note:
This is not only a nice-to-have output for users of the package, it's also required to make SPIFF work with multiple feature sizes.

@b-grimaud

b-grimaud commented Jul 9, 2026

Copy link
Copy Markdown

Very excited for this, we're tracking biological objects of varying sizes and setting a proper diameter is challenging. We miss a lot of meaningful signal if the diameter is too small, but linking (and MSD-based filtering) times become impractical if we set it too high.

@putnokiabel

Copy link
Copy Markdown
Contributor Author

Very excited for this, we're tracking biological objects of varying sizes and setting a proper diameter is challenging. We miss a lot of meaningful signal if the diameter is too small, but linking (and MSD-based filtering) times become impractical if we set it too high.

@b-grimaud
That's great! Feel free to test this PR on your dataset and leave feedback (since I've only tested using synthetic data so far) if you find any gaps :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants