voidspace 0.1.4__tar.gz → 0.1.5__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.
- {voidspace-0.1.4/src/voidspace.egg-info → voidspace-0.1.5}/PKG-INFO +1 -1
- {voidspace-0.1.4 → voidspace-0.1.5}/pyproject.toml +1 -1
- {voidspace-0.1.4 → voidspace-0.1.5}/src/voidspace/workflows.py +8 -2
- {voidspace-0.1.4 → voidspace-0.1.5/src/voidspace.egg-info}/PKG-INFO +1 -1
- {voidspace-0.1.4 → voidspace-0.1.5}/tests/test_workflows.py +41 -0
- {voidspace-0.1.4 → voidspace-0.1.5}/LICENSE +0 -0
- {voidspace-0.1.4 → voidspace-0.1.5}/README.md +0 -0
- {voidspace-0.1.4 → voidspace-0.1.5}/setup.cfg +0 -0
- {voidspace-0.1.4 → voidspace-0.1.5}/src/voidspace/__init__.py +0 -0
- {voidspace-0.1.4 → voidspace-0.1.5}/src/voidspace/change.py +0 -0
- {voidspace-0.1.4 → voidspace-0.1.5}/src/voidspace/cli.py +0 -0
- {voidspace-0.1.4 → voidspace-0.1.5}/src/voidspace/io.py +0 -0
- {voidspace-0.1.4 → voidspace-0.1.5}/src/voidspace/metrics.py +0 -0
- {voidspace-0.1.4 → voidspace-0.1.5}/src/voidspace/models.py +0 -0
- {voidspace-0.1.4 → voidspace-0.1.5}/src/voidspace/morphology.py +0 -0
- {voidspace-0.1.4 → voidspace-0.1.5}/src/voidspace/segment.py +0 -0
- {voidspace-0.1.4 → voidspace-0.1.5}/src/voidspace.egg-info/SOURCES.txt +0 -0
- {voidspace-0.1.4 → voidspace-0.1.5}/src/voidspace.egg-info/dependency_links.txt +0 -0
- {voidspace-0.1.4 → voidspace-0.1.5}/src/voidspace.egg-info/entry_points.txt +0 -0
- {voidspace-0.1.4 → voidspace-0.1.5}/src/voidspace.egg-info/requires.txt +0 -0
- {voidspace-0.1.4 → voidspace-0.1.5}/src/voidspace.egg-info/top_level.txt +0 -0
- {voidspace-0.1.4 → voidspace-0.1.5}/tests/test_aim_io.py +0 -0
- {voidspace-0.1.4 → voidspace-0.1.5}/tests/test_change.py +0 -0
- {voidspace-0.1.4 → voidspace-0.1.5}/tests/test_cli.py +0 -0
- {voidspace-0.1.4 → voidspace-0.1.5}/tests/test_metrics.py +0 -0
- {voidspace-0.1.4 → voidspace-0.1.5}/tests/test_models.py +0 -0
- {voidspace-0.1.4 → voidspace-0.1.5}/tests/test_morphology.py +0 -0
- {voidspace-0.1.4 → voidspace-0.1.5}/tests/test_segment.py +0 -0
|
@@ -69,8 +69,14 @@ def intersect_masks(
|
|
|
69
69
|
intersection, spacing, reference = read_mask(paths[0])
|
|
70
70
|
for path in paths[1:]:
|
|
71
71
|
mask, mask_spacing, mask_reference = read_mask(path)
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
# AIM header quantization and NIfTI float storage can differ below 1 nm.
|
|
73
|
+
# Tolerate that rounding, but do not silently mix scan resolutions.
|
|
74
|
+
if not np.allclose(mask_spacing, spacing, rtol=0, atol=1e-6):
|
|
75
|
+
raise ValueError(
|
|
76
|
+
f"mask spacing mismatch (z, y, x; mm): {paths[0]} has {spacing}; "
|
|
77
|
+
f"{path} has {mask_spacing}. Expected the matching native-space "
|
|
78
|
+
"common region at the same scan resolution."
|
|
79
|
+
)
|
|
74
80
|
mask = _array_on_reference_grid(mask, mask_reference, reference)
|
|
75
81
|
if mask.shape != intersection.shape:
|
|
76
82
|
raise ValueError("all masks must overlap the reference image space")
|
|
@@ -116,6 +116,47 @@ def test_intersect_masks_writes_boolean_intersection(tmp_path):
|
|
|
116
116
|
assert np.array_equal(result, full & common)
|
|
117
117
|
|
|
118
118
|
|
|
119
|
+
@pytest.mark.parametrize("shift", [0.0, 0.0607])
|
|
120
|
+
@pytest.mark.parametrize("source_spacing, common_spacing", [
|
|
121
|
+
((0.0607,) * 3, (float(np.float32(0.0607)),) * 3),
|
|
122
|
+
((0.06069900095462799, 0.06069900095462799, 0.06069599837064743),
|
|
123
|
+
(0.06069965288043022, 0.06069965288043022, 0.06069643050432205)),
|
|
124
|
+
])
|
|
125
|
+
def test_intersect_masks_accepts_header_rounding_and_preserves_physical_placement(
|
|
126
|
+
tmp_path, shift, source_spacing, common_spacing,
|
|
127
|
+
):
|
|
128
|
+
full = sitk.GetImageFromArray(np.ones((5, 5, 5), dtype=np.uint8))
|
|
129
|
+
full.SetSpacing(source_spacing)
|
|
130
|
+
sitk.WriteImage(full, str(tmp_path / "full.mha"))
|
|
131
|
+
common = sitk.GetImageFromArray(np.ones((3, 3, 3), dtype=np.uint8))
|
|
132
|
+
common.SetSpacing(common_spacing)
|
|
133
|
+
common.SetOrigin((shift,) * 3)
|
|
134
|
+
sitk.WriteImage(common, str(tmp_path / "common.nii.gz"))
|
|
135
|
+
|
|
136
|
+
output = intersect_masks(
|
|
137
|
+
[tmp_path / "full.mha", tmp_path / "common.nii.gz"],
|
|
138
|
+
output_path=tmp_path / "intersection.nii.gz",
|
|
139
|
+
)
|
|
140
|
+
image = sitk.ReadImage(str(output))
|
|
141
|
+
expected = np.zeros((5, 5, 5), dtype=bool)
|
|
142
|
+
start = 0 if shift == 0 else 1
|
|
143
|
+
expected[start:start + 3, start:start + 3, start:start + 3] = True
|
|
144
|
+
assert np.array_equal(sitk.GetArrayFromImage(image).astype(bool), expected)
|
|
145
|
+
assert np.allclose(image.GetSpacing(), source_spacing, rtol=0, atol=1e-8)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def test_intersect_masks_reports_genuinely_different_resolution(tmp_path):
|
|
149
|
+
for name, spacing in [("full", 0.0607), ("common", 0.082)]:
|
|
150
|
+
image = sitk.GetImageFromArray(np.ones((3, 3, 3), dtype=np.uint8))
|
|
151
|
+
image.SetSpacing((spacing,) * 3)
|
|
152
|
+
sitk.WriteImage(image, str(tmp_path / f"{name}.mha"))
|
|
153
|
+
with pytest.raises(ValueError, match="spacing.*full.*common"):
|
|
154
|
+
intersect_masks(
|
|
155
|
+
[tmp_path / "full.mha", tmp_path / "common.mha"],
|
|
156
|
+
output_path=tmp_path / "intersection.nii.gz",
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
|
|
119
160
|
def test_intersect_masks_resamples_cropped_mask_to_reference_grid(tmp_path):
|
|
120
161
|
full = np.ones((5, 5, 5), dtype=bool)
|
|
121
162
|
common = np.ones((3, 3, 3), dtype=bool)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|