图像缩放(Image Scaling)是最基础的几何变换之一,指按指定比例或尺寸调整图像的宽度和高度,核心是通过插值算法 填充缩放后新增的像素点,同时尽可能保留图像细节。OpenCV 提供了 cv2.resize() 函数实现灵活的缩放功能,支持固定比例、固定尺寸、不同插值算法等场景,广泛用于图像预处理(如神经网络输入适配)、多分辨率显示等。
一、核心原理
1. 缩放的本质
- 缩小图像:需丢弃部分像素,需通过算法保留关键信息(如边缘、纹理)。
- 放大图像:需新增像素,需通过插值算法推测新增像素的灰度 / 颜色值。
- 缩放比例:设原图尺寸为
(h, w)(高度 × 宽度),目标尺寸为(h_target, w_target),则高度缩放比例fx = h_target/h,宽度缩放比例fy = w_target/w。
2. OpenCV 支持的插值算法
插值算法直接影响缩放后图像的质量和速度,OpenCV 提供以下常用算法(通过 interpolation 参数指定):
| 插值算法参数 | 适用场景 | 特点 |
|---|---|---|
cv2.INTER_NEAREST |
快速缩放、对质量要求低 | 最近邻插值,速度最快,易产生锯齿(像素化) |
cv2.INTER_LINEAR |
默认放大 / 缩小(推荐) | 双线性插值,速度较快,质量均衡 |
cv2.INTER_CUBIC |
放大图像(追求高质量) | 双三次插值,基于 4×4 邻域像素,质量好,速度较慢 |
cv2.INTER_AREA |
缩小图像(推荐) | 区域插值,基于像素区域均值,缩小后无锯齿,保留细节更好 |
cv2.INTER_LANCZOS4 |
高质量放大 / 缩小(慢) | Lanczos 插值,基于 8×8 邻域,质量最优,速度最慢 |
关键结论:
- 缩小图像:优先用
INTER_AREA(无锯齿); - 放大图像:优先用
INTER_CUBIC或INTER_LINEAR(质量 / 速度平衡); - 追求速度:用
INTER_NEAREST(适合实时场景)。
二、cv2.resize() 函数详解
函数原型
python
cv2.resize(src, dsize, dst=None, fx=None, fy=None, interpolation=cv2.INTER_LINEAR)
参数说明
| 参数 | 含义 | 注意事项 |
|---|---|---|
src |
输入图像(单通道 / 多通道均可) | 必须是 numpy.ndarray 类型(OpenCV 图像格式) |
dsize |
目标尺寸,格式为 (width, height) |
与 OpenCV 图像的 (h, w) 格式相反;若设为 None,则需通过 fx/fy 指定比例 |
fx |
宽度方向的缩放比例(可选) | 若 dsize 为 None,则 fx 必须指定 |
fy |
高度方向的缩放比例(可选) | 与 fx 配合使用,单独指定无效 |
interpolation |
插值算法(默认 INTER_LINEAR) |
按场景选择(参考上文表格) |
返回值
缩放后的目标图像(numpy.ndarray 类型)。
三、完整实现代码(多种场景)
1. 基础场景:固定比例缩放
指定宽度 / 高度的缩放比例(如缩小为原图的 0.5 倍、放大为 2 倍):
python
import cv2
import matplotlib.pyplot as plt
# 1. 读取图像(灰度图/彩色图均可)
img = cv2.imread("lena.jpg") # 彩色图(BGR格式)
# img = cv2.imread("lena.jpg", cv2.IMREAD_GRAYSCALE) # 灰度图(可选)
if img is None:
print("无法读取图像,请检查路径!")
exit()
# 2. 获取原图尺寸(height, width, channels)
h, w = img.shape[:2]
print(f"原图尺寸:高度={h}, 宽度={w}")
# 3. 固定比例缩放(3种常见场景)
scale_down = 0.5 # 缩小为原图的50%
scale_up = 2.0 # 放大为原图的200%
# 方法1:通过 fx/fy 指定比例(dsize=None)
img_down = cv2.resize(img, dsize=None, fx=scale_down, fy=scale_down, interpolation=cv2.INTER_AREA) # 缩小用INTER_AREA
img_up = cv2.resize(img, dsize=None, fx=scale_up, fy=scale_up, interpolation=cv2.INTER_CUBIC) # 放大用INTER_CUBIC
# 4. 转换为RGB格式(用于matplotlib显示,cv2默认BGR)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img_down_rgb = cv2.cvtColor(img_down, cv2.COLOR_BGR2RGB)
img_up_rgb = cv2.cvtColor(img_up, cv2.COLOR_BGR2RGB)
# 5. 显示结果
plt.figure(figsize=(15, 5))
# 原图
plt.subplot(1, 3, 1)
plt.imshow(img_rgb)
plt.title(f"原图 ({h}×{w})")
plt.axis("off")
# 缩小图
plt.subplot(1, 3, 2)
plt.imshow(img_down_rgb)
plt.title(f"缩小50% ({img_down.shape[0]}×{img_down.shape[1]})")
plt.axis("off")
# 放大图
plt.subplot(1, 3, 3)
plt.imshow(img_up_rgb)
plt.title(f"放大200% ({img_up.shape[0]}×{img_up.shape[1]})")
plt.axis("off")
plt.tight_layout()
plt.show()
2. 固定目标尺寸缩放
指定缩放后的具体宽度和高度(如缩放到 300×200、800×600),需注意 dsize 格式为 (width, height):
python
import cv2
import matplotlib.pyplot as plt
# 1. 读取图像
img = cv2.imread("lena.jpg")
if img is None:
print("无法读取图像,请检查路径!")
exit()
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
h, w = img.shape[:2]
# 2. 固定目标尺寸(width, height)
target_size1 = (300, 200) # 宽300,高200(可能拉伸)
target_size2 = (800, 600) # 宽800,高600(放大)
# 缩放(按目标尺寸,指定插值算法)
img_fixed1 = cv2.resize(img, target_size1, interpolation=cv2.INTER_LINEAR)
img_fixed2 = cv2.resize(img, target_size2, interpolation=cv2.INTER_CUBIC)
# 转换为RGB用于显示
img_fixed1_rgb = cv2.cvtColor(img_fixed1, cv2.COLOR_BGR2RGB)
img_fixed2_rgb = cv2.cvtColor(img_fixed2, cv2.COLOR_BGR2RGB)
# 3. 显示结果
plt.figure(figsize=(15, 8))
plt.subplot(1, 3, 1)
plt.imshow(img_rgb)
plt.title(f"原图 ({h}×{w})")
plt.axis("off")
plt.subplot(1, 3, 2)
plt.imshow(img_fixed1_rgb)
plt.title(f"固定尺寸 {target_size1[1]}×{target_size1[0]}")
plt.axis("off")
plt.subplot(1, 3, 3)
plt.imshow(img_fixed2_rgb)
plt.title(f"固定尺寸 {target_size2[1]}×{target_size2[0]}")
plt.axis("off")
plt.tight_layout()
plt.show()
3. 保持宽高比缩放(避免拉伸)
实际应用中常需保持原图宽高比(如适配屏幕、网络传输),需先计算合理的目标尺寸:
python
import cv2
import matplotlib.pyplot as plt
def resize_with_aspect_ratio(img, target_width=None, target_height=None, interpolation=cv2.INTER_LINEAR):
"""
保持宽高比缩放图像
:param img: 输入图像
:param target_width: 目标宽度(二选一:target_width 或 target_height)
:param target_height: 目标高度
:param interpolation: 插值算法
:return: 缩放后的图像
"""
h, w = img.shape[:2]
# 仅指定宽度:计算对应高度
if target_width is not None and target_height is None:
ratio = target_width / w
new_h = int(h * ratio)
new_w = target_width
# 仅指定高度:计算对应宽度
elif target_height is not None and target_width is None:
ratio = target_height / h
new_w = int(w * ratio)
new_h = target_height
# 未指定则返回原图
else:
return img
return cv2.resize(img, (new_w, new_h), interpolation=interpolation)
# 1. 读取图像
img = cv2.imread("lena.jpg")
if img is None:
print("无法读取图像,请检查路径!")
exit()
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
h, w = img.shape[:2]
# 2. 保持宽高比缩放(2种场景)
img_ratio1 = resize_with_aspect_ratio(img, target_width=400, interpolation=cv2.INTER_LINEAR) # 固定宽度400
img_ratio2 = resize_with_aspect_ratio(img, target_height=300, interpolation=cv2.INTER_CUBIC) # 固定高度300
# 转换为RGB
img_ratio1_rgb = cv2.cvtColor(img_ratio1, cv2.COLOR_BGR2RGB)
img_ratio2_rgb = cv2.cvtColor(img_ratio2, cv2.COLOR_BGR2RGB)
# 3. 显示结果
plt.figure(figsize=(15, 6))
plt.subplot(1, 3, 1)
plt.imshow(img_rgb)
plt.title(f"原图 ({h}×{w})")
plt.axis("off")
plt.subplot(1, 3, 2)
plt.imshow(img_ratio1_rgb)
plt.title(f"固定宽度400 ({img_ratio1.shape[0]}×{img_ratio1.shape[1]})")
plt.axis("off")
plt.subplot(1, 3, 3)
plt.imshow(img_ratio2_rgb)
plt.title(f"固定高度300 ({img_ratio2.shape[0]}×{img_ratio2.shape[1]})")
plt.axis("off")
plt.tight_layout()
plt.show()
4. 不同插值算法效果对比
对比 4 种常用插值算法在放大 / 缩小时的效果差异:
python
import cv2
import matplotlib.pyplot as plt
# 1. 读取图像(缩小为小尺寸,再放大以凸显差异)
img = cv2.imread("lena.jpg")
img_small = cv2.resize(img, (50, 50), interpolation=cv2.INTER_AREA) # 先缩小为50×50
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 2. 用不同插值算法放大到300×300
inter_methods = {
"INTER_NEAREST": cv2.INTER_NEAREST,
"INTER_LINEAR": cv2.INTER_LINEAR,
"INTER_CUBIC": cv2.INTER_CUBIC,
"INTER_LANCZOS4": cv2.INTER_LANCZOS4
}
img_inter = {}
for name, method in inter_methods.items():
img_inter[name] = cv2.resize(img_small, (300, 300), interpolation=method)
img_inter[name] = cv2.cvtColor(img_inter[name], cv2.COLOR_BGR2RGB)
# 3. 显示对比
plt.figure(figsize=(12, 10))
# 原图和缩小图
plt.subplot(2, 3, 1)
plt.imshow(img_rgb)
plt.title("原图")
plt.axis("off")
plt.subplot(2, 3, 2)
plt.imshow(cv2.cvtColor(img_small, cv2.COLOR_BGR2RGB))
plt.title("缩小到50×50")
plt.axis("off")
# 插值算法对比
for i, (name, img_inter_rgb) in enumerate(img_inter.items(), start=3):
plt.subplot(2, 3, i)
plt.imshow(img_inter_rgb)
plt.title(name)
plt.axis("off")
plt.tight_layout()
plt.show()
四、关键说明与应用场景
1. 常见问题与解决方案
- 图像拉伸 :未保持宽高比导致,使用
resize_with_aspect_ratio函数自动计算比例。 - 缩放后模糊 / 锯齿 :缩小用
INTER_AREA、放大用INTER_CUBIC/INTER_LANCZOS4可改善。 - 速度慢 :实时场景(如视频缩放)用
INTER_NEAREST或INTER_LINEAR,牺牲少量质量换取速度。 - 通道不匹配 :彩色图缩放后仍为 3 通道,灰度图为 1 通道,无需额外处理(
cv2.resize自动适配)。
2. 典型应用场景
- 图像预处理:神经网络输入要求固定尺寸(如 224×224),需缩放图像适配。
- 多分辨率显示:同一图像在手机、电脑等不同设备上显示,需按屏幕尺寸缩放。
- 图像压缩:缩小图像尺寸可减少存储和传输带宽(如微信朋友圈图片压缩)。
- 视频处理:实时缩放视频帧(如直播推流时降低分辨率)。
五、注意事项
- 尺寸格式 :
cv2.resize的dsize是(width, height),而 OpenCV 图像的shape是(height, width, channels),避免混淆。 - 数据类型 :输入图像需为
uint8类型(0~255),若为其他类型(如 float32),需先归一化到 0~255 再缩放。 - 中文路径 :避免图像路径包含中文或空格,否则
cv2.imread可能返回None(可改用numpy读取或修改路径)。 - 超大图像缩放:对于万级像素的超大图像,建议先缩小比例再处理,避免内存溢出。
通过 cv2.resize() 函数结合不同插值算法和缩放策略,可满足绝大多数图像处理场景的缩放需求,掌握宽高比保持和插值算法选择是关键。