Skip to content

Commit ca2294f

Browse files
authored
Merge pull request #779 from dave3d/SimpleITKImageSupport
ENH: Added support for SimpleITK Image loading
2 parents d4bd7c4 + e8aeb8f commit ca2294f

File tree

4 files changed

+186
-0
lines changed

4 files changed

+186
-0
lines changed

.github/workflows/notebook-test.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ jobs:
3131
python3 -m pip install pyimagej urllib3
3232
python3 -c "import imagej; ij = imagej.init('2.5.0'); print(ij.getVersion())"
3333
python3 -m pip install "itk>=5.3.0"
34+
python3 -m pip install "simpleitk>=2.5.3"
3435
3536
- name: Test notebooks
3637
run: |
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": null,
6+
"id": "6d0d7f82",
7+
"metadata": {
8+
"jupyter": {
9+
"source_hidden": true
10+
}
11+
},
12+
"outputs": [],
13+
"source": [
14+
"# Install SimpleITK if needed\n",
15+
"# %pip install SimpleITK"
16+
]
17+
},
18+
{
19+
"cell_type": "code",
20+
"execution_count": null,
21+
"id": "e212530d",
22+
"metadata": {},
23+
"outputs": [],
24+
"source": [
25+
"import SimpleITK as sitk\n",
26+
"import numpy as np\n",
27+
"from itkwidgets import view"
28+
]
29+
},
30+
{
31+
"cell_type": "markdown",
32+
"id": "555d9dd2",
33+
"metadata": {},
34+
"source": [
35+
"## Create a Simple 3D Image\n",
36+
"\n",
37+
"Let's create a synthetic 3D image with SimpleITK:"
38+
]
39+
},
40+
{
41+
"cell_type": "code",
42+
"execution_count": null,
43+
"id": "03397401",
44+
"metadata": {},
45+
"outputs": [],
46+
"source": [
47+
"# Create a 3D image using SimpleITK's GaussianSource\n",
48+
"size = [64, 64, 64]\n",
49+
"image = sitk.GaussianSource(\n",
50+
" sitk.sitkFloat32,\n",
51+
" size,\n",
52+
" sigma=[10.0, 10.0, 10.0],\n",
53+
" mean=[32.0, 32.0, 32.0],\n",
54+
" scale=255.0,\n",
55+
" normalized=False\n",
56+
")\n",
57+
"\n",
58+
"# Set spacing and origin\n",
59+
"image.SetSpacing([1.0, 1.0, 2.0]) # Anisotropic spacing\n",
60+
"image.SetOrigin([0.0, 0.0, 0.0])\n",
61+
"\n",
62+
"print(f\"Image size: {image.GetSize()}\")\n",
63+
"print(f\"Image spacing: {image.GetSpacing()}\")\n",
64+
"print(f\"Image origin: {image.GetOrigin()}\")"
65+
]
66+
},
67+
{
68+
"cell_type": "markdown",
69+
"id": "03ec254a",
70+
"metadata": {},
71+
"source": [
72+
"## Visualize the SimpleITK Image\n",
73+
"\n",
74+
"Now we can directly pass the SimpleITK image to itkwidgets:"
75+
]
76+
},
77+
{
78+
"cell_type": "code",
79+
"execution_count": null,
80+
"id": "1ad1a724",
81+
"metadata": {},
82+
"outputs": [],
83+
"source": [
84+
"view(image)"
85+
]
86+
},
87+
{
88+
"cell_type": "code",
89+
"execution_count": null,
90+
"id": "0b44dd52-f7a4-4b84-842d-5fee9bc72232",
91+
"metadata": {},
92+
"outputs": [],
93+
"source": []
94+
}
95+
],
96+
"metadata": {
97+
"kernelspec": {
98+
"display_name": "widgets",
99+
"language": "python",
100+
"name": "python3"
101+
},
102+
"language_info": {
103+
"codemirror_mode": {
104+
"name": "ipython",
105+
"version": 3
106+
},
107+
"file_extension": ".py",
108+
"mimetype": "text/x-python",
109+
"name": "python",
110+
"nbconvert_exporter": "python",
111+
"pygments_lexer": "ipython3",
112+
"version": "3.12.12"
113+
}
114+
},
115+
"nbformat": 4,
116+
"nbformat_minor": 5
117+
}

