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

相关推荐
Draina28 分钟前
CBC填充预言攻击-CBC Padding Oracle Crypto Attack
python·安全·web安全·网络安全·密码学·安全性测试
Angel Q.1 小时前
特征提取 | DINO 到 DINOv3
图像处理·python
不如语冰1 小时前
AI大模型入门-pytorch-张量2 tensor创建
python
NPE~2 小时前
[AI]Agent开发——ADK框架使用
人工智能·python·ai·教程·adk·agent开发
矮个史蒂芬2 小时前
统计verilog .v文件中output 的bit数
python
gwf2162 小时前
磨损均衡算法(Wear Leveling)——SSD如何让每块闪存“公平退休“?
运维·数据库·人工智能·python·嵌入式硬件·算法·智能硬件
爱吃提升2 小时前
python 分布式爬虫、爬虫合规与综合实战项目
分布式·爬虫·python
审小匠OpenCPAi2 小时前
银行流水核查怎么自动化?单边匹配、双向勾稽与图聚类异常检测的工程对比
java·前端·人工智能·python·审计
元Y亨H3 小时前
Pandas 解析 Excel 导致的内存溢出(MemoryError)
python·excel
金銀銅鐵4 小时前
[Python] 用 turtle 来绘制瑞典国旗
python