问题描述
当前,我已经编写了一些插入到管道中的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中执行此操作的更快方法是什么?
TLDR; 查看Skimage的
方法和基准(无特定顺序)
1.Scipy 缩放(order = 3)
使用给定顺序的样条插值缩放数组,在这种情况下,默认值为order = 3.
%% timeit#from scipy.ndimage导入缩放向上=缩放(img,2)下=缩放(上,0.5)#163 ms±12.1 ms每个循环(平均±标准偏差,共运行7次,每个循环10个)
2.Scipy 缩放(order = 0)
使用给定阶数的样条插值对数组进行缩放,在这种情况下,阶数= 0.
%% timeit#from scipy.ndimage导入缩放向上= zoom(img,2,order = 0)向下=缩放(向上,0.5,阶数= 0)每个循环#18.7 ms±950 µs(平均±标准偏差,共运行7次,每个循环100个)
3.Skimage pyramid_gaussian
在高斯金字塔中,使用高斯平均(高斯模糊)对后续图像进行加权和缩小.将高斯模糊应用于图像与将图像与高斯函数卷积相同.模糊程度取决于标准偏差大小(sigma).
%% timeit#from skimage.transform import import pyramid_gaussian向上= pyramid_gaussian(img,2)下= pyramid_gaussian(上,0.5)#471 ns±30.4 ns/循环(平均±标准偏差,共7次运行,每个循环1000000次)
4.Skimage pyramid_expand 和 pyramid_reduce
图像金字塔是一种多尺度图像表示,其中图像要进行重复的平滑和二次采样.第一个函数先对图像进行平滑处理,然后再对图像进行升采样,而第二个函数进行相同的操作,但是对图像进行降采样,默认情况下,这两个函数都使用样条order = 1.
%% timeit#from skimage.transform import import导入pyramid_expand,pyramid_reduce向上= pyramid_expand(img,2)下= pyramid_reduce(up,2)每个循环#120 ms±3.08 ms(平均±标准偏差,共运行7次,每个循环10次)
5.Skimage 重新缩放
按一定比例缩放图像.执行样条插值(默认为order = 1)以按比例放大或按比例缩小N维图像.请注意,缩小图像尺寸时应启用抗锯齿功能,以免产生锯齿失真.
%% timeit#from skimage.transform import导入重新缩放向上=重新缩放(img,2)向下=重新缩放(向上,0.5)#83 ms±3.69 ms每个循环(平均±标准偏差,运行7次,每个循环10个)
返回此图像的调整大小后的副本.从输入图像中选择一个最近的像素.忽略所有其他输入像素.
%% timeit#from PIL导入图片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每个循环(平均±标准偏差,共运行7次,每个循环1000个)
返回此图像的调整大小后的副本.对于调整大小,在可能影响输出值的所有像素上使用线性插值计算输出像素值.对于其他转换,将在输入图像的2x2环境中使用线性插值.
%% timeit#from PIL导入图片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(平均±标准偏差,共运行7次,每个循环100个)
8.使用BICUBIC的PIL 调整大小.readthedocs.io/en/stable/handbook/concepts.html#concept-filters"rel =" nofollow noreferrer>过滤器用于重新采样
返回此图像的调整大小后的副本.对于调整大小,对所有可能影响输出值的像素使用三次插值计算输出像素值.对于其他变换,在输入图像中使用4x4环境的三次插值.
%% timeit#from PIL导入图片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(平均±标准偏差,共运行7次,每个循环100个)
9.与Lanczos一起PIL 调整大小.readthedocs.io/en/stable/handbook/concepts.html#concept-filters"rel =" nofollow noreferrer>过滤器用于重新采样
返回此图像的调整大小后的副本.在所有可能影响输出值的像素上,使用高质量的Lanczos滤波器(截短的sinc)计算输出像素值.
%% timeit#from PIL导入图片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(平均±标准偏差,共运行7次,每个循环100个)
Currently, I have written some Python code that is inserted into a pipeline.
The incoming data comes in in a numpy array of shape (1,512,19,25). I use the scipy.ndimage.interpolation.zoom
to bring the array up to shape (1,512,38,50). This can be accomplished with one call to the function. Basically, it resizes each (19,25) piece to size (38,50).
Later in the code, when the data is moving the other way, different data is again resized the in the other direction (38,50) to (19,25).
Everything works as implemented, however I am finding that this is really slow. For example, I tested the scipy.ndimage.interpolation.zoom
function to resize an image file and it was way slower than Matlab's imresize
function.
What are faster ways to do this in Python?
TLDR; Check out Skimage's pyramid_gaussian. On a single image of (512, 512) it shows an order of 0.3M times speed-up. (163 ms/471 ns = 346072). Pillow-SIMD does super-fast resamples/resize but requires you to uninstall PIL, Pillow before you install it. It uses parallel processing (single instruction, multiple data - SIMD) and better algorithms such as replacing a convolution-based Gaussian blur with a sequential-box one. Advice to use this for production environments after setting up a separate venv
.
There are multiple ways of upsampling and downsampling your image. I will add some benchmarks of the methods that I have worked with. I will keep updating this answer as I come across more methods so that this can act as a reference for others on 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')
IIUC, this is somewhat your pipeline -
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)
Methods & Benchmarks(in no specific order)
1. Scipy zoom (order=3)
The array is zoomed using spline interpolation of a given order, in this case, the default is 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)
The array is zoomed using spline interpolation of a given order, in this case, order = 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 pyramid_gaussian
In a Gaussian pyramid, subsequent images are weighted down using a Gaussian average (Gaussian blur) and scaled-down. Applying a Gaussian blur to an image is the same as convolving the image with a Gaussian function. The amount of blur depends on the standard deviation size (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 and pyramid_reduce
An image pyramid is a type of multi-scale image representation in which an image is subject to repeated smoothing and subsampling. The first function smooths and then upsamples the image, while the second does the same but instead downsamples, both of these use spline order=1 by default.
%%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 rescale
Scales an image by a certain factor. Performs spline interpolation (order=1 by default) to up-scale or down-scale N-dimensional images. Note that anti-aliasing should be enabled when down-sizing images to avoid aliasing artifacts.
%%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 with Nearest pixel filter for resampling
Returns a resized copy of this image. Picks one nearest pixel from the input image. Ignores all other input pixels.
%%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 with BILINEAR filter for resampling
Returns a resized copy of this image. For resize calculates the output pixel value using linear interpolation on all pixels that may contribute to the output value. For other transformations, linear interpolation over a 2x2 environment in the input image is used.
%%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 with BICUBIC filter for resampling
Returns a resized copy of this image. For resize calculates the output pixel value using cubic interpolation on all pixels that may contribute to the output value. For other transformations cubic interpolation over a 4x4 environment in the input image is used.
%%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 with Lanczos filter for resampling
Returns a resized copy of this image. Calculates the output pixel value using a high-quality Lanczos filter (a truncated sinc) on all pixels that may contribute to the output value.
%%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)
这篇关于快速插值/numpy数组重采样-Python的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!