重新采样表示图像的numpy数组


74

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

scipy.misc.imresize

通过包装PIL的调整大小功能可以做到这一点。唯一的问题是,由于它使用PIL,因此numpy数组必须符合图像格式,最多只能提供4个“颜色”通道。

我希望能够使用任意数量的“彩色”通道来调整任意图像的大小。我想知道是否有简单的方法可以在scipy / numpy中执行此操作,或者是否需要自己滚动。

对于如何炮制自己,我有两个想法:

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

对于大数据,第一个可能很慢,而第二个似乎没有提供除样条线之外的任何其他插值方法。


你看了scipy.interpolate.griddata吗?链接
艾萨克(Isaac)

看起来是一个很棒的功能,但是它是针对完全非结构化的数据的,它将运行比我需要的时间更长的算法。我已经看过了interp2d,但不仅它有很多错误,而且甚至不确定它是否可以正确地对数据进行下采样。
古斯塔夫·拉尔森

Answers:


112

根据您的描述,您想要scipy.ndimage.zoom

双线性插值将为order=1,最接近为order=0,三次为默认值(order=3)。

zoom 专门用于要重新采样为新分辨率的常规栅格数据。

作为一个简单的例子:

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)

结果:

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]]

编辑:正如Matt S.所指出的,放大多波段图像有两个注意事项。我正在从以前的答案之一中几乎逐字复制以下部分:

缩放也适用于3D(和nD)阵列。但是请注意,例如,如果放大2倍,则会沿所有轴缩放。

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

这样产生:

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)

对于多波段图像,通常不希望沿“ z”轴进行插值,从而创建新波段。

如果您要缩放3波段RGB图像,可以通过指定一个元组序列作为缩放因子来实现:

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

这样产生:

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]]]

1
供其他用户参考:如果您具有多通道图像数据,请在每个“通道切片”中调用它,以避免不必要的“通道扩展”。通过示例进行解释:如果您的图像的像素宽度为10,高度为5,然后是3个通道(每个RGB表示一个通道),则调用此按钮将其放大7.0倍后,您将获得一个“ 70 x 35”像素,但具有21个通道。“ scipy.ndimage.zoom(np.ones(10 * 5 * 3).reshape(10,5,3),7.0,order = 0).shape”将为您提供元组:'(70,35,21) PS。无关:它优雅地处理浮如“0.37”或“6.1”点缩放因素
马特S.

7
@MattS。-正如您所描述的,无需将其分别应用于每个频段。只需指定一个元组作为缩放因子即可。例如scipy.ndimage.zoom(data, (3,3,1)),将3d阵列沿x和y方向缩放3倍,而保留第三个维度。
乔·肯顿2013年

1
@MattS。-(针对您删除的评论)好的建议!对不起,我没有回复!我添加了有关缩放多波段图像的警告。
乔·金顿

1
仅仅是我还是scipy.ndimage.zoom实际上处理矩阵的边缘不同于scipy.misc.imresize?当具有值缩放10侧面是只有5个值宽(与imresize它是10)。
克里斯(Chris)

缩放不适用于小于1的值。请参见github.com/scipy/scipy/issues/7324
Kevin Johnsrude,

15

如果你想重新取样,那么你应该看看SciPy的的食谱进行重排。特别是,最后congrid定义的函数将支持重新绑定或插值(等效于IDL中具有相同名称的函数)。如果您不想插值,这应该是最快的选择。

您也可以直接使用scipy.ndimage.map_coordinates,它将对任何类型的重采样(包括非结构化网格)进行样条插值。我发现map_coordinates对于大型数组(nx,ny> 200)比较慢。

对于结构化网格上的插值,我倾向于使用scipy.interpolate.RectBivariateSpline。您可以选择样条曲线的顺序(线性,二次方,三次等),甚至可以为每个轴独立选择。一个例子:

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

在这种情况下,您要进行双线性插值(kx = ky = 1)。不支持“最近”类型的插值,因为所有这些操作都是在矩形网格上进行样条插值。这也不是最快的方法。

如果要进行双线性或双三次插值,则通常进行两个一维插值会快得多:

    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

您也可以使用kind='nearest',但是在这种情况下,可以消除横向阵列。



7

我最近刚刚发现了scipy.ndimage.interpolation.zoom问题,该问题已作为错误报告提交:https : //github.com/scipy/scipy/issues/3203

作为一种选择(或者至少对我来说),我发现scikit-image的skimage.transform.resize可以正常工作:http ://scikit-image.org/docs/dev/api/skimage.transform.html#skimage .transform.resize

但是,它与scipy的interpolation.zoom的工作方式有所不同-您无需指定多个,而是指定所需的输出形状。这适用于2D和3D图像。

对于仅2D图像,您可以使用transform.rescale并指定一个乘数或比例,就像使用interpolation.zoom一样。


谢谢,我也注意到zoom以前使用过奇怪的输出。我会紧resize记skimage ,谢谢!
Gustav Larsson 2014年

旧线程,但是否resize保留数组(图像)中值的大小?我只是第一次尝试过,而对于16位灰度图像,则没有。原始数组的中位数约为32000,调整大小后的图像的中位数介于0和1之间
Evan

2

您可以使用interpolate.interp2d

例如,考虑由numpy数组表示的图像arr,可以将其调整为任意高度和宽度,如下所示:

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))

当然,如果您的图像有多个通道,则必须对每个通道执行插值。


0

该解决方案可缩放馈送图像的X和Y,而不会影响RGB通道:

import numpy as np
import scipy.ndimage

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

希望这是有用的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.