<div dir="ltr">Hi Oliver,<div><br></div><div>The extra squares are due to an extra level of interpolation, you can get rid of them by using this:</div><div><br></div><div> plane_widget.SetResliceInterpolateToNearestNeighbour()</div><div><br></div><div>Also, in your code you have assumed that the origin of the image data volume is the outer corner of the first voxel. This is not correct. In VTK, the origin is always the center of the first voxel. As a result, you have placed the plane at the wrong position. Use this instead:</div><div><br></div><div><div> plane_widget.SetOrigin(-0.5, -0.5, 0.0)</div><div> plane_widget.SetPoint1(+7.5, -0.5, 0.0)</div><div> plane_widget.SetPoint2(-0.5, +7.5, 0.0)</div></div><div><br></div><div><div> create_sphere(-0.5, -0.5, 0, (1, 0, 0))</div><div> create_sphere(+2.5, -0.5, 0, (0, 1, 0))</div><div> create_sphere(-0.5, +2.5, 0, (0, 0, 1))</div><div> create_sphere(+2.5, +2.5, 0, (1, 1, 0))</div></div><div><br></div><div>Note that DICOM, NIfTI, and other medical image formats also measure positions from the center of the first voxel.</div><div><br></div><div><br></div><div>Now, here is some info about the vtkImagePlaneWidget. It's an old class and is a little clunky. It uses two steps:</div><div>1) first, vtkImageReslice extracts a slice (often padded or cropped) from the volume</div><div>2) next, the output of vtkImageReslice is used as a texture for a rectangular polygon</div><div><br></div><div>Both the reslicing and the texture mapping perform interpolation (the interpolation method for either can be controlled).</div><div><br></div><div>The main problem with vtkImagePlaneWidget is that the texels don't always match the image voxels. In fact, they only match under very specific circumstances:</div><div><br></div><div>1) the corner of the plane must be at the outer corner of the first voxel (i.e. a half-voxel offset in X and Y from the volume origin, or a multiple of the voxel spacing from the aforementioned position)</div><div><br></div><div>2) the size of the plane must be a power of two of the voxel size, because this class was written in a time when GPUs had a power of two restriction on texture sizes</div><div><br></div><div>With these restrictions in mind, if you have a volume made of slices that are power-of-two (e.g. 256x256 or 512x512), then as long as the plane is placed as specified above, it is possible to exactly render the image slices with vtkImagePlaneWidget.</div><div><br></div><div><br></div><div>I've written a mapper called vtkImageResliceMapper that overcomes the voxel vs. texel issue by putting the texels at exactly the same positions as the voxels (it also has another mode where the texels are placed exactly at screen pixel locations), but as far as I know no-one has used this class to make a VTK widget.</div><div><br></div><div> - David</div><div> </div><div><br></div><div><br><div class="gmail_extra"><br><div class="gmail_quote">On Thu, Sep 28, 2017 at 1:31 PM, Oliver Natt <span dir="ltr"><<a href="mailto:onatt@gmx.de" target="_blank">onatt@gmx.de</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<div bgcolor="#FFFFFF">
<p>Dear vtkusers,</p>
<p>during my work on a small programm for the analysis of computed
tomography data I found an unexpected behaviour of the
vtkImagePlaneWidget. I tried to boil down the problem to a small
python script, which uses a very small image dimension of only 3 x
3 x 3, where the effect becomes prominent (see python program
below).<br>
</p>
<p>The script makes a cut through a 3 x 3 x 3 volume using a
vtkImagePlaneWidget. The four coloured spheres indicate the
expected edges of the resulting image slice. The result is pretty
much, what I expected (see attachement screenshot1.png). However,
when I grab the vtkImagePlaneWidget using "Control + Middle Mouse
Button" an move the plane around in the x-y plane, some unexpected
effects occur (see screenshot2.png): Sometimes some kind of
interpolation happens, which results in an image that looks rather
like a 6 x 3 grid instead of 3 x 3 and also the position of the
image relative to the four coloured spheres changes. What I
require is a cut through the volume data that preserves the
correct voxel boundaries. Is that possible using the
vtkImagePlaneWidget or is there an alternative class, that serves
this purpose?</p>
<p>Many thanks<br>
Oliver</p>
<pre style="background-color:rgb(255,255,255);color:rgb(0,0,0);font-family:"Source Code Pro""><span style="color:rgb(0,0,221)">import </span>vtk
<span style="color:rgb(0,0,221)">import </span>vtk.util.numpy_support
<span style="color:rgb(0,0,221)">import </span>numpy <span style="color:rgb(0,0,221)">as </span>np
<span style="color:rgb(128,128,128)"># create a renderer
</span>renderer = vtk.vtkRenderer()
renderer.SetBackground(<span style="color:rgb(0,0,221)">0.2</span>, <span style="color:rgb(0,0,221)">0.2</span>, <span style="color:rgb(0,0,221)">0.5</span>)
renderer.SetBackground2(<span style="color:rgb(0,0,221)">0.4</span>, <span style="color:rgb(0,0,221)">0.4</span>, <span style="color:rgb(0,0,221)">1.0</span>)
renderer.<wbr>SetGradientBackground(<span style="color:rgb(0,0,221)">True</span>)
<span style="color:rgb(128,128,128)"># create a render_window
</span>render_window = vtk.vtkRenderWindow()
render_window.AddRenderer(<wbr>renderer)
<span style="color:rgb(128,128,128)"># create a renderwindowinteractor
</span>iren = vtk.vtkRenderWindowInteractor(<wbr>)
iren.SetRenderWindow(render_<wbr>window)
iren.Initialize()
<span style="color:rgb(128,128,128)"># Create a 3 x 3 x 3 image data with a spacing of 1.
</span>image = vtk.vtkImageData()
dat = np.linspace(<span style="color:rgb(0,0,221)">1</span>, <span style="color:rgb(0,0,221)">27</span>, <span style="color:rgb(0,0,221)">27</span>, <span style="color:rgb(102,0,153)">dtype</span>=np.int16) * <span style="color:rgb(0,0,221)">10
</span>dat = np.ascontiguousarray(dat)
dat = vtk.util.numpy_support.numpy_<wbr>to_vtk(dat, <span style="color:rgb(102,0,153)">deep</span>=<span style="color:rgb(0,0,221)">1</span>)
image.SetDimensions(<span style="color:rgb(0,0,221)">3</span>, <span style="color:rgb(0,0,221)">3</span>, <span style="color:rgb(0,0,221)">3</span>)
image.SetSpacing(<span style="color:rgb(0,0,221)">1</span>, <span style="color:rgb(0,0,221)">1</span>, <span style="color:rgb(0,0,221)">1</span>)
image.GetPointData().<wbr>SetScalars(dat)
image.Modified()
<span style="color:rgb(128,128,128)"># Create a vtkImagePlaneWidget and activate it
</span>plane_widget = vtk.vtkImagePlaneWidget()
plane_widget.<wbr>TextureInterpolateOff()
plane_widget.SetInputData(<wbr>image)
plane_widget.SetInteractor(<wbr>render_window.GetInteractor())
plane_widget.SetOrigin(<span style="color:rgb(0,0,221)">0</span>, <span style="color:rgb(0,0,221)">0</span>, <span style="color:rgb(0,0,221)">0</span>)
plane_widget.SetPoint1(<span style="color:rgb(0,0,221)">8</span>, <span style="color:rgb(0,0,221)">0</span>, <span style="color:rgb(0,0,221)">0</span>)
plane_widget.SetPoint2(<span style="color:rgb(0,0,221)">0</span>, <span style="color:rgb(0,0,221)">8</span>, <span style="color:rgb(0,0,221)">0</span>)
plane_widget.UpdatePlacement()
plane_widget.DisplayTextOn()
plane_widget.On()
plane_widget.SetWindowLevel(<span style="color:rgb(0,0,221)">20<wbr>0</span>, <span style="color:rgb(0,0,221)">50</span>)
rs = plane_widget.GetReslice()
<span style="color:rgb(0,0,221)">def </span>create_sphere(x, y, z, col):
<span style="color:rgb(0,100,0);font-weight:bold">"""Create a small sphere with color col at postion x, y, z"""
</span><span style="color:rgb(0,100,0);font-weight:bold"> </span>source = vtk.vtkSphereSource()
source.SetCenter(x, y, z)
source.SetRadius(<span style="color:rgb(0,0,221)">0.2</span>)
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputConnection(<wbr>source.GetOutputPort())
actor = vtk.vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetColor(<wbr>col)
renderer.AddActor(actor)
<span style="color:rgb(128,128,128)"># Position 4 small markers at the expected edges of the image slice.
</span>create_sphere(<span style="color:rgb(0,0,221)">0</span>, <span style="color:rgb(0,0,221)">0</span>, <span style="color:rgb(0,0,221)">0</span>, (<span style="color:rgb(0,0,221)">1</span>, <span style="color:rgb(0,0,221)">0</span>, <span style="color:rgb(0,0,221)">0</span>))
create_sphere(<span style="color:rgb(0,0,221)">3</span>, <span style="color:rgb(0,0,221)">0</span>, <span style="color:rgb(0,0,221)">0</span>, (<span style="color:rgb(0,0,221)">0</span>, <span style="color:rgb(0,0,221)">1</span>, <span style="color:rgb(0,0,221)">0</span>))
create_sphere(<span style="color:rgb(0,0,221)">0</span>, <span style="color:rgb(0,0,221)">3</span>, <span style="color:rgb(0,0,221)">0</span>, (<span style="color:rgb(0,0,221)">0</span>, <span style="color:rgb(0,0,221)">0</span>, <span style="color:rgb(0,0,221)">1</span>))
create_sphere(<span style="color:rgb(0,0,221)">3</span>, <span style="color:rgb(0,0,221)">3</span>, <span style="color:rgb(0,0,221)">0</span>, (<span style="color:rgb(0,0,221)">1</span>, <span style="color:rgb(0,0,221)">1</span>, <span style="color:rgb(0,0,221)">0</span>))
<span style="color:rgb(128,128,128)"># Reset the camera and start the RenderWindowInteractor
</span>renderer.ResetCamera()
iren.Start()</pre></div></blockquote></div></div></div></div>