CaImAn vs this CNMFe — benchmarking notes¶
What we learned comparing this from-scratch CNMFe reimplementation against
CaImAn's CNMF-E (caiman 1.13.1). Read this before quoting any "vs CaImAn"
number — several early "striking differences" turned out to be measurement
artifacts, and the one real edge is partly a home-field effect.
Reproduce: demo_notebooks/tutorial_caiman_compare_patches.ipynb (the
orientation-correct, per-pipeline-tuned, patch-based comparison).
Bottom line¶
- Clean synthetic data (Gaussian footprints, white noise, rank-1 bg): on par. After fixing the comparison artifacts below, footprints, detection, and both trace flavours match CaImAn within ~0.01–0.02 Pearson r; we are ~10× faster on these small movies (with the speed caveat below).
- Realistic simulator (
make_miniscope_movie): we are modestly ahead on the temporal trace — but ablations show this is largely because the simulator's background suits our model, not a general CaImAn deficiency. - Real recordings: unknown. This implementation has only been tested on synthetic data; CaImAn is validated on hundreds of real 1p recordings (some with paired electrophysiology). Do not generalise the synthetic results to real data.
Comparison-fairness issues we found (and fixed)¶
Matching parameter values is not the same as a fair comparison. These all made CaImAn look worse than it is until corrected:
- Footprint C-order vs Fortran-order mismatch. Our
A_true/Aare C-flattened (h*W+w); CaImAn returnsAFortran-flattened (h+w*H). Comparing them directly transposes CaImAn's footprints — near-harmless for round Gaussians, but on the realistic movie's elliptical footprints it pinned CaImAn's mean spatial r at 0.30 (and "recovered" at 3/16); the orientation fix raised it to 0.71 and 12/16. Fix: map CaImAn'sAinto true(h,w)space and auto-detect the reshape order by centroid alignment to truth (also catches any memmap movie-feed transpose). Verified: orderFaligns at ~1 px,Cat ~7 px. - Threshold operating-point mismatch.
min_corr/min_pnrcopied from our params land at the wrong place on CaImAn's corr/pnr images (e.g. ourmin_corr=0.8sat above CaImAn's corr p99 of 0.78 → starved seeding). Fix: tune CaImAn's thresholds from its owncorrelation_pnrdistribution; keep ours as-is (each algorithm at its own operating point). - No-patch CaImAn never refines its ring background. CaImAn's
compute_W(ring-Wrecomputation, the analogue of our per-iterationb0refresh) runs only in the patch path. Fix: run CaImAn in patches (rf/stride) on a FOV large enough for a real patch grid (≥128 px); 64×64 is below its regime. - (ours) PAVA deconvolution fallback bug. When the
oasis-deconvpackage is absent, we fell back to a pure-Python PAVA whose pool-merge test used bareginstead ofg**pool_length— collapsing smooth decays. This dropped our deconvolvedCvs truth to ~0.58 while CaImAn's was ~0.98, hidden behind the robustC+YrA(~0.96). Fixed inminicnmfe/temporal.py(commitb90f032); guarded bytests/test_temporal.py::test_pava_fallback_reconstructs_clean_ar1. After the fix ourCis ~0.96, on par with CaImAn.
Still intentionally not equalised: evaluate_components (CaImAn's
CNN+SNR+spatial-correlation quality filter) is not called — the comparison is
raw output vs raw output. On real data that filter is CaImAn's main precision
advantage, so omitting it flatters whichever side has fewer safeguards.
Results (after the fixes)¶
Synthetic, K=24, 128×128. ours(2) = n_iter_main=2; caiman = patch CNMF-E,
thresholds tuned to its own corr/pnr.
Clean synthetic movie:
| metric | ours(2) | caiman |
|---|---|---|
| K extracted | 24 | 24 |
| matched (>0.7 spatial) | 24/24 | 24/24 |
| mean spatial r | 0.982 | 0.994 |
mean temporal r (C) |
0.970 | 0.982 |
mean temporal r (C+YrA) |
0.970 | 0.978 |
| wall time | 16 s | 153 s |
Realistic miniscope movie (drifting multi-component bg, ghosts, vasculature,
vignette, photobleach, shot noise, 8-bit; g_true ∈ [0.73,0.78]):
| metric | ours(2) | caiman |
|---|---|---|
| spatial r | 0.943 | 0.925 |
| recovered | 24/24 | 23/24 |
C+YrA r |
0.851 | 0.710 |
Why CaImAn trails on the realistic movie (diagnosed by ablation)¶
The gap is temporal (C+YrA), not spatial or detection. Per-neuron, CaImAn's
footprints are good (spatial 0.84–0.99) but a subset of its projected traces
are contaminated despite good footprints — the signature of background leaking
into the residual at certain locations. Ablating one confound at a time
(make_miniscope_movie, median C+YrA gap ours−caiman):
| movie | median gap |
|---|---|
| baseline | 0.087 |
| no photobleach | 0.081 |
| no vasculature | 0.091 |
| rank-1 background | 0.020 |
Collapsing the background to rank-1 nearly closes the gap; removing bleach or
vasculature does not. So the cause is the multi-component structured
background. And it is largely a home-field effect: the simulator builds its
background as a sum of separable rank-1 components, structurally close to our
ring + per-iteration b0-refresh model (developed against this very simulator)
and simpler than real neuropil/hemodynamics. CaImAn's ring is tuned for real 1p
background. Expect this edge to shrink on real recordings.
(The mean C+YrA gap is further inflated by the one missed neuron being matched
to a wrong component; the median strips that out. A secondary wrinkle: the
per-movie threshold auto-tuner is sensitive — when the corr/pnr distribution
shifts it can pick a too-high min_pnr and hurt CaImAn's recall.)
Genuine algorithmic differences (not bugs)¶
| aspect | this CNMFe | CaImAn CNMF-E |
|---|---|---|
| Background | ring + per-iteration b0 refresh (+ optional rank-1 b_f·f) |
ring (compute_W), refined in the patch path |
| Deconvolution | OASIS via oasis-deconv; pure-Python PAVA fallback |
OASIS (constrained_foopsi) |
AR g |
estimated once from C_raw, cached (global or per-neuron); optional Bayesian decay_time/fps prior |
per-neuron, per-iteration |
| Spatial solve | per-pixel non-neg LASSO on raw traces | LASSO on StandardScaler-normalised traces |
| Quality filter | non-destructive SNR-amplitude auto-eval (accepted_mask) |
CNN + SNR + spatial-corr evaluate_components |
| Init | greedy corr/pnr (patch-parallel by default) | corr/pnr in patches |
Speed caveat: the ~10× wall-time advantage is partly because the comparison runs
CaImAn with N_PROCESSES=1 + patches (sequential); CaImAn parallelises across
patches, so the gap narrows with more processes.
How to read any "vs CaImAn" claim from this repo¶
Defensible statement: "On synthetic benchmarks with a matched, orientation-corrected, per-pipeline-tuned setup, this reimplementation achieves recovery comparable to CaImAn's raw CNMF-E output (and is faster on small movies). It has not been validated on real recordings or against CaImAn's full evaluation pipeline."
What would justify a stronger claim: real 1p recordings not produced by our
own simulator, with external ground truth (paired ephys or expert ROIs), running
CaImAn's full pipeline including evaluate_components.