Automated cropping of prostate MR images.
Project description
CROPro
CROPro prepares prostate MRI data for AI workflows.
It supports full-dataset download, resampling, normalization, and cropping for:
- T2W-only workflows
- bpMRI workflows (T2W + ADC + HBV)
- Negative, positive, and unknown patient groups
If you use CROPro, please cite the paper in the Citation section.
What Matters Most
- One CLI with 4 pipelines:
download,resample,normalize,crop - Dataset-agnostic core with optional local dataset plugins
- Batch workflows and per-case workflows
- Automatic bpMRI alignment checks to prevent misaligned crops
Install
Requirements:
- Python 3.13+
Install in a project:
uv add cropro
Or install as a standalone CLI:
uv tool install cropro
From this repository, run commands with uv run.
Quick Start (Generic)
- Download data:
cropro download --dataset mydataset --url https://host/path/dataset.zip
- Resample dataset to align ADC/HBV and masks to each T2W grid:
cropro resample --schema config/pipeline.toml
- Crop dataset:
cropro crop --schema config/pipeline.toml
Pipelines
| Pipeline | Command | Use it when |
|---|---|---|
| Download | cropro download |
You want CROPro to fetch archives from URLs or a local dataset plugin. |
| Resample | cropro resample |
You need spatially aligned bpMRI data before cropping. |
| Normalize | cropro normalize |
You want to normalize full T2W datasets in place or to a new folder. |
| Crop | cropro crop |
You want model-ready crops from a case or a dataset. |
cropro without subcommand defaults to crop for backward compatibility.
Pipeline Decision Guide
For bpMRI workflows, alignment to T2W is required before cropping ADC/HBV together with T2W. Choose one:
- Resample first (recommended) — run
cropro resampleonce to write aligned copies of the whole database, then crop those. - On-the-fly during cropping — pass
--resample_bpmri_to_t2w trueto align each case as you crop (no extra files written).
If neither is enabled and bpMRI volumes are misaligned, crop stops with a clear message instead of producing wrong crops.
flowchart TD
A[Start] --> B{sequence_type}
B -->|T2W| C[cropro crop]
B -->|bpMRI| D{Already aligned to T2W grid?}
D -->|Yes| C
D -->|No| E[Run cropro resample]
E --> F{Normalize T2W volumes first?}
F -->|Yes| G[cropro normalize or schema pre-step]
F -->|No| H[cropro crop]
G --> H
C --> I[Saved crops]
H --> I
Most Useful Commands
cropro --help
cropro download --help
cropro resample --help
cropro normalize --help
cropro crop --help
Download a dataset from URL(s):
cropro download --dataset mydataset --url https://host/path/dataset.zip
Crop a whole folder (batch mode):
cropro crop \
--images-root dataset/MyDataset/images_resampled \
--output-root dataset/cropro/MyDataset/stride_0.4_128 \
--sequence_type bpMRI \
--crop_method stride \
--pixel_spacing 0.4 \
--crop_image_size 128 \
--crop_stride 32 \
--saved_image_type png
Normalize all T2W volumes in a dataset:
cropro normalize \
--images-root dataset/MyDataset/images_resampled \
--method autoref
Python API
Crop a Single Case
Negative or unknown case:
from cropro import CROPro, CropConfig
config = CropConfig(
crop_method="stride",
patient_status="negative",
sequence_type="T2W",
orig_img_path_t2w="data/patient_001/t2w.nii.gz",
seg_img_path="data/patient_001/prostate_gland_mask.nii.gz",
pixel_spacing=0.4,
crop_image_size=128,
crop_stride=32,
saved_image_type="png",
path_to_save="outputs/patient_001",
)
CROPro(config).run()
Positive case:
from cropro import CROPro, CropConfig
config = CropConfig(
crop_method="stride",
patient_status="positive",
sequence_type="T2W",
orig_img_path_t2w="data/patient_002/t2w.nii.gz",
seg_img_path="data/patient_002/prostate_gland_mask.nii.gz",
seg_img_path_lesion="data/patient_002/lesion_mask.nii.gz",
tumor_label_level=1,
c_min_positive=0.2,
pixel_spacing=0.4,
crop_image_size=128,
crop_stride=32,
saved_image_type="png",
path_to_save="outputs/patient_002",
)
CROPro(config).run()
bpMRI case:
from cropro import CROPro, CropConfig
config = CropConfig(
crop_method="center",
patient_status="negative",
sequence_type="bpMRI",
orig_img_path_t2w="data/patient_003/t2w.nii.gz",
orig_img_path_adc="data/patient_003/adc.nii.gz",
orig_img_path_hbv="data/patient_003/hbv.nii.gz",
seg_img_path="data/patient_003/prostate_gland_mask.nii.gz",
pixel_spacing=0.5,
crop_image_size=128,
saved_image_type="png",
path_to_save="outputs/patient_003",
)
CROPro(config).run()
Inputs (Minimal)
- Negative/Unknown case:
- T2W image
- Gland mask
- Positive case:
- T2W image
- Gland mask
- Lesion mask
- bpMRI case:
- T2W image
- ADC image
- HBV image
- Required masks based on status above
Supported image formats are those readable by SimpleITK (for example .mha, .nii, .nii.gz).
Schema Files
Use schema files to keep paths and defaults in one place:
config/pipeline.toml(generic template)
You can override schema values with CLI flags.
Local Dataset Plugins (Git-Ignored)
CROPro is dataset-agnostic in committed code. Put dataset-specific behavior in:
.cropro_user/dataset_plugins.py(git-ignored)
Plugins can define:
- resample layout defaults (suffixes, mask roots)
- custom download logic (URLs, folds, labels repo, auth flow)
Use plugin names from CLI:
cropro resample --dataset-plugin mydataset --schema config/my_dataset.toml
cropro download --dataset mydataset --dataset-plugin mydataset
How To Add A Dataset Plugin
Create a file at .cropro_user/dataset_plugins.py (this folder is git-ignored) and define a class that inherits from DatasetPlugin.
Minimal example:
from pathlib import Path
from cropro.datasets import DatasetPlugin
from cropro.download import download_from_urls
class MyDatasetPlugin(DatasetPlugin):
name = "mydataset"
def apply_resample_defaults(self, *, images_root: Path, layout, options: dict) -> None:
# Optional defaults used by `cropro resample` when values are not
# explicitly provided in schema/CLI.
layout.t2w_suffix = "_t2w.mha"
layout.adc_suffix = "_adc.mha"
layout.hbv_suffix = "_hbv.mha"
layout.mask_suffix = ".nii.gz"
layout.gland_root = images_root.parent / "labels" / "gland"
layout.lesion_root = images_root.parent / "labels" / "lesion"
def download(self, *, config, urls: list[str], folds: list[str], skip_labels: bool) -> bool:
# Optional custom download behavior.
# If you return False, CROPro falls back to generic --url downloading.
if not urls:
urls = ["https://host.example.org/path/to/mydataset.zip"]
download_from_urls(config, urls=urls)
return True
PLUGINS = [MyDatasetPlugin()]
Optional: if your plugin file is not at .cropro_user/dataset_plugins.py, set:
# Windows PowerShell
$env:CROPRO_DATASET_PLUGIN_FILE = "C:/path/to/dataset_plugins.py"
You can also bind a schema to a plugin:
[dataset]
name = "MyDataset"
plugin = "mydataset"
Run with plugin examples:
# 1) Download via plugin handler
cropro download --dataset mydataset --dataset-plugin mydataset
# 2) Resample using plugin defaults + schema overrides
cropro resample --schema config/my_dataset.toml --dataset-plugin mydataset
# 3) Crop the dataset
cropro crop --schema config/my_dataset.toml --images-root dataset/MyDataset/images_resampled
Creating a Custom Schema
A schema describes your dataset layout, naming conventions, and default crop settings in one TOML file:
# config/my_dataset.toml
[dataset]
name = "MyDataset"
[paths]
images_root = "dataset/MyDataset/images"
output_root = "dataset/MyDataset/images_resampled"
gland_root = "dataset/MyDataset/masks/gland"
lesion_root = "dataset/MyDataset/masks/lesion"
archives_root = "none"
cropro_root = "dataset/MyDataset/cropped_images"
[naming]
t2w_suffix = "_t2w.nii.gz"
adc_suffix = "_adc.nii.gz"
hbv_suffix = "_hbv.nii.gz"
mask_suffix = ".nii.gz"
[crop]
sequence_type = "bpMRI"
pixel_spacing = 0.4
crop_image_size = 128
crop_stride = 32
crop_method = "random"
saved_image_type = "png"
[split]
enabled = true
train_ratio = 0.70
val_ratio = 0.15
test_ratio = 0.15
split_level = "patient"
Use the schema with any pipeline:
cropro resample --schema config/my_dataset.toml
cropro crop --schema config/my_dataset.toml
CLI flags always override schema values.
Archive auto-unpacking: if you have *.zip files in <images-root>/../archives, the resample pipeline automatically unpacks them before aligning.
Output
Crops are written to your selected output directory (--path_to_save for single-case mode, or --output-root for batch mode).
For schema-based runs, outputs are typically organized under each schema cropro_root path.
Troubleshooting
- If bpMRI crop fails with an alignment message, run
cropro resamplefirst. - If no crops are saved for positive cases, verify lesion label values and
--tumor_label_level. - If running from source and imports fail, run
uv sync(oruv sync --extra devfor contributor tools).
Development
Install project dependencies:
uv sync --extra dev
Run checks:
uv run ruff check .
uv run pytest
Citation
@article{10.1117/1.JMI.10.2.024004,
author = {Alexandros Patsanis and Mohammed R. S. Sunoqrot and Tone F. Bathen and Mattijs Elschot},
title = {{CROPro: a tool for automated cropping of prostate magnetic resonance images}},
volume = {10},
journal = {Journal of Medical Imaging},
number = {2},
publisher = {SPIE},
pages = {024004},
year = {2023},
doi = {10.1117/1.JMI.10.2.024004},
url = {https://doi.org/10.1117/1.JMI.10.2.024004}
}
License
CC BY-NC 4.0 (Creative Commons Attribution-NonCommercial 4.0 International)
CROPro is for research and educational use only. Commercial use is prohibited.
You are free to use, modify, and distribute CROPro for non-commercial purposes with proper attribution.
See LICENSE for full terms. Dataset licenses may vary by source and must be reviewed separately.
Detailed Reference
Configuration Reference
These variables are accepted by the Python CropConfig class and by CLI arguments with the same names.
| Setting | Default | Meaning |
|---|---|---|
crop_method |
center |
Crop strategy: center, random, or stride. |
orig_img_path_t2w |
None |
T2W image path. Required for all workflows. |
orig_img_path_adc |
None |
ADC image path. Required when sequence_type="bpMRI". |
orig_img_path_hbv |
None |
HBV image path. Required when sequence_type="bpMRI". |
seg_img_path |
None |
Prostate gland segmentation mask path. Required for negative, unknown, and positive workflows. |
seg_img_path_lesion |
None |
Lesion segmentation mask path. Required for positive patients unless the gland mask already contains lesion labels. |
prostate_gland_seg_contains_lesion |
False |
Set to True when seg_img_path contains both gland and lesion labels. |
tumor_label_level |
2 |
Label value used for lesion pixels. Use 1 if your lesion mask stores lesions as label 1. |
patient_status |
negative |
negative, positive, or unknown. |
pixel_spacing |
0.5 |
Target in-plane spacing in millimeters per pixel before cropping. |
crop_image_size |
128 |
Output crop width and height in pixels. |
sample_number |
12 |
Number of random crops to try when crop_method="random". |
crop_stride |
32 |
Step size in pixels when crop_method="stride". |
sequence_type |
T2W |
T2W for T2W-only crops, or bpMRI for T2W/ADC/HBV crops. |
t2w_normalization_method |
autoref |
Normalization strategy for T2W: percentile, autoref, gaussian, or zscore_clip. |
adc_normalization_method |
percentile |
Normalization strategy for ADC: percentile, gaussian, or zscore_clip. |
hbv_normalization_method |
percentile |
Normalization strategy for HBV: percentile, gaussian, or zscore_clip. |
t2w_min_percentile |
0.5 |
T2W-specific lower percentile for clipping/windowing. |
t2w_max_percentile |
99.5 |
T2W-specific upper percentile for clipping/windowing. |
adc_min_percentile |
0.5 |
ADC-specific lower percentile for clipping/windowing. |
adc_max_percentile |
99.5 |
ADC-specific upper percentile for clipping/windowing. |
hbv_min_percentile |
0.5 |
HBV-specific lower percentile for clipping/windowing. |
hbv_max_percentile |
99.9 |
HBV-specific upper percentile for clipping/windowing. |
saved_image_type |
tiff |
Output type: png, jpg, jpeg, tiff, tif, npy, nmp, or npm (nmp/npm map to npy). |
path_to_save |
save_crop |
Output directory. |
c_min_positive |
0.2 |
Minimum lesion overlap required for saving a positive crop. |
c_min_negative |
1 |
Minimum gland coverage rule used by negative crop selection. |
keep_all_slice |
True |
Keep all selected slices instead of applying slice filtering. |
random_seed |
None |
Optional integer seed for deterministic random crop sampling. |
Aligning ADC/HBV to T2W (bpMRI)
In many datasets, T2W, ADC and HBV sequences are acquired independently and can differ in slice count and in-plane size/spacing. Since CROPro crops all three at the same slice index and (x, y) origin, a mismatch can produce misaligned crops or an IndexError.
By default the crop pipeline checks that ADC and HBV are aligned to T2W before it starts. If they are not, it stops with a clear message instead of producing misaligned crops, and points you to the two ways of fixing it:
-
Resample pipeline (recommended) — run
cropro resampleonce to write aligned copies of the whole database, then crop those.cropro resample --images-root dataset/MyDataset/images --output-root dataset/MyDataset/images_resampled
This writes the resampled ADC/HBV files for every case into a new
dataset/MyDataset/images_resampledfolder. Omit--output-rootto write the aligned copies next to the originals with a_to_t2wsuffix instead. -
On-the-fly during cropping — pass
--resample_bpmri_to_t2w true. CROPro then resamples each ADC/HBV volume onto the T2W grid as it loads them (no extra files written) and the alignment check is skipped.
Per-modality Normalization
Different sequences benefit from different normalization strategies. T2W has no fixed quantitative meaning and works well with autoref (AutoRef fat/muscle reference normalization) or gaussian, while ADC and HBV are better kept on a robust percentile window.
Override any sequence to mix methods in a single run:
cropro crop --do_normalization true \
--t2w_normalization_method autoref \
--adc_normalization_method percentile \
--hbv_normalization_method percentile ...
Dataset Splitting
CROPro includes a patient-level dataset splitter that divides cases into train / validation / test subsets and generates crops with the appropriate strategy for each.
Splitting is always at the patient level so that all crops from the same patient end up in a single subset and there is no data leakage.
Split Strategies
| Subset | Recommended crop method | Reason |
|---|---|---|
| Train | random (or center) |
Stochastic sampling gives data augmentation and exposes the model to varied prostate sub-regions per slice. |
| Validation | stride |
Covers the entire prostate area on every slice without gaps, which is required for patient-level scoring. |
| Test | stride |
Same reason as validation — complete coverage is mandatory for fair patient-level evaluation. |
Split Configuration
The split_level option controls which slices are included when generating crops for each subset:
| Level | What is included |
|---|---|
"patient" (default) |
Every slice that contains the prostate gland mask is cropped. Required for patient-level inference. |
"lesion" |
Only lesion-containing slices (positive cases) or central gland slices (negative cases). Useful for slice/image-level training. |
Project Structure
CROPro/
src/cropro/ # Python package
cropping/ # Cropping implementation
cli.py # Command-line interface
config.py # CropConfig dataclass
core.py # CROPro runner
examples/ # Runnable examples
tests/ # Tests
config/ # Runtime configuration
pyproject.toml # Package metadata and tooling config
Development Checklist
Install development dependencies:
uv sync --extra dev
Run validation:
uv run ruff check .
uv run pytest
uv run python -m compileall src main.py examples tests
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cropro-0.1.8.tar.gz.
File metadata
- Download URL: cropro-0.1.8.tar.gz
- Upload date:
- Size: 95.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
69be5cf4602a1f3b65d646add2515543ce2b75c9f646aac31f9195787c9bba5a
|
|
| MD5 |
322409d3cae8420cd8b3aea1de0e0797
|
|
| BLAKE2b-256 |
863da1c27abb7f5bace454ac2d8ba552e475daf788aa7099550ccd54b71a2955
|
Provenance
The following attestation bundles were made for cropro-0.1.8.tar.gz:
Publisher:
pypi-publish.yml on alexofficial/CROPro
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cropro-0.1.8.tar.gz -
Subject digest:
69be5cf4602a1f3b65d646add2515543ce2b75c9f646aac31f9195787c9bba5a - Sigstore transparency entry: 2025695028
- Sigstore integration time:
-
Permalink:
alexofficial/CROPro@5eae3bc2acd440c06e2962434b457bb5f608c153 -
Branch / Tag:
refs/tags/v0.1.8 - Owner: https://github.com/alexofficial
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-publish.yml@5eae3bc2acd440c06e2962434b457bb5f608c153 -
Trigger Event:
push
-
Statement type:
File details
Details for the file cropro-0.1.8-py3-none-any.whl.
File metadata
- Download URL: cropro-0.1.8-py3-none-any.whl
- Upload date:
- Size: 88.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2ef43f9f933d28142ce225ae126ac05d5f27ad50059040fd936f5adddedb71e7
|
|
| MD5 |
be14692cee5aca85ff7550d377622ad4
|
|
| BLAKE2b-256 |
893cf0ca4c8da4d8e5ecea3fa62d75225d9395a1e3f0c2123b7cec212ac5a88b
|
Provenance
The following attestation bundles were made for cropro-0.1.8-py3-none-any.whl:
Publisher:
pypi-publish.yml on alexofficial/CROPro
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cropro-0.1.8-py3-none-any.whl -
Subject digest:
2ef43f9f933d28142ce225ae126ac05d5f27ad50059040fd936f5adddedb71e7 - Sigstore transparency entry: 2025695197
- Sigstore integration time:
-
Permalink:
alexofficial/CROPro@5eae3bc2acd440c06e2962434b457bb5f608c153 -
Branch / Tag:
refs/tags/v0.1.8 - Owner: https://github.com/alexofficial
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-publish.yml@5eae3bc2acd440c06e2962434b457bb5f608c153 -
Trigger Event:
push
-
Statement type: