4、图像处理基础回顾:图像读取与显示、色彩空间转换、图像几何变换、数据增强基础

各位同学,欢迎来到第四讲。

说实话,这一章的内容,是咱们整个语义分割课程的“地基”。你想想看,后面我们要处理各种复杂的道路场景,如果连图像怎么读、怎么转、怎么变都搞不清楚,那后面的模型训练根本跑不起来。

我个人习惯,在开始任何项目前,先把这些基础操作练得滚瓜烂熟。今天我们就来聊聊,图像处理里最常用的几个操作。

4.1 图像读取与显示:别小看这一步

很多新手觉得,读个图片有什么难的?不就是 cv2.imread() 吗?

嗯,这里要注意。OpenCV 默认读取的通道顺序是 BGR,不是我们常见的 RGB。我第一次做项目时,就因为这个坑,显示出来的图像颜色全乱了,找了半天 bug。

⚠️ 避坑指南: 我曾经在调试一个车道线检测模型时,发现分割结果总是偏蓝。排查了两天,最后发现是图像读取时忘了转换色彩空间。记住:OpenCV 读出来的是 BGR,显示要用 RGB。

来看一段标准代码:

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]
💡 小技巧: 在做数据预处理时,我通常会把图像同时保留 RGB 和 HSV 两个版本。RGB 用于模型输入,HSV 用于做颜色相关的数据增强。这样既保留了原始信息,又增强了模型对光照的鲁棒性。

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_REFLECTcv2.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))
🔑 关键点: 几何变换时,一定要同步变换对应的标签图(mask)。否则你旋转了图像,但标签没变,模型就学错了。我见过有人犯这个错误,训练了三天才发现。

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
💡 实战建议: 我在做数据增强时,会组合使用多种方法。比如:先随机裁剪,再水平翻转,最后加一点旋转。这样组合出来的样本,多样性会大大提升。但要注意,组合太多也可能引入噪声,一般3-4种组合就够了。

4.5 总结与思考

好了,这一章的内容就到这里。我们来回顾一下:

  • 图像读取:记住 BGR 和 RGB 的区别,这是最常见的坑
  • 色彩空间转换:HSV 对光照鲁棒,LAB 适合光照归一化
  • 几何变换:同步处理图像和标签,这是铁律
  • 数据增强:组合使用,但别过度

说实话,这些基础操作看起来简单,但真正用好了,能省下你大量调参的时间。我每次开始一个新项目,都会花一天时间把数据预处理流程跑通。磨刀不误砍柴工嘛。

下一章,我们会进入深度学习的基础知识。到时候,这些图像处理技巧就会派上大用场了。

加油,各位!