itkwidgets/integrations/__init__.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from .itk import HAVE_ITK
88
from .pytorch import HAVE_TORCH
99
from .monai import HAVE_MONAI
10+
from .simpleitk import HAVE_SIMPLEITK, simpleitk_image_to_ngff_image
1011
from .vtk import HAVE_VTK, vtk_image_to_ngff_image, vtk_polydata_to_vtkjs
1112
from .xarray import HAVE_XARRAY, HAVE_MULTISCALE_SPATIAL_IMAGE, xarray_data_array_to_numpy, xarray_data_set_to_numpy
1213
from ..render_types import RenderType
@@ -99,6 +100,14 @@ def _get_viewer_image(image, label=False):
99100
to_ngff_zarr(store, multiscales, chunk_store=chunk_store)
100101
return store
101102

103+
if HAVE_SIMPLEITK:
104+
import SimpleITK as sitk
105+
if isinstance(image, sitk.Image):
106+
ngff_image = simpleitk_image_to_ngff_image(image)
107+
multiscales = to_multiscales(ngff_image, method=method)
108+
to_ngff_zarr(store, multiscales, chunk_store=chunk_store)
109+
return store
110+
102111
if isinstance(image, dask.array.core.Array):
103112
ngff_image = to_ngff_image(image)
104113
multiscales = to_multiscales(ngff_image, method=method)
@@ -228,6 +237,10 @@ def _detect_render_type(data, input_type) -> RenderType:
228237
return RenderType.IMAGE
229238
elif isinstance(data, vtk.vtkPolyData):
230239
return RenderType.POINT_SET
240+
if HAVE_SIMPLEITK:
241+
import SimpleITK as sitk
242+
if isinstance(data, sitk.Image):
243+
return RenderType.IMAGE
231244
if isinstance(data, dask.array.core.Array):
232245
if data.ndim ==2 and data.shape[1] < 4:
233246
return RenderType.POINT_SET
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import importlib_metadata
2+
3+
HAVE_SIMPLEITK = False
4+
try:
5+
importlib_metadata.metadata("SimpleITK")
6+
HAVE_SIMPLEITK = True
7+
except importlib_metadata.PackageNotFoundError:
8+
pass
9+
10+
from ngff_zarr import to_ngff_image
11+
12+
13+
def simpleitk_image_to_ngff_image(image):
14+
"""Convert a SimpleITK image to an NGFF image.
15+
16+
Parameters
17+
----------
18+
image : sitk.Image
19+
The SimpleITK image to convert
20+
21+
Returns
22+
-------
23+
NgffImage
24+
The converted NGFF image with proper spacing and origin
25+
"""
26+
import SimpleITK as sitk
27+
28+
# Get the numpy array from SimpleITK image
29+
# Note: SimpleITK has XYZ ordering, numpy has ZYX ordering
30+
array = sitk.GetArrayFromImage(image)
31+
32+
# Get origin
33+
origin = image.GetOrigin()
34+
translation = {}
35+
if len(origin) >= 1:
36+
translation['x'] = origin[0]
37+
if len(origin) >= 2:
38+
translation['y'] = origin[1]
39+
if len(origin) >= 3:
40+
translation['z'] = origin[2]
41+
42+
# Get spacing
43+
spacing = image.GetSpacing()
44+
scale = {}
45+
if len(spacing) >= 1:
46+
scale['x'] = spacing[0]
47+
if len(spacing) >= 2:
48+
scale['y'] = spacing[1]
49+
if len(spacing) >= 3:
50+
scale['z'] = spacing[2]
51+
52+
# Create NGFF image with metadata
53+
ngff_image = to_ngff_image(array, scale=scale, translation=translation)
54+
55+
return ngff_image

0 commit comments

Comments
 (0)