Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 71 additions & 50 deletions Python/05_Results_Visualization.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -292,59 +292,80 @@
"metadata": {},
"outputs": [],
"source": [
"def mask_image_multiply(mask, image):\n",
" components_per_pixel = image.GetNumberOfComponentsPerPixel()\n",
" if components_per_pixel == 1:\n",
" return mask * image\n",
"def alpha_blend(image1, image2, alpha=0.5):\n",
" \"\"\"\n",
" Alpha blend two images, pixels can be scalars or vectors.\n",
" The alpha blending factor can be either a scalar or an image whose\n",
" pixel type is sitkFloat32 and values are in [0,1].\n",
" \"\"\"\n",
" components_per_pixel = image1.GetNumberOfComponentsPerPixel()\n",
" if components_per_pixel > 1:\n",
" # MultiplyImageFilter doesn't support a vector image times a scalar\n",
" # (per-pixel) image, so blend one channel at a time. The 1-alpha\n",
" # factor doesn't depend on the channel, so it's only computed once.\n",
" one_minus_alpha = 1 - alpha\n",
" channels = []\n",
" for channel in range(components_per_pixel):\n",
" scaled1 = sitk.VectorIndexSelectionCast(image1, channel, sitk.sitkFloat32)\n",
" scaled1 *= alpha\n",
" blended_channel = sitk.VectorIndexSelectionCast(\n",
" image2, channel, sitk.sitkFloat32\n",
" )\n",
" blended_channel *= one_minus_alpha\n",
" blended_channel += scaled1\n",
" channels.append(blended_channel)\n",
" return sitk.Compose(channels)\n",
" else:\n",
" return sitk.Compose(\n",
" [\n",
" mask * sitk.VectorIndexSelectionCast(image, channel)\n",
" for channel in range(components_per_pixel)\n",
" ]\n",
" )\n",
" scaled1 = sitk.Cast(image1, sitk.sitkFloat32)\n",
" scaled1 *= alpha\n",
" blended = sitk.Cast(image2, sitk.sitkFloat32)\n",
" blended *= 1 - alpha\n",
" blended += scaled1\n",
" return blended\n",
"\n",
"\n",
"def alpha_blend(image1, image2, alpha=0.5, mask1=None, mask2=None):\n",
"def alpha_blend_masked(image1, image2, alpha=0.5, mask1=None, mask2=None):\n",
" \"\"\"\n",
" Alaph blend two images, pixels can be scalars or vectors.\n",
" The alpha blending factor can be either a scalar or an image whose\n",
" pixel type is sitkFloat32 and values are in [0,1].\n",
" The region that is alpha blended is controled by the given masks.\n",
" Alpha blend two images as done by alpha_blend, restricted to the region(s)\n",
" indicated by the given masks. Masks are treated as binary indicators\n",
" (non-zero selects the region), as returned by, e.g., OtsuThreshold.\n",
" \"\"\"\n",
" if mask1 is None and mask2 is None:\n",
" return alpha_blend(image1, image2, alpha)\n",
"\n",
" if not mask1:\n",
" mask1 = sitk.Image(image1.GetSize(), sitk.sitkFloat32) + 1.0\n",
" mask1.CopyInformation(image1)\n",
" else:\n",
" mask1 = sitk.Cast(mask1, sitk.sitkFloat32)\n",
" if not mask2:\n",
" mask2 = sitk.Image(image2.GetSize(), sitk.sitkFloat32) + 1\n",
" mask2.CopyInformation(image2)\n",
" else:\n",
" mask2 = sitk.Cast(mask2, sitk.sitkFloat32)\n",
" # if we received a scalar, convert it to an image\n",
" # Build a per-pixel alpha image so the mask1/mask2-only regions can be\n",
" # expressed as alpha=1/alpha=0 and blended by reusing alpha_blend. The\n",
" # neither-mask region can't be expressed this way (it's 0, not image1\n",
" # or image2), so it's overwritten separately below.\n",
" if type(alpha) != sitk.SimpleITK.Image:\n",
" alpha = sitk.Image(image1.GetSize(), sitk.sitkFloat32) + alpha\n",
" alpha.CopyInformation(image1)\n",
" components_per_pixel = image1.GetNumberOfComponentsPerPixel()\n",
" if components_per_pixel > 1:\n",
" img1 = sitk.Cast(image1, sitk.sitkVectorFloat32)\n",
" img2 = sitk.Cast(image2, sitk.sitkVectorFloat32)\n",
" alpha_image = sitk.Image(image1.GetSize(), sitk.sitkFloat32) + alpha\n",
" alpha_image.CopyInformation(image1)\n",
" else:\n",
" img1 = sitk.Cast(image1, sitk.sitkFloat32)\n",
" img2 = sitk.Cast(image2, sitk.sitkFloat32)\n",
"\n",
" intersection_mask = mask1 * mask2\n",
"\n",
" intersection_image = mask_image_multiply(\n",
" alpha * intersection_mask, img1\n",
" ) + mask_image_multiply((1 - alpha) * intersection_mask, img2)\n",
" return (\n",
" intersection_image\n",
" + mask_image_multiply(mask2 - intersection_mask, img2)\n",
" + mask_image_multiply(mask1 - intersection_mask, img1)\n",
" )"
" # Copy so the caller's alpha image isn't mutated by the in-place\n",
" # mask edits below.\n",
" alpha_image = sitk.Image(alpha)\n",
"\n",
" # Comparison operators always return a sitkUInt8 image, regardless of the\n",
" # input pixel type, so there's no need to cast beforehand.\n",
" if mask1 is not None:\n",
" mask1 = mask1 != 0\n",
" if mask2 is not None:\n",
" mask2 = mask2 != 0\n",
"\n",
" if mask2 is None: # only mask1 given, outside it show image2 as is\n",
" alpha_image[mask1 == 0] = 0.0\n",
" elif mask1 is None: # only mask2 given, outside it show image1 as is\n",
" alpha_image[mask2 == 0] = 1.0\n",
" else: # both given, only the intersection is blended\n",
" alpha_image[mask1 * (mask2 == 0)] = 1.0\n",
" alpha_image[mask2 * (mask1 == 0)] = 0.0\n",
"\n",
" blended = alpha_blend(image1, image2, alpha_image)\n",
"\n",
" if mask1 is not None and mask2 is not None:\n",
" neither = (mask1 == 0) * (mask2 == 0)\n",
" blended[neither] = 0\n",
" return blended"
]
},
{
Expand All @@ -363,10 +384,10 @@
"# Combine the two volumes\n",
"images_list = [\n",
" (alpha_blend(img1_255, img2_255), \"alpha_blend_standard\"),\n",
" (alpha_blend(img1_255, img2_255, mask1=msk1), \"alpha_blend_mask1\"),\n",
" (alpha_blend(img1_255, img2_255, mask2=msk2), \"alpha_blend_mask2\"),\n",
" (alpha_blend_masked(img1_255, img2_255, mask1=msk1), \"alpha_blend_mask1\"),\n",
" (alpha_blend_masked(img1_255, img2_255, mask2=msk2), \"alpha_blend_mask2\"),\n",
" (\n",
" alpha_blend(img1_255, img2_255, mask1=msk1, mask2=msk2),\n",
" alpha_blend_masked(img1_255, img2_255, mask1=msk1, mask2=msk2),\n",
" \"alpha_blend_mask1_mask2\",\n",
" ),\n",
"]\n",
Expand Down Expand Up @@ -557,7 +578,7 @@
"source": [
"gui.MultiImageDisplay(\n",
" image_list=[\n",
" alpha_blend(img1_255, img2_255, mask2=msk2),\n",
" alpha_blend_masked(img1_255, img2_255, mask2=msk2),\n",
" multiresolution_blend(img1_255, img2_255, msk2),\n",
" ],\n",
" title_list=[\"alpha_blend_mask2\", \"multiresolution_blend_mask2\"],\n",
Expand Down Expand Up @@ -718,7 +739,7 @@
" )\n",
"elif blending_approach == \"mask\":\n",
" combined_volume = sitk.Cast(\n",
" alpha_blend(\n",
" alpha_blend_masked(\n",
" sitk.Compose(img1_255, img1_255, img1_255), overlay_color_img, mask2=roi\n",
" ),\n",
" sitk.sitkVectorUInt8,\n",
Expand Down
1 change: 1 addition & 0 deletions tests/additional_dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ MeanSquares
MetaDataDictionaryArrayUpdateOn
MetaImageIO
MetricEvaluate
MultiplyImageFilter
Multiresolution
NamedTemporaryFile
Nanometers
Expand Down
Loading