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.
Files changed (131) hide show
  1. automorphotrack-2.5.0/CITATION.cff +12 -0
  2. automorphotrack-2.5.0/MANIFEST.in +12 -0
  3. automorphotrack-2.5.0/PKG-INFO +112 -0
  4. automorphotrack-2.5.0/README.md +68 -0
  5. {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack/__init__.py +37 -22
  6. automorphotrack-2.5.0/automorphotrack/adaptive_segmentation.py +121 -0
  7. automorphotrack-2.5.0/automorphotrack/benchmarking.py +188 -0
  8. automorphotrack-2.5.0/automorphotrack/cli.py +250 -0
  9. {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack/colocalization.py +38 -14
  10. automorphotrack-2.5.0/automorphotrack/mcp_server.py +272 -0
  11. {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack/motility.py +51 -1
  12. automorphotrack-2.5.0/automorphotrack/napari.yaml +35 -0
  13. automorphotrack-2.5.0/automorphotrack/napari_plugin.py +272 -0
  14. automorphotrack-2.5.0/automorphotrack/refined/__init__.py +8 -0
  15. automorphotrack-2.5.0/automorphotrack/refined/__main__.py +20 -0
  16. automorphotrack-2.5.0/automorphotrack/refined/pipeline.py +134 -0
  17. automorphotrack-2.5.0/automorphotrack/refined/segmentation.py +85 -0
  18. automorphotrack-2.5.0/automorphotrack/refined/tracking.py +93 -0
  19. {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack/shape_profiling.py +19 -4
  20. automorphotrack-2.5.0/automorphotrack/utils.py +127 -0
  21. automorphotrack-2.5.0/automorphotrack/validated/__init__.py +20 -0
  22. automorphotrack-2.5.0/automorphotrack/validated/__main__.py +22 -0
  23. automorphotrack-2.5.0/automorphotrack/validated/colocalization.py +11 -0
  24. automorphotrack-2.5.0/automorphotrack/validated/config.py +88 -0
  25. automorphotrack-2.5.0/automorphotrack/validated/core.py +225 -0
  26. automorphotrack-2.5.0/automorphotrack/validated/detection.py +22 -0
  27. automorphotrack-2.5.0/automorphotrack/validated/io.py +66 -0
  28. automorphotrack-2.5.0/automorphotrack/validated/lyso_count.py +11 -0
  29. automorphotrack-2.5.0/automorphotrack/validated/morphology.py +11 -0
  30. automorphotrack-2.5.0/automorphotrack/validated/motility.py +25 -0
  31. automorphotrack-2.5.0/automorphotrack/validated/pipeline.py +195 -0
  32. automorphotrack-2.5.0/automorphotrack/validated/plotting.py +57 -0
  33. automorphotrack-2.5.0/automorphotrack/validated/shape_features.py +22 -0
  34. automorphotrack-2.5.0/automorphotrack/validated/shape_profiling.py +16 -0
  35. automorphotrack-2.5.0/automorphotrack/validated/summary.py +36 -0
  36. automorphotrack-2.5.0/automorphotrack/validated/tracking.py +11 -0
  37. automorphotrack-2.5.0/automorphotrack/validated/tracking_overlay.py +11 -0
  38. automorphotrack-2.5.0/automorphotrack/validated/utils.py +39 -0
  39. {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack/validation.py +138 -20
  40. automorphotrack-2.5.0/automorphotrack.egg-info/PKG-INFO +112 -0
  41. automorphotrack-2.5.0/automorphotrack.egg-info/SOURCES.txt +121 -0
  42. automorphotrack-2.5.0/automorphotrack.egg-info/entry_points.txt +10 -0
  43. automorphotrack-2.5.0/automorphotrack.egg-info/requires.txt +25 -0
  44. automorphotrack-2.5.0/docs/GUI.md +75 -0
  45. automorphotrack-2.5.0/docs/MCP.md +82 -0
  46. automorphotrack-2.5.0/docs/PARTICLE_VALIDATION.md +15 -0
  47. automorphotrack-2.1.0/automorphotrack.egg-info/PKG-INFO → automorphotrack-2.5.0/docs/V2_INTERFACE.md +93 -53
  48. automorphotrack-2.5.0/docs/VALIDATED_CORE.md +14 -0
  49. automorphotrack-2.5.0/docs/reviewer_concerns.md +40 -0
  50. automorphotrack-2.5.0/examples/Refined_Pipeline.ipynb +71 -0
  51. automorphotrack-2.5.0/examples/refined_verified.py +52 -0
  52. automorphotrack-2.5.0/pyproject.toml +68 -0
  53. automorphotrack-2.5.0/setup.py +3 -0
  54. automorphotrack-2.5.0/tests/__init__.py +0 -0
  55. automorphotrack-2.5.0/tests/test_benchmarking.py +61 -0
  56. automorphotrack-2.5.0/tests/test_gui_import.py +55 -0
  57. automorphotrack-2.5.0/tests/test_install_and_imports.py +41 -0
  58. automorphotrack-2.5.0/tests/test_refined.py +141 -0
  59. automorphotrack-2.5.0/tests/test_validated_revision.py +223 -0
  60. automorphotrack-2.5.0/tests/test_validation_and_segmentation.py +74 -0
  61. automorphotrack-2.5.0/validation/api_reconciliation.json +28 -0
  62. automorphotrack-2.5.0/validation/check_installed_core.py +52 -0
  63. automorphotrack-2.5.0/validation/core_manifest.json +28 -0
  64. automorphotrack-2.5.0/validation/known_motion.ome.tif +0 -0
  65. automorphotrack-2.5.0/validation/known_motion_config.json +40 -0
  66. automorphotrack-2.5.0/validation/known_motion_expected/Colocalization.csv +5 -0
  67. automorphotrack-2.5.0/validation/known_motion_expected/Frame_Correlation_N.csv +20 -0
  68. automorphotrack-2.5.0/validation/known_motion_expected/Frame_Descriptive_Spearman.csv +20 -0
  69. automorphotrack-2.5.0/validation/known_motion_expected/Frame_Summary.csv +5 -0
  70. automorphotrack-2.5.0/validation/known_motion_expected/Lyso_InstanceLabels.tif +0 -0
  71. automorphotrack-2.5.0/validation/known_motion_expected/Lyso_Masks.tif +0 -0
  72. automorphotrack-2.5.0/validation/known_motion_expected/Lyso_MotilitySteps.csv +5 -0
  73. automorphotrack-2.5.0/validation/known_motion_expected/Lyso_MotilitySummary.csv +2 -0
  74. automorphotrack-2.5.0/validation/known_motion_expected/Lyso_ShapeMetrics.csv +5 -0
  75. automorphotrack-2.5.0/validation/known_motion_expected/Lyso_Tracks.csv +5 -0
  76. automorphotrack-2.5.0/validation/known_motion_expected/Mito_InstanceLabels.tif +0 -0
  77. automorphotrack-2.5.0/validation/known_motion_expected/Mito_Masks.tif +0 -0
  78. automorphotrack-2.5.0/validation/known_motion_expected/Mito_MotilitySteps.csv +5 -0
  79. automorphotrack-2.5.0/validation/known_motion_expected/Mito_MotilitySummary.csv +2 -0
  80. automorphotrack-2.5.0/validation/known_motion_expected/Mito_ShapeMetrics.csv +5 -0
  81. automorphotrack-2.5.0/validation/known_motion_expected/Mito_Tracks.csv +5 -0
  82. automorphotrack-2.5.0/validation/known_motion_expected/Motility_PerFrame.csv +9 -0
  83. automorphotrack-2.5.0/validation/known_motion_expected/Sample_Summary.csv +2 -0
  84. automorphotrack-2.5.0/validation/known_motion_expected/Thresholds.csv +9 -0
  85. automorphotrack-2.5.0/validation/known_motion_truth.json +61 -0
  86. automorphotrack-2.5.0/validation/platform_check.py +108 -0
  87. automorphotrack-2.5.0/validation/refined_manifest.json +71 -0
  88. automorphotrack-2.5.0/validation/refined_motion.ome.tif +0 -0
  89. automorphotrack-2.5.0/validation/refined_motion_config.json +28 -0
  90. automorphotrack-2.5.0/validation/refined_motion_expected/channel_0_labels.tif +0 -0
  91. automorphotrack-2.5.0/validation/refined_motion_expected/channel_0_motility.csv +3 -0
  92. automorphotrack-2.5.0/validation/refined_motion_expected/channel_0_motility_per_frame.csv +6 -0
  93. automorphotrack-2.5.0/validation/refined_motion_expected/channel_0_raw_labels.tif +0 -0
  94. automorphotrack-2.5.0/validation/refined_motion_expected/channel_0_raw_tracks.csv +11 -0
  95. automorphotrack-2.5.0/validation/refined_motion_expected/channel_0_shapes.csv +11 -0
  96. automorphotrack-2.5.0/validation/refined_motion_expected/channel_0_steps.csv +11 -0
  97. automorphotrack-2.5.0/validation/refined_motion_expected/channel_0_thresholds.csv +6 -0
  98. automorphotrack-2.5.0/validation/refined_motion_expected/channel_0_tracks.csv +11 -0
  99. automorphotrack-2.5.0/validation/refined_motion_expected/channel_1_labels.tif +0 -0
  100. automorphotrack-2.5.0/validation/refined_motion_expected/channel_1_motility.csv +3 -0
  101. automorphotrack-2.5.0/validation/refined_motion_expected/channel_1_motility_per_frame.csv +6 -0
  102. automorphotrack-2.5.0/validation/refined_motion_expected/channel_1_raw_labels.tif +0 -0
  103. automorphotrack-2.5.0/validation/refined_motion_expected/channel_1_raw_tracks.csv +11 -0
  104. automorphotrack-2.5.0/validation/refined_motion_expected/channel_1_shapes.csv +11 -0
  105. automorphotrack-2.5.0/validation/refined_motion_expected/channel_1_steps.csv +11 -0
  106. automorphotrack-2.5.0/validation/refined_motion_expected/channel_1_thresholds.csv +6 -0
  107. automorphotrack-2.5.0/validation/refined_motion_expected/channel_1_tracks.csv +11 -0
  108. automorphotrack-2.5.0/validation/refined_motion_expected/colocalization.csv +6 -0
  109. automorphotrack-2.5.0/validation/refined_motion_expected/run_manifest.json +99 -0
  110. automorphotrack-2.1.0/MANIFEST.in +0 -4
  111. automorphotrack-2.1.0/PKG-INFO +0 -258
  112. automorphotrack-2.1.0/README.md +0 -222
  113. automorphotrack-2.1.0/automorphotrack/utils.py +0 -37
  114. automorphotrack-2.1.0/automorphotrack.egg-info/SOURCES.txt +0 -26
  115. automorphotrack-2.1.0/automorphotrack.egg-info/requires.txt +0 -8
  116. automorphotrack-2.1.0/pyproject.toml +0 -40
  117. automorphotrack-2.1.0/setup.py +0 -46
  118. {automorphotrack-2.1.0 → automorphotrack-2.5.0}/LICENSE.md +0 -0
  119. {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack/detection.py +0 -0
  120. {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack/lyso_count.py +0 -0
  121. {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack/morphology.py +0 -0
  122. {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack/network_analysis.py +0 -0
  123. {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack/shape_features.py +0 -0
  124. {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack/spatial_statistics.py +0 -0
  125. {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack/summary.py +0 -0
  126. {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack/temporal_dynamics.py +0 -0
  127. {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack/tracking.py +0 -0
  128. {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack/tracking_overlay.py +0 -0
  129. {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack.egg-info/dependency_links.txt +0 -0
  130. {automorphotrack-2.1.0 → automorphotrack-2.5.0}/automorphotrack.egg-info/top_level.txt +0 -0
  131. {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.1.0"
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
- "ensure_dir",
73
- "save_high_dpi",
74
- "upscale_frame",
75
- "write_video",
76
- "detect_organelles",
77
- "count_lysosomes_per_frame",
78
- "classify_morphology",
79
- "analyze_shape_features",
80
- "profile_shape_data",
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
- "validate_segmentation",
90
- "sensitivity_analysis",
91
- "validate_tracking",
92
- "generate_validation_report"
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)