重新采样表示图像的 numpy 数组
Posted
技术标签:
【中文标题】重新采样表示图像的 numpy 数组【英文标题】:Resampling a numpy array representing an image 【发布时间】:2012-10-25 21:29:16 【问题描述】:我正在寻找如何以新的大小对表示图像数据的 numpy 数组进行重新采样,最好可以选择插值方法(最近的、双线性等)。我知道有
scipy.misc.imresize
它通过包装 PIL 的 resize 函数来做到这一点。唯一的问题是,由于它使用 PIL,numpy 数组必须符合图像格式,最多提供 4 个“颜色”通道。
我希望能够使用任意数量的“颜色”通道调整任意图像的大小。我想知道在 scipy/numpy 中是否有一种简单的方法可以做到这一点,或者我是否需要自己动手。
对于如何自己制作一个,我有两个想法:
在每个通道上分别运行scipy.misc.imresize
的函数
使用scipy.ndimage.interpolation.affine_transform
创建我自己的
第一个对于大数据可能会很慢,第二个似乎不提供除样条之外的任何其他插值方法。
【问题讨论】:
你看过scipy.interpolate.griddata
吗? link
看起来是个很棒的函数,但它是针对完全非结构化数据的,它运行的算法比我需要的要耗时得多。我查看了interp2d
,但它不仅非常错误,而且我什至不确定它是否会正确地对数据进行下采样。
【参考方案1】:
根据你的描述,你想要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. 指出的,缩放多波段图像有几个注意事项。我正在从我的earlier answers 之一中逐字复制以下部分:
缩放也适用于 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]]]
【讨论】:
仅供参考:如果您有多通道 image 数据,请在每个“通道切片”中调用它以避免不必要的“通道扩展”。通过示例解释:如果您的图像像素宽度为 10,高度为 5,然后是 3 个通道(每个 RGB 通道),在您调用它以缩放 7.0 x 之后,您将返回一个数组“70 x 35”像素,但有 21 个通道。 "scipy.ndimage.zoom(np.ones(10*5*3).reshape(10, 5, 3), 7.0, order=0).shape" 会给你元组:'(70, 35, 21) '附言。不相关:它可以优雅地处理浮点缩放因子,例如“0.37”或“6.1” @MattS。 - 正如你所描述的,没有必要将它分别用于每个乐队。只需指定一个元组作为缩放因子。例如。scipy.ndimage.zoom(data, (3,3,1))
沿 x 和 y 维度将 3d 数组缩放 3 倍,同时保留第三维度。
@MattS。 - (回应您删除的评论)好建议!抱歉我没有早点回复!我添加了关于缩放多波段图像的警告。
只是我还是 scipy.ndimage.zoom
实际上处理矩阵边缘的方式与 scipy.misc.imresize
不同?当使用 10
的值缩放时,边只有 5 个值宽(imresize
是 10
)。
@Kevin,从 1.6.0 版开始,该问题已得到解决【参考方案2】:
如果您想重新采样,那么您应该查看 Scipy 的 rebinning 食谱。特别是最后定义的congrid
函数将支持rebinning或interpolation(相当于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)
。不支持“最近”类型的插值,因为这只是在矩形网格上进行样条插值。这也不是最快的方法。
如果您使用双线性或双三次插值,执行两次 1D 插值通常要快得多:
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'
,但在这种情况下,请去掉横向数组。
【讨论】:
【参考方案3】:你看过Scikit-image吗?它的transform.pyramid_*
函数可能对你有用。
【讨论】:
【参考方案4】:我最近刚刚发现 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
的奇怪输出。我会记住 skimage 的 resize
,谢谢!
旧线程,但resize
是否保留了数组(图像)中值的大小?我刚刚第一次尝试,对于16位灰度图像,它没有;原始数组的中位数约为 32000,调整大小的图像的中位数在 0 到 1 之间。【参考方案5】:
您可以使用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))
当然,如果你的图像有多个通道,你必须对每个通道进行插值。
【讨论】:
【参考方案6】:此解决方案在不影响 RGB 通道的情况下缩放输入图像的 X 和 Y:
import numpy as np
import scipy.ndimage
matplotlib.pyplot.imshow(scipy.ndimage.zoom(image_np_array, zoom = (7,7,1), order = 1))
希望这是有用的。
【讨论】:
以上是关于重新采样表示图像的 numpy 数组的主要内容,如果未能解决你的问题,请参考以下文章