# Exposing \`vtkImageInterpolator\` border mode to the public API

**URL:** https://discourse.vtk.org/t/exposing-vtkimageinterpolator-border-mode-to-the-public-api/16046
**Category:** Development
**Tags:** python, proposal
**Created:** [September 16, 2025, 5:01pm UTC](https://discourse.vtk.org/t/exposing-vtkimageinterpolator-border-mode-to-the-public-api/16046 "2025-09-16T17:01:13Z")
**Posts on this page:** 14
**Page:** 1

<div class="post-metadata">

### Author: ![user27182](https://discourse.vtk.org/user_avatar/discourse.vtk.org/user27182/32/10104_2.png) [@user27182](https://discourse.vtk.org/u/user27182)
#### Post date: [September 16, 2025, 5:01pm UTC](https://discourse.vtk.org/t/exposing-vtkimageinterpolator-border-mode-to-the-public-api/16046/1 "2025-09-16T17:01:13Z")

</div>

The `vtkImageInterpolator` class uses an internal border mode with `clamp`, `repeat`, and `mirror` as options. [VTK: vtkImageInterpolator Class Reference](https://vtk.org/doc/nightly/html/classvtkImageInterpolator.html) [VTK: Imaging/Core/vtkAbstractImageInterpolator.h File Reference](https://vtk.org/doc/nightly/html/vtkAbstractImageInterpolator_8h.html#a4807967685a58efe9f4a6bad3c4962b2)  
It would be nice to give users the option to select the desired mode.

In addition, it would be useful to have a new `constant` mode.

For comparison, `scipy` has a [`zoom`](https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.zoom.html) function for up-sampling arrays, and has a `mode` option for selecting the desired mode, with the `constant` mode used by default.

Motivation:  
A [recent PyVista discussion](https://github.com/pyvista/pyvista/discussions/7954) revealed that using vtk’s cubic interpolator produces substantially different (and suprising) results when compared to scipy’s zoom interpolator (with order=3 for cubic), and the use (or absence) of a `constant` mode was found to be the reason why.

---

<div class="post-metadata">

### Author: ![dgobbi](https://discourse.vtk.org/user_avatar/discourse.vtk.org/dgobbi/32/18_2.png) [@dgobbi](https://discourse.vtk.org/u/dgobbi)
#### Post date: [September 16, 2025, 6:37pm UTC](https://discourse.vtk.org/t/exposing-vtkimageinterpolator-border-mode-to-the-public-api/16046/2 "2025-09-16T18:37:37Z")

</div>

In VTK, `SetBorderMode()` method is a public method, and pyvista should be able to expose it via `resample()` without any changes to VTK itself. I’m not sure why pyvista does not already do so.

There are reasons why VTK doesn’t implement the `constant` mode:

1. it requires an additional piece of information (the constant)
2. having a default constant value of zero can lead to surprising results

To illustrate (2), consider a typical CT image. Let’s say the raw CT data is in the range `[0,4095]` and that we also have a copy of the CT image converted to Hounsfield units in the range `[-1024,3071]` (here, the conversion is done by subtracting 1024 from the raw data). Ideally, we expect to get the same results (to within roundoff error) if we interpolate before converting to Housfield units as compared to interpolating after converting to Hounsfield units.

_However_ if we use the `constant` border mode with a constant of zero, we would get different results! In order to get the same results, the user would have to be savvy enough to use a constant of zero when working in Hounsfield units, and a constant of 1024 when working with the raw pixel data.

The `clamp` mode gives the same result in either case, because the `clamp` mode is invariant under linear transformations of the pixel values. The same is true of the `repeat` and `mirror` methods (and the `reflect` method). The `constant` mode stands out from the rest because it is _not_ invariant.

Also, consider what most of the border modes are trying to achieve: they are extrapolating the data values past the edge of the image. If the image is smooth (as most medical images are) then we expect that the data values in the border will have similar values to their neighbors in the image. That’s what the `clamp` mode provides.

I’m not actually opposed to adding a `constant` mode to vtkImageInterpolator, I’m just trying to illustrate why it isn’t my first choice when interpolating images. Any of the other modes will generally give superior results.

---

<div class="post-metadata">

### Author: ![user27182](https://discourse.vtk.org/user_avatar/discourse.vtk.org/user27182/32/10104_2.png) [@user27182](https://discourse.vtk.org/u/user27182)
#### Post date: [September 16, 2025, 7:41pm UTC](https://discourse.vtk.org/t/exposing-vtkimageinterpolator-border-mode-to-the-public-api/16046/3 "2025-09-16T19:41:31Z")

</div>

> In VTK, `SetBorderMode()` method is a public method, and pyvista should be able to expose it via `resample()` without any changes to VTK itself. I’m not sure why pyvista does not already do so.

Thanks for the info! For some reason I couldn’t see it in the docs, I think I was looking at the abstract interpolator docs [VTK: vtkImageInterpolator Class Reference](https://vtk.org/doc/nightly/html/classvtkImageInterpolator.html#details). Will be sure to add this option to PyVista’s API.

As for your comments/reasons for not including a `constant` mode by default, they do make sense, especially for the CT context with Hounsfield units. A similar argument can probably be made for the default value for other image filters like `vtkImageConstantPad` and `vtkImageThreshold` where using a default of `0` may not be appropriate for some use cases. (I don’t think these vtk filters actually make this default assumption, but PyVista’s wrappers of these filters do).

When creating filters for PyVista, there is often a tussle between

- strictly adhering to VTK’s implementation and defaults
- adding quality-of-life changes or defaults
- making the API more similar to other pythonic libraries like `scipy`

In this case, I think we’ll probably stick with the vtk default for the border mode, and add a few notes to the documentation about the effects this has on the output.

Given that a `constant` mode is not yet available for `vtkImageInterpolator`, is it sensible to emulate this by simply using `vtkImageConstantPad` before resampling? And if so, how much padding should be given? E.g. in the linked pyvista discussion, padding the image with zeros seemed to yield different results depending on the number of zeros that the input was padded with… Maybe padding with a single zero at the borders _and_ setting `BorderMode` to `repeat` is sufficient to emulate this?

---

<div class="post-metadata">

### Author: ![MattTheCuber](https://discourse.vtk.org/user_avatar/discourse.vtk.org/mattthecuber/32/9365_2.png) [@MattTheCuber](https://discourse.vtk.org/u/MattTheCuber)
#### Post date: [September 16, 2025, 8:15pm UTC](https://discourse.vtk.org/t/exposing-vtkimageinterpolator-border-mode-to-the-public-api/16046/4 "2025-09-16T20:15:50Z")

</div>

I just want to note that it is not reasonable to pad before resampling long-term, due to the extra memory that would be used by adding this padding and resampling the padding as well.

---

<div class="post-metadata">

### Author: ![dgobbi](https://discourse.vtk.org/user_avatar/discourse.vtk.org/dgobbi/32/18_2.png) [@dgobbi](https://discourse.vtk.org/u/dgobbi)
#### Post date: [September 16, 2025, 8:17pm UTC](https://discourse.vtk.org/t/exposing-vtkimageinterpolator-border-mode-to-the-public-api/16046/5 "2025-09-16T20:17:00Z")

</div>

The number of zeros in the padding depends on the size of the interpolation kernel, i.e. the padding should be half of the kernel width. Yes, you can use `repeat` to double the padding. Also note that `vtkImageResize` has an option to crop the image, which can be used to remove the padding without an additional step.

But if the goal is to replicate the scipy `zoom` function, perhaps you could add a similar `zoom` method to pyvista? Assuming that scipy `zoom` uses b-splines (its docs do not say), it could be duplicated almost exactly with VTK, including setting the order of the spline and having the option of turning off the prefilter.

---

<div class="post-metadata">

### Author: ![user27182](https://discourse.vtk.org/user_avatar/discourse.vtk.org/user27182/32/10104_2.png) [@user27182](https://discourse.vtk.org/u/user27182)
#### Post date: [September 16, 2025, 9:41pm UTC](https://discourse.vtk.org/t/exposing-vtkimageinterpolator-border-mode-to-the-public-api/16046/6 "2025-09-16T21:41:00Z")

</div>

> Yes, you can use `repeat` to double the padding. Also note that `vtkImageResize` has an option to crop the image, which can be used to remove the padding without an additional step.

Thanks for the feedback! Will probably add a `constant` mode with this implementation for PyVista’s `resample`.

> But if the goal is to replicate the scipy `zoom` function, perhaps you could add a similar `zoom` method to pyvista?

I am personally OK with `resample` as-is (my original use case for implementing it was to _down_-sample images), but this is a good suggestion, especially to have a different default `border_mode` (`constant`) by default. And I suppose this would also need to use [VTK: vtkImageBSplineInterpolator Class Reference](https://vtk.org/doc/nightly/html/classvtkImageBSplineInterpolator.html) ? This is not (yet) an option for `resample`, so that would also be a key difference. Maybe @MattTheCuber is interested in contributing this kind of filter?

---

<div class="post-metadata">

### Author: ![dgobbi](https://discourse.vtk.org/user_avatar/discourse.vtk.org/dgobbi/32/18_2.png) [@dgobbi](https://discourse.vtk.org/u/dgobbi)
#### Post date: [September 16, 2025, 10:08pm UTC](https://discourse.vtk.org/t/exposing-vtkimageinterpolator-border-mode-to-the-public-api/16046/7 "2025-09-16T22:08:43Z")

</div>

Yes, vtkImageBSplineInterpolator could be used for the implementation. But note the docs:

> [vtkImageBSplineInterpolator](https://vtk.org/doc/nightly/html/classvtkImageBSplineInterpolator.html) can be used to perform b-spline interpolation on images that have been filtered with [vtkImageBSplineCoefficients](https://vtk.org/doc/nightly/html/classvtkImageBSplineCoefficients.html).

B-spline interpolation is achieved by first applying the prefilter to compute the coefficients, and then applying the interpolator to the output of this prefilter. The prefilter is most likely the reason that it isn’t already available as an interpolator for `resample()`.

And speaking of b-splines, I just remembered another potential issue with adding new border modes to VTK: implementing border modes for the b-spline prefilter is **hard**. The papers that describe the prefilter are very math-heavy. Though hopefully if scipy is using b-splines, it might already provide an implementation.

---

<div class="post-metadata">

### Author: ![user27182](https://discourse.vtk.org/user_avatar/discourse.vtk.org/user27182/32/10104_2.png) [@user27182](https://discourse.vtk.org/u/user27182)
#### Post date: [September 19, 2025, 9:44pm UTC](https://discourse.vtk.org/t/exposing-vtkimageinterpolator-border-mode-to-the-public-api/16046/8 "2025-09-19T21:44:40Z")

</div>

Thanks for the info about the splines.  
Going back to the possible issue with cubic vs linear interpolation, it may not be related to the border mode after all…

Maybe I’m not understanding correctly, but shouldn’t the interpolators only interpolate _between_ values? I.e., the original values from the input should remain in the output, no?

E.g. with this code, a 2D image with values:

```py
[[0. 1.]
 [2. 3.]]

```

is interpolated with linear interpolation, and the output is

```py
[[0. 0.5 1.]
 [1. 1.5 2.]
 [2. 2.5 3.]]

```

Where we can see the original values remain as-is in the corners. But if I use cubic interpolation instead (with `SetInterpolationModeToCubic()`), I get:

```py
[[0. 0.5 1.]
 [0.5 1. 1.5]
 [1. 1.5 2.]]

```

But shouldn’t the bottom left value be `2`, and the bottom right `3` ?

Code:

```py
import vtk
import numpy as np

image = vtk.vtkImageData()
image.SetDimensions(2, 2, 1)

scalars = vtk.vtkFloatArray()
scalars.SetNumberOfComponents(1)
scalars.SetName("Scalars")

for val in [0., 1., 2., 3.]:
    scalars.InsertNextValue(val)

image.GetPointData().SetScalars(scalars)

interp = vtk.vtkImageInterpolator()
interp.SetInterpolationModeToLinear()

resize = vtk.vtkImageResize()
resize.SetInputData(image)
resize.SetInterpolator(interp)
resize.SetResizeMethodToMagnificationFactors()
resize.SetMagnificationFactors(2, 2, 1)
resize.Update()
output = resize.GetOutput()

out_scalars = output.GetPointData().GetScalars()
npts = output.GetNumberOfPoints()

dims = output.GetDimensions()
arr = np.array([out_scalars.GetTuple1(i) for i in range(npts)])
arr_2d = arr.reshape((dims[1], dims[0]))

print(arr_2d)

```

---

<div class="post-metadata">

### Author: ![user27182](https://discourse.vtk.org/user_avatar/discourse.vtk.org/user27182/32/10104_2.png) [@user27182](https://discourse.vtk.org/u/user27182)
#### Post date: [September 19, 2025, 11:44pm UTC](https://discourse.vtk.org/t/exposing-vtkimageinterpolator-border-mode-to-the-public-api/16046/9 "2025-09-19T23:44:19Z")

</div>

For comparison, here is a very similar example, but for the 1D case. For an input of

```py
[0., 1., 2., 3.]

```

With linear interpolation, we get:

```py
[0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0]

```

and with cubic:

```py
[0.0, 0.4375, 1.0, 1.5, 2.0, 2.5625, 3.0]

```

Unlike with the 2D case, here we can see that for both linear and cubic, the original values  
`[0., 1., 2., 3.]` are preserved in the output.

Code:

```py
import vtk

image = vtk.vtkImageData()
image.SetDimensions(4, 1, 1)

scalars = vtk.vtkFloatArray()
scalars.SetNumberOfComponents(1)
scalars.SetName("Scalars")

for val in [0., 1., 2., 3.]:
    scalars.InsertNextValue(val)

image.GetPointData().SetScalars(scalars)

interp = vtk.vtkImageInterpolator()
interp.SetInterpolationModeToLinear()

resize = vtk.vtkImageResize()
resize.SetInputData(image)
resize.SetInterpolator(interp)
resize.SetResizeMethodToMagnificationFactors()
resize.SetMagnificationFactors(2, 1, 1)
resize.Update()
output = resize.GetOutput()

out_scalars = output.GetPointData().GetScalars()
npts = output.GetNumberOfPoints()

dims = output.GetDimensions()
arr = [out_scalars.GetTuple1(i) for i in range(npts)]

print(arr)

```

---

<div class="post-metadata">

### Author: ![user27182](https://discourse.vtk.org/user_avatar/discourse.vtk.org/user27182/32/10104_2.png) [@user27182](https://discourse.vtk.org/u/user27182)
#### Post date: [September 19, 2025, 11:54pm UTC](https://discourse.vtk.org/t/exposing-vtkimageinterpolator-border-mode-to-the-public-api/16046/10 "2025-09-19T23:54:06Z")

</div>

And for completeness, here is the 3D case. The input:

```py
[[[0. 1.]
  [2. 3.]]

 [[4. 5.]
  [6. 7.]]]

```

Output with linear interpolation. Looks good to me, values range from 0 to 7.

```py
[[[0. 0.5 1.]
  [1. 1.5 2.]
  [2. 2.5 3.]]

 [[2. 2.5 3.]
  [3. 3.5 4.]
  [4. 4.5 5.]]

 [[4. 4.5 5.]
  [5. 5.5 6.]
  [6. 6.5 7.]]]

```

Output with cubic. Like the 2D case, this looks wrong, the values range from 0 to 3.

```py
[[[0. 0.5 1.]
  [0.5 1. 1.5]
  [1. 1.5 2.]]

 [[0.5 1. 1.5]
  [1. 1.5 2.]
  [1.5 2. 2.5]]

 [[1. 1.5 2.]
  [1.5 2. 2.5]
  [2. 2.5 3.]]]

```

Code:

```py
import vtk
import numpy as np

image = vtk.vtkImageData()
image.SetDimensions(2, 2, 2)

scalars = vtk.vtkFloatArray()
scalars.SetNumberOfComponents(1)
scalars.SetName("Scalars")

for val in [0., 1., 2., 3., 4., 5., 6., 7.]:
    scalars.InsertNextValue(val)

image.GetPointData().SetScalars(scalars)

interp = vtk.vtkImageInterpolator()
interp.SetInterpolationModeToLinear()

resize = vtk.vtkImageResize()
resize.SetInputData(image)
resize.SetInterpolator(interp)
resize.SetResizeMethodToMagnificationFactors()
resize.SetMagnificationFactors(2, 2, 2)
resize.Update()
output = resize.GetOutput()

out_scalars = output.GetPointData().GetScalars()
npts = output.GetNumberOfPoints()

dims = output.GetDimensions()
arr = np.array([out_scalars.GetTuple1(i) for i in range(npts)])
arr_3d = arr.reshape((dims[2], dims[1], dims[0]))

print(arr_3d)

```

In summary, it appears that cubic (1D) works, but bicubic (2D) and tricubic (3D) do not work as expected.

---

<div class="post-metadata">

### Author: ![dgobbi](https://discourse.vtk.org/user_avatar/discourse.vtk.org/dgobbi/32/18_2.png) [@dgobbi](https://discourse.vtk.org/u/dgobbi)
#### Post date: [September 19, 2025, 11:57pm UTC](https://discourse.vtk.org/t/exposing-vtkimageinterpolator-border-mode-to-the-public-api/16046/11 "2025-09-19T23:57:40Z")

</div>

Yes, it looks like a bug. The following adjustment (using vtkImageReslice) provides correct results:

```python
reslice = vtk.vtkImageReslice()
reslice.SetInterpolationModeToCubic()
reslice.OptimizationOff() # this fixes the issue
reslice.SetInputData(image)
reslice.SetOutputSpacing(0.5, 0.5, 1.0)
reslice.Update()
output = reslice.GetOutput()

```

output:

```plaintext
[[0. 0.5 1.]
 [1. 1.5 2.]
 [2. 2.5 3.]]

```

The `reslice.OptimizationOff()` here is the key, since it changes how vtkImageReslice uses the interpolator, and narrows down which code paths in vtkImageInterpolator might be responsible.

I’ll try for a fix this weekend. In the meantime, feel free to post a bug report to gitlab (it doesn’t need to be detailed, the reproducer is the most important piece).

---

<div class="post-metadata">

### Author: ![user27182](https://discourse.vtk.org/user_avatar/discourse.vtk.org/user27182/32/10104_2.png) [@user27182](https://discourse.vtk.org/u/user27182)
#### Post date: [September 20, 2025, 12:03am UTC](https://discourse.vtk.org/t/exposing-vtkimageinterpolator-border-mode-to-the-public-api/16046/12 "2025-09-20T00:03:34Z")

</div>

Thanks for confirming! Bug reported here: [https://gitlab.kitware.com/vtk/vtk/-/issues/19788](https://gitlab.kitware.com/vtk/vtk/-/issues/19788)

---

<div class="post-metadata">

### Author: ![MattTheCuber](https://discourse.vtk.org/user_avatar/discourse.vtk.org/mattthecuber/32/9365_2.png) [@MattTheCuber](https://discourse.vtk.org/u/MattTheCuber)
#### Post date: [September 21, 2025, 12:30am UTC](https://discourse.vtk.org/t/exposing-vtkimageinterpolator-border-mode-to-the-public-api/16046/13 "2025-09-21T00:30:53Z")

</div>

Thanos for all your help on this! Do you expect this fix to resolve all the problems we’ve been seeing? Will the additions to VTK/PyVista for border mode likely not be needed?

---

<div class="post-metadata">

### Author: ![user27182](https://discourse.vtk.org/user_avatar/discourse.vtk.org/user27182/32/10104_2.png) [@user27182](https://discourse.vtk.org/u/user27182)
#### Post date: [September 21, 2025, 7:11pm UTC](https://discourse.vtk.org/t/exposing-vtkimageinterpolator-border-mode-to-the-public-api/16046/14 "2025-09-21T19:11:39Z")

</div>

Based on the description from the fix in [https://gitlab.kitware.com/vtk/vtk/-/merge\_requests/12474](https://gitlab.kitware.com/vtk/vtk/-/merge_requests/12474), it seems that the bug is isolated to inputs with small dimensions, and therefore padding the input first to emulate constant mode was likely fixing the issue not because constant mode is somehow better, but only because the padding added the extra dimensions necessary to avoid executing the branch of code with the bug.

I’m not sure a constant mode is needed after all, and agree with @dgobbi [earlier arguments](https://discourse.vtk.org/t/exposing-vtkimageinterpolator-border-mode-to-the-public-api/16046/2) for why clamp mode is the right choice as the default mode.
