3. OpenCV基础(二):色彩空间转换、几何变换与图像滤波

各位同学,欢迎来到第三章。上一章我们把OpenCV的安装、图像读写和像素操作捋了一遍。这一章,咱们要深入几个真正在YOLO实战中高频使用的核心操作:色彩空间转换、几何变换和图像滤波。

说实话,这三个东西在YOLO的预处理和后处理里几乎无处不在。你训练模型时,数据增强要用到几何变换;你部署模型时,输入图像的尺寸和色彩格式必须对齐;你调试模型时,滤波能帮你剔除噪声。所以,这一章的内容,说白了就是YOLO流水线上的“基本功”。

3.1 色彩空间转换:BGR、RGB、HSV

先问大家一个问题:OpenCV默认读进来的图像是什么颜色顺序?

答案是BGR,不是RGB。这一点坑过无数新手,也包括当年的我。我第一次用OpenCV显示图像,发现颜色偏蓝,查了半天才发现是通道顺序搞反了。

3.1.1 BGR与RGB的互转

YOLO模型训练时,大部分框架(比如PyTorch)用的是RGB顺序。所以你在做推理前,必须把OpenCV读进来的BGR图像转成RGB。

转换方式有两种:

  • 方法一:使用cv2.cvtColor()
  • 方法二:手动翻转通道
import cv2

# 读取图像,默认BGR
img_bgr = cv2.imread('image.jpg')

# 方法一:cvtColor转换
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)

# 方法二:手动翻转通道(性能略高)
img_rgb_manual = img_bgr[:, :, ::-1]
我的习惯:在YOLO推理管线里,我通常用cvtColor,因为代码可读性更好。但在需要极致性能的嵌入式设备上,我会用切片翻转,能省下几毫秒。

3.1.2 BGR与HSV的转换

HSV(色调、饱和度、明度)在目标检测中非常有用。为什么?因为HSV把颜色和亮度分开了。你在做颜色阈值分割时,HSV比BGR稳定得多。

我记得有一次做交通标志检测,红色标志在BGR空间里受光照影响很大,换成HSV后,只用H通道就能稳定提取红色区域。

# BGR转HSV
img_hsv = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2HSV)

# HSV各通道范围
# H: 0-179, S: 0-255, V: 0-255
注意:OpenCV中HSV的H范围是0-179,不是0-359。很多人在做颜色过滤时,直接用0-360的范围,结果什么都滤不出来。我曾经在这个问题上浪费了半小时。

3.1.3 色彩空间对比

色彩空间 通道含义 YOLO中的典型用途
BGR 蓝、绿、红 OpenCV默认读取格式
RGB 红、绿、蓝 模型训练输入格式
HSV 色调、饱和度、明度 颜色阈值分割、目标跟踪
灰度 单通道亮度 轻量模型输入、边缘检测

3.2 图像缩放与几何变换

YOLO模型要求输入图像是固定尺寸的,比如640x640。但实际拍摄的图像千奇百怪,所以缩放是必须的。除此之外,数据增强时还会用到旋转、平移、仿射变换。

3.2.1 图像缩放:cv2.resize()

resize是最常用的函数。但插值方法的选择,直接影响图像质量和推理速度。

# 缩放到640x640
img_resized = cv2.resize(img, (640, 640))

# 按比例缩放(宽度不变,高度缩放)
h, w = img.shape[:2]
new_w = 640
new_h = int(h * new_w / w)
img_resized = cv2.resize(img, (new_w, new_h))

插值方法的选择:

  • INTER_NEAREST:最近邻插值,速度快,但锯齿明显。适合做分类任务的数据增强。
  • INTER_LINEAR:双线性插值,默认方法。质量适中,速度也快。我大部分场景都用这个。
  • INTER_CUBIC:双三次插值,质量高,但慢。适合放大图像。
  • INTER_AREA:区域插值,适合缩小图像。YOLO预处理中,我推荐用这个,能减少混叠效应。
实战建议:在YOLO推理管线里,缩放时先用INTER_AREA缩小,再用INTER_LINEAR放大到目标尺寸。这样既能保持细节,又能减少锯齿。

3.2.2 仿射变换:cv2.warpAffine()

仿射变换是YOLO数据增强的核心。旋转、平移、缩放、剪切,本质上都是仿射变换。

