Numpy数组的快速插值/重采样 - Python

12

目前,我编写了一些 Python 代码并将其插入到了一个管道中。

输入的数据以形状为 (1,512,19,25) 的 numpy 数组的形式进入。我使用 scipy.ndimage.interpolation.zoom 将数组调整为形状为 (1,512,38,50)。这可以通过一次调用该函数来完成。基本上,它将每个 (19,25) 片段调整为尺寸为 (38,50)。

在代码的后面,当数据反向移动时,不同的数据再次被调整为另一方向上的大小(38,50) 到 (19,25)。

虽然实现了一切功能,但是我发现这样做非常慢。例如,我测试了调整图像文件大小的 scipy.ndimage.interpolation.zoom 函数,它比 Matlab 的 imresize 函数慢得多。

有什么更快的方法在 Python 中实现这个功能?


1
你在使用 ndimage.zoom 时采用了什么样的插值方法(即 order 参数是什么)?是只使用最近邻插值还是同时使用线性插值、双三次插值等? - ali_m
默认设置是三阶,我没有更改过,所以就按照这个来吧。 - Amy Stevens
1
双三次插值本质上是非常昂贵的。你能否使用双线性或最近邻插值来代替? - ali_m
5
order=0将给出最近邻插值,order=1将给出双线性插值等。我预计这两种方法都比order=3快很多,但也可能有更快的方法。我主要是想问最近邻插值或双线性插值是否适合您的需求。顺便说一下,我建议您编辑问题的标题——您所做的不仅仅是调整大小或重复数组,而是重新采样或插值处理。 - ali_m
2
请您能否提供一个最简单的示例代码?我预计这将包括数组创建和缩放调用,以及您想要使用的缩放参数。我希望使用该精确代码来回答您的问题。 - DrM
显示剩余4条评论
2个回答

7

TLDR; 请查看Skimage的金字塔高斯。在单张(512,512)的图像上,它显示了0.3M倍的加速。(163毫秒/471纳秒=346072)。Pillow-SIMD可以进行超快速的重采样/调整大小,但需要您在安装之前卸载PIL、Pillow。它使用并行处理(单指令,多数据-SIMD)和更好的算法,例如用顺序框替换基于卷积的高斯模糊。建议在设置单独的venv后将其用于生产环境。


有多种方法可以对图像进行上采样和下采样。我将添加一些我使用过的方法的基准测试结果。随着我发现更多的方法,我将不断更新这个答案,以便它可以成为SO上其他人的参考。

#Utility function for plotting original, upsampled, and downsampled image

def plotit(img, up, down):
    fig, axes = plt.subplots(1,3, figsize=(10,15))
    axes[0].imshow(img)
    axes[1].imshow(up)
    axes[2].imshow(down)
    axes[0].title.set_text('Original')
    axes[1].title.set_text('Upsample')
    axes[2].title.set_text('Downsample')

如果我理解正确,这在某种程度上是您的管道 -

from scipy.ndimage import zoom
from skimage.data import camera

img = camera() #(512,512)
up = zoom(img,2) #upsample image

#some code
...

down = zoom(up,0.5) #downsample the upsampled image

plotit(img, up, down)

enter image description here


方法和基准测试 (没有特定顺序)

1. Scipy 缩放(order=3)

使用给定阶数的样条插值对数组进行缩放,此处默认为order = 3。

%%timeit
#from scipy.ndimage import zoom
up = zoom(img,2)
down = zoom(up,0.5)

#163 ms ± 12.1 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

2. Scipy 缩放 (order=0)

使用给定阶数的样条插值对该数组进行缩放,本例中阶数为 0。

%%timeit
#from scipy.ndimage import zoom
up = zoom(img,2, order=0)
down = zoom(up,0.5, order=0)

#18.7 ms ± 950 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

3. Skimage 金字塔高斯

在高斯金字塔中,后续图像使用高斯平均值(高斯模糊)进行加权并缩小。对图像应用高斯模糊相当于将图像与高斯函数卷积。模糊的程度取决于标准差大小(sigma)。

%%timeit
#from skimage.transform import import pyramid_gaussian
up = pyramid_gaussian(img,2)
down = pyramid_gaussian(up,0.5)

#471 ns ± 30.4 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

4. Skimage 金字塔扩展金字塔缩减

图像金字塔是一种多尺度图像表示类型,其中图像经过重复平滑和子采样。第一个函数对图像进行平滑处理,然后上采样,而第二个函数则相同但下采样,这两个函数默认使用样条阶数=1。

%%timeit
#from skimage.transform import import pyramid_expand, pyramid_reduce
up = pyramid_expand(img,2)
down = pyramid_reduce(up,2)

#120 ms ± 3.08 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

5. Skimage 重新缩放

按一定比例缩放图像。对N维图像进行样条插值(默认为order=1)以进行上采样或下采样。请注意,在缩小图像时,应启用抗锯齿以避免出现混叠伪影。

%%timeit
#from skimage.transform import import rescale
up = rescale(img,2)
down = rescale(up,0.5)

#83 ms ± 3.69 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

6. 使用最近像素过滤器进行重采样的PIL 调整大小

返回此图像的调整大小副本。从输入图像中选择一个最近的像素。忽略所有其他输入像素。

%%timeit
#from PIL import Image
im = Image.fromarray(img)
up = im.resize((im.width*2, im.height*2),resample=Image.NEAREST)
down = up.resize((up.width//2, up.height//2),resample=Image.NEAREST)

#704 µs ± 29.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

7. 使用双线性滤波器filter进行重采样的 PIL resize

返回此图像的调整大小副本。对于调整大小,使用所有可能对输出值产生贡献的像素上的线性插值计算输出像素值。对于其他变换,则在输入图像的2x2环境中使用线性插值。

%%timeit
#from PIL import Image
im = Image.fromarray(img)
up = im.resize((im.width*2, im.height*2),resample=Image.BILINEAR)
down = up.resize((up.width//2, up.height//2),resample=Image.BILINEAR)

#10.2 ms ± 877 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

8. 使用BICUBIC过滤器进行重采样的PIL resize

返回此图像的调整大小副本。对于调整大小,使用所有可能对输出值产生贡献的像素上的立方插值来计算输出像素值。对于其他转换,使用输入图像中4x4环境上的立方插值。

%%timeit
#from PIL import Image
im = Image.fromarray(img)
up = im.resize((im.width*2, im.height*2),resample=Image.BICUBIC)
down = up.resize((up.width//2, up.height//2),resample=Image.BICUBIC)

#12.3 ms ± 326 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

9. 使用Lanczos滤波器resize PIL resize 进行重采样

返回此图像的调整大小副本。使用高质量的Lanczos滤波器(截断的sinc)计算可能对输出值有贡献的所有像素的输出像素值。

%%timeit
#from PIL import Image
im = Image.fromarray(img)
up = im.resize((im.width*2, im.height*2),resample=Image.LANCZOS)
down = up.resize((up.width//2, up.height//2),resample=Image.LANCZOS)

#15.7 ms ± 184 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

1
你确实检查了结果吗?pyramid_gaussian只返回一个生成器,而不是重新缩放后的图像。 你需要实际迭代该生成器才能真正获得重新缩放后的图像。然后我得到了与Scipy zoom order = 0类似的速度。 - Albert

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接