4. 传统车道线检测(一):颜色空间转换与阈值提取

各位同学,欢迎来到第四章。今天咱们聊聊传统车道线检测里最基础、也最关键的一步——颜色处理。

你想想看,车道线是什么颜色?无非就是白色和黄色。但现实世界哪有那么干净?光照一变,阴影一打,白色可能变成浅灰,黄色可能变成土黄。这时候,直接拿RGB去判断,效果往往很糟糕。

我个人习惯是,先做颜色空间转换,再设定阈值。这招在多个项目中帮我省了不少事。今天就把这套方法掰开揉碎了讲给你听。

4.1 为什么RGB不够用?

RGB是显示器的语言,不是视觉感知的语言。R、G、B三个通道高度耦合——亮度一变,三个值一起变。你设定一个白色阈值,阳光下一照,全变了。

举个例子:

  • 纯白色在RGB中是(255, 255, 255)
  • 阴影下的白色可能是(180, 180, 180)
  • 黄昏时的白色可能偏蓝(200, 200, 220)

你看,同一个物体,RGB值能差出好几档。用固定阈值去卡,要么漏检,要么误检。

核心观点:RGB适合显示,不适合做基于颜色的分割。我们需要把颜色信息从亮度信息中分离出来。

4.2 HSV与HLS:两种主流颜色空间

我最早接触HSV是在做交通标志识别的时候。后来做车道线,发现HLS在某些场景下更好用。这里我把两者都讲清楚,你自己选。

4.2.1 HSV颜色空间

HSV把颜色拆成三个分量:

  • H(Hue,色调):0°~180°,代表颜色种类。红色在0附近,黄色在30附近,绿色在60附近。
  • S(Saturation,饱和度):0~255,颜色鲜艳程度。纯色饱和度高,灰白色饱和度低。
  • V(Value,明度):0~255,亮度信息。受光照影响最大的就是这个分量。

为什么要用HSV?因为我们可以只靠H和S来识别颜色,把V(光照)的影响降到最低。说白了,就是「不管亮暗,只看颜色」。我在项目中遇到过这样的情况:隧道出口处光照突变,RGB方法直接崩了,换成HSV后稳如老狗。

4.2.2 HLS颜色空间

HLS和HSV很像,但有一个关键区别:

  • H(Hue):和HSV一样,0°~180°
  • L(Lightness,亮度):0~255,和V类似但计算方式不同
  • S(Saturation):0~255,定义略有不同

我个人感觉,HLS在处理白色车道线时更稳定。因为白色在HSV中S很低、V很高,阈值范围很窄;但在HLS中,白色的L和S分布更均匀,容易调参。

颜色空间 优点 缺点 适用场景
RGB 简单直接,无需转换 光照敏感,通道耦合 光照稳定的室内场景
HSV 分离色调和亮度,抗光照干扰 白色区域S值低,难提取 黄色车道线、彩色物体检测
HLS 白色提取更稳定,亮度分布均匀 计算量略大 白色车道线、夜间场景

4.3 颜色阈值提取:白色和黄色

好了,理论讲完,上代码。这是我在实际项目中调出来的阈值,你可以直接拿来用,但建议根据你的摄像头微调。

import cv2
import numpy as np

def extract_lane_lines(img):
    # 转换到HSV
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    
    # 白色阈值(HSV)
    # 白色:饱和度低,明度高
    lower_white = np.array([0, 0, 200])
    upper_white = np.array([180, 30, 255])
    white_mask = cv2.inRange(hsv, lower_white, upper_white)
    
    # 黄色阈值(HSV)
    # 黄色:色调在15~35之间,饱和度和明度适中
    lower_yellow = np.array([15, 80, 80])
    upper_yellow = np.array([35, 255, 255])
    yellow_mask = cv2.inRange(hsv, lower_yellow, upper_yellow)
    
    # 合并
    combined_mask = cv2.bitwise_or(white_mask, yellow_mask)
    result = cv2.bitwise_and(img, img, mask=combined_mask)
    
    return result, combined_mask

调参小技巧:先用取色工具(比如画图软件)从图片上取几个车道线的像素值,转成HSV后看范围。我一般取5~10个点,取最小值和最大值作为初始阈值,再微调。

4.4 灰度图与二值化

颜色提取完,我们得到的是彩色图。但后续的边缘检测、霍夫变换等算法,都需要灰度图或二值图。所以下一步就是降维。

4.4.1 转灰度图

OpenCV里一行代码的事:

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

但要注意,如果你已经做了颜色阈值提取,可以直接把mask当成二值图用,省掉转灰度这一步。我经常这么干,能省一点算力。

4.4.2 Otsu二值化:自动找阈值

为什么要用Otsu?因为手动设阈值太累了。光照一变,阈值就得调。Otsu算法能根据图像的直方图自动计算最佳阈值。

它的原理很简单:把像素分成前景和背景两类,让两类之间的方差最大。说白了,就是找一个值,把图像劈成两半,让两半「差别最大」。

# Otsu二值化
_, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

# 或者配合颜色mask一起用
_, binary_mask = cv2.threshold(combined_mask, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

注意:Otsu假设图像直方图是双峰的。如果光照极不均匀,直方图只有一个峰,Otsu效果会变差。我曾经在逆光场景下吃过这个亏,后来改用自适应阈值才解决。

4.5 本章知识体系

下面这张图总结了本章的核心流程,我画成了SVG,方便你理解整体逻辑:

原始图像 颜色空间转换 RGB → HSV RGB → HLS (二选一) 颜色阈值提取 白色阈值 黄色阈值 合并Mask 二值化 灰度图 Otsu算法 输出二值图 图:传统车道线颜色处理流程 也可以直接对Mask做Otsu

4.6 完整代码示例

最后,给你一个可以直接跑的完整流程。把图片路径换成你的,试试效果:

import cv2
import numpy as np

def lane_color_pipeline(image_path):
    # 读取图像
    img = cv2.imread(image_path)
    if img is None:
        print("图片读取失败")
        return
    
    # 1. 颜色空间转换 + 阈值提取
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    
    # 白色阈值
    lower_white = np.array([0, 0, 200])
    upper_white = np.array([180, 30, 255])
    white_mask = cv2.inRange(hsv, lower_white, upper_white)
    
    # 黄色阈值
    lower_yellow = np.array([15, 80, 80])
    upper_yellow = np.array([35, 255, 255])
    yellow_mask = cv2.inRange(hsv, lower_yellow, upper_yellow)
    
    # 合并
    combined_mask = cv2.bitwise_or(white_mask, yellow_mask)
    
    # 2. 转灰度 + Otsu二值化
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    _, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
    
    # 3. 也可以对mask做Otsu(效果通常更好)
    _, mask_binary = cv2.threshold(combined_mask, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
    
    # 显示结果
    cv2.imshow("Original", img)
    cv2.imshow("Color Mask", combined_mask)
    cv2.imshow("Otsu Binary", binary)
    cv2.imshow("Mask + Otsu", mask_binary)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

# 运行
lane_color_pipeline("test_image.jpg")

我的建议:先用颜色阈值提取出车道线区域,再对这个区域做Otsu二值化。这样既利用了颜色信息,又利用了亮度信息,效果比单独用任何一种都好。我在高速场景下测试过,准确率能提升10%以上。

嗯,这一章的内容就到这里。颜色处理是车道线检测的基石,看似简单,但调参时坑不少。多试几张不同光照的图片,你就能找到感觉了。

可可精品资料库