【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() 函数结合不同插值算法和缩放策略,可满足绝大多数图像处理场景的缩放需求,掌握宽高比保持和插值算法选择是关键。

相关推荐
dagouaofei13 小时前
2026 年工作计划 PPT 制作方式对比:AI 与传统方法差异
人工智能·python·powerpoint
虚拟搬运工13 小时前
xformers造成comfyu启动失败
python·comfyui
Hello.Reader13 小时前
PyFlink DataStream Operators 算子分类、函数写法、类型系统、链路优化(Chaining)与工程化踩坑
前端·python·算法
Learner13 小时前
Python函数
开发语言·python
万行13 小时前
机器学习&第五章生成式生成器
人工智能·python·算法·机器学习
_李小白13 小时前
【Android FrameWork】延伸阅读:AMS 的 handleApplicationCrash
android·开发语言·python
万行14 小时前
机器学习&第一章
人工智能·python·机器学习·flask·计算机组成原理
2301_7973122614 小时前
学习java37天
开发语言·python
山海青风14 小时前
图像识别零基础实战入门 3 第一次训练图像分类模型
图像处理·人工智能·分类
WJSKad123514 小时前
果园树干识别与定位_faster-rcnn_x101-32x4d_fpn_1x_coco改进实践
python