Automated cropping of prostate MR images.
Project description
CROPro
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.
The package supports:
center,random, andstridecrop strategies- T2W-only and bpMRI cropping workflows
- negative, positive, and unknown patient-status workflows
- configurable in-plane resampling through
pixel_spacing - Python API and command-line usage
If you use CROPro in research, please cite the paper listed in Citation:
CROPro: a tool for automated cropping of prostate magnetic resonance images.
Contents
- Installation
- Quick Start
- Input Data
- Patient Workflows
- Visual Examples
- Pixel Spacing
- PI-CAI Dataset Setup
- Command Line
- Configuration Reference
- Development
- PyPI Release
- Citation
Installation
This repository uses uv for dependency management.
Install uv if needed:
curl -LsSf https://astral.sh/uv/install.sh | sh
Clone the repository and install the environment:
git clone https://github.com/alexofficial/CROPro.git
cd CROPro
uv sync
Check that the CLI is available:
uv run cropro --help
For development tools such as tests and linting:
uv sync --extra dev
When CROPro is published to PyPI, users will be able to install it in a project with:
uv add cropro
For CLI-only use after PyPI publication:
uv tool install cropro
cropro --help
PyPI Release
This repository is configured to publish to PyPI through GitHub Actions using Trusted Publishing.
One-Time Setup
- On PyPI, create a project named
cropro(or claim it if it already exists under your account). - In PyPI project settings, configure a Trusted Publisher for this GitHub repository and workflow:
- owner/repo:
alexofficial/CROPro - workflow filename:
.github/workflows/pypi-publish.yml - environment:
pypi
Release Flow
- Update version in
pyproject.toml. - Commit and push to
main. - Create a GitHub release (for example tag
v0.1.1). - The
Publish to PyPIworkflow builds the package and uploads it to PyPI.
Optional Local Validation
python -m build
python -m twine check dist/*
Quick Start
Crop A Negative Or Unknown Case
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.
from cropro import CROPro, CropConfig
config = CropConfig(
crop_method="stride",
patient_status="negative", # "negative" or "unknown"
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()
Crop A Positive Case
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.
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()
Crop bpMRI
Set sequence_type="bpMRI" and provide T2W, ADC, and HBV images. CROPro saves aligned crops for each modality.
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()
Input Data
CROPro reads 3D medical image files through SimpleITK. Common formats include .nii, .nii.gz, .mha, .mhd, and other SimpleITK-readable formats.
For a negative or unknown patient, provide:
- T2W image
- prostate gland segmentation mask
For a positive patient, provide:
- T2W image
- prostate gland segmentation mask
- lesion segmentation mask
For sequence_type="bpMRI", also provide:
- ADC image
- HBV image
All images and masks should already be spatially aligned. CROPro resamples the image spacing for crop generation, but it does not perform image registration.
Patient Workflows
| Status | Intended use | Segmentation behavior |
|---|---|---|
negative |
Cancer-free cases | Uses the prostate gland mask to crop the prostate region. |
positive |
Cancer-present cases | Uses the prostate gland mask for crop placement and the lesion mask to keep crops containing enough lesion area. |
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. |
Visual Examples
Negative Or Unknown Workflow
For negative and unknown cases, the prostate gland mask defines the region to crop.
Stride-cropped bpMRI output at 0.4 mm/pixel. With crop_image_size=128, the crop covers about 51.2 x 51.2 mm.
The same case at 0.5 mm/pixel covers about 64.0 x 64.0 mm and includes more surrounding context.
Positive Workflow
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.
Stride-cropped bpMRI output at 0.4 mm/pixel:
The same positive case at 0.5 mm/pixel:
Pixel Spacing
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.
With crop_image_size=128:
| Pixel spacing | Crop size in pixels | Approximate physical area |
|---|---|---|
0.4 mm/pixel |
128 x 128 |
51.2 x 51.2 mm |
0.5 mm/pixel |
128 x 128 |
64.0 x 64.0 mm |
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.
from cropro import CROPro, CropConfig
tight_crop = CropConfig(
crop_method="center",
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,
path_to_save="outputs/patient_001_spacing_0.4",
)
wide_crop = CropConfig(
crop_method="center",
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.5,
crop_image_size=128,
path_to_save="outputs/patient_001_spacing_0.5",
)
CROPro(tight_crop).run()
CROPro(wide_crop).run()
PI-CAI Dataset Setup
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.
The default download fetches public image fold 0, which is about 5.4 GB, and clones the labels:
uv sync
bash scripts/download_dataset.sh
The script writes data under dataset/PI-CAI/. This directory is ignored by git.
The image download is resumable because the script uses curl -C -. If the download is interrupted, run the same command again:
bash scripts/download_dataset.sh
To download all five public image folds, about 26.9 GB:
CROPRO_PICAI_FOLDS="0 1 2 3 4" bash scripts/download_dataset.sh
To place the PI-CAI data somewhere else:
CROPRO_DATASET_ROOT="/path/to/PI-CAI" bash scripts/download_dataset.sh
Expected layout:
dataset/PI-CAI/
archives/
picai_public_images_fold0.zip
images/
10001/
10001_1000001_t2w.mha
10001_1000001_adc.mha
10001_1000001_hbv.mha
picai_labels/
anatomical_delineations/whole_gland/AI/Bosma22b/
csPCa_lesion_delineations/human_expert/resampled/
Run the examples:
uv run python examples/PI-CAI_negative_crop.py
uv run python examples/PI-CAI_positive_crop.py
Or run both:
bash scripts/examples.sh
PI-CAI notes:
- Public images:
https://zenodo.org/records/6624726 - Labels:
https://github.com/DIAGNijmegen/picai_labels - Official image names use
[patient_id]_[study_id]_t2w.mha,[patient_id]_[study_id]_adc.mha, and[patient_id]_[study_id]_hbv.mha. - Whole-gland masks are used for
negativeandunknowncrops. - Positive crops use the whole-gland mask plus the csPCa lesion mask. Set
tumor_label_levelto the lesion value used by the selected label file. The bundled PI-CAI positive example uses case10032_1000032, whose lesion label is3.
Command Line
After installation, use the cropro command. From this repository, prefix commands with uv run.
Negative or unknown patient:
uv run cropro \
--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
Positive patient:
uv run cropro \
--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
Boolean CLI arguments require explicit values:
uv run cropro --keep_all_slice false --do_normalization true ...
Output
CROPro writes cropped files to path_to_save. Filenames include:
- sequence type
- slice number
- crop index when applicable
- crop coordinates
- modality suffix such as
T2W,ADC, orHBV
Example:
outputs/patient_001/
T2W_slice_7_of_21_1_cord_160_166_T2W.png
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. |
normalized_image |
True |
Set to True when the source image is already normalized. |
normalized_vmaxNumber |
242 |
Maximum value used by the legacy normalization/saving path. |
do_normalization |
False |
Normalize image intensity before saving. |
min_percentile |
0 |
Lower percentile for intensity normalization. |
max_percentile |
99.5 |
Upper percentile for intensity normalization. |
saved_image_type |
tiff |
Output type: png, jpg, jpeg, tiff, tif, npy, or nmp. |
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. |
percentage_of_allowed_overlapping_betweeing_gland_lesions_mask |
50.0 |
Allowed overlap percentage between gland and lesion masks for mask consistency checks. |
number_of_slices_to_exclude_from_mask_gland |
1 |
Number of gland-mask edge slices to exclude from crop selection. |
keep_all_slice |
True |
Keep all selected slices instead of applying slice filtering. |
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
assets/readme/ # README images
scripts/ # Dataset and example scripts
pyproject.toml # Package metadata and tooling config
uv.lock # Locked development environment
Development
Install development dependencies:
uv sync --extra dev
Run checks:
uv run ruff check .
uv run pytest
uv run python -m compileall src main.py examples tests
uv build --no-sources
Before publishing, make sure the package name and version in pyproject.toml are correct. After publication, verify installation in a fresh project:
uv init cropro-smoke-test
cd cropro-smoke-test
uv add cropro
uv run python -c "from cropro import CROPro, CropConfig; print(CropConfig().crop_method)"
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.
Troubleshooting
ModuleNotFoundError: No module named 'cropro'
Install the package from the repository root:
uv sync
Then run commands with uv run.
ModuleNotFoundError: No module named 'SimpleITK'
Install dependencies:
uv sync
No crops are saved
Check that:
patient_statusmatches the case.seg_img_pathpoints to a non-empty prostate mask.- Positive cases include
seg_img_path_lesion, or setprostate_gland_seg_contains_lesion=Trueif lesion labels are inside the gland mask. tumor_label_levelmatches the lesion label value in the mask.crop_image_size,pixel_spacing, andc_min_positiveare not too restrictive.- T2W, ADC, HBV, gland mask, and lesion mask are spatially aligned.
PI-CAI download is interrupted
Run the downloader again. It resumes partial archives:
bash scripts/download_dataset.sh
Citation
If you use CROPro, please cite:
@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
CROPro is distributed under the MIT License. See LICENSE.
Project details
Release history Release notifications | RSS feed
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.2.tar.gz.
File metadata
- Download URL: cropro-0.1.2.tar.gz
- Upload date:
- Size: 30.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b782d3a200f98ce6b05ecaa488ea180d300eec2e7daae7571b083fdc1677e903
|
|
| MD5 |
7870357d25de50dafc06816897b99bfb
|
|
| BLAKE2b-256 |
2414a6690d16d8ef4730c38d9a10acc8e26abe443ea13570324446b9f7bc3ba8
|
Provenance
The following attestation bundles were made for cropro-0.1.2.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.2.tar.gz -
Subject digest:
b782d3a200f98ce6b05ecaa488ea180d300eec2e7daae7571b083fdc1677e903 - Sigstore transparency entry: 1615216637
- Sigstore integration time:
-
Permalink:
alexofficial/CROPro@afffeb0015ed150df8a08cf6ba2617f3b1e70797 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/alexofficial
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-publish.yml@afffeb0015ed150df8a08cf6ba2617f3b1e70797 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file cropro-0.1.2-py3-none-any.whl.
File metadata
- Download URL: cropro-0.1.2-py3-none-any.whl
- Upload date:
- Size: 29.9 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 |
7f332a4ee1dff39fac5de849f5ca30a9b3ebf807b8ca00177ff31ae196a1cf8b
|
|
| MD5 |
8016b65341d320e6ee5ebcf1bcc04d9c
|
|
| BLAKE2b-256 |
d84af73ea0fc9fa760b2c865212dcb1a2375d031eb6b9566161f808029b76865
|
Provenance
The following attestation bundles were made for cropro-0.1.2-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.2-py3-none-any.whl -
Subject digest:
7f332a4ee1dff39fac5de849f5ca30a9b3ebf807b8ca00177ff31ae196a1cf8b - Sigstore transparency entry: 1615216641
- Sigstore integration time:
-
Permalink:
alexofficial/CROPro@afffeb0015ed150df8a08cf6ba2617f3b1e70797 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/alexofficial
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-publish.yml@afffeb0015ed150df8a08cf6ba2617f3b1e70797 -
Trigger Event:
workflow_dispatch
-
Statement type: