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

Amy*_*ens 6 python interpolation numpy scipy

目前,我已经编写了一些插入管道的Python代码.

传入的数据形状为numpy数组(1,512,19,25).我使用它scipy.ndimage.interpolation.zoom来使阵列形状(1,512,38,50).这可以通过一次调用函数来完成.基本上,它将每个(19,25)件调整为大小(38,50).

稍后在代码中,当数据以另一种方式移动时,不同的数据再次在另一个方向(38,50)到(19,25)上调整大小.

一切都按实施的方式运作,但我发现这确实很慢.例如,我测试了scipy.ndimage.interpolation.zoom调整图像文件大小的函数,它比Matlab的imresize函数慢.

有什么更快的方法在Python中执行此操作?

Aks*_*gal 7

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

\n
\n

有多种方法可以对图像进行上采样和下采样。我将添加一些我使用过的方法的基准。当我遇到更多方法时,我会不断更新这个答案,以便这可以作为其他人的参考。

\n
#Utility function for plotting original, upsampled, and downsampled image\n\ndef plotit(img, up, down):\n    fig, axes = plt.subplots(1,3, figsize=(10,15))\n    axes[0].imshow(img)\n    axes[1].imshow(up)\n    axes[2].imshow(down)\n    axes[0].title.set_text(\'Original\')\n    axes[1].title.set_text(\'Upsample\')\n    axes[2].title.set_text(\'Downsample\')\n
Run Code Online (Sandbox Code Playgroud)\n

IIUC,这在某种程度上是你的管道 -

\n
from scipy.ndimage import zoom\nfrom skimage.data import camera\n\nimg = camera() #(512,512)\nup = zoom(img,2) #upsample image\n\n#some code\n...\n\ndown = zoom(up,0.5) #downsample the upsampled image\n\nplotit(img, up, down)\n
Run Code Online (Sandbox Code Playgroud)\n

在此输入图像描述

\n
\n

方法和基准\n(in no specific order)

\n

1.Scipy缩放(阶数=3)

\n

使用给定阶数的样条插值来缩放数组,在本例中,默认阶数为 3。

\n
%%timeit\n#from scipy.ndimage import zoom\nup = zoom(img,2)\ndown = zoom(up,0.5)\n\n#163 ms \xc2\xb1 12.1 ms per loop (mean \xc2\xb1 std. dev. of 7 runs, 10 loops each)\n
Run Code Online (Sandbox Code Playgroud)\n

2.Scipy缩放(order=0)

\n

使用给定阶数的样条插值来缩放数组,在本例中,阶数 = 0。

\n
%%timeit\n#from scipy.ndimage import zoom\nup = zoom(img,2, order=0)\ndown = zoom(up,0.5, order=0)\n\n#18.7 ms \xc2\xb1 950 \xc2\xb5s per loop (mean \xc2\xb1 std. dev. of 7 runs, 100 loops each)\n
Run Code Online (Sandbox Code Playgroud)\n

3. Skimagepyramid_gaussian

\n

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

\n
%%timeit\n#from skimage.transform import import pyramid_gaussian\nup = pyramid_gaussian(img,2)\ndown = pyramid_gaussian(up,0.5)\n\n#471 ns \xc2\xb1 30.4 ns per loop (mean \xc2\xb1 std. dev. of 7 runs, 1000000 loops each)\n
Run Code Online (Sandbox Code Playgroud)\n

4. Skimage的pyramid_expandpyramid_reduce

\n

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

\n
%%timeit\n#from skimage.transform import import pyramid_expand, pyramid_reduce\nup = pyramid_expand(img,2)\ndown = pyramid_reduce(up,2)\n\n#120 ms \xc2\xb1 3.08 ms per loop (mean \xc2\xb1 std. dev. of 7 runs, 10 loops each)\n
Run Code Online (Sandbox Code Playgroud)\n

5.Skimage重新缩放

\n

按特定因子缩放图像。对放大或缩小的 N 维图像执行样条插值(默认阶数=1)。请注意,缩小图像尺寸时应启用抗锯齿功能,以避免出现锯齿现象。

\n
%%timeit\n#from skimage.transform import import rescale\nup = rescale(img,2)\ndown = rescale(up,0.5)\n\n#83 ms \xc2\xb1 3.69 ms per loop (mean \xc2\xb1 std. dev. of 7 runs, 10 loops each)\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

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

\n
%%timeit\n#from PIL import Image\nim = Image.fromarray(img)\nup = im.resize((im.width*2, im.height*2),resample=Image.NEAREST)\ndown = up.resize((up.width//2, up.height//2),resample=Image.NEAREST)\n\n#704 \xc2\xb5s \xc2\xb1 29.7 \xc2\xb5s per loop (mean \xc2\xb1 std. dev. of 7 runs, 1000 loops each)\n
Run Code Online (Sandbox Code Playgroud)\n

7. PIL使用 BILINEAR滤波器调整大小以进行重采样

\n

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

\n
%%timeit\n#from PIL import Image\nim = Image.fromarray(img)\nup = im.resize((im.width*2, im.height*2),resample=Image.BILINEAR)\ndown = up.resize((up.width//2, up.height//2),resample=Image.BILINEAR)\n\n#10.2 ms \xc2\xb1 877 \xc2\xb5s per loop (mean \xc2\xb1 std. dev. of 7 runs, 100 loops each)\n
Run Code Online (Sandbox Code Playgroud)\n

8.使用 BICUBIC滤波器调整PIL大小以进行重采样

\n

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

\n
%%timeit\n#from PIL import Image\nim = Image.fromarray(img)\nup = im.resize((im.width*2, im.height*2),resample=Image.BICUBIC)\ndown = up.resize((up.width//2, up.height//2),resample=Image.BICUBIC)\n\n#12.3 ms \xc2\xb1 326 \xc2\xb5s per loop (mean \xc2\xb1 std. dev. of 7 runs, 100 loops each)\n
Run Code Online (Sandbox Code Playgroud)\n

9. PIL使用 Lanczos滤波器调整大小以进行重采样

\n

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

\n
%%timeit\n#from PIL import Image\nim = Image.fromarray(img)\nup = im.resize((im.width*2, im.height*2),resample=Image.LANCZOS)\ndown = up.resize((up.width//2, up.height//2),resample=Image.LANCZOS)\n\n#15.7 ms \xc2\xb1 184 \xc2\xb5s per loop (mean \xc2\xb1 std. dev. of 7 runs, 100 loops each)\n
Run Code Online (Sandbox Code Playgroud)\n


xbo*_*ard 0

检查https://python-pillow.org/pillow-perf/中的基准测试

TL;DR:对于大多数情况,Python 中最快的方法是使用pillow-simd