<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Simpleitk | Amod Jog</title><link>https://www.amodjog.net/tag/simpleitk/</link><atom:link href="https://www.amodjog.net/tag/simpleitk/index.xml" rel="self" type="application/rss+xml"/><description>Simpleitk</description><generator>Hugo Blox Builder (https://hugoblox.com)</generator><language>en-us</language><copyright>Amod Jog `2025`</copyright><lastBuildDate>Sat, 16 Mar 2024 00:00:00 +0000</lastBuildDate><image><url>https://www.amodjog.net/media/icon_hu_aa508c7936d4aaed.png</url><title>Simpleitk</title><link>https://www.amodjog.net/tag/simpleitk/</link></image><item><title>Resampling in SimpleITK</title><link>https://www.amodjog.net/post/tech-simpleitk-resampling/</link><pubDate>Sat, 16 Mar 2024 00:00:00 +0000</pubDate><guid>https://www.amodjog.net/post/tech-simpleitk-resampling/</guid><description>&lt;p&gt;&lt;a href="https://simpleitk.org/" target="_blank" rel="noopener"&gt;SimpleITK&lt;/a&gt; has a fantastic python API for medical image processing, but it has
a steep learning curve. In this post I will document how to
resample images using the Resample filter via the complicated, but really useful task of extracting oblique slices in a 3D image. If you imagine a 3D
image as a cube then an oblique slice is formed by sampling the image on a
plane that has an arbitrary normal and does not align with the canonical
axial, sagittal, coronal planes (see Fig. 1).&lt;/p&gt;
&lt;p&gt;Figure 1 shows the typical setup in SimpleITK where the image field of view
is denoted by the dotted cube. Let $\mathbf{p} = [p_x, p_y, p_z]$ be the physical coordinates of a point around which the oblique slice is centered. Let $\mathbf{x} = [x_1, x_2, x_3]$, $\mathbf{y} = [y_1, y_2, y_3]$, and $\mathbf{z} = [z_1, z_2, z_3]$ be the reference frame of the slice. $\mathbf{z}$ will also
be the normal to the slice. For the sampled slice, let $\mathbf{o}$ be the SimpleITK origin of the slice (bottom left voxel).&lt;/p&gt;
&lt;p&gt;I am assuming that we are given $\mathbf{p}$ and the unit
vectors $\mathbf{x}$, $\mathbf{y}$, $\mathbf{z}$.&lt;/p&gt;
&lt;figure id="figure-oblique-slice-in-an-image-fov"&gt;
&lt;div class="d-flex justify-content-center"&gt;
&lt;div class="w-100" &gt;&lt;img alt="Oblique slice in an image FOV." srcset="
/post/tech-simpleitk-resampling/oblique_slice_hu_e8d5cf8a0e42b45c.webp 400w,
/post/tech-simpleitk-resampling/oblique_slice_hu_fe21d58a910453fd.webp 760w,
/post/tech-simpleitk-resampling/oblique_slice_hu_8569a58f626936b2.webp 1200w"
src="https://www.amodjog.net/post/tech-simpleitk-resampling/oblique_slice_hu_e8d5cf8a0e42b45c.webp"
width="720"
height="405"
loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;figcaption data-pre="Figure&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered"&gt;
Oblique slice in an image FOV.
&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Given a SimpleITK img, I typically do the following to resample it:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;resample = sitk.ResampleImageFilter()
resample.SetOutputDirection(output_direction)
resample.SetOutputOrigin(output_origin)
resample.SetOutputSpacing(output_spacing)
resample.SetSize(output_size)
resample.SetTransform(sitk.Transform())
resample.SetDefaultPixelValue(0)
resample.SetInterpolator(sitk.sitkLinear)
resample.Execute(img)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have to specify the &lt;em&gt;direction&lt;/em&gt;, &lt;em&gt;origin&lt;/em&gt;, &lt;em&gt;spacing&lt;/em&gt;, and &lt;em&gt;size&lt;/em&gt; (in voxels) for the final resampled slice.
&lt;em&gt;direction&lt;/em&gt; in a SimpleITK header is a (9, 1)-shaped array that expresses the rotation matrix (in a row-major form) that takes the physical reference frame ($\mathbf{i} = [1, 0, 0]$, $\mathbf{y} = [0, 1, 0]$, $\mathbf{k} = [0, 0, 1]$) to the frame of the image.&lt;/p&gt;
&lt;p&gt;Let the rotation matrix be $\mathbf{R}$, then notice that $\mathbf{R}\mathbf{k} = [r_{13}, r_{23}, r_{33}]$&amp;ndash;the third column of $\mathbf{R}$. Applying the rotation $\mathbf{R}$ to the physical z axis should give us the new z axis which is $\mathbf{z}$. Therefore, we have $[r_{13}, r_{23}, r_{33}] = [z_1, z_2, z_3]$. Similarly the first and second columns
of $\mathbf{R}$ will be $\mathbf{x}$ and $\mathbf{y}$ respectively.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;R = np.zeros((3,3))
R[:,0] = x
R[:,1] = y
R[:,2] = z
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The SimpleITK &lt;em&gt;direction&lt;/em&gt; part of the header of the output slice will therefore be,&lt;/p&gt;
&lt;p&gt;&lt;em&gt;direction&lt;/em&gt; = $[r_{11}, r_{12}, r_{13}, r_{21}, r_{22}, r_{23}, r_{31}, r_{32}, r_{33}]$. Or in python:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;output_direction = R.ravel()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;output_direction_ = [x[0], y[0], z[0], x[1], y[1], z[1], x[2], y[2], z[2]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To identify the physical coordinates of the origin $\mathbf{o}$ we have to do
some vector arithmetic. From Figure 1, it is clear that&lt;/p&gt;
&lt;p&gt;$\mathbf{o} = \mathbf{p} - d \mathbf{u}$,&lt;/p&gt;
&lt;p&gt;where the unit vector $\mathbf{u}$ in the direction from $\mathbf{o}$ to $\mathbf{p}$ is given by $\mathbf{u} = \frac{\mathbf{x} + \mathbf{y}}{\sqrt{2}}$&lt;/p&gt;
&lt;p&gt;$d$ is the distance between $\mathbf{o}$ and $\mathbf{p}$. If $w$ is the slice
width, then $d = \frac{w}{\sqrt{2}}$. $w$ is in millimetres, so if the output
slice size is in voxels $s$, then $w = s * r$, where $r$ is the voxel spacing in millimetres. Here we assume that we have the same voxel spacing in x-y directions.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;u = (x + y)/np.sqrt(2)
d = (s * voxel_spacing)/np.sqrt(2)
output_origin = p - d*u
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&amp;rsquo;s about it. We can specify the rest of the output image parameters and
execute the resample filter to extract an oblique slice centered at a point
$\mathbf{p}$ and oriented along the frame $\mathbf{x}-\mathbf{y}-\mathbf{z}$,
with the slice normal being $\mathbf{z}$.&lt;/p&gt;
&lt;p&gt;SimpleITK has a nice set of notebooks &lt;a href="https://github.com/InsightSoftwareConsortium/SimpleITK-Notebooks/tree/master/Python" target="_blank" rel="noopener"&gt;here&lt;/a&gt; and perhaps if I had done the homework exercises my learning curve would not have been so steep.&lt;/p&gt;</description></item></channel></rss>