Skip to content

Commit 24665aa

Browse files
Add clinical DICOM preprocessing Python module, test module, PDF; remove notebooks
1 parent aeabbbd commit 24665aa

File tree

3 files changed

+116
-0
lines changed

3 files changed

+116
-0
lines changed

docs/clinical_dicom_workflow.pdf

96.7 KB
Binary file not shown.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import numpy as np
2+
import pytest
3+
4+
from monai.transforms import ScaleIntensityRange, NormalizeIntensity
5+
6+
7+
def test_ct_windowing_range_and_shape():
8+
rng = np.random.default_rng(0)
9+
10+
sample_ct = rng.integers(
11+
-1024, 2048, size=(64, 64, 64), dtype=np.int16
12+
)
13+
14+
transform = ScaleIntensityRange(
15+
a_min=-1000,
16+
a_max=400,
17+
b_min=0.0,
18+
b_max=1.0,
19+
clip=True,
20+
)
21+
22+
output = transform(sample_ct)
23+
output = np.asarray(output)
24+
25+
assert output.shape == sample_ct.shape
26+
assert np.isfinite(output).all()
27+
assert output.min() >= -1e-6
28+
assert output.max() <= 1.0 + 1e-6
29+
30+
31+
def test_mri_normalization_mean_std():
32+
rng = np.random.default_rng(0)
33+
34+
sample_mri = rng.random((64, 64, 64), dtype=np.float32)
35+
36+
transform = NormalizeIntensity(nonzero=True)
37+
38+
output = transform(sample_mri)
39+
output = np.asarray(output)
40+
41+
mean_val = float(output.mean())
42+
std_val = float(output.std())
43+
44+
assert output.shape == sample_mri.shape
45+
assert np.isclose(mean_val, 0.0, atol=0.1)
46+
assert np.isclose(std_val, 1.0, atol=0.1)
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
from typing import Union
2+
3+
from monai.transforms import (
4+
Compose,
5+
LoadImage,
6+
EnsureChannelFirst,
7+
ScaleIntensityRange,
8+
NormalizeIntensity,
9+
)
10+
11+
12+
def get_ct_preprocessing_pipeline():
13+
"""
14+
CT preprocessing pipeline using standard HU windowing.
15+
"""
16+
return Compose(
17+
[
18+
LoadImage(image_only=True),
19+
EnsureChannelFirst(),
20+
ScaleIntensityRange(
21+
a_min=-1000,
22+
a_max=400,
23+
b_min=0.0,
24+
b_max=1.0,
25+
clip=True,
26+
),
27+
]
28+
)
29+
30+
31+
def get_mri_preprocessing_pipeline():
32+
"""
33+
MRI preprocessing pipeline using intensity normalization.
34+
"""
35+
return Compose(
36+
[
37+
LoadImage(image_only=True),
38+
EnsureChannelFirst(),
39+
NormalizeIntensity(nonzero=True),
40+
]
41+
)
42+
43+
44+
def preprocess_dicom_series(
45+
dicom_path: Union[str, bytes],
46+
modality: str,
47+
):
48+
"""
49+
Preprocess a DICOM series based on modality.
50+
51+
Args:
52+
dicom_path: Path to DICOM file or directory.
53+
modality: CT, MR, or MRI.
54+
55+
Returns:
56+
Preprocessed image.
57+
"""
58+
if not isinstance(modality, str):
59+
raise TypeError("modality must be a string")
60+
61+
modality = modality.strip().upper()
62+
63+
if modality == "CT":
64+
transform = get_ct_preprocessing_pipeline()
65+
elif modality in ("MR", "MRI"):
66+
transform = get_mri_preprocessing_pipeline()
67+
else:
68+
raise ValueError("Unsupported modality")
69+
70+
return transform(dicom_path)

0 commit comments

Comments
 (0)