对表示图像的数字数组进行重新采样

我正在寻找如何重新采样一个数字数组表示图像数据在一个新的大小,最好有一个选择的插值方法(最近,双线性,等)。我知道有

scipy.misc.imresize

它通过包装 PIL 的调整函数来实现这一点。唯一的问题是,因为它使用 PIL,数字阵列必须符合图像格式,给我最多4个“彩色”通道。

我希望能够调整任意图像的大小,与任意数量的“彩色”通道。我想知道是否有一个简单的方法来做到这一点在 scypy/numpy,或者如果我需要卷自己的。

我自己有两个想法:

  • 在每个通道上分别运行 scipy.misc.imresize的函数
  • 使用 scipy.ndimage.interpolation.affine_transform创建我自己的

第一种方法对于大数据来说可能比较慢,而第二种方法似乎除了样条外没有提供任何其他的插值方法。

112600 次浏览

If you want to resample, then you should look at Scipy's cookbook for rebinning. In particular, the congrid function defined at the end will support rebinning or interpolation (equivalent to the function in IDL with the same name). This should be the fastest option if you don't want interpolation.

You can also use directly scipy.ndimage.map_coordinates, which will do a spline interpolation for any kind of resampling (including unstructured grids). I find map_coordinates to be slow for large arrays (nx, ny > 200).

For interpolation on structured grids, I tend to use scipy.interpolate.RectBivariateSpline. You can choose the order of the spline (linear, quadratic, cubic, etc) and even independently for each axis. An example:

    import scipy.interpolate as interp
f = interp.RectBivariateSpline(x, y, im, kx=1, ky=1)
new_im = f(new_x, new_y)

In this case you're doing a bi-linear interpolation (kx = ky = 1). The 'nearest' kind of interpolation is not supported, as all this does is a spline interpolation over a rectangular mesh. It's also not the fastest method.

If you're after bi-linear or bi-cubic interpolation, it is generally much faster to do two 1D interpolations:

    f = interp.interp1d(y, im, kind='linear')
temp = f(new_y)
f = interp.interp1d(x, temp.T, kind='linear')
new_im = f(new_x).T

You can also use kind='nearest', but in that case get rid of the transverse arrays.

Have you looked at Scikit-image? Its transform.pyramid_* functions might be useful for you.

Based on your description, you want scipy.ndimage.zoom.

Bilinear interpolation would be order=1, nearest is order=0, and cubic is the default (order=3).

zoom is specifically for regularly-gridded data that you want to resample to a new resolution.

As a quick example:

import numpy as np
import scipy.ndimage


x = np.arange(9).reshape(3,3)


print 'Original array:'
print x


print 'Resampled by a factor of 2 with nearest interpolation:'
print scipy.ndimage.zoom(x, 2, order=0)




print 'Resampled by a factor of 2 with bilinear interpolation:'
print scipy.ndimage.zoom(x, 2, order=1)




print 'Resampled by a factor of 2 with cubic interpolation:'
print scipy.ndimage.zoom(x, 2, order=3)

And the result:

Original array:
[[0 1 2]
[3 4 5]
[6 7 8]]
Resampled by a factor of 2 with nearest interpolation:
[[0 0 1 1 2 2]
[0 0 1 1 2 2]
[3 3 4 4 5 5]
[3 3 4 4 5 5]
[6 6 7 7 8 8]
[6 6 7 7 8 8]]
Resampled by a factor of 2 with bilinear interpolation:
[[0 0 1 1 2 2]
[1 2 2 2 3 3]
[2 3 3 4 4 4]
[4 4 4 5 5 6]
[5 5 6 6 6 7]
[6 6 7 7 8 8]]
Resampled by a factor of 2 with cubic interpolation:
[[0 0 1 1 2 2]
[1 1 1 2 2 3]
[2 2 3 3 4 4]
[4 4 5 5 6 6]
[5 6 6 7 7 7]
[6 6 7 7 8 8]]

Edit: As Matt S. pointed out, there are a couple of caveats for zooming multi-band images. I'm copying the portion below almost verbatim from one of my earlier answers:

Zooming also works for 3D (and nD) arrays. However, be aware that if you zoom by 2x, for example, you'll zoom along all axes.

data = np.arange(27).reshape(3,3,3)
print 'Original:\n', data
print 'Zoomed by 2x gives an array of shape:', ndimage.zoom(data, 2).shape

This yields:

Original:
[[[ 0  1  2]
[ 3  4  5]
[ 6  7  8]]


[[ 9 10 11]
[12 13 14]
[15 16 17]]


[[18 19 20]
[21 22 23]
[24 25 26]]]
Zoomed by 2x gives an array of shape: (6, 6, 6)

In the case of multi-band images, you usually don't want to interpolate along the "z" axis, creating new bands.

If you have something like a 3-band, RGB image that you'd like to zoom, you can do this by specifying a sequence of tuples as the zoom factor:

print 'Zoomed by 2x along the last two axes:'
print ndimage.zoom(data, (1, 2, 2))

This yields:

Zoomed by 2x along the last two axes:
[[[ 0  0  1  1  2  2]
[ 1  1  1  2  2  3]
[ 2  2  3  3  4  4]
[ 4  4  5  5  6  6]
[ 5  6  6  7  7  7]
[ 6  6  7  7  8  8]]


[[ 9  9 10 10 11 11]
[10 10 10 11 11 12]
[11 11 12 12 13 13]
[13 13 14 14 15 15]
[14 15 15 16 16 16]
[15 15 16 16 17 17]]


[[18 18 19 19 20 20]
[19 19 19 20 20 21]
[20 20 21 21 22 22]
[22 22 23 23 24 24]
[23 24 24 25 25 25]
[24 24 25 25 26 26]]]

I've recently just found an issue with scipy.ndimage.interpolation.zoom, which I've submitted as a bug report: https://github.com/scipy/scipy/issues/3203

As an alternative (or at least for me), I've found that scikit-image's skimage.transform.resize works correctly: http://scikit-image.org/docs/dev/api/skimage.transform.html#skimage.transform.resize

However it works differently to scipy's interpolation.zoom - rather than specifying a mutliplier, you specify the the output shape that you want. This works for 2D and 3D images.

For just 2D images, you can use transform.rescale and specify a multiplier or scale as you would with interpolation.zoom.

This solution scales X and Y of the fed image without affecting RGB channels:

import numpy as np
import scipy.ndimage


matplotlib.pyplot.imshow(scipy.ndimage.zoom(image_np_array, zoom = (7,7,1), order = 1))

Hope this is useful.

You can use interpolate.interp2d.

For example, considering an image represented by a numpy array arr, you can resize it to an arbitrary height and width as follows:

W, H = arr.shape[:2]
new_W, new_H = (600,300)
xrange = lambda x: np.linspace(0, 1, x)


f = interp2d(xrange(W), xrange(H), arr, kind="linear")
new_arr = f(xrange(new_W), xrange(new_H))

Of course, if your image has multiple channels, you have to perform the interpolation for each one.