bone-mechanoregulation 0.1.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 (25) hide show
  1. bone_mechanoregulation-0.1.0/PKG-INFO +271 -0
  2. bone_mechanoregulation-0.1.0/README.md +244 -0
  3. bone_mechanoregulation-0.1.0/bone_mechanoregulation.egg-info/PKG-INFO +271 -0
  4. bone_mechanoregulation-0.1.0/bone_mechanoregulation.egg-info/SOURCES.txt +23 -0
  5. bone_mechanoregulation-0.1.0/bone_mechanoregulation.egg-info/dependency_links.txt +1 -0
  6. bone_mechanoregulation-0.1.0/bone_mechanoregulation.egg-info/entry_points.txt +2 -0
  7. bone_mechanoregulation-0.1.0/bone_mechanoregulation.egg-info/requires.txt +14 -0
  8. bone_mechanoregulation-0.1.0/bone_mechanoregulation.egg-info/top_level.txt +1 -0
  9. bone_mechanoregulation-0.1.0/bonemechreg/__init__.py +16 -0
  10. bone_mechanoregulation-0.1.0/bonemechreg/cli.py +56 -0
  11. bone_mechanoregulation-0.1.0/bonemechreg/mechreg.py +1281 -0
  12. bone_mechanoregulation-0.1.0/bonemechreg/parosol.py +88 -0
  13. bone_mechanoregulation-0.1.0/bonemechreg/post_timelapse.py +192 -0
  14. bone_mechanoregulation-0.1.0/bonemechreg/results.py +217 -0
  15. bone_mechanoregulation-0.1.0/bonemechreg/standalone.py +493 -0
  16. bone_mechanoregulation-0.1.0/bonemechreg/timelapse.py +149 -0
  17. bone_mechanoregulation-0.1.0/pyproject.toml +49 -0
  18. bone_mechanoregulation-0.1.0/setup.cfg +4 -0
  19. bone_mechanoregulation-0.1.0/tests/test_cli_run.py +55 -0
  20. bone_mechanoregulation-0.1.0/tests/test_mechreg_module.py +551 -0
  21. bone_mechanoregulation-0.1.0/tests/test_package_surface.py +17 -0
  22. bone_mechanoregulation-0.1.0/tests/test_parosol_solver.py +80 -0
  23. bone_mechanoregulation-0.1.0/tests/test_post_timelapse_workflow.py +227 -0
  24. bone_mechanoregulation-0.1.0/tests/test_standalone.py +151 -0
  25. bone_mechanoregulation-0.1.0/tests/test_timelapse_dataset.py +97 -0
