Skip to content

Commit 01ba1e2

Browse files
authored
Merge pull request #416 from zivy/resampling
Additional resampling example.
2 parents 94e875e + a91b65f commit 01ba1e2

File tree

3 files changed

+132
-3
lines changed

3 files changed

+132
-3
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ jobs:
7272
sudo apt-get update
7373
sudo apt-get install enchant-2
7474
elif [ "$RUNNER_OS" == "macOS" ]; then
75-
brew update
75+
brew update-reset
7676
brew install enchant
7777
fi
7878
- name: Install dependencies

Python/21_Transforms_and_Resampling.ipynb

Lines changed: 130 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"source": [
2323
"import SimpleITK as sitk\n",
2424
"import numpy as np\n",
25+
"import itertools\n",
2526
"\n",
2627
"%matplotlib inline\n",
2728
"import gui\n",
@@ -874,7 +875,9 @@
874875
{
875876
"cell_type": "code",
876877
"execution_count": null,
877-
"metadata": {},
878+
"metadata": {
879+
"scrolled": true
880+
},
878881
"outputs": [],
879882
"source": [
880883
"def resize_and_scale_uint8(image, new_size, outside_pixel_value=0):\n",
@@ -930,7 +933,132 @@
930933
"# Select the arbitrary new size\n",
931934
"new_size = [128, 128]\n",
932935
"resized_images = [resize_and_scale_uint8(image, new_size, 50) for image in images]\n",
933-
"gui.multi_image_display2D(resized_images);"
936+
"gui.multi_image_display2D(resized_images, figure_size=[8, 4]);"
937+
]
938+
},
939+
{
940+
"cell_type": "markdown",
941+
"metadata": {},
942+
"source": [
943+
"### Resampling after registration\n",
944+
"\n",
945+
"The result of registration is a transformation which maps points from the fixed image coordinate system to the moving image coordinate system. Most often, we then resample the moving image onto the fixed image grid using this transformation.\n",
946+
"\n",
947+
"This implicitly assumes that the information we are interested in is contained only in the overlapping regions. In some instances, this is not the case. For example, when registration is used to align multiple images to create a composite image which has a larger physical extent. This is common in microscopy where multiple tiles are combined to create the full field of view (the interested reader is referred to the [ITKMontage module](https://github.com/InsightSoftwareConsortium/ITKMontage), not available in SimpleITK).\n",
948+
"\n",
949+
"In the cells below we illustrate how to combine the result of registration using the common approach, resampling onto the fixed image grid and onto a grid that contains all image data. The only difference between these two is the definition of the resampling grid. Thus, resampling using the same registration result can yield significantly different images."
950+
]
951+
},
952+
{
953+
"cell_type": "code",
954+
"execution_count": null,
955+
"metadata": {},
956+
"outputs": [],
957+
"source": [
958+
"# Create an artificial registration dataset and result.\n",
959+
"image = sitk.ReadImage(fdata(\"cthead1.png\"))\n",
960+
"fixed_image = image[80:200, 100:250]\n",
961+
"moving_image = image[20:150, 60:160]\n",
962+
"registration_result = sitk.Euler2DTransform([50, 60], -0.785, [70, 80])\n",
963+
"# Modify the moving image origin and direction cosine using the\n",
964+
"# \"registration\" transformation and then add noise to the transformation\n",
965+
"# mimicking registration error.\n",
966+
"moving_image.SetOrigin(registration_result.TransformPoint(moving_image.GetOrigin()))\n",
967+
"moving_image.SetDirection(registration_result.GetMatrix())\n",
968+
"registration_result.SetTranslation(\n",
969+
" [t + np.random.random() * 5 for t in registration_result.GetTranslation()]\n",
970+
")\n",
971+
"gui.multi_image_display2D(\n",
972+
" [fixed_image, moving_image], [\"fixed image\", \"moving image\"], figure_size=[5, 4]\n",
973+
");"
974+
]
975+
},
976+
{
977+
"cell_type": "code",
978+
"execution_count": null,
979+
"metadata": {},
980+
"outputs": [],
981+
"source": [
982+
"# Resample the moving image onto the fixed image grid, notice that parts of the original moving image are not included\n",
983+
"# because they are outside the physical region occupied by the fixed image.\n",
984+
"resampled_moving = sitk.Resample(moving_image, fixed_image, registration_result)\n",
985+
"\n",
986+
"# Use green/magenta color scheme, image is gray in overlap regions where the registration is aligned.\n",
987+
"gui.multi_image_display2D(\n",
988+
" [\n",
989+
" sitk.Cast(\n",
990+
" sitk.Compose(fixed_image, resampled_moving, fixed_image),\n",
991+
" sitk.sitkVectorUInt8,\n",
992+
" )\n",
993+
" ],\n",
994+
" [\"combined image\"],\n",
995+
" figure_size=[4, 4],\n",
996+
");"
997+
]
998+
},
999+
{
1000+
"cell_type": "code",
1001+
"execution_count": null,
1002+
"metadata": {},
1003+
"outputs": [],
1004+
"source": [
1005+
"# Compute the axis aligned bounding box grid for all images. Treat all images the same way, so\n",
1006+
"# code is concise and applicable to N images of the same dimension, 2D or 3D.\n",
1007+
"# The choice of spacing is arbitrary and implicitly defines the size of the grid.\n",
1008+
"images = [fixed_image, moving_image]\n",
1009+
"transforms = [sitk.Transform(2, sitk.sitkIdentity), registration_result]\n",
1010+
"dim = images[0].GetDimension()\n",
1011+
"\n",
1012+
"boundary_points = []\n",
1013+
"for image, transform in zip(images, transforms):\n",
1014+
" for boundary_index in list(\n",
1015+
" itertools.product(*zip([0] * dim, [sz - 1 for sz in image.GetSize()]))\n",
1016+
" ): # Points from the moving image(s) are mapped to the fixed coordinate system using the inverse\n",
1017+
" # of the registration_result.\n",
1018+
" boundary_points.append(\n",
1019+
" transform.GetInverse().TransformPoint(\n",
1020+
" image.TransformIndexToPhysicalPoint(boundary_index)\n",
1021+
" )\n",
1022+
" )\n",
1023+
"max_coords = np.max(boundary_points, axis=0)\n",
1024+
"min_coords = np.min(boundary_points, axis=0)\n",
1025+
"\n",
1026+
"new_origin = min_coords\n",
1027+
"# Arbitrarily use the spacing of the first image and its pixel type,\n",
1028+
"# change these to suite your needs.\n",
1029+
"new_spacing = images[0].GetSpacing()\n",
1030+
"new_pixel_type = images[0].GetPixelID()\n",
1031+
"new_size = (((max_coords - min_coords) / new_spacing).round().astype(int)).tolist()\n",
1032+
"new_direction = np.identity(dim).ravel()\n",
1033+
"\n",
1034+
"# Resample all images onto the common grid.\n",
1035+
"resampled_images = []\n",
1036+
"for image, transform in zip(images, transforms):\n",
1037+
" resampled_images.append(\n",
1038+
" sitk.Resample(\n",
1039+
" image,\n",
1040+
" new_size,\n",
1041+
" transform,\n",
1042+
" sitk.sitkLinear,\n",
1043+
" new_origin,\n",
1044+
" new_spacing,\n",
1045+
" new_direction,\n",
1046+
" 0.0,\n",
1047+
" new_pixel_type,\n",
1048+
" )\n",
1049+
" )\n",
1050+
"\n",
1051+
"# Use green/magenta color scheme, image is gray in overlap regions where the registration is aligned\n",
1052+
"gui.multi_image_display2D(\n",
1053+
" [\n",
1054+
" sitk.Cast(\n",
1055+
" sitk.Compose(resampled_images[0], resampled_images[1], resampled_images[0]),\n",
1056+
" sitk.sitkVectorUInt8,\n",
1057+
" )\n",
1058+
" ],\n",
1059+
" [\"combined image\"],\n",
1060+
" figure_size=[4, 4],\n",
1061+
");"
9341062
]
9351063
}
9361064
],

tests/additional_dictionary.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ IPython
126126
IQR
127127
ITK
128128
ITK's
129+
ITKMontage
129130
ImageFileReader
130131
ImageFileReader's
131132
ImageIO

0 commit comments

Comments
 (0)