TLDR;查看 Skimage 的pyramid_gaussian。在 (512, 512) 的单个图像上,它显示了 0.3M 倍的加速。 (163 毫秒/471 纳秒 = 346072)。 Pillow-SIMD 执行 super-fast 重新采样/调整大小,但要求您在安装之前卸载 PIL、Pillow。它使用并行处理(单指令,多数据 - SIMD)和更好的算法,例如用顺序框替换基于卷积的高斯模糊。建议在设置单独的venv 后将其用于生产环境。
有多种方法可以对图像进行上采样和下采样。我将添加一些我使用过的方法的基准。当我遇到更多方法时,我会不断更新这个答案,以便可以作为其他人的参考。
#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')
IIUC,这有点像你的管道 -
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)
方法和基准
(in no specific order)
1. Scipy zoom (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 zoom (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. Skimagepyramid_gaussian
在高斯金字塔中,后续图像使用高斯平均值(高斯模糊)加权并按比例缩小。对图像应用高斯模糊与使用高斯函数对图像进行卷积相同。模糊量取决于标准偏差大小 (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 pyramid_expand 和 pyramid_reduce
图像金字塔是一种多尺度图像表示,其中图像经过反复平滑和二次采样。第一个函数对图像进行平滑然后上采样,而第二个函数做同样的事情,但不是下采样,这两个函数都默认使用 spline order=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. Skimagerescale
按特定因子缩放图像。对放大或缩小 N 维图像执行样条插值(默认顺序为 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 resize 与最近像素 filter 用于重采样
返回此图像的调整大小的副本。从输入图像中选择一个最近的像素。忽略所有其他输入像素。
%%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. PIL resize 和 BILINEAR filter 用于重采样
返回此图像的调整大小的副本。对于调整大小,使用线性插值对可能对输出值有贡献的所有像素计算输出像素值。对于其他变换,使用输入图像中 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. PIL resize 和 BICUBIC filter 用于重采样
返回此图像的调整大小的副本。对于调整大小,使用三次插值对可能对输出值有贡献的所有像素计算输出像素值。对于其他变换,使用输入图像中 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. PIL resize 和 Lanczos filter 进行重采样
返回此图像的调整大小的副本。使用高质量的 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)