4、图像处理基础回顾:图像读取与显示、色彩空间转换、图像几何变换、数据增强基础
各位同学,欢迎来到第四讲。
说实话,这一章的内容,是咱们整个语义分割课程的“地基”。你想想看,后面我们要处理各种复杂的道路场景,如果连图像怎么读、怎么转、怎么变都搞不清楚,那后面的模型训练根本跑不起来。
我个人习惯,在开始任何项目前,先把这些基础操作练得滚瓜烂熟。今天我们就来聊聊,图像处理里最常用的几个操作。
4.1 图像读取与显示:别小看这一步
很多新手觉得,读个图片有什么难的?不就是 cv2.imread() 吗?
嗯,这里要注意。OpenCV 默认读取的通道顺序是 BGR,不是我们常见的 RGB。我第一次做项目时,就因为这个坑,显示出来的图像颜色全乱了,找了半天 bug。
来看一段标准代码:
import cv2
import matplotlib.pyplot as plt
# 读取图像
img = cv2.imread('road.jpg')
# 转换色彩空间
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 显示
plt.imshow(img_rgb)
plt.axis('off')
plt.show()
# 获取图像信息
print(f"图像尺寸:{img.shape}") # (高度, 宽度, 通道数)
print(f"图像类型:{img.dtype}") # uint8
我个人习惯,在读取图像后,第一时间打印 shape 和 dtype。这能帮你快速确认数据格式是否正确。
4.2 色彩空间转换:为什么需要这么多颜色模型?
你可能会有疑问:RGB 不是挺好的吗?为什么还要转成 HSV、LAB 这些?
说白了,RGB 模型对光照变化太敏感了。你想想看,自动驾驶场景里,一天中光照变化巨大,同一个物体在不同光照下 RGB 值差异很大。但 HSV 中的 H(色调)分量,对光照就不那么敏感。
我在项目中遇到过,用 RGB 做道路分割,晴天效果很好,一到阴天就崩了。后来换成 HSV 空间提取特征,鲁棒性提升了不少。
| 色彩空间 | 特点 | 适用场景 |
|---|---|---|
| RGB | 最常用,但受光照影响大 | 通用显示、数据标注 |
| HSV | 色调、饱和度、明度分离 | 颜色分割、阴影处理 |
| LAB | 感知均匀,L通道独立 | 光照归一化、图像增强 |
| GRAY | 单通道,计算量小 | 边缘检测、特征提取 |
# 色彩空间转换示例
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
img_lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 提取HSV中的H通道
h_channel = img_hsv[:, :, 0]
4.3 图像几何变换:让模型学会“看”不同角度
几何变换,说白了就是让图像动起来。平移、旋转、缩放、仿射变换……这些操作在数据增强里太常用了。
为什么会这样?因为自动驾驶场景中,车辆不可能永远正对着道路。摄像头会有抖动,车辆会有转弯。如果模型没见过倾斜的道路,它可能就认不出来了。
4.3.1 缩放与旋转
import cv2
import numpy as np
# 缩放
img_resized = cv2.resize(img, (224, 224))
# 旋转
(h, w) = img.shape[:2]
center = (w // 2, h // 2)
M = cv2.getRotationMatrix2D(center, 15, 1.0) # 旋转15度
img_rotated = cv2.warpAffine(img, M, (w, h))
嗯,这里要注意。旋转后图像边缘会出现黑色区域。我建议用 cv2.BORDER_REFLECT 或 cv2.BORDER_WRAP 模式填充,而不是默认的黑色填充。否则模型可能会学到“黑色区域就是背景”这种错误特征。
4.3.2 仿射变换与透视变换
仿射变换保持平行线,透视变换则更自由。在道路场景中,透视变换特别有用——它可以模拟不同视角下的道路形状。
# 仿射变换
pts1 = np.float32([[50,50], [200,50], [50,200]])
pts2 = np.float32([[10,100], [200,50], [100,250]])
M_affine = cv2.getAffineTransform(pts1, pts2)
img_affine = cv2.warpAffine(img, M_affine, (w, h))
# 透视变换
pts1 = np.float32([[0,0], [w-1,0], [0,h-1], [w-1,h-1]])
pts2 = np.float32([[50,0], [w-50,0], [0,h-1], [w-1,h-1]])
M_perspective = cv2.getPerspectiveTransform(pts1, pts2)
img_perspective = cv2.warpPerspective(img, M_perspective, (w, h))
4.4 数据增强基础:让有限的数据发挥最大价值
数据增强,说白了就是“无中生有”。你只有1000张图,但通过翻转、旋转、裁剪,可以变出10000张。这在自动驾驶领域特别重要——因为标注成本太高了。
4.4.1 翻转(Flip)
水平翻转是最常用的。为什么?因为道路左右对称啊!你想想看,左车道和右车道,本质上是一样的。
# 水平翻转
img_flip_h = cv2.flip(img, 1)
# 垂直翻转(不常用,但某些场景有用)
img_flip_v = cv2.flip(img, 0)
np.fliplr() 和 np.flipud() 同步处理图像和标签。
4.4.2 旋转(Rotation)
旋转角度一般控制在 ±15° 以内。为什么?因为自动驾驶场景中,车辆不会出现90°的倾斜。过大的旋转反而会引入不真实的样本。
def rotate_image_and_mask(image, mask, angle):
h, w = image.shape[:2]
center = (w // 2, h // 2)
M = cv2.getRotationMatrix2D(center, angle, 1.0)
# 同步旋转图像和标签
rotated_image = cv2.warpAffine(image, M, (w, h),
borderMode=cv2.BORDER_REFLECT)
rotated_mask = cv2.warpAffine(mask, M, (w, h),
borderMode=cv2.BORDER_REFLECT)
return rotated_image, rotated_mask
4.4.3 裁剪(Crop)
随机裁剪可以模拟不同视野下的场景。我个人习惯,裁剪时保持一定的重叠区域,这样模型能学到局部和全局的关系。
def random_crop(image, mask, crop_size=(256, 256)):
h, w = image.shape[:2]
ch, cw = crop_size
# 随机选择裁剪起点
top = np.random.randint(0, h - ch)
left = np.random.randint(0, w - cw)
# 同步裁剪
cropped_image = image[top:top+ch, left:left+cw]
cropped_mask = mask[top:top+ch, left:left+cw]
return cropped_image, cropped_mask
4.5 总结与思考
好了,这一章的内容就到这里。我们来回顾一下:
- 图像读取:记住 BGR 和 RGB 的区别,这是最常见的坑
- 色彩空间转换:HSV 对光照鲁棒,LAB 适合光照归一化
- 几何变换:同步处理图像和标签,这是铁律
- 数据增强:组合使用,但别过度
说实话,这些基础操作看起来简单,但真正用好了,能省下你大量调参的时间。我每次开始一个新项目,都会花一天时间把数据预处理流程跑通。磨刀不误砍柴工嘛。
下一章,我们会进入深度学习的基础知识。到时候,这些图像处理技巧就会派上大用场了。
加油,各位!