{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Mutual Information Metric\n",
    "\n",
    "The `MutualInformationImageToImageMetric` class computes the mutual information between two images, i.e. the degree to which information content in one image is dependent on the other image. This example shows how `MutualInformationImageToImageMetric` can be used to map transformation parameters and register two images using a gradient ascent algorithm."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from matplotlib import cm\n",
    "from urllib.request import urlretrieve\n",
    "\n",
    "import itk\n",
    "from itkwidgets import view"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "dim = 2\n",
    "ImageType = itk.Image[itk.F, dim]\n",
    "FixedImageType = ImageType\n",
    "MovingImageType = ImageType"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Retrieve fixed and moving images for registration\n",
    "\n",
    "We aim to register two slice images, one of which has an arbitrary offset."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "fixed_img_path = \"BrainT1SliceBorder20.png\"\n",
    "moving_img_path = \"BrainProtonDensitySliceShifted13x17y.png\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "if not os.path.exists(fixed_img_path):\n",
    "    url = \"https://data.kitware.com/api/v1/file/5cad1ae88d777f072b18183d/download\"\n",
    "    urlretrieve(url, fixed_img_path)\n",
    "if not os.path.exists(moving_img_path):\n",
    "    url = \"https://data.kitware.com/api/v1/file/5cad1ae88d777f072b181831/download\"\n",
    "    urlretrieve(url, moving_img_path)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "fixed_img = itk.imread(\"BrainT1SliceBorder20.png\", itk.F)\n",
    "moving_img = itk.imread(\"BrainProtonDensitySliceShifted13x17y.png\", itk.F)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "                <img id=\"screenshot_imjoy_window_15d35f96-4154-4003-9976-3f94016e2822\" src=data:image/png;base64,[encoded data, 376220 characters]>\n",
       "                <script type=\"text/javascript\">\n",
       "                    var image = document.getElementById(\"screenshot_imjoy_window_15d35f96-4154-4003-9976-3f94016e2822\");\n",
       "                    image.src = \"data:image/png;base64,[encoded data, 376220 characters]\";\n",
       "                    var viewer = document.getElementById(\"imjoy_window_15d35f96-4154-4003-9976-3f94016e2822\");\n",
       "                    // Hide the static image if the Viewer is visible\n",
       "                    image.style.display = viewer ? \"none\" : \"block\";\n",
       "                </script>\n",
       "            "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/javascript": [
       "window.connectPlugin && window.connectPlugin(\"7556c24b-3c17-4430-921a-59b423c6399b\")"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<div id=\"7fe01b2d-f2f1-4c58-ba31-1bcb11884818\"></div>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "<itkwidgets.viewer.Viewer at 0x7f244ecbcfa0>"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "view(fixed_img)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "                <img id=\"screenshot_imjoy_window_c2e77874-8a16-4fc8-9061-97cb8e24b025\" src=data:image/png;base64,[encoded data, 334880 characters]>\n",
       "                <script type=\"text/javascript\">\n",
       "                    var image = document.getElementById(\"screenshot_imjoy_window_c2e77874-8a16-4fc8-9061-97cb8e24b025\");\n",
       "                    image.src = \"data:image/png;base64,[encoded data, 334880 characters]\";\n",
       "                    var viewer = document.getElementById(\"imjoy_window_c2e77874-8a16-4fc8-9061-97cb8e24b025\");\n",
       "                    // Hide the static image if the Viewer is visible\n",
       "                    image.style.display = viewer ? \"none\" : \"block\";\n",
       "                </script>\n",
       "            "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/javascript": [
       "window.connectPlugin && window.connectPlugin(\"7556c24b-3c17-4430-921a-59b423c6399b\")"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<div id=\"564deefe-a9fa-49f9-8d7e-7bfbace625cb\"></div>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "<itkwidgets.viewer.Viewer at 0x7f244c4f8a60>"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "view(moving_img)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Prepare images for registration"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "fixed_normalized_image = itk.normalize_image_filter(fixed_img)\n",
    "fixed_smoothed_image = itk.discrete_gaussian_image_filter(fixed_normalized_image, variance=2.0)\n",
    "\n",
    "moving_normalized_image = itk.normalize_image_filter(moving_img)\n",
    "moving_smoothed_image = itk.discrete_gaussian_image_filter(moving_normalized_image, variance=2.0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "                <img id=\"screenshot_imjoy_window_fe1ad4e4-ebe5-46db-aa0d-dd118e37b64d\" src=data:image/png;base64,[encoded data, 305912 characters]>\n",
       "                <script type=\"text/javascript\">\n",
       "                    var image = document.getElementById(\"screenshot_imjoy_window_fe1ad4e4-ebe5-46db-aa0d-dd118e37b64d\");\n",
       "                    image.src = \"data:image/png;base64,[encoded data, 305912 characters]\";\n",
       "                    var viewer = document.getElementById(\"imjoy_window_fe1ad4e4-ebe5-46db-aa0d-dd118e37b64d\");\n",
       "                    // Hide the static image if the Viewer is visible\n",
       "                    image.style.display = viewer ? \"none\" : \"block\";\n",
       "                </script>\n",
       "            "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/javascript": [
       "window.connectPlugin && window.connectPlugin(\"7556c24b-3c17-4430-921a-59b423c6399b\")"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<div id=\"e9237158-d805-4b52-9a78-1eddbd3adf3d\"></div>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "<itkwidgets.viewer.Viewer at 0x7f243fdc6ac0>"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "view(fixed_smoothed_image)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Plot the `MutualInformationImageToImageMetric` surface\n",
    "\n",
    "For this relatively simple example we seek to adjust only the x- and y-offset of the moving image with a `TranslationTransform`. We can acquire `MutualInformationImageToImageMetric` values comparing the two images at many different possible offset pairs with `ExhaustiveOptimizer` and visualize this data set as a surface with `matplotlib`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Move at most 20 pixels away from the initial position\n",
    "window_size = [20, 20]\n",
    "# Collect 100 steps of data along each axis\n",
    "n_steps = [100, 100]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "TransformType = itk.TranslationTransform[itk.D, dim]\n",
    "OptimizerType = itk.GradientDescentOptimizer\n",
    "ExhaustiveOptimizerType = itk.ExhaustiveOptimizer\n",
    "MetricType = itk.MutualInformationImageToImageMetric[ImageType, ImageType]\n",
    "RegistrationType = itk.ImageRegistrationMethod[ImageType, ImageType]\n",
    "InterpolatorType = itk.LinearInterpolateImageFunction[ImageType, itk.D]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "transform = TransformType.New()\n",
    "metric = MetricType.New()\n",
    "optimizer = ExhaustiveOptimizerType.New()\n",
    "registrar = RegistrationType.New()\n",
    "interpolator = InterpolatorType.New()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "metric.SetNumberOfSpatialSamples(100)\n",
    "metric.SetFixedImageStandardDeviation(0.4)\n",
    "metric.SetMovingImageStandardDeviation(0.4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "optimizer.SetNumberOfSteps(n_steps)\n",
    "\n",
    "# Initialize scales and set back to optimizer\n",
    "scales = optimizer.GetScales()\n",
    "scales.SetSize(2)\n",
    "scales.SetElement(0, window_size[0] / n_steps[0])\n",
    "scales.SetElement(1, window_size[1] / n_steps[1])\n",
    "optimizer.SetScales(scales)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "registrar.SetFixedImage(fixed_smoothed_image)\n",
    "registrar.SetMovingImage(moving_smoothed_image)\n",
    "registrar.SetOptimizer(optimizer)\n",
    "registrar.SetTransform(transform)\n",
    "registrar.SetInterpolator(interpolator)\n",
    "registrar.SetMetric(metric)\n",
    "\n",
    "registrar.SetFixedImageRegion(fixed_img.GetBufferedRegion())\n",
    "registrar.SetInitialTransformParameters(transform.GetParameters())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Collect data describing the parametric surface with an observer\n",
    "surface = dict()\n",
    "\n",
    "\n",
    "def print_iteration():\n",
    "    surface[tuple(optimizer.GetCurrentPosition())] = optimizer.GetCurrentValue()\n",
    "\n",
    "\n",
    "optimizer.AddObserver(itk.IterationEvent(), print_iteration)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "registrar.Update()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[13.600000000000001, 16.0]\n",
      "[-8.200000000000001, -9.200000000000001]\n"
     ]
    }
   ],
   "source": [
    "# Check the extreme positions within the observed window\n",
    "max_position = list(optimizer.GetMaximumMetricValuePosition())\n",
    "min_position = list(optimizer.GetMinimumMetricValuePosition())\n",
    "\n",
    "max_val = optimizer.GetMaximumMetricValue()\n",
    "min_val = optimizer.GetMinimumMetricValue()\n",
    "\n",
    "print(max_position)\n",
    "print(min_position)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Set up values for the plot\n",
    "x_vals = [list(set([x[i] for x in surface.keys()])) for i in range(0, 2)]\n",
    "\n",
    "for i in range(0, 2):\n",
    "    x_vals[i].sort()\n",
    "\n",
    "X, Y = np.meshgrid(x_vals[0], x_vals[1])\n",
    "Z = np.array([[surface[(x0, x1)] for x1 in x_vals[0]] for x0 in x_vals[1]])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x7f23e837d4f0>]"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 458228 characters]\n",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Plot the surface as a 2D heat map\n",
    "fig = plt.figure()\n",
    "plt.gca().invert_yaxis()\n",
    "ax = plt.gca()\n",
    "\n",
    "surf = ax.scatter(X, Y, c=Z, cmap=cm.coolwarm)\n",
    "ax.plot(max_position[0], max_position[1], \"k^\")\n",
    "ax.plot(min_position[0], min_position[1], \"kv\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 113892 characters]\n",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Plot the surface as a 3D scatter plot\n",
    "fig = plt.figure()\n",
    "ax = fig.add_subplot(projection=\"3d\")\n",
    "\n",
    "surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Follow gradient ascent\n",
    "\n",
    "Once we understand the shape of the parametric surface it is easier to visualize the gradient ascent algorithm. We see that there is some roughness to the surface, but it has a clear slope upwards. We want to maximize the mutual information between the two images in order to optimize registration. The results of gradient ascent optimization can be superimposed onto the `matplotlib` plot."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "n_iterations = 200"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "transform = TransformType.New()\n",
    "metric = MetricType.New()\n",
    "optimizer = OptimizerType.New()\n",
    "registrar = RegistrationType.New()\n",
    "interpolator = InterpolatorType.New()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "registrar.SetFixedImage(fixed_smoothed_image)\n",
    "registrar.SetMovingImage(moving_smoothed_image)\n",
    "registrar.SetOptimizer(optimizer)\n",
    "registrar.SetTransform(transform)\n",
    "registrar.SetInterpolator(interpolator)\n",
    "registrar.SetMetric(metric)\n",
    "\n",
    "registrar.SetFixedImageRegion(fixed_img.GetBufferedRegion())\n",
    "registrar.SetInitialTransformParameters(transform.GetParameters())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "metric.SetNumberOfSpatialSamples(100)\n",
    "metric.SetFixedImageStandardDeviation(0.4)\n",
    "metric.SetMovingImageStandardDeviation(0.4)\n",
    "\n",
    "optimizer.SetLearningRate(15)\n",
    "optimizer.SetNumberOfIterations(n_iterations)\n",
    "optimizer.MaximizeOn()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "descent_data = dict()\n",
    "descent_data[0] = (0, 0)\n",
    "\n",
    "\n",
    "def log_iteration():\n",
    "    descent_data[optimizer.GetCurrentIteration() + 1] = tuple(optimizer.GetCurrentPosition())\n",
    "\n",
    "\n",
    "optimizer.AddObserver(itk.IterationEvent(), log_iteration)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "registrar.Update()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Its: 200\n",
      "Final Value: 0.6287786636295714\n",
      "Final Position: [13.06874645780319, 16.707359598970985]\n"
     ]
    }
   ],
   "source": [
    "print(f\"Its: {optimizer.GetCurrentIteration()}\")\n",
    "print(f\"Final Value: {optimizer.GetValue()}\")\n",
    "print(f\"Final Position: {list(registrar.GetLastTransformParameters())}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_vals = [descent_data[i][0] for i in range(0, n_iterations)]\n",
    "y_vals = [descent_data[i][1] for i in range(0, n_iterations)]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We see in the plot that the metric improves as transformation parameters are updated with each iteration. The value of the metric at each step generally increases, yielding a final value very close to the optimal position in the parameter space window."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x7f23d9dd5dc0>]"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 459592 characters]\n",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig = plt.figure()\n",
    "# Note: We invert the y-axis to represent the image coordinate system\n",
    "plt.gca().invert_yaxis()\n",
    "ax = plt.gca()\n",
    "\n",
    "surf = ax.scatter(X, Y, c=Z, cmap=cm.coolwarm)\n",
    "\n",
    "for i in range(0, n_iterations - 1):\n",
    "    plt.plot(x_vals[i : i + 2], y_vals[i : i + 2], \"wx-\")\n",
    "plt.plot(descent_data[0][0], descent_data[0][1], \"bo\")\n",
    "plt.plot(descent_data[n_iterations - 1][0], descent_data[n_iterations - 1][1], \"ro\")\n",
    "\n",
    "plt.plot(max_position[0], max_position[1], \"k^\")\n",
    "plt.plot(min_position[0], min_position[1], \"kv\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[13.600000000000001, 16.0]"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "max_position"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Resample the moving image\n",
    "\n",
    "In order to apply the results of gradient ascent we must resample the moving image into the domain of the fixed image. The `TranslationTransform` whose parameters have been selected through gradient ascent is used to dictate how the moving image is sampled from the fixed image domain. We can compare the two images with `itkwidgets` to verify that registration is successful."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [],
   "source": [
    "ResampleFilterType = itk.ResampleImageFilter[MovingImageType, FixedImageType]\n",
    "resample = ResampleFilterType.New(\n",
    "    Transform=transform,\n",
    "    Input=moving_img,\n",
    "    Size=fixed_img.GetLargestPossibleRegion().GetSize(),\n",
    "    OutputOrigin=fixed_img.GetOrigin(),\n",
    "    OutputSpacing=fixed_img.GetSpacing(),\n",
    "    OutputDirection=fixed_img.GetDirection(),\n",
    "    DefaultPixelValue=100,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [],
   "source": [
    "resample.Update()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "                <img id=\"screenshot_imjoy_window_19027dfa-4056-43f5-a2e4-6b23f12a5680\" src=data:image/png;base64,[encoded data, 20032 characters]>\n",
       "                <script type=\"text/javascript\">\n",
       "                    var image = document.getElementById(\"screenshot_imjoy_window_19027dfa-4056-43f5-a2e4-6b23f12a5680\");\n",
       "                    image.src = \"data:image/png;base64,[encoded data, 20032 characters]\";\n",
       "                    var viewer = document.getElementById(\"imjoy_window_19027dfa-4056-43f5-a2e4-6b23f12a5680\");\n",
       "                    // Hide the static image if the Viewer is visible\n",
       "                    image.style.display = viewer ? \"none\" : \"block\";\n",
       "                </script>\n",
       "            "
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/javascript": [
       "window.connectPlugin && window.connectPlugin(\"7556c24b-3c17-4430-921a-59b423c6399b\")"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<div id=\"18cb46da-9b89-41f7-87ca-38c170109a32\"></div>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "<itkwidgets.viewer.Viewer at 0x7f23d9d70610>"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "view(resample.GetOutput())\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Clean up"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [],
   "source": [
    "os.remove(fixed_img_path)\n",
    "os.remove(moving_img_path)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.15"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
