cropro 0.1.2__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.
- cropro-0.1.2/LICENSE +21 -0
- cropro-0.1.2/PKG-INFO +601 -0
- cropro-0.1.2/README.md +570 -0
- cropro-0.1.2/pyproject.toml +62 -0
- cropro-0.1.2/setup.cfg +4 -0
- cropro-0.1.2/src/cropro/__init__.py +6 -0
- cropro-0.1.2/src/cropro/__main__.py +4 -0
- cropro-0.1.2/src/cropro/cli.py +78 -0
- cropro-0.1.2/src/cropro/config.py +147 -0
- cropro-0.1.2/src/cropro/core.py +49 -0
- cropro-0.1.2/src/cropro/cropping/__init__.py +1 -0
- cropro-0.1.2/src/cropro/cropping/croppingCrontrollerClass.py +304 -0
- cropro-0.1.2/src/cropro/cropping/negativeCenterC.py +118 -0
- cropro-0.1.2/src/cropro/cropping/negativeRandomC.py +78 -0
- cropro-0.1.2/src/cropro/cropping/negativeStrideC.py +136 -0
- cropro-0.1.2/src/cropro/cropping/patientCropC.py +179 -0
- cropro-0.1.2/src/cropro/cropping/positiveCenterC.py +144 -0
- cropro-0.1.2/src/cropro/cropping/positiveRandomC.py +153 -0
- cropro-0.1.2/src/cropro/cropping/positiveStrideC.py +192 -0
- cropro-0.1.2/src/cropro/cropping/saveFilesC.py +400 -0
- cropro-0.1.2/src/cropro.egg-info/PKG-INFO +601 -0
- cropro-0.1.2/src/cropro.egg-info/SOURCES.txt +26 -0
- cropro-0.1.2/src/cropro.egg-info/dependency_links.txt +1 -0
- cropro-0.1.2/src/cropro.egg-info/entry_points.txt +2 -0
- cropro-0.1.2/src/cropro.egg-info/requires.txt +12 -0
- cropro-0.1.2/src/cropro.egg-info/top_level.txt +1 -0
- cropro-0.1.2/tests/test_cli_config.py +60 -0
- cropro-0.1.2/tests/test_config_validation.py +36 -0
cropro-0.1.2/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Alexandros Patsanis
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
cropro-0.1.2/PKG-INFO
ADDED
|
@@ -0,0 +1,601 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cropro
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Automated cropping of prostate MR images.
|
|
5
|
+
Author: Alexandors Patsanis
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/alexofficial/CROPro
|
|
8
|
+
Project-URL: Repository, https://github.com/alexofficial/CROPro
|
|
9
|
+
Project-URL: Issues, https://github.com/alexofficial/CROPro/issues
|
|
10
|
+
Keywords: medical-imaging,mri,prostate,cropping,picai
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Image Processing
|
|
16
|
+
Requires-Python: >=3.13
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: matplotlib>=3.10.9
|
|
20
|
+
Requires-Dist: numpy>=2.4.6
|
|
21
|
+
Requires-Dist: opencv-python>=4.13.0.92
|
|
22
|
+
Requires-Dist: SimpleITK>=2.5.5
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: bandit>=1.7; extra == "dev"
|
|
25
|
+
Requires-Dist: build>=1; extra == "dev"
|
|
26
|
+
Requires-Dist: pip-audit>=2.7; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
28
|
+
Requires-Dist: ruff>=0.5; extra == "dev"
|
|
29
|
+
Requires-Dist: twine>=5; extra == "dev"
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
# CROPro
|
|
33
|
+
|
|
34
|
+
CROPro is a Python package for automated cropping of prostate MRI volumes. It was developed for prostate MR preprocessing workflows where a model or reviewer needs consistent image patches around the prostate gland or clinically significant prostate cancer lesions.
|
|
35
|
+
|
|
36
|
+
The package supports:
|
|
37
|
+
|
|
38
|
+
- `center`, `random`, and `stride` crop strategies
|
|
39
|
+
- T2W-only and bpMRI cropping workflows
|
|
40
|
+
- negative, positive, and unknown patient-status workflows
|
|
41
|
+
- configurable in-plane resampling through `pixel_spacing`
|
|
42
|
+
- Python API and command-line usage
|
|
43
|
+
|
|
44
|
+
If you use CROPro in research, please cite the paper listed in [Citation](#citation):
|
|
45
|
+
`CROPro: a tool for automated cropping of prostate magnetic resonance images`.
|
|
46
|
+
|
|
47
|
+
## Contents
|
|
48
|
+
|
|
49
|
+
- [Installation](#installation)
|
|
50
|
+
- [Quick Start](#quick-start)
|
|
51
|
+
- [Input Data](#input-data)
|
|
52
|
+
- [Patient Workflows](#patient-workflows)
|
|
53
|
+
- [Visual Examples](#visual-examples)
|
|
54
|
+
- [Pixel Spacing](#pixel-spacing)
|
|
55
|
+
- [PI-CAI Dataset Setup](#pi-cai-dataset-setup)
|
|
56
|
+
- [Command Line](#command-line)
|
|
57
|
+
- [Configuration Reference](#configuration-reference)
|
|
58
|
+
- [Development](#development)
|
|
59
|
+
- [PyPI Release](#pypi-release)
|
|
60
|
+
- [Citation](#citation)
|
|
61
|
+
|
|
62
|
+
## Installation
|
|
63
|
+
|
|
64
|
+
This repository uses [`uv`](https://docs.astral.sh/uv/) for dependency management.
|
|
65
|
+
|
|
66
|
+
Install `uv` if needed:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Clone the repository and install the environment:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
git clone https://github.com/alexofficial/CROPro.git
|
|
76
|
+
cd CROPro
|
|
77
|
+
uv sync
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Check that the CLI is available:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
uv run cropro --help
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
For development tools such as tests and linting:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
uv sync --extra dev
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
When CROPro is published to PyPI, users will be able to install it in a project with:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
uv add cropro
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
For CLI-only use after PyPI publication:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
uv tool install cropro
|
|
102
|
+
cropro --help
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## PyPI Release
|
|
106
|
+
|
|
107
|
+
This repository is configured to publish to PyPI through GitHub Actions using Trusted Publishing.
|
|
108
|
+
|
|
109
|
+
### One-Time Setup
|
|
110
|
+
|
|
111
|
+
1. On PyPI, create a project named `cropro` (or claim it if it already exists under your account).
|
|
112
|
+
2. In PyPI project settings, configure a Trusted Publisher for this GitHub repository and workflow:
|
|
113
|
+
- owner/repo: `alexofficial/CROPro`
|
|
114
|
+
- workflow filename: `.github/workflows/pypi-publish.yml`
|
|
115
|
+
- environment: `pypi`
|
|
116
|
+
|
|
117
|
+
### Release Flow
|
|
118
|
+
|
|
119
|
+
1. Update version in `pyproject.toml`.
|
|
120
|
+
2. Commit and push to `main`.
|
|
121
|
+
3. Create a GitHub release (for example tag `v0.1.1`).
|
|
122
|
+
4. The `Publish to PyPI` workflow builds the package and uploads it to PyPI.
|
|
123
|
+
|
|
124
|
+
### Optional Local Validation
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
python -m build
|
|
128
|
+
python -m twine check dist/*
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Quick Start
|
|
132
|
+
|
|
133
|
+
### Crop A Negative Or Unknown Case
|
|
134
|
+
|
|
135
|
+
Use this when you have a T2W image and a prostate gland mask. For `unknown` patient status, CROPro uses the same gland-mask workflow as `negative`, which is useful for inference or review cases where cancer status is not known yet.
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from cropro import CROPro, CropConfig
|
|
139
|
+
|
|
140
|
+
config = CropConfig(
|
|
141
|
+
crop_method="stride",
|
|
142
|
+
patient_status="negative", # "negative" or "unknown"
|
|
143
|
+
sequence_type="T2W",
|
|
144
|
+
orig_img_path_t2w="data/patient_001/t2w.nii.gz",
|
|
145
|
+
seg_img_path="data/patient_001/prostate_gland_mask.nii.gz",
|
|
146
|
+
pixel_spacing=0.4,
|
|
147
|
+
crop_image_size=128,
|
|
148
|
+
crop_stride=32,
|
|
149
|
+
saved_image_type="png",
|
|
150
|
+
path_to_save="outputs/patient_001",
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
CROPro(config).run()
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Crop A Positive Case
|
|
157
|
+
|
|
158
|
+
Use this when you have a prostate gland mask and a lesion mask. The gland mask defines the anatomical search region; the lesion mask is used to keep crops that contain enough lesion area.
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
from cropro import CROPro, CropConfig
|
|
162
|
+
|
|
163
|
+
config = CropConfig(
|
|
164
|
+
crop_method="stride",
|
|
165
|
+
patient_status="positive",
|
|
166
|
+
sequence_type="T2W",
|
|
167
|
+
orig_img_path_t2w="data/patient_002/t2w.nii.gz",
|
|
168
|
+
seg_img_path="data/patient_002/prostate_gland_mask.nii.gz",
|
|
169
|
+
seg_img_path_lesion="data/patient_002/lesion_mask.nii.gz",
|
|
170
|
+
tumor_label_level=1,
|
|
171
|
+
c_min_positive=0.2,
|
|
172
|
+
pixel_spacing=0.4,
|
|
173
|
+
crop_image_size=128,
|
|
174
|
+
crop_stride=32,
|
|
175
|
+
saved_image_type="png",
|
|
176
|
+
path_to_save="outputs/patient_002",
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
CROPro(config).run()
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Crop bpMRI
|
|
183
|
+
|
|
184
|
+
Set `sequence_type="bpMRI"` and provide T2W, ADC, and HBV images. CROPro saves aligned crops for each modality.
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
from cropro import CROPro, CropConfig
|
|
188
|
+
|
|
189
|
+
config = CropConfig(
|
|
190
|
+
crop_method="center",
|
|
191
|
+
patient_status="negative",
|
|
192
|
+
sequence_type="bpMRI",
|
|
193
|
+
orig_img_path_t2w="data/patient_003/t2w.nii.gz",
|
|
194
|
+
orig_img_path_adc="data/patient_003/adc.nii.gz",
|
|
195
|
+
orig_img_path_hbv="data/patient_003/hbv.nii.gz",
|
|
196
|
+
seg_img_path="data/patient_003/prostate_gland_mask.nii.gz",
|
|
197
|
+
pixel_spacing=0.5,
|
|
198
|
+
crop_image_size=128,
|
|
199
|
+
saved_image_type="png",
|
|
200
|
+
path_to_save="outputs/patient_003",
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
CROPro(config).run()
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Input Data
|
|
207
|
+
|
|
208
|
+
CROPro reads 3D medical image files through SimpleITK. Common formats include `.nii`, `.nii.gz`, `.mha`, `.mhd`, and other SimpleITK-readable formats.
|
|
209
|
+
|
|
210
|
+
For a negative or unknown patient, provide:
|
|
211
|
+
|
|
212
|
+
- T2W image
|
|
213
|
+
- prostate gland segmentation mask
|
|
214
|
+
|
|
215
|
+
For a positive patient, provide:
|
|
216
|
+
|
|
217
|
+
- T2W image
|
|
218
|
+
- prostate gland segmentation mask
|
|
219
|
+
- lesion segmentation mask
|
|
220
|
+
|
|
221
|
+
For `sequence_type="bpMRI"`, also provide:
|
|
222
|
+
|
|
223
|
+
- ADC image
|
|
224
|
+
- HBV image
|
|
225
|
+
|
|
226
|
+
All images and masks should already be spatially aligned. CROPro resamples the image spacing for crop generation, but it does not perform image registration.
|
|
227
|
+
|
|
228
|
+
## Patient Workflows
|
|
229
|
+
|
|
230
|
+
| Status | Intended use | Segmentation behavior |
|
|
231
|
+
| --- | --- | --- |
|
|
232
|
+
| `negative` | Cancer-free cases | Uses the prostate gland mask to crop the prostate region. |
|
|
233
|
+
| `positive` | Cancer-present cases | Uses the prostate gland mask for crop placement and the lesion mask to keep crops containing enough lesion area. |
|
|
234
|
+
| `unknown` | Inference, testing, or review cases with unknown health status | Uses the prostate gland mask like the negative workflow and returns candidate prostate-region crops. |
|
|
235
|
+
|
|
236
|
+
## Visual Examples
|
|
237
|
+
|
|
238
|
+
### Negative Or Unknown Workflow
|
|
239
|
+
|
|
240
|
+
For negative and unknown cases, the prostate gland mask defines the region to crop.
|
|
241
|
+
|
|
242
|
+
<p>
|
|
243
|
+
<img src="./assets/readme/segmentation/negative/PICAI/10001_1000001/T2W/T2W_axial0007.png" width="220" alt="T2W prostate gland segmentation example" />
|
|
244
|
+
<img src="./assets/readme/segmentation/negative/PICAI/10001_1000001/ADC/ADC_axial0007.png" width="220" alt="ADC prostate gland segmentation example" />
|
|
245
|
+
<img src="./assets/readme/segmentation/negative/PICAI/10001_1000001/HBV/HBV_axial0007.png" width="220" alt="HBV prostate gland segmentation example" />
|
|
246
|
+
</p>
|
|
247
|
+
|
|
248
|
+
Stride-cropped bpMRI output at `0.4` mm/pixel. With `crop_image_size=128`, the crop covers about `51.2 x 51.2` mm.
|
|
249
|
+
|
|
250
|
+
<p>
|
|
251
|
+
<img src="./assets/readme/negative/PICAI_stride_0.4_128_negative/10001_1000001/bpMRI_slice_7_of_21_1_cord_160_166_T2W.png" width="128" alt="Negative T2W crop at 0.4 mm pixel spacing" />
|
|
252
|
+
<img src="./assets/readme/negative/PICAI_stride_0.4_128_negative/10001_1000001/bpMRI_slice_7_of_21_1_cord_160_166_ADC.png" width="128" alt="Negative ADC crop at 0.4 mm pixel spacing" />
|
|
253
|
+
<img src="./assets/readme/negative/PICAI_stride_0.4_128_negative/10001_1000001/bpMRI_slice_7_of_21_1_cord_160_166_HBV.png" width="128" alt="Negative HBV crop at 0.4 mm pixel spacing" />
|
|
254
|
+
</p>
|
|
255
|
+
|
|
256
|
+
The same case at `0.5` mm/pixel covers about `64.0 x 64.0` mm and includes more surrounding context.
|
|
257
|
+
|
|
258
|
+
<p>
|
|
259
|
+
<img src="./assets/readme/negative/PICAI_stride_0.5_128_negative/10001_1000001/bpMRI_slice_7_of_21_1_cord_128_132_T2W.png" width="128" alt="Negative T2W crop at 0.5 mm pixel spacing" />
|
|
260
|
+
<img src="./assets/readme/negative/PICAI_stride_0.5_128_negative/10001_1000001/bpMRI_slice_7_of_21_1_cord_128_132_ADC.png" width="128" alt="Negative ADC crop at 0.5 mm pixel spacing" />
|
|
261
|
+
<img src="./assets/readme/negative/PICAI_stride_0.5_128_negative/10001_1000001/bpMRI_slice_7_of_21_1_cord_128_132_HBV.png" width="128" alt="Negative HBV crop at 0.5 mm pixel spacing" />
|
|
262
|
+
</p>
|
|
263
|
+
|
|
264
|
+
### Positive Workflow
|
|
265
|
+
|
|
266
|
+
For positive cases, CROPro uses the prostate gland mask plus a lesion mask. A crop is saved only when the lesion area inside the crop satisfies the configured threshold.
|
|
267
|
+
|
|
268
|
+
<p>
|
|
269
|
+
<img src="./assets/readme/segmentation/positive/PICAI/10117_1000117/human/t2w-human/axial15_seg.png" width="220" alt="T2W lesion segmentation example" />
|
|
270
|
+
<img src="./assets/readme/segmentation/positive/PICAI/10117_1000117/human/adc-human/axial15_seg.png" width="220" alt="ADC lesion segmentation example" />
|
|
271
|
+
<img src="./assets/readme/segmentation/positive/PICAI/10117_1000117/human/hbv-human/axial15_seg.png" width="220" alt="HBV lesion segmentation example" />
|
|
272
|
+
</p>
|
|
273
|
+
|
|
274
|
+
Stride-cropped bpMRI output at `0.4` mm/pixel:
|
|
275
|
+
|
|
276
|
+
<p>
|
|
277
|
+
<img src="./assets/readme/positive/PICAI_stride_0.4_128_positive/10117_1000117/bpMRI_slice_15_of_27_1_cord_157_136_T2W.png" width="128" alt="Positive T2W crop at 0.4 mm pixel spacing" />
|
|
278
|
+
<img src="./assets/readme/positive/PICAI_stride_0.4_128_positive/10117_1000117/bpMRI_slice_15_of_27_1_cord_157_136_ADC.png" width="128" alt="Positive ADC crop at 0.4 mm pixel spacing" />
|
|
279
|
+
<img src="./assets/readme/positive/PICAI_stride_0.4_128_positive/10117_1000117/bpMRI_slice_15_of_27_1_cord_157_136_HBV.png" width="128" alt="Positive HBV crop at 0.4 mm pixel spacing" />
|
|
280
|
+
</p>
|
|
281
|
+
|
|
282
|
+
The same positive case at `0.5` mm/pixel:
|
|
283
|
+
|
|
284
|
+
<p>
|
|
285
|
+
<img src="./assets/readme/positive/PICAI_stride_0.5_128_positive/10117_1000117/bpMRI_slice_15_of_27_1_cord_126_99_T2W.png" width="128" alt="Positive T2W crop at 0.5 mm pixel spacing" />
|
|
286
|
+
<img src="./assets/readme/positive/PICAI_stride_0.5_128_positive/10117_1000117/bpMRI_slice_15_of_27_1_cord_126_99_ADC.png" width="128" alt="Positive ADC crop at 0.5 mm pixel spacing" />
|
|
287
|
+
<img src="./assets/readme/positive/PICAI_stride_0.5_128_positive/10117_1000117/bpMRI_slice_15_of_27_1_cord_126_99_HBV.png" width="128" alt="Positive HBV crop at 0.5 mm pixel spacing" />
|
|
288
|
+
</p>
|
|
289
|
+
|
|
290
|
+
## Pixel Spacing
|
|
291
|
+
|
|
292
|
+
`pixel_spacing` controls the target in-plane resolution in millimeters per pixel before cropping. This matters because prostate MRI scans can come from different scanners, protocols, and reconstruction settings. Resampling to a consistent spacing makes crops more comparable across patients.
|
|
293
|
+
|
|
294
|
+
With `crop_image_size=128`:
|
|
295
|
+
|
|
296
|
+
| Pixel spacing | Crop size in pixels | Approximate physical area |
|
|
297
|
+
| --- | --- | --- |
|
|
298
|
+
| `0.4` mm/pixel | `128 x 128` | `51.2 x 51.2` mm |
|
|
299
|
+
| `0.5` mm/pixel | `128 x 128` | `64.0 x 64.0` mm |
|
|
300
|
+
|
|
301
|
+
A `0.4` mm/pixel crop is tighter around the anatomy. A `0.5` mm/pixel crop covers a wider physical region and can preserve more surrounding anatomical context.
|
|
302
|
+
|
|
303
|
+
```python
|
|
304
|
+
from cropro import CROPro, CropConfig
|
|
305
|
+
|
|
306
|
+
tight_crop = CropConfig(
|
|
307
|
+
crop_method="center",
|
|
308
|
+
patient_status="negative",
|
|
309
|
+
sequence_type="T2W",
|
|
310
|
+
orig_img_path_t2w="data/patient_001/t2w.nii.gz",
|
|
311
|
+
seg_img_path="data/patient_001/prostate_gland_mask.nii.gz",
|
|
312
|
+
pixel_spacing=0.4,
|
|
313
|
+
crop_image_size=128,
|
|
314
|
+
path_to_save="outputs/patient_001_spacing_0.4",
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
wide_crop = CropConfig(
|
|
318
|
+
crop_method="center",
|
|
319
|
+
patient_status="negative",
|
|
320
|
+
sequence_type="T2W",
|
|
321
|
+
orig_img_path_t2w="data/patient_001/t2w.nii.gz",
|
|
322
|
+
seg_img_path="data/patient_001/prostate_gland_mask.nii.gz",
|
|
323
|
+
pixel_spacing=0.5,
|
|
324
|
+
crop_image_size=128,
|
|
325
|
+
path_to_save="outputs/patient_001_spacing_0.5",
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
CROPro(tight_crop).run()
|
|
329
|
+
CROPro(wide_crop).run()
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## PI-CAI Dataset Setup
|
|
333
|
+
|
|
334
|
+
The runnable examples use the official PI-CAI Public Training and Development Dataset. Images are hosted on Zenodo, and annotations are maintained in the `DIAGNijmegen/picai_labels` repository.
|
|
335
|
+
|
|
336
|
+
The default download fetches public image fold 0, which is about 5.4 GB, and clones the labels:
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
uv sync
|
|
340
|
+
bash scripts/download_dataset.sh
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
The script writes data under `dataset/PI-CAI/`. This directory is ignored by git.
|
|
344
|
+
|
|
345
|
+
The image download is resumable because the script uses `curl -C -`. If the download is interrupted, run the same command again:
|
|
346
|
+
|
|
347
|
+
```bash
|
|
348
|
+
bash scripts/download_dataset.sh
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
To download all five public image folds, about 26.9 GB:
|
|
352
|
+
|
|
353
|
+
```bash
|
|
354
|
+
CROPRO_PICAI_FOLDS="0 1 2 3 4" bash scripts/download_dataset.sh
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
To place the PI-CAI data somewhere else:
|
|
358
|
+
|
|
359
|
+
```bash
|
|
360
|
+
CROPRO_DATASET_ROOT="/path/to/PI-CAI" bash scripts/download_dataset.sh
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
Expected layout:
|
|
364
|
+
|
|
365
|
+
```text
|
|
366
|
+
dataset/PI-CAI/
|
|
367
|
+
archives/
|
|
368
|
+
picai_public_images_fold0.zip
|
|
369
|
+
images/
|
|
370
|
+
10001/
|
|
371
|
+
10001_1000001_t2w.mha
|
|
372
|
+
10001_1000001_adc.mha
|
|
373
|
+
10001_1000001_hbv.mha
|
|
374
|
+
picai_labels/
|
|
375
|
+
anatomical_delineations/whole_gland/AI/Bosma22b/
|
|
376
|
+
csPCa_lesion_delineations/human_expert/resampled/
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
Run the examples:
|
|
380
|
+
|
|
381
|
+
```bash
|
|
382
|
+
uv run python examples/PI-CAI_negative_crop.py
|
|
383
|
+
uv run python examples/PI-CAI_positive_crop.py
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
Or run both:
|
|
387
|
+
|
|
388
|
+
```bash
|
|
389
|
+
bash scripts/examples.sh
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
PI-CAI notes:
|
|
393
|
+
|
|
394
|
+
- Public images: `https://zenodo.org/records/6624726`
|
|
395
|
+
- Labels: `https://github.com/DIAGNijmegen/picai_labels`
|
|
396
|
+
- Official image names use `[patient_id]_[study_id]_t2w.mha`, `[patient_id]_[study_id]_adc.mha`, and `[patient_id]_[study_id]_hbv.mha`.
|
|
397
|
+
- Whole-gland masks are used for `negative` and `unknown` crops.
|
|
398
|
+
- Positive crops use the whole-gland mask plus the csPCa lesion mask. Set `tumor_label_level` to the lesion value used by the selected label file. The bundled PI-CAI positive example uses case `10032_1000032`, whose lesion label is `3`.
|
|
399
|
+
|
|
400
|
+
## Command Line
|
|
401
|
+
|
|
402
|
+
After installation, use the `cropro` command. From this repository, prefix commands with `uv run`.
|
|
403
|
+
|
|
404
|
+
Negative or unknown patient:
|
|
405
|
+
|
|
406
|
+
```bash
|
|
407
|
+
uv run cropro \
|
|
408
|
+
--crop_method stride \
|
|
409
|
+
--patient_status negative \
|
|
410
|
+
--sequence_type T2W \
|
|
411
|
+
--orig_img_path_t2w data/patient_001/t2w.nii.gz \
|
|
412
|
+
--seg_img_path data/patient_001/prostate_gland_mask.nii.gz \
|
|
413
|
+
--pixel_spacing 0.4 \
|
|
414
|
+
--crop_image_size 128 \
|
|
415
|
+
--crop_stride 32 \
|
|
416
|
+
--saved_image_type png \
|
|
417
|
+
--path_to_save outputs/patient_001
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
Positive patient:
|
|
421
|
+
|
|
422
|
+
```bash
|
|
423
|
+
uv run cropro \
|
|
424
|
+
--crop_method stride \
|
|
425
|
+
--patient_status positive \
|
|
426
|
+
--sequence_type T2W \
|
|
427
|
+
--orig_img_path_t2w data/patient_002/t2w.nii.gz \
|
|
428
|
+
--seg_img_path data/patient_002/prostate_gland_mask.nii.gz \
|
|
429
|
+
--seg_img_path_lesion data/patient_002/lesion_mask.nii.gz \
|
|
430
|
+
--tumor_label_level 1 \
|
|
431
|
+
--c_min_positive 0.2 \
|
|
432
|
+
--pixel_spacing 0.4 \
|
|
433
|
+
--crop_image_size 128 \
|
|
434
|
+
--crop_stride 32 \
|
|
435
|
+
--saved_image_type png \
|
|
436
|
+
--path_to_save outputs/patient_002
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
Boolean CLI arguments require explicit values:
|
|
440
|
+
|
|
441
|
+
```bash
|
|
442
|
+
uv run cropro --keep_all_slice false --do_normalization true ...
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
## Output
|
|
446
|
+
|
|
447
|
+
CROPro writes cropped files to `path_to_save`. Filenames include:
|
|
448
|
+
|
|
449
|
+
- sequence type
|
|
450
|
+
- slice number
|
|
451
|
+
- crop index when applicable
|
|
452
|
+
- crop coordinates
|
|
453
|
+
- modality suffix such as `T2W`, `ADC`, or `HBV`
|
|
454
|
+
|
|
455
|
+
Example:
|
|
456
|
+
|
|
457
|
+
```text
|
|
458
|
+
outputs/patient_001/
|
|
459
|
+
T2W_slice_7_of_21_1_cord_160_166_T2W.png
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
## Configuration Reference
|
|
463
|
+
|
|
464
|
+
These variables are accepted by the Python `CropConfig` class and by CLI arguments with the same names.
|
|
465
|
+
|
|
466
|
+
| Setting | Default | Meaning |
|
|
467
|
+
| --- | --- | --- |
|
|
468
|
+
| `crop_method` | `center` | Crop strategy: `center`, `random`, or `stride`. |
|
|
469
|
+
| `orig_img_path_t2w` | `None` | T2W image path. Required for all workflows. |
|
|
470
|
+
| `orig_img_path_adc` | `None` | ADC image path. Required when `sequence_type="bpMRI"`. |
|
|
471
|
+
| `orig_img_path_hbv` | `None` | HBV image path. Required when `sequence_type="bpMRI"`. |
|
|
472
|
+
| `seg_img_path` | `None` | Prostate gland segmentation mask path. Required for negative, unknown, and positive workflows. |
|
|
473
|
+
| `seg_img_path_lesion` | `None` | Lesion segmentation mask path. Required for positive patients unless the gland mask already contains lesion labels. |
|
|
474
|
+
| `prostate_gland_seg_contains_lesion` | `False` | Set to `True` when `seg_img_path` contains both gland and lesion labels. |
|
|
475
|
+
| `tumor_label_level` | `2` | Label value used for lesion pixels. Use `1` if your lesion mask stores lesions as label `1`. |
|
|
476
|
+
| `patient_status` | `negative` | `negative`, `positive`, or `unknown`. |
|
|
477
|
+
| `pixel_spacing` | `0.5` | Target in-plane spacing in millimeters per pixel before cropping. |
|
|
478
|
+
| `crop_image_size` | `128` | Output crop width and height in pixels. |
|
|
479
|
+
| `sample_number` | `12` | Number of random crops to try when `crop_method="random"`. |
|
|
480
|
+
| `crop_stride` | `32` | Step size in pixels when `crop_method="stride"`. |
|
|
481
|
+
| `sequence_type` | `T2W` | `T2W` for T2W-only crops, or `bpMRI` for T2W/ADC/HBV crops. |
|
|
482
|
+
| `normalized_image` | `True` | Set to `True` when the source image is already normalized. |
|
|
483
|
+
| `normalized_vmaxNumber` | `242` | Maximum value used by the legacy normalization/saving path. |
|
|
484
|
+
| `do_normalization` | `False` | Normalize image intensity before saving. |
|
|
485
|
+
| `min_percentile` | `0` | Lower percentile for intensity normalization. |
|
|
486
|
+
| `max_percentile` | `99.5` | Upper percentile for intensity normalization. |
|
|
487
|
+
| `saved_image_type` | `tiff` | Output type: `png`, `jpg`, `jpeg`, `tiff`, `tif`, `npy`, or `nmp`. |
|
|
488
|
+
| `path_to_save` | `save_crop` | Output directory. |
|
|
489
|
+
| `c_min_positive` | `0.2` | Minimum lesion overlap required for saving a positive crop. |
|
|
490
|
+
| `c_min_negative` | `1` | Minimum gland coverage rule used by negative crop selection. |
|
|
491
|
+
| `percentage_of_allowed_overlapping_betweeing_gland_lesions_mask` | `50.0` | Allowed overlap percentage between gland and lesion masks for mask consistency checks. |
|
|
492
|
+
| `number_of_slices_to_exclude_from_mask_gland` | `1` | Number of gland-mask edge slices to exclude from crop selection. |
|
|
493
|
+
| `keep_all_slice` | `True` | Keep all selected slices instead of applying slice filtering. |
|
|
494
|
+
|
|
495
|
+
## Project Structure
|
|
496
|
+
|
|
497
|
+
```text
|
|
498
|
+
CROPro/
|
|
499
|
+
src/cropro/ # Python package
|
|
500
|
+
cropping/ # Cropping implementation
|
|
501
|
+
cli.py # Command-line interface
|
|
502
|
+
config.py # CropConfig dataclass
|
|
503
|
+
core.py # CROPro runner
|
|
504
|
+
examples/ # Runnable examples
|
|
505
|
+
tests/ # Tests
|
|
506
|
+
config/ # Runtime configuration
|
|
507
|
+
assets/readme/ # README images
|
|
508
|
+
scripts/ # Dataset and example scripts
|
|
509
|
+
pyproject.toml # Package metadata and tooling config
|
|
510
|
+
uv.lock # Locked development environment
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
## Development
|
|
514
|
+
|
|
515
|
+
Install development dependencies:
|
|
516
|
+
|
|
517
|
+
```bash
|
|
518
|
+
uv sync --extra dev
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
Run checks:
|
|
522
|
+
|
|
523
|
+
```bash
|
|
524
|
+
uv run ruff check .
|
|
525
|
+
uv run pytest
|
|
526
|
+
uv run python -m compileall src main.py examples tests
|
|
527
|
+
uv build --no-sources
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
Before publishing, make sure the package name and version in `pyproject.toml` are correct. After publication, verify installation in a fresh project:
|
|
531
|
+
|
|
532
|
+
```bash
|
|
533
|
+
uv init cropro-smoke-test
|
|
534
|
+
cd cropro-smoke-test
|
|
535
|
+
uv add cropro
|
|
536
|
+
uv run python -c "from cropro import CROPro, CropConfig; print(CropConfig().crop_method)"
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
For publishing, prefer PyPI Trusted Publishing from CI. If publishing manually, use a scoped PyPI token and avoid storing it in shell history or repository files.
|
|
540
|
+
|
|
541
|
+
## Troubleshooting
|
|
542
|
+
|
|
543
|
+
### `ModuleNotFoundError: No module named 'cropro'`
|
|
544
|
+
|
|
545
|
+
Install the package from the repository root:
|
|
546
|
+
|
|
547
|
+
```bash
|
|
548
|
+
uv sync
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
Then run commands with `uv run`.
|
|
552
|
+
|
|
553
|
+
### `ModuleNotFoundError: No module named 'SimpleITK'`
|
|
554
|
+
|
|
555
|
+
Install dependencies:
|
|
556
|
+
|
|
557
|
+
```bash
|
|
558
|
+
uv sync
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
### No crops are saved
|
|
562
|
+
|
|
563
|
+
Check that:
|
|
564
|
+
|
|
565
|
+
- `patient_status` matches the case.
|
|
566
|
+
- `seg_img_path` points to a non-empty prostate mask.
|
|
567
|
+
- Positive cases include `seg_img_path_lesion`, or set `prostate_gland_seg_contains_lesion=True` if lesion labels are inside the gland mask.
|
|
568
|
+
- `tumor_label_level` matches the lesion label value in the mask.
|
|
569
|
+
- `crop_image_size`, `pixel_spacing`, and `c_min_positive` are not too restrictive.
|
|
570
|
+
- T2W, ADC, HBV, gland mask, and lesion mask are spatially aligned.
|
|
571
|
+
|
|
572
|
+
### PI-CAI download is interrupted
|
|
573
|
+
|
|
574
|
+
Run the downloader again. It resumes partial archives:
|
|
575
|
+
|
|
576
|
+
```bash
|
|
577
|
+
bash scripts/download_dataset.sh
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
## Citation
|
|
581
|
+
|
|
582
|
+
If you use CROPro, please cite:
|
|
583
|
+
|
|
584
|
+
```bibtex
|
|
585
|
+
@article{10.1117/1.JMI.10.2.024004,
|
|
586
|
+
author = {Alexandros Patsanis and Mohammed R. S. Sunoqrot and Tone F. Bathen and Mattijs Elschot},
|
|
587
|
+
title = {{CROPro: a tool for automated cropping of prostate magnetic resonance images}},
|
|
588
|
+
volume = {10},
|
|
589
|
+
journal = {Journal of Medical Imaging},
|
|
590
|
+
number = {2},
|
|
591
|
+
publisher = {SPIE},
|
|
592
|
+
pages = {024004},
|
|
593
|
+
year = {2023},
|
|
594
|
+
doi = {10.1117/1.JMI.10.2.024004},
|
|
595
|
+
url = {https://doi.org/10.1117/1.JMI.10.2.024004}
|
|
596
|
+
}
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
## License
|
|
600
|
+
|
|
601
|
+
CROPro is distributed under the MIT License. See [LICENSE](LICENSE).
|