Hi — thanks again for open-sourcing the whole pipeline and the sample data. I've been trying to reproduce the vasculature render from the ultrasound-brain post, and while digging in I ran into something that looks like a reproducibility bug.
I installed ultratrace-ulm straight from the repo (no changes) and ran it on the public sanitized_neutral_ultratrace.h5. The beamforming is deterministic — I get identical beamformed compounds across runs. But the detection stage is a different story.
Running track with the adaptive recipe (--svd-method adaptive --frame-rate 222 --sigma-threshold 2.0 --knee-filter --temporal-sigma 0) on the same single acquisition twice, I get ~39,000 and ~38,000 detections — similar counts, but when I compare the actual detected positions at voxel resolution, only about 14% overlap. The median nearest-neighbor distance between the two runs' detections is ~0.27 mm, so it's not sub-pixel jitter — the peaks themselves move, and roughly 6 of every 7 detections are different between two runs of the identical code on identical data.
I think I know what's going on: spectral_centroid_cutoff scores the SVD modes with a phase-sensitive |rfft(u.real)|², and the eigenvectors from eigh on the float32 covariance (matrix @ matrix.conj().T) aren't stable across runs. So the chosen clutter cutoff — the number of modes removed — shifts from run to run, which changes the filtered magnitude everywhere, which moves all the z-score peaks. And since ~99.9% of the detections are a noise floor sitting right at 2σ (only ~48 of ~42,000 detections per acquisition actually link into tracks), that near-threshold floor reshuffles almost completely each run.
If that's right, it'd explain why I can't match the figure: the detection field I'm linking from is mostly a non-reproducible noise floor, so the tracks and the final render come out different — and much noisier — every time. A few things would let me get past it:
- The clutter cutoff you actually used — the number of SVD components removed per acquisition, or an
--svd-n-components value that reproduces the figure. Pinning that removes the non-determinism entirely, which is what I'd really like.
- Is the public
sanitized_neutral_ultratrace.h5 the exact input behind the figure, and what's the real acquisition frame rate? I couldn't find a PRF in the H5, so I've been passing --frame-rate 222, and that value feeds the adaptive cutoff through the 100 Hz tissue boundary, not just the color scale.
- If it's easy, the actual
tracks_smoothed.pkl / tracks.bin behind the figure, or the exact track + track-viewer command — I'm most curious about --smooth-method (gaussian vs 3dulm), --sigma-threshold, --min-track-length, and the viewer's --min-length. Even just the figure's track-length distribution (median frames per track) would tell me whether I should be getting long linked tracks or lots of short ones.
- Which viewer produced the screenshot? The
web/track_viewer in the repo doesn't have the s1/s2 switcher or the scale bar, so I'm assuming the figure came from a different front-end. If there's any masking or filtering between the smoothed tracks and the final image, that'd be good to know too.
Happy to share a minimal repro (two track runs on one acquisition plus the overlap computation) if it's useful. Thanks for taking a look.
Hi — thanks again for open-sourcing the whole pipeline and the sample data. I've been trying to reproduce the vasculature render from the ultrasound-brain post, and while digging in I ran into something that looks like a reproducibility bug.
I installed
ultratrace-ulmstraight from the repo (no changes) and ran it on the publicsanitized_neutral_ultratrace.h5. The beamforming is deterministic — I get identical beamformed compounds across runs. But the detection stage is a different story.Running
trackwith the adaptive recipe (--svd-method adaptive --frame-rate 222 --sigma-threshold 2.0 --knee-filter --temporal-sigma 0) on the same single acquisition twice, I get ~39,000 and ~38,000 detections — similar counts, but when I compare the actual detected positions at voxel resolution, only about 14% overlap. The median nearest-neighbor distance between the two runs' detections is ~0.27 mm, so it's not sub-pixel jitter — the peaks themselves move, and roughly 6 of every 7 detections are different between two runs of the identical code on identical data.I think I know what's going on:
spectral_centroid_cutoffscores the SVD modes with a phase-sensitive|rfft(u.real)|², and the eigenvectors fromeighon the float32 covariance (matrix @ matrix.conj().T) aren't stable across runs. So the chosen clutter cutoff — the number of modes removed — shifts from run to run, which changes the filtered magnitude everywhere, which moves all the z-score peaks. And since ~99.9% of the detections are a noise floor sitting right at 2σ (only ~48 of ~42,000 detections per acquisition actually link into tracks), that near-threshold floor reshuffles almost completely each run.If that's right, it'd explain why I can't match the figure: the detection field I'm linking from is mostly a non-reproducible noise floor, so the tracks and the final render come out different — and much noisier — every time. A few things would let me get past it:
--svd-n-componentsvalue that reproduces the figure. Pinning that removes the non-determinism entirely, which is what I'd really like.sanitized_neutral_ultratrace.h5the exact input behind the figure, and what's the real acquisition frame rate? I couldn't find a PRF in the H5, so I've been passing--frame-rate 222, and that value feeds the adaptive cutoff through the 100 Hz tissue boundary, not just the color scale.tracks_smoothed.pkl/tracks.binbehind the figure, or the exacttrack+track-viewercommand — I'm most curious about--smooth-method(gaussian vs 3dulm),--sigma-threshold,--min-track-length, and the viewer's--min-length. Even just the figure's track-length distribution (median frames per track) would tell me whether I should be getting long linked tracks or lots of short ones.web/track_viewerin the repo doesn't have the s1/s2 switcher or the scale bar, so I'm assuming the figure came from a different front-end. If there's any masking or filtering between the smoothed tracks and the final image, that'd be good to know too.Happy to share a minimal repro (two
trackruns on one acquisition plus the overlap computation) if it's useful. Thanks for taking a look.