AutoMorphoTrack 2.1.0__tar.gz → 2.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- automorphotrack-2.5.0/CITATION.cff +12 -0
- automorphotrack-2.5.0/MANIFEST.in +12 -0
- automorphotrack-2.5.0/PKG-INFO +112 -0
- automorphotrack-2.5.0/README.md +68 -0
- {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack/__init__.py +37 -22
- automorphotrack-2.5.0/automorphotrack/adaptive_segmentation.py +121 -0
- automorphotrack-2.5.0/automorphotrack/benchmarking.py +188 -0
- automorphotrack-2.5.0/automorphotrack/cli.py +250 -0
- {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack/colocalization.py +38 -14
- automorphotrack-2.5.0/automorphotrack/mcp_server.py +272 -0
- {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack/motility.py +51 -1
- automorphotrack-2.5.0/automorphotrack/napari.yaml +35 -0
- automorphotrack-2.5.0/automorphotrack/napari_plugin.py +272 -0
- automorphotrack-2.5.0/automorphotrack/refined/__init__.py +8 -0
- automorphotrack-2.5.0/automorphotrack/refined/__main__.py +20 -0
- automorphotrack-2.5.0/automorphotrack/refined/pipeline.py +134 -0
- automorphotrack-2.5.0/automorphotrack/refined/segmentation.py +85 -0
- automorphotrack-2.5.0/automorphotrack/refined/tracking.py +93 -0
- {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack/shape_profiling.py +19 -4
- automorphotrack-2.5.0/automorphotrack/utils.py +127 -0
- automorphotrack-2.5.0/automorphotrack/validated/__init__.py +20 -0
- automorphotrack-2.5.0/automorphotrack/validated/__main__.py +22 -0
- automorphotrack-2.5.0/automorphotrack/validated/colocalization.py +11 -0
- automorphotrack-2.5.0/automorphotrack/validated/config.py +88 -0
- automorphotrack-2.5.0/automorphotrack/validated/core.py +225 -0
- automorphotrack-2.5.0/automorphotrack/validated/detection.py +22 -0
- automorphotrack-2.5.0/automorphotrack/validated/io.py +66 -0
- automorphotrack-2.5.0/automorphotrack/validated/lyso_count.py +11 -0
- automorphotrack-2.5.0/automorphotrack/validated/morphology.py +11 -0
- automorphotrack-2.5.0/automorphotrack/validated/motility.py +25 -0
- automorphotrack-2.5.0/automorphotrack/validated/pipeline.py +195 -0
- automorphotrack-2.5.0/automorphotrack/validated/plotting.py +57 -0
- automorphotrack-2.5.0/automorphotrack/validated/shape_features.py +22 -0
- automorphotrack-2.5.0/automorphotrack/validated/shape_profiling.py +16 -0
- automorphotrack-2.5.0/automorphotrack/validated/summary.py +36 -0
- automorphotrack-2.5.0/automorphotrack/validated/tracking.py +11 -0
- automorphotrack-2.5.0/automorphotrack/validated/tracking_overlay.py +11 -0
- automorphotrack-2.5.0/automorphotrack/validated/utils.py +39 -0
- {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack/validation.py +138 -20
- automorphotrack-2.5.0/automorphotrack.egg-info/PKG-INFO +112 -0
- automorphotrack-2.5.0/automorphotrack.egg-info/SOURCES.txt +121 -0
- automorphotrack-2.5.0/automorphotrack.egg-info/entry_points.txt +10 -0
- automorphotrack-2.5.0/automorphotrack.egg-info/requires.txt +25 -0
- automorphotrack-2.5.0/docs/GUI.md +75 -0
- automorphotrack-2.5.0/docs/MCP.md +82 -0
- automorphotrack-2.5.0/docs/PARTICLE_VALIDATION.md +15 -0
- automorphotrack-2.1.0/automorphotrack.egg-info/PKG-INFO → automorphotrack-2.5.0/docs/V2_INTERFACE.md +93 -53
- automorphotrack-2.5.0/docs/VALIDATED_CORE.md +14 -0
- automorphotrack-2.5.0/docs/reviewer_concerns.md +40 -0
- automorphotrack-2.5.0/examples/Refined_Pipeline.ipynb +71 -0
- automorphotrack-2.5.0/examples/refined_verified.py +52 -0
- automorphotrack-2.5.0/pyproject.toml +68 -0
- automorphotrack-2.5.0/setup.py +3 -0
- automorphotrack-2.5.0/tests/__init__.py +0 -0
- automorphotrack-2.5.0/tests/test_benchmarking.py +61 -0
- automorphotrack-2.5.0/tests/test_gui_import.py +55 -0
- automorphotrack-2.5.0/tests/test_install_and_imports.py +41 -0
- automorphotrack-2.5.0/tests/test_refined.py +141 -0
- automorphotrack-2.5.0/tests/test_validated_revision.py +223 -0
- automorphotrack-2.5.0/tests/test_validation_and_segmentation.py +74 -0
- automorphotrack-2.5.0/validation/api_reconciliation.json +28 -0
- automorphotrack-2.5.0/validation/check_installed_core.py +52 -0
- automorphotrack-2.5.0/validation/core_manifest.json +28 -0
- automorphotrack-2.5.0/validation/known_motion.ome.tif +0 -0
- automorphotrack-2.5.0/validation/known_motion_config.json +40 -0
- automorphotrack-2.5.0/validation/known_motion_expected/Colocalization.csv +5 -0
- automorphotrack-2.5.0/validation/known_motion_expected/Frame_Correlation_N.csv +20 -0
- automorphotrack-2.5.0/validation/known_motion_expected/Frame_Descriptive_Spearman.csv +20 -0
- automorphotrack-2.5.0/validation/known_motion_expected/Frame_Summary.csv +5 -0
- automorphotrack-2.5.0/validation/known_motion_expected/Lyso_InstanceLabels.tif +0 -0
- automorphotrack-2.5.0/validation/known_motion_expected/Lyso_Masks.tif +0 -0
- automorphotrack-2.5.0/validation/known_motion_expected/Lyso_MotilitySteps.csv +5 -0
- automorphotrack-2.5.0/validation/known_motion_expected/Lyso_MotilitySummary.csv +2 -0
- automorphotrack-2.5.0/validation/known_motion_expected/Lyso_ShapeMetrics.csv +5 -0
- automorphotrack-2.5.0/validation/known_motion_expected/Lyso_Tracks.csv +5 -0
- automorphotrack-2.5.0/validation/known_motion_expected/Mito_InstanceLabels.tif +0 -0
- automorphotrack-2.5.0/validation/known_motion_expected/Mito_Masks.tif +0 -0
- automorphotrack-2.5.0/validation/known_motion_expected/Mito_MotilitySteps.csv +5 -0
- automorphotrack-2.5.0/validation/known_motion_expected/Mito_MotilitySummary.csv +2 -0
- automorphotrack-2.5.0/validation/known_motion_expected/Mito_ShapeMetrics.csv +5 -0
- automorphotrack-2.5.0/validation/known_motion_expected/Mito_Tracks.csv +5 -0
- automorphotrack-2.5.0/validation/known_motion_expected/Motility_PerFrame.csv +9 -0
- automorphotrack-2.5.0/validation/known_motion_expected/Sample_Summary.csv +2 -0
- automorphotrack-2.5.0/validation/known_motion_expected/Thresholds.csv +9 -0
- automorphotrack-2.5.0/validation/known_motion_truth.json +61 -0
- automorphotrack-2.5.0/validation/platform_check.py +108 -0
- automorphotrack-2.5.0/validation/refined_manifest.json +71 -0
- automorphotrack-2.5.0/validation/refined_motion.ome.tif +0 -0
- automorphotrack-2.5.0/validation/refined_motion_config.json +28 -0
- automorphotrack-2.5.0/validation/refined_motion_expected/channel_0_labels.tif +0 -0
- automorphotrack-2.5.0/validation/refined_motion_expected/channel_0_motility.csv +3 -0
- automorphotrack-2.5.0/validation/refined_motion_expected/channel_0_motility_per_frame.csv +6 -0
- automorphotrack-2.5.0/validation/refined_motion_expected/channel_0_raw_labels.tif +0 -0
- automorphotrack-2.5.0/validation/refined_motion_expected/channel_0_raw_tracks.csv +11 -0
- automorphotrack-2.5.0/validation/refined_motion_expected/channel_0_shapes.csv +11 -0
- automorphotrack-2.5.0/validation/refined_motion_expected/channel_0_steps.csv +11 -0
- automorphotrack-2.5.0/validation/refined_motion_expected/channel_0_thresholds.csv +6 -0
- automorphotrack-2.5.0/validation/refined_motion_expected/channel_0_tracks.csv +11 -0
- automorphotrack-2.5.0/validation/refined_motion_expected/channel_1_labels.tif +0 -0
- automorphotrack-2.5.0/validation/refined_motion_expected/channel_1_motility.csv +3 -0
- automorphotrack-2.5.0/validation/refined_motion_expected/channel_1_motility_per_frame.csv +6 -0
- automorphotrack-2.5.0/validation/refined_motion_expected/channel_1_raw_labels.tif +0 -0
- automorphotrack-2.5.0/validation/refined_motion_expected/channel_1_raw_tracks.csv +11 -0
- automorphotrack-2.5.0/validation/refined_motion_expected/channel_1_shapes.csv +11 -0
- automorphotrack-2.5.0/validation/refined_motion_expected/channel_1_steps.csv +11 -0
- automorphotrack-2.5.0/validation/refined_motion_expected/channel_1_thresholds.csv +6 -0
- automorphotrack-2.5.0/validation/refined_motion_expected/channel_1_tracks.csv +11 -0
- automorphotrack-2.5.0/validation/refined_motion_expected/colocalization.csv +6 -0
- automorphotrack-2.5.0/validation/refined_motion_expected/run_manifest.json +99 -0
- automorphotrack-2.1.0/MANIFEST.in +0 -4
- automorphotrack-2.1.0/PKG-INFO +0 -258
- automorphotrack-2.1.0/README.md +0 -222
- automorphotrack-2.1.0/automorphotrack/utils.py +0 -37
- automorphotrack-2.1.0/automorphotrack.egg-info/SOURCES.txt +0 -26
- automorphotrack-2.1.0/automorphotrack.egg-info/requires.txt +0 -8
- automorphotrack-2.1.0/pyproject.toml +0 -40
- automorphotrack-2.1.0/setup.py +0 -46
- {automorphotrack-2.1.0 → automorphotrack-2.5.0}/LICENSE.md +0 -0
- {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack/detection.py +0 -0
- {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack/lyso_count.py +0 -0
- {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack/morphology.py +0 -0
- {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack/network_analysis.py +0 -0
- {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack/shape_features.py +0 -0
- {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack/spatial_statistics.py +0 -0
- {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack/summary.py +0 -0
- {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack/temporal_dynamics.py +0 -0
- {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack/tracking.py +0 -0
- {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack/tracking_overlay.py +0 -0
- {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack.egg-info/dependency_links.txt +0 -0
- {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack.egg-info/top_level.txt +0 -0
- {automorphotrack-2.1.0 → automorphotrack-2.5.0}/setup.cfg +0 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "Cite this release for the particle revision and reproduction materials."
|
|
3
|
+
type: software
|
|
4
|
+
title: "AutoMorphoTrack 2.5.0: particle refinement and manuscript revision"
|
|
5
|
+
authors:
|
|
6
|
+
- family-names: Bayati
|
|
7
|
+
given-names: Armin
|
|
8
|
+
version: 2.5.0
|
|
9
|
+
date-released: 2026-11-14
|
|
10
|
+
repository-code: "https://github.com/abayatibrain/AutoMorphoTrack"
|
|
11
|
+
url: "https://github.com/abayatibrain/AutoMorphoTrack/releases/tag/v2.5.0"
|
|
12
|
+
license: MIT
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
include README.md
|
|
2
|
+
include LICENSE.md
|
|
3
|
+
include pyproject.toml
|
|
4
|
+
include CITATION.cff
|
|
5
|
+
recursive-include examples *.py *.ipynb
|
|
6
|
+
recursive-include automorphotrack *.py
|
|
7
|
+
|
|
8
|
+
recursive-include validation *.json *.py *.tif *.csv
|
|
9
|
+
recursive-include docs *.md
|
|
10
|
+
recursive-include tests *.py
|
|
11
|
+
prune integration
|
|
12
|
+
prune release_artifacts
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: automorphotrack
|
|
3
|
+
Version: 2.5.0
|
|
4
|
+
Summary: Automated pipeline for mitochondrial and lysosomal detection, tracking, morphology, and colocalization analysis in microscopy images.
|
|
5
|
+
Author-email: "Armin Bayati, Ph.D." <a.bayati.brain@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Documentation, https://github.com/abayatibrain/AutoMorphoTrack
|
|
8
|
+
Project-URL: Source, https://github.com/abayatibrain/AutoMorphoTrack
|
|
9
|
+
Project-URL: Tracker, https://github.com/abayatibrain/AutoMorphoTrack/issues
|
|
10
|
+
Keywords: image-processing,microscopy,organelle-tracking,mitochondria,lysosomes,colocalization,live-cell-imaging
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Intended Audience :: Science/Research
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Image Processing
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE.md
|
|
22
|
+
Requires-Dist: numpy>=2.0
|
|
23
|
+
Requires-Dist: pandas>=2.2
|
|
24
|
+
Requires-Dist: matplotlib>=3.6.0
|
|
25
|
+
Requires-Dist: seaborn>=0.12.0
|
|
26
|
+
Requires-Dist: opencv-python>=4.6.0
|
|
27
|
+
Requires-Dist: scikit-image>=0.25
|
|
28
|
+
Requires-Dist: scipy>=1.13
|
|
29
|
+
Requires-Dist: tifffile>=2024.8
|
|
30
|
+
Provides-Extra: mcp
|
|
31
|
+
Requires-Dist: mcp>=1.0.0; extra == "mcp"
|
|
32
|
+
Provides-Extra: compressed-tiff
|
|
33
|
+
Requires-Dist: imagecodecs>=2024.1; extra == "compressed-tiff"
|
|
34
|
+
Provides-Extra: napari
|
|
35
|
+
Requires-Dist: napari>=0.4.18; extra == "napari"
|
|
36
|
+
Requires-Dist: magicgui>=0.7.0; extra == "napari"
|
|
37
|
+
Requires-Dist: qtpy>=2.3.0; extra == "napari"
|
|
38
|
+
Provides-Extra: dev
|
|
39
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
40
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
41
|
+
Requires-Dist: build>=1.0; extra == "dev"
|
|
42
|
+
Requires-Dist: twine>=4.0; extra == "dev"
|
|
43
|
+
Dynamic: license-file
|
|
44
|
+
|
|
45
|
+
# AutoMorphoTrack 2.5.0
|
|
46
|
+
|
|
47
|
+
AutoMorphoTrack 2.5.0 requires Python 3.11 or later and has two explicitly versioned analysis routes. The automorphotrack.validated namespace conserves the 18 modules of core 1.1.0rc2. The automorphotrack.refined namespace includes five additional modules that provide particle detection and association, described below, and make use of the unchanged baseline measurement modules. NumPy, pandas, SciPy, scikit-image, and tifffile are the main dependencies. The two implementations are distinguishable by their source, and examples show which route produced a given result.
|
|
48
|
+
|
|
49
|
+
## Installation
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
python -m pip install "automorphotrack==2.5.0"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Minimal Debian and Ubuntu systems may need libxcb1, libgl1, and libglib2.0-0 for the OpenCV import. The GitHub CI matrix completed wheel and source installations on Windows, Linux, and macOS with Python 3.11 and 3.12, and retained test reports, dependency versions, source checks, and known-motion outputs.
|
|
56
|
+
|
|
57
|
+
## Particle pipeline
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from automorphotrack.refined import RefinedConfig, run_pipeline
|
|
61
|
+
config = RefinedConfig(channels=(0, 1), sample_id="recording")
|
|
62
|
+
result = run_pipeline("recording.ome.tif", "new_results", config)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
amt-refined recording.ome.tif --out new_results --config configuration.json
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The particle example uses a two-channel synthetic movie with known motion. Run the guarded adapter from the release directory after installing the 2.5.0 wheel. It checks the 18 baseline and five particle modules before importing the numerical implementation. Use a new output directory for every run. For a static image, set min_observations to 1. Particle support masks have not been validated to be mitochondrial membrane boundaries. The default setting requires three observations, thus short-lived events will not be retained and retained masks and motion summaries will not reflect these. Raw outputs preserve these events.
|
|
70
|
+
|
|
71
|
+
The input must identify time, channel, and spatial axes. Calibration requires a recorded source; otherwise motion remains in pixels per frame. Label masks and track IDs are linked through each frame's Label column. A different configuration requires its own evaluation.
|
|
72
|
+
|
|
73
|
+
## Recorded validation
|
|
74
|
+
|
|
75
|
+
Six reserved movies contain 145 annotated trajectories and 2,764 adjacent reference links. Mean accepted-link recall for the frozen default and fixed top-hat configurations was 0.014 and 0.212, respectively. With the particle pipeline, mean accepted-link recall was 0.696, and mean localization recall was 0.768. The new pipeline recovered 2,024 accepted reference links, and four additional accepted links related to different annotated identities, giving conditional precision of 2,024/2,028 among the links with two reference-matched endpoints. Remaining predictions were left unverified. Thirteen trajectories were completely missed, and 33 were fully recovered with accepted links, compared to 41 missed and two fully recovered with the top-hat configuration. Accepted-link recall was 0.739 with a 5-pixel tolerance and 0.587 with a 2-pixel tolerance. Individual reserved movies yielded accepted-link recall from 0.408 to 0.911. Supplementary Fig. 10 and Table 9 show the paired comparisons and their shortcomings.
|
|
76
|
+
|
|
77
|
+
The original 50 synthetic sequences, when rerun, yielded mean Dice scores of 0.877, 0.752 and 0.532, for the new particle masks, the fixed top-hat configuration and the frozen default, respectively. Mean instance precision and accepted-link recall increased from 0.407 to 0.966 and 0.178 to 0.965, respectively. The two baselines reproduced their earlier results to within an absolute error of 10^-12. Transfer to the external mitochondrial masks was limited. Without the length filter, the selected detector yielded mean Dice scores of 0.369 and 0.060 for the seven MoDL SIM patches and the 13 Myofibril patches, respectively. The MoDL score was still below the earlier top-hat result of 0.421. The particle pipeline thus does not replace target-specific segmentation validation for elongated or densely packed mitochondria.
|
|
78
|
+
|
|
79
|
+
AutoMorphoTrack 2.5.0 passed 72 tests in each of 12 GitHub CI installations. It built wheels and source distributions for Windows, Linux, and macOS, with Python 3.11 and 3.12. All installations kept the 18 baseline and five particle source modules. Each test compared 19 baseline and 19 particle CSV or TIFFs with recorded references. Integer masks were exact, and for baseline and particle outputs, numerical tolerances were 10^-12 and 10^-9, respectively. The test failed initially on Windows due to Git line-ending conversion which changed the source hashes. Enforcing LF line endings solved this, and the numerical code remained the same. The OpenCV import failure and its system-library remedy documented for minimal Linux remain documented.
|
|
80
|
+
|
|
81
|
+
Supplementary figures and tables are in the manuscript files attached to [release v2.5.0](https://github.com/abayatibrain/AutoMorphoTrack/releases/tag/v2.5.0). The [reproduction archive](https://github.com/abayatibrain/AutoMorphoTrack/releases/download/v2.5.0/AMT_Revision_Reproducibility_v2.5.0.zip) includes the development protocol, every candidate result, reserved-movie outputs, controls, and prior analyses. [The 12-job CI run](https://github.com/abayatibrain/AutoMorphoTrack/actions/runs/43602083124) records Windows, Linux, and macOS installations.
|
|
82
|
+
|
|
83
|
+
Biological source: [Richens et al., PLOS Biology 2025](https://doi.org/10.1371/journal.pbio.3003099), [data version 1](https://doi.org/10.17863/CAM.114338), CC BY 4.0. All 12 movies had been assessed with the baseline before six were reserved from the new parameter selection. This is a within-study split. Manual annotations cover a subset of vesicles; unmatched predictions remain unverified.
|
|
84
|
+
|
|
85
|
+
## Reproducible integration
|
|
86
|
+
|
|
87
|
+
For the 2.5.0 route, Gemini wrote a new adapter and Codex ran the captured source against the installed wheel. The first adapter returned a file-hash mapping where the contract required a digest string. A focused change resolved that metadata error. The corrected adapter, the Codex adapter, a standalone command line, and an executed Jupyter notebook reproduced the 19 reference particle files exactly on the local host. Altered manifests and source files were rejected before analysis. The Codex guard verifies all 23 modules across both numerical namespaces. Prior to the requests, Argon High was selected in Gemini, but the interface changed its model label after the responses; the underlying model identity was not verified. These records test software integration, and numerical reproducibility, without constituting independent biological validation by either assistant.
|
|
88
|
+
|
|
89
|
+
The runnable adapter is `examples/refined_verified.py`. The notebook is `examples/Refined_Pipeline.ipynb`. Keep both source manifests under version control. Run a fixture after changing an adapter, configuration, preprocessing step, environment, or downstream calculation. Source integrity alone does not validate those changes.
|
|
90
|
+
|
|
91
|
+
```sh
|
|
92
|
+
python -I examples/refined_verified.py --input validation/refined_motion.ome.tif --out new_fixture_results --config validation/refined_motion_config.json --validation-dir validation
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Baseline route
|
|
96
|
+
|
|
97
|
+
Install the pinned 2.5.0 wheel in a virtual environment. Use amt-refined, or import RefinedConfig and run_pipeline from automorphotrack.refined, for the particle pipeline. The amt-validated command retains the baseline implementation for Figures 1-17 and Supplementary Figures 1-9. Supplementary Figure 10 reports the new particle evaluation. The automorphotrack and amt commands remain unchanged. The compressed-tiff extra supplies a TIFF decoder.
|
|
98
|
+
|
|
99
|
+
```sh
|
|
100
|
+
amt-validated recording.ome.tif --out baseline_results --config baseline_configuration.json
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
| Implementation | Modules | Source digest |
|
|
104
|
+
| --- | ---: | --- |
|
|
105
|
+
| automorphotrack.validated, core 1.1.0rc2 | 18 | 8a4125a5ebe2b49e019f79b783503f31403a24ea3134d2d10b1bd4610ac3bce8 |
|
|
106
|
+
| automorphotrack.refined, package 2.5.0 | 5 | dcacbe6eede1cfef2cfa165418f8e0c9428aafebf3632a27a6a7655e3605f3a6 |
|
|
107
|
+
|
|
108
|
+
## License and citation
|
|
109
|
+
|
|
110
|
+
Software: MIT. External datasets retain their individual licenses, recorded in the reproduction archive. See `CITATION.cff` for this release.
|
|
111
|
+
|
|
112
|
+
Bayati A, Schumacher JG, Chen X. AutoMorphoTrack. [eLife reviewed preprint 109936, version 1](https://doi.org/10.7554/eLife.109936.1). This is the older preprint, preceding the v2.5.0 particle revision. The revised manuscript is deposited with the current GitHub release.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# AutoMorphoTrack 2.5.0
|
|
2
|
+
|
|
3
|
+
AutoMorphoTrack 2.5.0 requires Python 3.11 or later and has two explicitly versioned analysis routes. The automorphotrack.validated namespace conserves the 18 modules of core 1.1.0rc2. The automorphotrack.refined namespace includes five additional modules that provide particle detection and association, described below, and make use of the unchanged baseline measurement modules. NumPy, pandas, SciPy, scikit-image, and tifffile are the main dependencies. The two implementations are distinguishable by their source, and examples show which route produced a given result.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
python -m pip install "automorphotrack==2.5.0"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Minimal Debian and Ubuntu systems may need libxcb1, libgl1, and libglib2.0-0 for the OpenCV import. The GitHub CI matrix completed wheel and source installations on Windows, Linux, and macOS with Python 3.11 and 3.12, and retained test reports, dependency versions, source checks, and known-motion outputs.
|
|
12
|
+
|
|
13
|
+
## Particle pipeline
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from automorphotrack.refined import RefinedConfig, run_pipeline
|
|
17
|
+
config = RefinedConfig(channels=(0, 1), sample_id="recording")
|
|
18
|
+
result = run_pipeline("recording.ome.tif", "new_results", config)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
amt-refined recording.ome.tif --out new_results --config configuration.json
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The particle example uses a two-channel synthetic movie with known motion. Run the guarded adapter from the release directory after installing the 2.5.0 wheel. It checks the 18 baseline and five particle modules before importing the numerical implementation. Use a new output directory for every run. For a static image, set min_observations to 1. Particle support masks have not been validated to be mitochondrial membrane boundaries. The default setting requires three observations, thus short-lived events will not be retained and retained masks and motion summaries will not reflect these. Raw outputs preserve these events.
|
|
26
|
+
|
|
27
|
+
The input must identify time, channel, and spatial axes. Calibration requires a recorded source; otherwise motion remains in pixels per frame. Label masks and track IDs are linked through each frame's Label column. A different configuration requires its own evaluation.
|
|
28
|
+
|
|
29
|
+
## Recorded validation
|
|
30
|
+
|
|
31
|
+
Six reserved movies contain 145 annotated trajectories and 2,764 adjacent reference links. Mean accepted-link recall for the frozen default and fixed top-hat configurations was 0.014 and 0.212, respectively. With the particle pipeline, mean accepted-link recall was 0.696, and mean localization recall was 0.768. The new pipeline recovered 2,024 accepted reference links, and four additional accepted links related to different annotated identities, giving conditional precision of 2,024/2,028 among the links with two reference-matched endpoints. Remaining predictions were left unverified. Thirteen trajectories were completely missed, and 33 were fully recovered with accepted links, compared to 41 missed and two fully recovered with the top-hat configuration. Accepted-link recall was 0.739 with a 5-pixel tolerance and 0.587 with a 2-pixel tolerance. Individual reserved movies yielded accepted-link recall from 0.408 to 0.911. Supplementary Fig. 10 and Table 9 show the paired comparisons and their shortcomings.
|
|
32
|
+
|
|
33
|
+
The original 50 synthetic sequences, when rerun, yielded mean Dice scores of 0.877, 0.752 and 0.532, for the new particle masks, the fixed top-hat configuration and the frozen default, respectively. Mean instance precision and accepted-link recall increased from 0.407 to 0.966 and 0.178 to 0.965, respectively. The two baselines reproduced their earlier results to within an absolute error of 10^-12. Transfer to the external mitochondrial masks was limited. Without the length filter, the selected detector yielded mean Dice scores of 0.369 and 0.060 for the seven MoDL SIM patches and the 13 Myofibril patches, respectively. The MoDL score was still below the earlier top-hat result of 0.421. The particle pipeline thus does not replace target-specific segmentation validation for elongated or densely packed mitochondria.
|
|
34
|
+
|
|
35
|
+
AutoMorphoTrack 2.5.0 passed 72 tests in each of 12 GitHub CI installations. It built wheels and source distributions for Windows, Linux, and macOS, with Python 3.11 and 3.12. All installations kept the 18 baseline and five particle source modules. Each test compared 19 baseline and 19 particle CSV or TIFFs with recorded references. Integer masks were exact, and for baseline and particle outputs, numerical tolerances were 10^-12 and 10^-9, respectively. The test failed initially on Windows due to Git line-ending conversion which changed the source hashes. Enforcing LF line endings solved this, and the numerical code remained the same. The OpenCV import failure and its system-library remedy documented for minimal Linux remain documented.
|
|
36
|
+
|
|
37
|
+
Supplementary figures and tables are in the manuscript files attached to [release v2.5.0](https://github.com/abayatibrain/AutoMorphoTrack/releases/tag/v2.5.0). The [reproduction archive](https://github.com/abayatibrain/AutoMorphoTrack/releases/download/v2.5.0/AMT_Revision_Reproducibility_v2.5.0.zip) includes the development protocol, every candidate result, reserved-movie outputs, controls, and prior analyses. [The 12-job CI run](https://github.com/abayatibrain/AutoMorphoTrack/actions/runs/43602083124) records Windows, Linux, and macOS installations.
|
|
38
|
+
|
|
39
|
+
Biological source: [Richens et al., PLOS Biology 2025](https://doi.org/10.1371/journal.pbio.3003099), [data version 1](https://doi.org/10.17863/CAM.114338), CC BY 4.0. All 12 movies had been assessed with the baseline before six were reserved from the new parameter selection. This is a within-study split. Manual annotations cover a subset of vesicles; unmatched predictions remain unverified.
|
|
40
|
+
|
|
41
|
+
## Reproducible integration
|
|
42
|
+
|
|
43
|
+
For the 2.5.0 route, Gemini wrote a new adapter and Codex ran the captured source against the installed wheel. The first adapter returned a file-hash mapping where the contract required a digest string. A focused change resolved that metadata error. The corrected adapter, the Codex adapter, a standalone command line, and an executed Jupyter notebook reproduced the 19 reference particle files exactly on the local host. Altered manifests and source files were rejected before analysis. The Codex guard verifies all 23 modules across both numerical namespaces. Prior to the requests, Argon High was selected in Gemini, but the interface changed its model label after the responses; the underlying model identity was not verified. These records test software integration, and numerical reproducibility, without constituting independent biological validation by either assistant.
|
|
44
|
+
|
|
45
|
+
The runnable adapter is `examples/refined_verified.py`. The notebook is `examples/Refined_Pipeline.ipynb`. Keep both source manifests under version control. Run a fixture after changing an adapter, configuration, preprocessing step, environment, or downstream calculation. Source integrity alone does not validate those changes.
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
python -I examples/refined_verified.py --input validation/refined_motion.ome.tif --out new_fixture_results --config validation/refined_motion_config.json --validation-dir validation
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Baseline route
|
|
52
|
+
|
|
53
|
+
Install the pinned 2.5.0 wheel in a virtual environment. Use amt-refined, or import RefinedConfig and run_pipeline from automorphotrack.refined, for the particle pipeline. The amt-validated command retains the baseline implementation for Figures 1-17 and Supplementary Figures 1-9. Supplementary Figure 10 reports the new particle evaluation. The automorphotrack and amt commands remain unchanged. The compressed-tiff extra supplies a TIFF decoder.
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
amt-validated recording.ome.tif --out baseline_results --config baseline_configuration.json
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
| Implementation | Modules | Source digest |
|
|
60
|
+
| --- | ---: | --- |
|
|
61
|
+
| automorphotrack.validated, core 1.1.0rc2 | 18 | 8a4125a5ebe2b49e019f79b783503f31403a24ea3134d2d10b1bd4610ac3bce8 |
|
|
62
|
+
| automorphotrack.refined, package 2.5.0 | 5 | dcacbe6eede1cfef2cfa165418f8e0c9428aafebf3632a27a6a7655e3605f3a6 |
|
|
63
|
+
|
|
64
|
+
## License and citation
|
|
65
|
+
|
|
66
|
+
Software: MIT. External datasets retain their individual licenses, recorded in the reproduction archive. See `CITATION.cff` for this release.
|
|
67
|
+
|
|
68
|
+
Bayati A, Schumacher JG, Chen X. AutoMorphoTrack. [eLife reviewed preprint 109936, version 1](https://doi.org/10.7554/eLife.109936.1). This is the older preprint, preceding the v2.5.0 particle revision. The revised manuscript is deposited with the current GitHub release.
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
# )
|
|
34
34
|
# ============================================================
|
|
35
35
|
|
|
36
|
-
__version__ = "2.
|
|
36
|
+
__version__ = "2.5.0"
|
|
37
37
|
__author__ = "Armin Bayati"
|
|
38
38
|
|
|
39
39
|
# --- Utility Imports ---
|
|
@@ -64,30 +64,45 @@ from automorphotrack.validation import (
|
|
|
64
64
|
validate_segmentation,
|
|
65
65
|
sensitivity_analysis,
|
|
66
66
|
validate_tracking,
|
|
67
|
-
generate_validation_report
|
|
67
|
+
generate_validation_report,
|
|
68
|
+
generate_synthetic_ground_truth,
|
|
68
69
|
)
|
|
69
70
|
|
|
71
|
+
# --- Adaptive segmentation backends (R2.4) ---
|
|
72
|
+
from automorphotrack.adaptive_segmentation import (
|
|
73
|
+
segment,
|
|
74
|
+
segment_otsu,
|
|
75
|
+
segment_sauvola,
|
|
76
|
+
segment_niblack,
|
|
77
|
+
segment_local_otsu,
|
|
78
|
+
segment_subtracted,
|
|
79
|
+
BACKENDS as SEGMENTATION_BACKENDS,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# --- Benchmarking against other tools (R1.2 / R2.3) ---
|
|
83
|
+
from automorphotrack import benchmarking
|
|
84
|
+
|
|
85
|
+
# --- Stats annotation helper (R2.5.iv) ---
|
|
86
|
+
from automorphotrack.utils import annotate_stats
|
|
87
|
+
|
|
70
88
|
# --- Convenience Alias (optional shortcut API) ---
|
|
71
89
|
__all__ = [
|
|
72
|
-
|
|
73
|
-
"save_high_dpi",
|
|
74
|
-
"
|
|
75
|
-
|
|
76
|
-
"detect_organelles",
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"track_organelles",
|
|
82
|
-
"track_overlay",
|
|
83
|
-
"analyze_motility",
|
|
84
|
-
"analyze_colocalization",
|
|
85
|
-
"summarize_integrated_data",
|
|
86
|
-
"analyze_temporal_dynamics",
|
|
87
|
-
"analyze_spatial_statistics",
|
|
90
|
+
# utilities
|
|
91
|
+
"ensure_dir", "save_high_dpi", "upscale_frame", "write_video",
|
|
92
|
+
"annotate_stats",
|
|
93
|
+
# pipeline
|
|
94
|
+
"detect_organelles", "count_lysosomes_per_frame",
|
|
95
|
+
"classify_morphology", "analyze_shape_features", "profile_shape_data",
|
|
96
|
+
"track_organelles", "track_overlay", "analyze_motility",
|
|
97
|
+
"analyze_colocalization", "summarize_integrated_data",
|
|
98
|
+
"analyze_temporal_dynamics", "analyze_spatial_statistics",
|
|
88
99
|
"analyze_network_topology",
|
|
89
|
-
|
|
90
|
-
"sensitivity_analysis",
|
|
91
|
-
"
|
|
92
|
-
|
|
100
|
+
# validation
|
|
101
|
+
"validate_segmentation", "sensitivity_analysis", "validate_tracking",
|
|
102
|
+
"generate_validation_report", "generate_synthetic_ground_truth",
|
|
103
|
+
# adaptive segmentation backends
|
|
104
|
+
"segment", "segment_otsu", "segment_sauvola", "segment_niblack",
|
|
105
|
+
"segment_local_otsu", "segment_subtracted", "SEGMENTATION_BACKENDS",
|
|
106
|
+
# benchmarking
|
|
107
|
+
"benchmarking",
|
|
93
108
|
]
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# ============================================================
|
|
2
|
+
# AutoMorphoTrack – Adaptive Segmentation Backends
|
|
3
|
+
# ============================================================
|
|
4
|
+
# Author: Armin Bayati, Ph.D.
|
|
5
|
+
#
|
|
6
|
+
# Reviewer #2 (R2.4) noted that pure Otsu thresholding plus morphological
|
|
7
|
+
# opening is sensitive to SNR, background heterogeneity, bleaching, and
|
|
8
|
+
# organelle density. This module provides drop-in alternative backends so
|
|
9
|
+
# users can swap the segmentation step without modifying the rest of the
|
|
10
|
+
# pipeline:
|
|
11
|
+
#
|
|
12
|
+
# - segment_otsu : the default global-threshold path
|
|
13
|
+
# - segment_sauvola : Sauvola adaptive thresholding for uneven background
|
|
14
|
+
# - segment_niblack : Niblack adaptive thresholding
|
|
15
|
+
# - segment_local_otsu : Otsu inside a sliding window for very large fields
|
|
16
|
+
# - segment_subtracted : rolling-ball / top-hat background subtraction
|
|
17
|
+
# followed by Otsu (handles bleaching/heterogeneity)
|
|
18
|
+
#
|
|
19
|
+
# All backends expose the same signature::
|
|
20
|
+
#
|
|
21
|
+
# mask = segment_<name>(image, **kwargs)
|
|
22
|
+
#
|
|
23
|
+
# where ``image`` is a 2D float array in [0, 1] and ``mask`` is a 2D bool
|
|
24
|
+
# array. This lets the validation.sensitivity_analysis sweep run against any
|
|
25
|
+
# of them.
|
|
26
|
+
# ============================================================
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
import numpy as np
|
|
31
|
+
from skimage.filters import (
|
|
32
|
+
threshold_otsu, threshold_sauvola, threshold_niblack, threshold_local
|
|
33
|
+
)
|
|
34
|
+
from skimage.morphology import (
|
|
35
|
+
remove_small_objects, binary_opening, disk, white_tophat
|
|
36
|
+
)
|
|
37
|
+
from skimage.segmentation import clear_border
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _clean(mask: np.ndarray, min_size: int = 3,
|
|
41
|
+
opening_radius: int = 1,
|
|
42
|
+
clear_borders: bool = True) -> np.ndarray:
|
|
43
|
+
if opening_radius > 0:
|
|
44
|
+
mask = binary_opening(mask, footprint=disk(opening_radius))
|
|
45
|
+
if clear_borders:
|
|
46
|
+
mask = clear_border(mask)
|
|
47
|
+
if min_size > 0:
|
|
48
|
+
mask = remove_small_objects(mask, max(1, int(min_size)))
|
|
49
|
+
return mask.astype(bool)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def segment_otsu(image: np.ndarray, thr_factor: float = 0.8,
|
|
53
|
+
min_size: int = 3, **clean_kwargs) -> np.ndarray:
|
|
54
|
+
"""Global Otsu (the default path), exposed for completeness."""
|
|
55
|
+
thr = threshold_otsu(image) * thr_factor
|
|
56
|
+
return _clean(image > thr, min_size=min_size, **clean_kwargs)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def segment_sauvola(image: np.ndarray, window_size: int = 25,
|
|
60
|
+
k: float = 0.2, min_size: int = 3,
|
|
61
|
+
**clean_kwargs) -> np.ndarray:
|
|
62
|
+
"""Sauvola adaptive thresholding (handles uneven illumination)."""
|
|
63
|
+
if window_size % 2 == 0:
|
|
64
|
+
window_size += 1
|
|
65
|
+
thr_map = threshold_sauvola(image, window_size=window_size, k=k)
|
|
66
|
+
return _clean(image > thr_map, min_size=min_size, **clean_kwargs)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def segment_niblack(image: np.ndarray, window_size: int = 25,
|
|
70
|
+
k: float = 0.2, min_size: int = 3,
|
|
71
|
+
**clean_kwargs) -> np.ndarray:
|
|
72
|
+
"""Niblack adaptive thresholding."""
|
|
73
|
+
if window_size % 2 == 0:
|
|
74
|
+
window_size += 1
|
|
75
|
+
thr_map = threshold_niblack(image, window_size=window_size, k=k)
|
|
76
|
+
return _clean(image > thr_map, min_size=min_size, **clean_kwargs)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def segment_local_otsu(image: np.ndarray, block_size: int = 51,
|
|
80
|
+
offset: float = 0.0, min_size: int = 3,
|
|
81
|
+
**clean_kwargs) -> np.ndarray:
|
|
82
|
+
"""Tile-wise local Otsu via skimage.filters.threshold_local."""
|
|
83
|
+
if block_size % 2 == 0:
|
|
84
|
+
block_size += 1
|
|
85
|
+
thr_map = threshold_local(image, block_size=block_size, offset=offset)
|
|
86
|
+
return _clean(image > thr_map, min_size=min_size, **clean_kwargs)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def segment_subtracted(image: np.ndarray, background_radius: int = 25,
|
|
90
|
+
thr_factor: float = 0.8, min_size: int = 3,
|
|
91
|
+
**clean_kwargs) -> np.ndarray:
|
|
92
|
+
"""Top-hat background subtraction + Otsu.
|
|
93
|
+
|
|
94
|
+
Equivalent to ImageJ/FIJI's rolling-ball background subtraction step. Use
|
|
95
|
+
when the field of view has slow intensity gradients from out-of-focus
|
|
96
|
+
haze, photobleaching, or uneven illumination.
|
|
97
|
+
"""
|
|
98
|
+
bg_subtracted = white_tophat(image, footprint=disk(background_radius))
|
|
99
|
+
if bg_subtracted.max() <= 0:
|
|
100
|
+
return np.zeros_like(image, dtype=bool)
|
|
101
|
+
bg_subtracted = bg_subtracted / bg_subtracted.max()
|
|
102
|
+
thr = threshold_otsu(bg_subtracted) * thr_factor
|
|
103
|
+
return _clean(bg_subtracted > thr, min_size=min_size, **clean_kwargs)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
BACKENDS = {
|
|
107
|
+
"otsu": segment_otsu,
|
|
108
|
+
"sauvola": segment_sauvola,
|
|
109
|
+
"niblack": segment_niblack,
|
|
110
|
+
"local_otsu": segment_local_otsu,
|
|
111
|
+
"subtracted": segment_subtracted,
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def segment(image: np.ndarray, backend: str = "otsu", **kwargs) -> np.ndarray:
|
|
116
|
+
"""Dispatch to a named backend. ``backend`` is one of ``BACKENDS``."""
|
|
117
|
+
if backend not in BACKENDS:
|
|
118
|
+
raise ValueError(
|
|
119
|
+
f"Unknown backend '{backend}'. Choices: {sorted(BACKENDS)}"
|
|
120
|
+
)
|
|
121
|
+
return BACKENDS[backend](image, **kwargs)
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# ============================================================
|
|
2
|
+
# AutoMorphoTrack – Benchmarking & Comparative Positioning
|
|
3
|
+
# ============================================================
|
|
4
|
+
# Author: Armin Bayati, Ph.D.
|
|
5
|
+
#
|
|
6
|
+
# Reviewer #1 (R1.2) and Reviewer #2 (R2.3) asked for explicit comparison to
|
|
7
|
+
# established tools: CellProfiler, MiNA, MitoGraph, Mitochondria Analyzer,
|
|
8
|
+
# and learning-based tools like CellPose / StarDist.
|
|
9
|
+
#
|
|
10
|
+
# AutoMorphoTrack does not re-implement those packages — that would
|
|
11
|
+
# duplicate excellent existing work. Instead, this module:
|
|
12
|
+
#
|
|
13
|
+
# 1. Provides exporters that translate AMT outputs into the CSV layouts
|
|
14
|
+
# those tools consume / produce, so users can directly compare results
|
|
15
|
+
# side-by-side.
|
|
16
|
+
# 2. Provides importers for the other tools' outputs so AMT can compute
|
|
17
|
+
# Dice/IoU/Pearson agreement between its segmentations and theirs.
|
|
18
|
+
# 3. Provides a "comparison table" generator that summarises pairwise
|
|
19
|
+
# agreement across morphology metrics, useful as supplementary material
|
|
20
|
+
# in publications.
|
|
21
|
+
#
|
|
22
|
+
# The supported tools are:
|
|
23
|
+
# - CellProfiler (CSV per object)
|
|
24
|
+
# - MiNA (FIJI plugin; tabular network stats)
|
|
25
|
+
# - MitoGraph (.gnet / .csv per cell)
|
|
26
|
+
# - Mitochondria Analyzer (FIJI; per-image summary)
|
|
27
|
+
# - CellPose / StarDist (label-image masks, np.uint16)
|
|
28
|
+
#
|
|
29
|
+
# Each importer is tolerant: it tries to find the relevant columns by
|
|
30
|
+
# regex-matching common naming conventions so the user does not have to
|
|
31
|
+
# rename anything by hand.
|
|
32
|
+
# ============================================================
|
|
33
|
+
|
|
34
|
+
from __future__ import annotations
|
|
35
|
+
|
|
36
|
+
import re
|
|
37
|
+
from pathlib import Path
|
|
38
|
+
from typing import Mapping
|
|
39
|
+
|
|
40
|
+
import numpy as np
|
|
41
|
+
import pandas as pd
|
|
42
|
+
|
|
43
|
+
from automorphotrack.validation import validate_segmentation
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# ------------------------------------------------------------------
|
|
47
|
+
# AMT → other tools (exporters)
|
|
48
|
+
# ------------------------------------------------------------------
|
|
49
|
+
def export_for_cellprofiler(shape_metrics_csv: str | Path,
|
|
50
|
+
out_path: str | Path) -> Path:
|
|
51
|
+
"""Write a CSV in a CellProfiler-friendly schema.
|
|
52
|
+
|
|
53
|
+
CellProfiler MeasureObjectSizeShape output uses columns like
|
|
54
|
+
``AreaShape_Area``, ``AreaShape_Eccentricity``, etc. We map AMT columns
|
|
55
|
+
to that schema so users can drop the CSV into a CellProfiler analysis
|
|
56
|
+
workflow without rewriting downstream scripts.
|
|
57
|
+
"""
|
|
58
|
+
df = pd.read_csv(shape_metrics_csv)
|
|
59
|
+
mapping = {
|
|
60
|
+
"Area": "AreaShape_Area",
|
|
61
|
+
"Eccentricity": "AreaShape_Eccentricity",
|
|
62
|
+
"Solidity": "AreaShape_Solidity",
|
|
63
|
+
"Circularity": "AreaShape_FormFactor", # CellProfiler's name
|
|
64
|
+
"Aspect_Ratio": "AreaShape_MajorAxisLength_over_MinorAxisLength",
|
|
65
|
+
"Orientation": "AreaShape_Orientation",
|
|
66
|
+
"Frame": "Image_Metadata_Frame",
|
|
67
|
+
}
|
|
68
|
+
df = df.rename(columns={k: v for k, v in mapping.items() if k in df.columns})
|
|
69
|
+
df["ImageNumber"] = df.get("Image_Metadata_Frame", 0) + 1
|
|
70
|
+
df["ObjectNumber"] = range(1, len(df) + 1)
|
|
71
|
+
out_path = Path(out_path)
|
|
72
|
+
df.to_csv(out_path, index=False)
|
|
73
|
+
return out_path
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def export_for_mina(shape_metrics_csv: str | Path,
|
|
77
|
+
out_path: str | Path) -> Path:
|
|
78
|
+
"""Write a CSV in MiNA-style per-mitochondrion network table.
|
|
79
|
+
|
|
80
|
+
MiNA reports ``Mean Length``, ``Mean Area``, ``Footprint``, ``Mean
|
|
81
|
+
Branch Length`` — AMT does not compute branch topology, so the columns
|
|
82
|
+
we cannot fill are left blank (NaN). This keeps the schema compatible
|
|
83
|
+
while making the gap explicit.
|
|
84
|
+
"""
|
|
85
|
+
df = pd.read_csv(shape_metrics_csv)
|
|
86
|
+
out = pd.DataFrame({
|
|
87
|
+
"Frame": df.get("Frame"),
|
|
88
|
+
"Mean Length": np.nan,
|
|
89
|
+
"Mean Area": df.get("Area"),
|
|
90
|
+
"Footprint": df.get("Area"),
|
|
91
|
+
"Mean Branch Length": np.nan,
|
|
92
|
+
"Eccentricity": df.get("Eccentricity"),
|
|
93
|
+
"Solidity": df.get("Solidity"),
|
|
94
|
+
"Form Factor": df.get("Circularity"),
|
|
95
|
+
})
|
|
96
|
+
out_path = Path(out_path)
|
|
97
|
+
out.to_csv(out_path, index=False)
|
|
98
|
+
return out_path
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def export_for_mitograph(shape_metrics_csv: str | Path,
|
|
102
|
+
out_path: str | Path) -> Path:
|
|
103
|
+
"""Write a MitoGraph-style per-cell summary CSV."""
|
|
104
|
+
df = pd.read_csv(shape_metrics_csv)
|
|
105
|
+
grouped = df.groupby("Frame").agg(
|
|
106
|
+
total_area=("Area", "sum"),
|
|
107
|
+
mean_area=("Area", "mean"),
|
|
108
|
+
n_objects=("Area", "size"),
|
|
109
|
+
mean_aspect_ratio=("Aspect_Ratio", "mean"),
|
|
110
|
+
mean_solidity=("Solidity", "mean"),
|
|
111
|
+
).reset_index()
|
|
112
|
+
grouped.columns = [
|
|
113
|
+
"Image", "Total_Volume_um3", "Mean_Volume_um3",
|
|
114
|
+
"N_Mitochondria", "Mean_Aspect_Ratio", "Mean_Solidity",
|
|
115
|
+
]
|
|
116
|
+
out_path = Path(out_path)
|
|
117
|
+
grouped.to_csv(out_path, index=False)
|
|
118
|
+
return out_path
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
# ------------------------------------------------------------------
|
|
122
|
+
# Other tools → AMT (importers + agreement)
|
|
123
|
+
# ------------------------------------------------------------------
|
|
124
|
+
def load_cellprofiler_csv(path: str | Path) -> pd.DataFrame:
|
|
125
|
+
"""Parse a CellProfiler ``Per_Object.csv`` into AMT's shape-metric schema."""
|
|
126
|
+
df = pd.read_csv(path)
|
|
127
|
+
rename = {}
|
|
128
|
+
for c in df.columns:
|
|
129
|
+
if re.fullmatch(r"AreaShape_Area", c): rename[c] = "Area"
|
|
130
|
+
elif re.fullmatch(r"AreaShape_Eccentricity", c): rename[c] = "Eccentricity"
|
|
131
|
+
elif re.fullmatch(r"AreaShape_Solidity", c): rename[c] = "Solidity"
|
|
132
|
+
elif re.fullmatch(r"AreaShape_FormFactor", c): rename[c] = "Circularity"
|
|
133
|
+
elif re.fullmatch(r"AreaShape_Orientation", c): rename[c] = "Orientation"
|
|
134
|
+
elif "MajorAxisLength_over_MinorAxisLength" in c: rename[c] = "Aspect_Ratio"
|
|
135
|
+
elif "Frame" in c: rename[c] = "Frame"
|
|
136
|
+
return df.rename(columns=rename)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def load_label_mask(path: str | Path) -> np.ndarray:
|
|
140
|
+
"""Load a CellPose / StarDist label image as a binary mask."""
|
|
141
|
+
import tifffile
|
|
142
|
+
img = tifffile.imread(str(path))
|
|
143
|
+
return (img > 0).astype(bool)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def agreement(amt_mask: np.ndarray, other_mask: np.ndarray) -> Mapping[str, float]:
|
|
147
|
+
"""Compute Dice / IoU / precision / recall / F1 between two binary masks."""
|
|
148
|
+
return validate_segmentation(amt_mask.astype(bool), other_mask.astype(bool))
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def comparison_table(amt_shape_csv: str | Path,
|
|
152
|
+
other_shape_csv: str | Path,
|
|
153
|
+
other_tool_name: str = "Other") -> pd.DataFrame:
|
|
154
|
+
"""Build a side-by-side morphology comparison table.
|
|
155
|
+
|
|
156
|
+
Per-metric this reports: mean ± std for AMT, mean ± std for the other
|
|
157
|
+
tool, the Spearman rank correlation between matched objects (where
|
|
158
|
+
matching is by ``Frame`` and row order), and a percentage absolute
|
|
159
|
+
difference of the means.
|
|
160
|
+
"""
|
|
161
|
+
from scipy.stats import spearmanr
|
|
162
|
+
|
|
163
|
+
a = pd.read_csv(amt_shape_csv)
|
|
164
|
+
b = pd.read_csv(other_shape_csv)
|
|
165
|
+
metrics = ["Area", "Eccentricity", "Solidity", "Circularity", "Aspect_Ratio"]
|
|
166
|
+
rows = []
|
|
167
|
+
for m in metrics:
|
|
168
|
+
if m not in a.columns or m not in b.columns:
|
|
169
|
+
continue
|
|
170
|
+
av, bv = a[m].dropna(), b[m].dropna()
|
|
171
|
+
n = min(len(av), len(bv))
|
|
172
|
+
if n < 3:
|
|
173
|
+
continue
|
|
174
|
+
rho, p = spearmanr(av.iloc[:n], bv.iloc[:n])
|
|
175
|
+
mean_a, mean_b = av.mean(), bv.mean()
|
|
176
|
+
pct_diff = 100 * abs(mean_a - mean_b) / max(abs(mean_a), abs(mean_b), 1e-9)
|
|
177
|
+
rows.append({
|
|
178
|
+
"Metric": m,
|
|
179
|
+
"AMT_mean": round(mean_a, 4),
|
|
180
|
+
"AMT_std": round(av.std(), 4),
|
|
181
|
+
f"{other_tool_name}_mean": round(mean_b, 4),
|
|
182
|
+
f"{other_tool_name}_std": round(bv.std(), 4),
|
|
183
|
+
"Spearman_rho": round(float(rho), 4),
|
|
184
|
+
"p_value": round(float(p), 6),
|
|
185
|
+
"Percent_diff_means": round(pct_diff, 2),
|
|
186
|
+
"N": n,
|
|
187
|
+
})
|
|
188
|
+
return pd.DataFrame(rows)
|