图像基础与预处理(上):图像在计算机中的表示与基本操作
各位同学,欢迎来到车道线检测实战的第二讲。今天我们要聊的是图像在计算机里到底长什么样,以及怎么把它“请”到我们的程序里来。这部分内容看似基础,但说实话,我见过太多项目因为预处理没做好,后面模型再牛也白搭。
咱们先从一个最朴素的问题开始:计算机看到的图像,和我们人眼看到的,是一回事吗?
图像在计算机中的表示:RGB与灰度
你想想看,我们看到的彩色照片,在计算机眼里其实就是一堆数字。说白了,就是三个二维数组叠在一起。
RGB色彩空间:每个像素由红(R)、绿(G)、蓝(B)三个通道组成。每个通道的值范围是0到255。0表示没有该颜色,255表示该颜色最亮。比如纯红色就是(255, 0, 0),纯白色是(255, 255, 255),纯黑色是(0, 0, 0)。
我在项目中遇到过一个问题:用OpenCV读取图像时,默认是BGR顺序,而不是我们熟悉的RGB。这个坑我踩过,后面会细说。
灰度图像:只有一个通道,每个像素值0到255,0是黑,255是白。灰度图在车道线检测里非常常用,因为很多边缘检测算法(比如Canny)都是在灰度图上操作的。
核心概念:彩色图像 = 三个通道(H×W×3),灰度图像 = 单通道(H×W)。
嗯,这里要注意:图像的数据类型通常是uint8,也就是无符号8位整数。如果你不小心把它转成了float32,显示的时候可能会出问题——我刚开始学的时候,就因为这个折腾了一下午。
图像读取与显示
在Python里,我们最常用的库就是OpenCV。读取图像用cv2.imread(),显示用cv2.imshow()。
import cv2
# 读取图像
img = cv2.imread('road.jpg')
# 检查是否读取成功
if img is None:
print("图像读取失败,检查路径")
else:
print(f"图像尺寸:{img.shape}") # (高度, 宽度, 通道数)
# 显示图像
cv2.imshow('Road Image', img)
cv2.waitKey(0) # 等待按键
cv2.destroyAllWindows()
我个人习惯在读取图像后,第一时间打印shape。为什么?因为这样可以快速确认图像是否加载成功,以及它的尺寸是否符合预期。我曾经在项目里因为路径写错,读了个空图像,结果后面所有代码都报错,排查了半天。
避坑指南:cv2.imread()如果路径错误,不会报错,只会返回None。所以一定要加判空检查。
另外,cv2.waitKey(0)这个参数0表示无限等待,直到你按任意键。如果你设成1000,那就是等待1秒后自动关闭。这在调试时很有用。
颜色空间转换:BGR2GRAY 与 BGR2HSV
为什么需要转换颜色空间?说白了,就是让图像更适合我们后续的处理。
BGR → 灰度(GRAY):车道线检测中,我们通常先转灰度,再做边缘检测。因为灰度图计算量小,而且边缘信息保留得不错。
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
这里有个细节:OpenCV的默认色彩顺序是BGR,不是RGB。所以如果你用cv2.imread()读图,然后直接用matplotlib显示,颜色会偏蓝偏黄。我刚开始做项目时,就因为这个被导师骂了一顿——显示出来的车道线颜色完全不对。
BGR → HSV:HSV(色调、饱和度、明度)在颜色分割任务中特别好用。比如我们要提取黄色车道线,在RGB空间里很难设定阈值,但在HSV空间里,只需要设定色调范围就行。
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
个人经验:在提取黄色车道线时,我通常把H通道范围设在20-40之间,S和V适当放宽。这个范围是我在多个项目里试出来的,效果比较稳定。
为什么会这样?因为黄色在HSV空间里,色调集中在30度附近。而白色车道线呢?它的饱和度很低,明度很高,所以更适合用灰度图处理。
图像缩放与裁剪
实际项目中,我们很少直接用原始尺寸的图像。原因很简单:计算资源有限,而且车道线通常只出现在图像的下半部分。
缩放(Resize):用cv2.resize()可以改变图像尺寸。常用的插值方法有:
| 插值方法 | 适用场景 |
|---|---|
| INTER_NEAREST | 速度最快,但质量差,适合实时性要求极高的场景 |
| INTER_LINEAR | 默认方法,速度和质量的平衡,我一般用这个 |
| INTER_CUBIC | 质量最好,但速度慢,适合离线处理 |
# 缩放到指定尺寸
resized = cv2.resize(img, (640, 480))
# 按比例缩放
scale_percent = 50 # 缩小到50%
width = int(img.shape[1] * scale_percent / 100)
height = int(img.shape[0] * scale_percent / 100)
resized = cv2.resize(img, (width, height))
裁剪(Cropping):其实就是数组切片操作。因为图像在NumPy里就是一个多维数组。
# 裁剪图像的上半部分
cropped = img[0:img.shape[0]//2, :]
嗯,这里要注意:裁剪时,第一个维度是高度(行),第二个维度是宽度(列)。我见过有人把顺序搞反,结果图像被旋转了90度。
实战建议:在车道线检测中,我通常先把图像缩放到640×480,然后裁剪掉上半部分(天空、树木等无关区域),只保留下半部分。这样既能减少计算量,又能提高检测精度。
本章知识体系
下面这张图,是我自己总结的图像预处理核心流程。你可以把它当作一个检查清单:
这张图展示了我们今天讲的所有内容:从原始BGR图像出发,根据任务需求选择转灰度还是HSV,最后统一做缩放和裁剪。我个人习惯把这个流程封装成一个函数,每次调用就行,省得重复写代码。
小技巧:你可以把预处理参数(缩放比例、裁剪区域、颜色空间)放在一个配置文件里。这样调参时不用改代码,改配置文件就行。我在实际项目中就是这么做的,效率高很多。
好了,今天的内容就到这里。记住:图像预处理不是可有可无的步骤,它直接决定了后续算法的上限。你想想看,如果输入图像都是歪的、模糊的、颜色不对的,再好的模型也白搭。
下一节我们会继续深入图像预处理的下半部分——滤波、边缘检测和感兴趣区域提取。这些都是车道线检测的核心前置操作。