它的原理很简单:用一个2x3的变换矩阵,把原图上的点映射到新图上。

import numpy as np

# 旋转30度,中心点不变
h, w = img.shape[:2]
center = (w // 2, h // 2)
M = cv2.getRotationMatrix2D(center, 30, 1.0)
img_rotated = cv2.warpAffine(img, M, (w, h))

# 平移 (向右50像素,向下30像素)
M_trans = np.float32([[1, 0, 50], [0, 1, 30]])
img_translated = cv2.warpAffine(img, M_trans, (w, h))

这里有个坑:旋转后,图像边缘会被裁剪掉。如果你不想丢失信息,需要手动计算新画布尺寸。

我曾经踩过的坑:在做YOLO数据增强时,旋转后没有更新标注框的坐标,导致模型训练时框和物体对不上。记住:图像变换了,标注框也要跟着变换。

3.2.3 透视变换:cv2.warpPerspective()

透视变换比仿射变换更灵活,可以模拟视角变化。比如,把一张倾斜拍摄的文档矫正为正视图。

# 定义四个源点和四个目标点
src_pts = np.float32([[0, 0], [w-1, 0], [0, h-1], [w-1, h-1]])
dst_pts = np.float32([[50, 0], [w-50, 0], [0, h], [w, h]])

M_persp = cv2.getPerspectiveTransform(src_pts, dst_pts)
img_persp = cv2.warpPerspective(img, M_persp, (w, h))

3.3 图像滤波:去噪与保边

滤波在YOLO预处理中,主要用于去除噪声。但要注意:滤波会损失细节,所以不能过度使用。

3.3.1 高斯滤波

高斯滤波是最常用的平滑滤波器。它用高斯核做卷积,能有效去除高斯噪声。

# 高斯滤波,核大小5x5,标准差1.5
img_gaussian = cv2.GaussianBlur(img, (5, 5), 1.5)

核大小必须是奇数。标准差越大,图像越模糊。我一般用(5,5)和1.5的组合,效果比较均衡。

3.3.2 中值滤波

中值滤波对椒盐噪声特别有效。它用邻域内像素的中值代替中心像素值。

# 中值滤波,核大小5
img_median = cv2.medianBlur(img, 5)

中值滤波的核大小也必须是奇数。我建议在图像有大量椒盐噪声时使用,比如摄像头传输不稳定导致的噪点。

3.3.3 双边滤波

双边滤波是“保边去噪”的神器。它同时考虑空间距离和像素值差异,所以能在平滑噪声的同时保留边缘。

# 双边滤波,d=9, sigmaColor=75, sigmaSpace=75
img_bilateral = cv2.bilateralFilter(img, 9, 75, 75)

参数说明:

  • d:邻域直径
  • sigmaColor:颜色空间标准差,值越大,颜色差异大的像素也会被纳入平滑
  • sigmaSpace:空间标准差,值越大,距离远的像素也会被纳入平滑
我的经验:双边滤波效果很好,但速度慢。在YOLO实时推理中,我一般不用它做预处理,而是用在后处理中,比如对检测到的目标区域做局部增强。

3.3.4 三种滤波对比

滤波类型 适用噪声 边缘保留 速度 YOLO中的典型用途
高斯滤波 高斯噪声 差 快 预处理去噪
中值滤波 椒盐噪声 中等 中等 摄像头噪点去除
双边滤波 高斯噪声+保留边缘 好 慢 后处理局部增强

3.4 本章知识体系

下面这张图,把本章的核心知识点串起来了。你可以把它当作一个快速参考。

第三章知识体系:色彩空间、几何变换与滤波 第三章 核心知识体系 色彩空间转换 几何变换 图像滤波 BGR ↔ RGB BGR ↔ HSV resize缩放 warpAffine 高斯滤波 中值滤波 双边滤波 YOLO实战中:预处理用resize + 高斯滤波,数据增强用仿射变换,后处理用双边滤波 预处理管线 数据增强 后处理优化

嗯,这一章的内容就到这里。色彩空间转换、几何变换和图像滤波,这三个东西在YOLO的整个生命周期里都会反复用到。你可以在自己的项目里多试试不同的参数组合,找到最适合你场景的那一套。