【OpenCV】Python图像处理几何变换之缩放

图像缩放(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_CUBICINTER_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 宽度方向的缩放比例(可选) dsizeNone,则 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_NEARESTINTER_LINEAR,牺牲少量质量换取速度。
  • 通道不匹配 :彩色图缩放后仍为 3 通道,灰度图为 1 通道,无需额外处理(cv2.resize 自动适配)。

2. 典型应用场景

  • 图像预处理:神经网络输入要求固定尺寸(如 224×224),需缩放图像适配。
  • 多分辨率显示:同一图像在手机、电脑等不同设备上显示,需按屏幕尺寸缩放。
  • 图像压缩:缩小图像尺寸可减少存储和传输带宽(如微信朋友圈图片压缩)。
  • 视频处理:实时缩放视频帧(如直播推流时降低分辨率)。

五、注意事项

  1. 尺寸格式cv2.resizedsize(width, height),而 OpenCV 图像的 shape(height, width, channels),避免混淆。
  2. 数据类型 :输入图像需为 uint8 类型(0~255),若为其他类型(如 float32),需先归一化到 0~255 再缩放。
  3. 中文路径 :避免图像路径包含中文或空格,否则 cv2.imread 可能返回 None(可改用 numpy 读取或修改路径)。
  4. 超大图像缩放:对于万级像素的超大图像,建议先缩小比例再处理,避免内存溢出。

通过 cv2.resize() 函数结合不同插值算法和缩放策略,可满足绝大多数图像处理场景的缩放需求,掌握宽高比保持和插值算法选择是关键。

相关推荐
塔克Tark3 小时前
【Python】xxx.py文件打包为.exe可执行文件
开发语言·python
会笑的小熊3 小时前
解决安装torch出现SSL 连接异常
python
ASD123asfadxv3 小时前
排球动作识别与检测:基于YOLO11-C2PSA-CGLU的攻击、拦网、防守、发球、传球和排球检测六类动作自动识别与定位方法研究
python
roman_日积跬步-终至千里3 小时前
【计算机视觉(9)】运动恢复结构:从图像到三维点云的完整流程
人工智能·数码相机·计算机视觉
德卡先生的信箱3 小时前
深度学习图像处理(2)----一阶段目标检测
图像处理·深度学习·目标检测
祝余Eleanor3 小时前
Day 41 训练和测试的规范写法
python·深度学习·机器学习
百锦再3 小时前
与AI沟通的正确方式——AI提示词:原理、策略与精通之道
android·java·开发语言·人工智能·python·ui·uni-app
roman_日积跬步-终至千里3 小时前
【计算机视觉(12)】神经网络与反向传播基础篇:从线性分类器到多层感知机
人工智能·神经网络·计算机视觉
怎么全是重名3 小时前
Stacked U-Nets: A No-Frills Approach to Natural Image Segmentation
深度学习·神经网络·计算机视觉·语义分割