@@ -0,0 +1,271 @@
1
+ Metadata-Version: 2.4
2
+ Name: bone-mechanoregulation
3
+ Version: 0.1.0
4
+ Summary: Post-timelapse mechanoregulation addon for HR-pQCT datasets
5
+ Author: Matthias Walle
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/wallematthias/BoneMechanoregulation
8
+ Project-URL: Issues, https://github.com/wallematthias/BoneMechanoregulation/issues
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Science/Research
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
16
+ Requires-Python: <3.14,>=3.11
17
+ Description-Content-Type: text/markdown
18
+ Requires-Dist: numpy<2.0,>=1.26; python_version < "3.13"
19
+ Requires-Dist: numpy<3.0,>=2.1; python_version >= "3.13"
20
+ Requires-Dist: matplotlib>=3.7
21
+ Requires-Dist: parosol-py>=0.1.13
22
+ Requires-Dist: scipy>=1.10
23
+ Requires-Dist: SimpleITK>=2.2
24
+ Provides-Extra: test
25
+ Requires-Dist: pytest; extra == "test"
26
+ Requires-Dist: pytest-cov; extra == "test"
27
+
28
+ # BoneMechanoregulation
29
+
30
+ Post-timelapse mechanoregulation analysis for HR-pQCT datasets.
31
+
32
+ This package is a downstream addon. Run `TimelapsedHRpQCT` first, then run
33
+ BoneMechanoregulation on the Timelapsed output dataset root. The addon reuses
34
+ the Timelapsed pairwise remodelling label images, solves baseline SED from the
35
+ native baseline segmentation, and writes mechanoregulation summaries next to
36
+ each pairwise case.
37
+
38
+ ## Prerequisite
39
+
40
+ First create TimelapsedHRpQCT remodelling outputs.
41
+
42
+ With the core TimelapsedHRpQCT CLI, the pipeline convention is:
43
+
44
+ ```bash
45
+ timelapsedhrpqct run /path/to/raw_aim_input --output-root /path/to/TimelapsedHRpQCT
46
+ ```
47
+
48
+ In the 3D Slicer toolbox, use the `Timelapsed HR-pQCT` module:
49
+
50
+ 1. select the AIM dataset root;
51
+ 2. choose a results folder, defaulting to `<dataset_root>/TimelapsedHRpQCT`;
52
+ 3. click `Run pipeline`;
53
+ 4. inspect the generated remodelling images.
54
+
55
+ The Slicer wrapper is the recommended route for interactive preparation, mask
56
+ review, and visual QA before running this mechanoregulation addon.
57
+
58
+ ## Install
59
+
60
+ From PyPI:
61
+
62
+ ```bash
63
+ pip install bone-mechanoregulation
64
+ ```
65
+
66
+ For local development:
67
+
68
+ ```bash
69
+ pip install -e .
70
+ ```
71
+
72
+ `parosol-py>=0.1.13` must be available in the same Python environment. The
73
+ mechanoregulation workflow writes a standard ParOSol material-label input from
74
+ the native baseline segmentation, then calls the native `parosol-py`
75
+ XtremeCTI/XtremeCTII profile for the finite-element solve.
76
+
77
+ ## Install In 3D Slicer
78
+
79
+ BoneMechanoregulation can also be installed into Slicer's Python environment so
80
+ the same command-line analysis is available from Slicer-side workflows or
81
+ scripted modules.
82
+
83
+ From Slicer's Python interactor or a Slicer Python shell:
84
+
85
+ ```python
86
+ import sys
87
+ import subprocess
88
+
89
+ subprocess.check_call([
90
+ sys.executable,
91
+ "-m",
92
+ "pip",
93
+ "install",
94
+ "bone-mechanoregulation",
95
+ ])
96
+ ```
97
+
98
+ For local development from a checkout:
99
+
100
+ ```python
101
+ import sys
102
+ import subprocess
103
+
104
+ subprocess.check_call([
105
+ sys.executable,
106
+ "-m",
107
+ "pip",
108
+ "install",
109
+ "-e",
110
+ "/path/to/BoneMechanoregulation",
111
+ ])
112
+ ```
113
+
114
+ The package supports the same Python version range used by the current
115
+ TimelapsedHRpQCT packaging matrix: Python 3.11, 3.12, and 3.13. If a Slicer
116
+ release ships an older Python, use an external Python environment for the batch
117
+ analysis or install through the matching Slicer toolbox once it provides a
118
+ compatible runtime.
119
+
120
+ ## Batch Usage
121
+
122
+ Run this after TimelapsedHRpQCT has produced its derivative tree:
123
+
124
+ ```bash
125
+ mechanoregulation run \
126
+ /path/to/TimelapsedHRpQCT \
127
+ --profile XtremeCTII
128
+ ```
129
+
130
+ The positional `dataset_root` follows the TimelapsedHRpQCT core CLI convention:
131
+ the root path is the main command argument. Here it is the TimelapsedHRpQCT
132
+ output root, not the raw AIM input folder. The addon expects to find pairwise
133
+ remodelling cases under:
134
+
135
+ ```text
136
+ <dataset-root>/derivatives/TimelapsedHRpQCT/sub-*/analysis/pairwise_t0/*/
137
+ ```
138
+
139
+ Current TimelapsedHRpQCT outputs under
140
+ `sub-*/site-*/analysis/visualize/*remodelling.nii.gz` are also supported. For
141
+ these outputs, the mechanics solve uses the native stack-level baseline
142
+ segmentation and compartment masks under `sub-*/site-*/ses-*/stacks/`.
143
+
144
+ Useful flags:
145
+
146
+ - `--profile XtremeCTI` or `--profile XtremeCTII`: native `parosol-py` scanner
147
+ profile. The generated material image uses the standard profile labels:
148
+ `100` for trabecular baseline bone, `127` for cortical baseline bone, and
149
+ all other labels as non-bone.
150
+ - `--dry-run`: count discoverable cases without writing files.
151
+ - `--overwrite`: recompute outputs even if they already exist.
152
+ - `--verbose`: re-raise case errors instead of counting failed cases.
153
+
154
+ ## Outputs
155
+
156
+ For current TimelapsedHRpQCT outputs, results are written into a site-level
157
+ `mechanoregulation` folder beside `analysis`, `ses-*`, and
158
+ `transformed_images`:
159
+
160
+ ```text
161
+ sub-<id>/site-<site>/
162
+ analysis/
163
+ visualize/
164
+ *_remodelling.nii.gz
165
+ mechanoregulation/
166
+ <case>_sed.nii.gz
167
+ <case>_baseline_material_labels.nii.gz
168
+ <case>_conditional_curves.png
169
+ <case>_mechanoregulation_summary.csv
170
+ <case>_mechanoregulation_summary.json
171
+ ```
172
+
173
+ Older Timelapsed layouts that already store each pairwise case in its own
174
+ folder keep writing a `mechanoregulation` subfolder beside that pairwise case.
175
+
176
+ Output files:
177
+
178
+ - `<case>_baseline_material_labels.nii.gz`: ParOSol input image built from the
179
+ native baseline segmentation. Label `100` is trabecular baseline bone, label
180
+ `127` is cortical baseline bone, and background plus future formation sites
181
+ are `0`.
182
+ - `<case>_sed.nii.gz`: baseline SED solved with `parosol-py` and aligned to the
183
+ Timelapsed remodelling image grid. If this file already exists, it is reused unless
184
+ `--overwrite` is set.
185
+ - `<case>_conditional_curves.png`: two-panel summary figure. The left panel
186
+ shows Schulte-style binned conditional probability curves for resorption,
187
+ quiescence, and formation. The right panel shows bootstrap logistic
188
+ probability curves.
189
+ - `<case>_mechanoregulation_summary.csv`: one-row table for downstream
190
+ statistics.
191
+ - `<case>_mechanoregulation_summary.json`: full reproducibility payload,
192
+ including curves, settings, sample counts, and confidence intervals.
193
+
194
+ The CSV is intentionally flat and stable. A TimelapsedHRpQCT or Slicer export
195
+ button can merge it into cohort-level exports by scanning each pairwise case's
196
+ `mechanoregulation/*.csv` file and joining by subject/site/session-pair stem.
197
+ That export integration is separate from this package; this package writes the
198
+ per-case CSV files that such an exporter can pick up.
199
+
200
+ ## Output Measures
201
+
202
+ The analysis samples baseline SED on the quiescent baseline surface. Formation
203
+ and resorption events are projected symmetrically onto neighbouring baseline
204
+ surface voxels. If projected formation and resorption overlap on the same
205
+ surface voxel, that voxel is counted as quiescent/ambiguous.
206
+
207
+ CSV columns:
208
+
209
+ - `CCR`: maximum correct classification rate from Schulte-style conditional
210
+ probability curves. It summarizes how well low SED maps to resorption,
211
+ middle SED maps to quiescence, and high SED maps to formation.
212
+ - `CCR_low_threshold`: normalized SED threshold separating the resorption side
213
+ from the lazy zone in the binned CCR analysis.
214
+ - `CCR_high_threshold`: normalized SED threshold separating the lazy zone from
215
+ the formation side in the binned CCR analysis.
216
+ - `binned_lazy_zone_low`, `binned_lazy_zone_high`: aliases of the CCR
217
+ thresholds, reported explicitly as the binned lazy-zone bounds.
218
+ - `logistic_lazy_zone_low`, `logistic_lazy_zone_high`: lazy-zone bounds from
219
+ the smooth logistic probability curves, estimated from R/Q and F/Q curve
220
+ crossings.
221
+ - `OR_F`: percent increase in formation odds per one normalized SED
222
+ percentage-point increase. This is named `OR_F` for reporting compatibility,
223
+ but its value is `100 * (OR_F_ratio - 1)`.
224
+ - `OR_R`: percent increase in resorption odds per one normalized SED
225
+ percentage-point decrease. This is named `OR_R` for reporting compatibility,
226
+ but its value is `100 * (OR_R_ratio - 1)`.
227
+ - `OR_F_CI_low`, `OR_F_CI_high`: bootstrap confidence interval for reported
228
+ percent `OR_F`.
229
+ - `OR_R_CI_low`, `OR_R_CI_high`: bootstrap confidence interval for reported
230
+ percent `OR_R`.
231
+ - `OR_F_ratio`, `OR_R_ratio`: raw logistic odds ratios retained for
232
+ reproducibility.
233
+ - `formation_odds_increase_percent_per_sed_percent`: percent odds increase for
234
+ formation per one normalized SED percentage-point increase, computed as
235
+ `100 * (OR_F_ratio - 1)`. This duplicates reported `OR_F`.
236
+ - `resorption_odds_increase_percent_per_sed_percent_decrease`: percent odds
237
+ increase for resorption per one normalized SED percentage-point decrease,
238
+ computed as `100 * (OR_R_ratio - 1)`. This duplicates reported `OR_R`.
239
+ - `formation_odds_increase_percent_CI_low`,
240
+ `formation_odds_increase_percent_CI_high`: confidence interval for the
241
+ formation percent odds increase.
242
+ - `resorption_odds_increase_percent_CI_low`,
243
+ `resorption_odds_increase_percent_CI_high`: confidence interval for the
244
+ resorption percent odds increase.
245
+ - `n_surface_voxels`: number of baseline surface voxels available before final
246
+ class sampling.
247
+ - `n_sampled_voxels`: number of surface voxels used in the final
248
+ mechanoregulation analysis.
249
+ - `n_formation`, `n_resorption`, `n_quiescence`: final class counts after
250
+ symmetric surface projection.
251
+ - `n_cancelled_overlap`: number of surface voxels where projected formation and
252
+ resorption overlapped and were cancelled to quiescence.
253
+
254
+ ## Standalone Usage
255
+
256
+ For synthetic advection folders or one-off debugging, use:
257
+
258
+ ```bash
259
+ mechanoregulation analyze /path/to/folder
260
+ ```
261
+
262
+ The standalone command looks for a baseline image, a remodelling label image or
263
+ T1 follow-up, an optional mask, and a baseline SED image or nearby exported
264
+ ParOSol `fields/sed.nii.gz`. It writes:
265
+
266
+ ```text
267
+ <folder>/mechanoregulation/
268
+ <run>_mechanoregulation_summary.csv
269
+ <run>_mechanoregulation_summary.json
270
+ <run>_mechanoregulation_curves.png
271
+ ```
@@ -0,0 +1,244 @@
1
+ # BoneMechanoregulation
2
+
3
+ Post-timelapse mechanoregulation analysis for HR-pQCT datasets.
4
+
5
+ This package is a downstream addon. Run `TimelapsedHRpQCT` first, then run
6
+ BoneMechanoregulation on the Timelapsed output dataset root. The addon reuses
7
+ the Timelapsed pairwise remodelling label images, solves baseline SED from the
8
+ native baseline segmentation, and writes mechanoregulation summaries next to
9
+ each pairwise case.
10
+
11
+ ## Prerequisite
12
+
13
+ First create TimelapsedHRpQCT remodelling outputs.
14
+
15
+ With the core TimelapsedHRpQCT CLI, the pipeline convention is:
16
+
17
+ ```bash
18
+ timelapsedhrpqct run /path/to/raw_aim_input --output-root /path/to/TimelapsedHRpQCT
19
+ ```
20
+
21
+ In the 3D Slicer toolbox, use the `Timelapsed HR-pQCT` module:
22
+
23
+ 1. select the AIM dataset root;
24
+ 2. choose a results folder, defaulting to `<dataset_root>/TimelapsedHRpQCT`;
25
+ 3. click `Run pipeline`;
26
+ 4. inspect the generated remodelling images.
27
+
28
+ The Slicer wrapper is the recommended route for interactive preparation, mask
29
+ review, and visual QA before running this mechanoregulation addon.
30
+
31
+ ## Install
32
+
33
+ From PyPI:
34
+
35
+ ```bash
36
+ pip install bone-mechanoregulation
37
+ ```
38
+
39
+ For local development:
40
+
41
+ ```bash
42
+ pip install -e .
43
+ ```
44
+
45
+ `parosol-py>=0.1.13` must be available in the same Python environment. The
46
+ mechanoregulation workflow writes a standard ParOSol material-label input from
47
+ the native baseline segmentation, then calls the native `parosol-py`
48
+ XtremeCTI/XtremeCTII profile for the finite-element solve.
49
+
50
+ ## Install In 3D Slicer
51
+
52
+ BoneMechanoregulation can also be installed into Slicer's Python environment so
53
+ the same command-line analysis is available from Slicer-side workflows or
54
+ scripted modules.
55
+
56
+ From Slicer's Python interactor or a Slicer Python shell:
57
+
58
+ ```python
59
+ import sys
60
+ import subprocess
61
+
62
+ subprocess.check_call([
63
+ sys.executable,
64
+ "-m",
65
+ "pip",
66
+ "install",
67
+ "bone-mechanoregulation",
68
+ ])
69
+ ```
70
+
71
+ For local development from a checkout:
72
+
73
+ ```python
74
+ import sys
75
+ import subprocess
76
+
77
+ subprocess.check_call([
78
+ sys.executable,
79
+ "-m",
80
+ "pip",
81
+ "install",
82
+ "-e",
83
+ "/path/to/BoneMechanoregulation",
84
+ ])
85
+ ```
86
+
87
+ The package supports the same Python version range used by the current
88
+ TimelapsedHRpQCT packaging matrix: Python 3.11, 3.12, and 3.13. If a Slicer
89
+ release ships an older Python, use an external Python environment for the batch
90
+ analysis or install through the matching Slicer toolbox once it provides a
91
+ compatible runtime.
92
+
93
+ ## Batch Usage
94
+
95
+ Run this after TimelapsedHRpQCT has produced its derivative tree:
96
+
97
+ ```bash
98
+ mechanoregulation run \
99
+ /path/to/TimelapsedHRpQCT \
100
+ --profile XtremeCTII
101
+ ```
102
+
103
+ The positional `dataset_root` follows the TimelapsedHRpQCT core CLI convention:
104
+ the root path is the main command argument. Here it is the TimelapsedHRpQCT
105
+ output root, not the raw AIM input folder. The addon expects to find pairwise
106
+ remodelling cases under:
107
+
108
+ ```text
109
+ <dataset-root>/derivatives/TimelapsedHRpQCT/sub-*/analysis/pairwise_t0/*/
110
+ ```
111
+
112
+ Current TimelapsedHRpQCT outputs under
113
+ `sub-*/site-*/analysis/visualize/*remodelling.nii.gz` are also supported. For
114
+ these outputs, the mechanics solve uses the native stack-level baseline
115
+ segmentation and compartment masks under `sub-*/site-*/ses-*/stacks/`.
116
+
117
+ Useful flags:
118
+
119
+ - `--profile XtremeCTI` or `--profile XtremeCTII`: native `parosol-py` scanner
120
+ profile. The generated material image uses the standard profile labels:
121
+ `100` for trabecular baseline bone, `127` for cortical baseline bone, and
122
+ all other labels as non-bone.
123
+ - `--dry-run`: count discoverable cases without writing files.
124
+ - `--overwrite`: recompute outputs even if they already exist.
125
+ - `--verbose`: re-raise case errors instead of counting failed cases.
126
+
127
+ ## Outputs
128
+
129
+ For current TimelapsedHRpQCT outputs, results are written into a site-level
130
+ `mechanoregulation` folder beside `analysis`, `ses-*`, and
131
+ `transformed_images`:
132
+
133
+ ```text
134
+ sub-<id>/site-<site>/
135
+ analysis/
136
+ visualize/
137
+ *_remodelling.nii.gz
138
+ mechanoregulation/
139
+ <case>_sed.nii.gz
140
+ <case>_baseline_material_labels.nii.gz
141
+ <case>_conditional_curves.png
142
+ <case>_mechanoregulation_summary.csv
143
+ <case>_mechanoregulation_summary.json
144
+ ```
145
+
146
+ Older Timelapsed layouts that already store each pairwise case in its own
147
+ folder keep writing a `mechanoregulation` subfolder beside that pairwise case.
148
+
149
+ Output files:
150
+
151
+ - `<case>_baseline_material_labels.nii.gz`: ParOSol input image built from the
152
+ native baseline segmentation. Label `100` is trabecular baseline bone, label
153
+ `127` is cortical baseline bone, and background plus future formation sites
154
+ are `0`.
155
+ - `<case>_sed.nii.gz`: baseline SED solved with `parosol-py` and aligned to the
156
+ Timelapsed remodelling image grid. If this file already exists, it is reused unless
157
+ `--overwrite` is set.
158
+ - `<case>_conditional_curves.png`: two-panel summary figure. The left panel
159
+ shows Schulte-style binned conditional probability curves for resorption,
160
+ quiescence, and formation. The right panel shows bootstrap logistic
161
+ probability curves.
162
+ - `<case>_mechanoregulation_summary.csv`: one-row table for downstream
163
+ statistics.
164
+ - `<case>_mechanoregulation_summary.json`: full reproducibility payload,
165
+ including curves, settings, sample counts, and confidence intervals.
166
+
167
+ The CSV is intentionally flat and stable. A TimelapsedHRpQCT or Slicer export
168
+ button can merge it into cohort-level exports by scanning each pairwise case's
169
+ `mechanoregulation/*.csv` file and joining by subject/site/session-pair stem.
170
+ That export integration is separate from this package; this package writes the
171
+ per-case CSV files that such an exporter can pick up.
172
+
173
+ ## Output Measures
174
+
175
+ The analysis samples baseline SED on the quiescent baseline surface. Formation
176
+ and resorption events are projected symmetrically onto neighbouring baseline
177
+ surface voxels. If projected formation and resorption overlap on the same
178
+ surface voxel, that voxel is counted as quiescent/ambiguous.
179
+
180
+ CSV columns:
181
+
182
+ - `CCR`: maximum correct classification rate from Schulte-style conditional
183
+ probability curves. It summarizes how well low SED maps to resorption,
184
+ middle SED maps to quiescence, and high SED maps to formation.
185
+ - `CCR_low_threshold`: normalized SED threshold separating the resorption side
186
+ from the lazy zone in the binned CCR analysis.
187
+ - `CCR_high_threshold`: normalized SED threshold separating the lazy zone from
188
+ the formation side in the binned CCR analysis.
189
+ - `binned_lazy_zone_low`, `binned_lazy_zone_high`: aliases of the CCR
190
+ thresholds, reported explicitly as the binned lazy-zone bounds.
191
+ - `logistic_lazy_zone_low`, `logistic_lazy_zone_high`: lazy-zone bounds from
192
+ the smooth logistic probability curves, estimated from R/Q and F/Q curve
193
+ crossings.
194
+ - `OR_F`: percent increase in formation odds per one normalized SED
195
+ percentage-point increase. This is named `OR_F` for reporting compatibility,
196
+ but its value is `100 * (OR_F_ratio - 1)`.
197
+ - `OR_R`: percent increase in resorption odds per one normalized SED
198
+ percentage-point decrease. This is named `OR_R` for reporting compatibility,
199
+ but its value is `100 * (OR_R_ratio - 1)`.
200
+ - `OR_F_CI_low`, `OR_F_CI_high`: bootstrap confidence interval for reported
201
+ percent `OR_F`.
202
+ - `OR_R_CI_low`, `OR_R_CI_high`: bootstrap confidence interval for reported
203
+ percent `OR_R`.
204
+ - `OR_F_ratio`, `OR_R_ratio`: raw logistic odds ratios retained for
205
+ reproducibility.
206
+ - `formation_odds_increase_percent_per_sed_percent`: percent odds increase for
207
+ formation per one normalized SED percentage-point increase, computed as
208
+ `100 * (OR_F_ratio - 1)`. This duplicates reported `OR_F`.
209
+ - `resorption_odds_increase_percent_per_sed_percent_decrease`: percent odds
210
+ increase for resorption per one normalized SED percentage-point decrease,
211
+ computed as `100 * (OR_R_ratio - 1)`. This duplicates reported `OR_R`.
212
+ - `formation_odds_increase_percent_CI_low`,
213
+ `formation_odds_increase_percent_CI_high`: confidence interval for the
214
+ formation percent odds increase.
215
+ - `resorption_odds_increase_percent_CI_low`,
216
+ `resorption_odds_increase_percent_CI_high`: confidence interval for the
217
+ resorption percent odds increase.
218
+ - `n_surface_voxels`: number of baseline surface voxels available before final
219
+ class sampling.
220
+ - `n_sampled_voxels`: number of surface voxels used in the final
221
+ mechanoregulation analysis.
222
+ - `n_formation`, `n_resorption`, `n_quiescence`: final class counts after
223
+ symmetric surface projection.
224
+ - `n_cancelled_overlap`: number of surface voxels where projected formation and
225
+ resorption overlapped and were cancelled to quiescence.
226
+
227
+ ## Standalone Usage
228
+
229
+ For synthetic advection folders or one-off debugging, use:
230
+
231
+ ```bash
232
+ mechanoregulation analyze /path/to/folder
233
+ ```
234
+
235
+ The standalone command looks for a baseline image, a remodelling label image or
236
+ T1 follow-up, an optional mask, and a baseline SED image or nearby exported
237
+ ParOSol `fields/sed.nii.gz`. It writes:
238
+
239
+ ```text
240
+ <folder>/mechanoregulation/
241
+ <run>_mechanoregulation_summary.csv
242
+ <run>_mechanoregulation_summary.json
243
+ <run>_mechanoregulation_curves.png
244
+ ```