如何基于Numpy/SciPy实现NumPy数组格式图像的精准缩放?
当然有!你完全不用局限于隔行删除这种粗暴的方式,Numpy生态里有好几个工具能帮你实现平滑的图像缩放,效果和图像编辑器里的差不多。下面给你几个实用的方案:
方案1:用SciPy的ndimage模块(原生SciPy方案)
SciPy的scipy.ndimage.zoom()专门用来处理多维数组的缩放,支持多种插值方法,完全能满足你的平滑缩小需求。它会根据周围像素的权重计算新像素值,而不是直接丢弃像素。
举个针对你的例子的代码:
import numpy as np from scipy import ndimage # 假设你的原图像数组是bottle_1(形状(528, 203, 3)) bottle_1 = np.random.randint(0, 255, size=(528, 203, 3), dtype=np.uint8) target_shape = (140, 54, 3) # 计算每个维度的缩放因子 zoom_factors = ( target_shape[0] / bottle_1.shape[0], target_shape[1] / bottle_1.shape[1], 1.0 # 颜色通道不需要缩放 ) # 使用双三次插值(比双线性更平滑,适合缩小) bottle_2_smooth = ndimage.zoom(bottle_1, zoom_factors, order=3) # 如果需要确保数据类型是uint8(图像常用格式) bottle_2_smooth = bottle_2_smooth.astype(np.uint8)
这里的order参数控制插值方式:
- order=1:双线性插值(速度快,效果不错)
- order=3:双三次插值(效果更平滑,计算稍慢)
方案2:用Pillow库(简单易上手,和Numpy无缝兼容)
Pillow是Python最常用的图像处理库,它能直接把Numpy数组转换成图像对象,然后用专业的插值算法缩放,再转回Numpy数组。
示例代码:
import numpy as np from PIL import Image bottle_1 = np.random.randint(0, 255, size=(528, 203, 3), dtype=np.uint8) target_shape = (140, 54, 3) # Numpy数组转PIL图像 img = Image.fromarray(bottle_1) # 注意Pillow的resize参数是(width, height),和Numpy的(rows, cols)顺序相反 resized_img = img.resize((target_shape[1], target_shape[0]), Image.LANCZOS) # 转回Numpy数组 bottle_2_smooth = np.array(resized_img)
Image.LANCZOS是一种高质量的插值算法,特别适合缩小图像,能最大程度保留细节并避免锯齿。
方案3:用OpenCV(适合需要更底层图像处理的场景)
OpenCV的cv2.resize()同样支持Numpy数组,而且性能很好,插值选项也很丰富。
示例代码:
import numpy as np import cv2 bottle_1 = np.random.randint(0, 255, size=(528, 203, 3), dtype=np.uint8) target_shape = (140, 54, 3) # OpenCV的resize参数也是(width, height),注意顺序 bottle_2_smooth = cv2.resize( bottle_1, (target_shape[1], target_shape[0]), interpolation=cv2.INTER_LANCZOS4 )
cv2.INTER_LANCZOS4和Pillow的LANCZOS类似,是高质量缩小的首选,另外cv2.INTER_CUBIC也是不错的选择。
为什么这些方法比隔行删除好?
隔行删除本质是下采样但不做抗锯齿,很容易出现锯齿、丢失细节的问题。而上面这些插值方法会在缩放前先对图像进行平滑滤波(避免混叠),然后通过加权计算新像素值,最终得到的图像和你在Photoshop、GIMP里缩小的效果一致,能很好保留原图的特征。
内容的提问来源于stack exchange,提问作者Brian Hamill
相关产品推荐
相关产品推荐

