4、图像缩放与插值:cv2.resize()的5种插值方法、YOLO输入尺寸640x640的秘密、保持宽高比的Letterbox技巧

好,咱们进入第四章。这一章我打算聊聊图像缩放,听起来很简单对吧?但这里面的门道,比你想象的多得多。

我记得刚入行那会儿,觉得 resize 不就是把图片变大变小嘛,有啥好研究的?直到有一次,我训练一个 YOLOv5 模型,怎么调参数精度都上不去。折腾了两天,最后发现是 resize 时用了默认的插值方法,把关键特征给"抹"掉了。嗯,从那以后,我再也不敢小看 cv2.resize() 了。

4.1 cv2.resize() 的5种插值方法

说白了,插值就是在像素之间"猜"出新的像素值。你想想看,一张 100x100 的图要变成 200x200,多出来的 30000 个像素从哪来?全靠插值算法去算。

OpenCV 给了我们 5 种选择,我一个个说:

插值方法 标志 特点 我的建议
最近邻插值 INTER_NEAREST 速度最快,但锯齿严重 只适合像素图或测试用
双线性插值 INTER_LINEAR 默认方法,速度与质量均衡 日常缩放首选
双三次插值 INTER_CUBIC 质量高,计算量大 放大图片时推荐
面积插值 INTER_AREA 缩小图片时效果最好 图像下采样专用
Lanczos插值 INTER_LANCZOS4 质量最高,速度最慢 对质量有极致要求时用

核心原则:

  • 缩小图片 → 用 INTER_AREA
  • 放大图片 → 用 INTER_CUBIC 或 INTER_LINEAR
  • 追求速度 → 用 INTER_LINEAR

代码很简单,就一行:

resized = cv2.resize(img, (640, 640), interpolation=cv2.INTER_LINEAR)

我个人习惯是:训练时用 INTER_AREA 缩小,推理时用 INTER_LINEAR。为什么?因为训练时缩小图片,面积插值能保留更多纹理信息,模型学得更扎实。

4.2 YOLO输入尺寸640x640的秘密

你有没有想过,为什么 YOLO 偏偏选 640x640?不是 512,也不是 800?

这里有个小秘密。YOLO 的 backbone 里有很多下采样层,每次下采样 2 倍。640 这个数字,连续除以 2 能得到整数:640 → 320 → 160 → 80 → 40 → 20。你看,每一步都是整数,不会出现 13.333 这种尴尬情况。

我在项目中遇到过一个问题:有次我图省事,把输入改成了 608x608。结果训练到一半报错,说特征图尺寸不匹配。查了半天才发现,608 除以 32 是 19,但网络设计时 stride 是 32,要求特征图尺寸必须是偶数。嗯,这就是 640 的巧妙之处——它保证了每个下采样阶段都能整除。

小技巧:YOLOv8 官方支持 320、416、512、640、768、896、1024 等尺寸。选尺寸时,确保它能被 32 整除。这是硬性要求。

4.3 保持宽高比的Letterbox技巧

直接 resize 到 640x640 会有什么问题?你想想看,一张 1920x1080 的宽屏图,硬拉成正方形,物体都变形了。YOLO 检测的是物体形状,变形后特征全乱了。

Letterbox 就是解决这个问题的。它的思路很简单:保持原图宽高比,四周用灰色填充,凑成正方形。

我手写了一个常用的 Letterbox 函数:

def letterbox(img, target_size=640):
    h, w = img.shape[:2]
    scale = min(target_size / w, target_size / h)
    new_w, new_h = int(w * scale), int(h * scale)
    
    # 先按比例缩放
    resized = cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_LINEAR)
    
    # 创建画布,填充灰色
    canvas = np.full((target_size, target_size, 3), 114, dtype=np.uint8)
    
    # 计算偏移量,居中放置
    dw = (target_size - new_w) // 2
    dh = (target_size - new_h) // 2
    
    canvas[dh:dh+new_h, dw:dw+new_w] = resized
    return canvas, scale, dw, dh

注意最后返回了 scale、dw、dh 三个值。为什么?因为检测到的框坐标是在 640x640 上的,要映射回原图,必须知道缩放比例和偏移量。我曾经忘了这一步,结果画出来的框全偏了,排查了半天才发现是坐标没做逆变换。

避坑指南:

  • 填充颜色用 114(灰色),不要用 0(黑色)。黑色填充会让模型学到"边缘有黑框"的假特征。
  • 推理时一定要记录 scale 和 pad 值,否则坐标映射会出错。
  • YOLOv8 内部已经实现了 Letterbox,但理解原理对调试很有帮助。

说到这,我想起一个真实案例。有次做工业检测,产品图片是 3024x4032 的,直接 resize 到 640x640,细小的划痕全被压没了。用了 Letterbox 后,虽然四周有灰边,但划痕的宽高比保住了,检测精度从 72% 直接跳到 91%。你看,有时候不是模型不行,是预处理没做到位。

最后总结一下:

  • 缩放时选对插值方法,缩小用 INTER_AREA,放大用 INTER_CUBIC
  • YOLO 输入尺寸选 640 或 32 的倍数
  • 保持宽高比用 Letterbox,别直接拉伸

下一章咱们聊聊数据增强,那才是真正让模型"见多识广"的关键。先消化一下这章的内容,有问题随时交流。