TLDR; 请查看Skimage的金字塔高斯。在单张(512,512)的图像上,它显示了0.3M倍的加速。(163毫秒/471纳秒=346072)。Pillow-SIMD可以进行超快速的重采样/调整大小,但需要您在安装之前卸载PIL、Pillow。它使用并行处理(单指令,多数据-SIMD)和更好的算法,例如用顺序框替换基于卷积的高斯模糊。建议在设置单独的venv
后将其用于生产环境。
有多种方法可以对图像进行上采样和下采样。我将添加一些我使用过的方法的基准测试结果。随着我发现更多的方法,我将不断更新这个答案,以便它可以成为SO上其他人的参考。
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()
up = zoom(img,2)
...
down = zoom(up,0.5)
plotit(img, up, down)
![enter image description here](https://istack.dev59.com/yDXXq.webp)
方法和基准测试
(没有特定顺序)
1. Scipy 缩放(order=3)
使用给定阶数的样条插值对数组进行缩放,此处默认为order = 3。
%%timeit
up = zoom(img,2)
down = zoom(up,0.5)
2. Scipy 缩放 (order=0)
使用给定阶数的样条插值对该数组进行缩放,本例中阶数为 0。
%%timeit
up = zoom(img,2, order=0)
down = zoom(up,0.5, order=0)
3. Skimage 金字塔高斯
在高斯金字塔中,后续图像使用高斯平均值(高斯模糊)进行加权并缩小。对图像应用高斯模糊相当于将图像与高斯函数卷积。模糊的程度取决于标准差大小(sigma)。
%%timeit
up = pyramid_gaussian(img,2)
down = pyramid_gaussian(up,0.5)
4. Skimage 金字塔扩展 和 金字塔缩减
图像金字塔是一种多尺度图像表示类型,其中图像经过重复平滑和子采样。第一个函数对图像进行平滑处理,然后上采样,而第二个函数则相同但下采样,这两个函数默认使用样条阶数=1。
%%timeit
up = pyramid_expand(img,2)
down = pyramid_reduce(up,2)
5. Skimage 重新缩放
按一定比例缩放图像。对N维图像进行样条插值(默认为order=1)以进行上采样或下采样。请注意,在缩小图像时,应启用抗锯齿以避免出现混叠伪影。
%%timeit
up = rescale(img,2)
down = rescale(up,0.5)
6. 使用最近像素过滤器进行重采样的PIL 调整大小
返回此图像的调整大小副本。从输入图像中选择一个最近的像素。忽略所有其他输入像素。
%%timeit
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)
7. 使用双线性滤波器filter进行重采样的 PIL resize
返回此图像的调整大小副本。对于调整大小,使用所有可能对输出值产生贡献的像素上的线性插值计算输出像素值。对于其他变换,则在输入图像的2x2环境中使用线性插值。
%%timeit
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)
8. 使用BICUBIC过滤器进行重采样的PIL resize
返回此图像的调整大小副本。对于调整大小,使用所有可能对输出值产生贡献的像素上的立方插值来计算输出像素值。对于其他转换,使用输入图像中4x4环境上的立方插值。
%%timeit
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)
9. 使用Lanczos滤波器resize PIL resize 进行重采样
返回此图像的调整大小副本。使用高质量的Lanczos滤波器(截断的sinc)计算可能对输出值有贡献的所有像素的输出像素值。
%%timeit
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)
ndimage.zoom
时采用了什么样的插值方法(即order
参数是什么)?是只使用最近邻插值还是同时使用线性插值、双三次插值等? - ali_morder=0
将给出最近邻插值,order=1
将给出双线性插值等。我预计这两种方法都比order=3
快很多,但也可能有更快的方法。我主要是想问最近邻插值或双线性插值是否适合您的需求。顺便说一下,我建议您编辑问题的标题——您所做的不仅仅是调整大小或重复数组,而是重新采样或插值处理。 - ali_m