交通信号灯的检测与识别

交通信号灯的检测与识别是无人驾驶与辅助驾驶必不可少的一部分,其识别精度直接关乎智能驾驶的安全。一般而言,在实际的道路场景中采集的交通信号灯图像具有复杂的背景,且感兴趣的信号灯区域只占很少的一部分,如下图所示。针对这些难点,国内外的众多研究者提出了相应的解决方案。总的来说,大多基于传统的图像处理方法;但目前也有用强学习能力的卷积神经网络去进行识别,但这类方法往往需要大量的训练样本避免过拟合的风险。截至目前的大多数方法都是在各种颜色空间中利用信号灯颜色的先验进行分割得到兴趣区域,然后再通过信号灯所特有的形状特征和角点特征等进行进一步的判定。比如,Masako Omachi等人提出在RGB色彩空间分割交通信号灯,使用HOUGH变换检测分割出的信号灯所在的圆形区域;徐成等提出在Lab色彩空间分割交通信号灯,使用模板匹配的方法识别交通信号灯的状态;谷明琴等则在HSV色彩空间中使用颜色直方图统计图像的H分量,确定交通信号灯的类型。本博客将基于传统的图像处理算法来进行交通信号灯的识别,重点介绍核心技术。

这里写图片描述

正如前面所述,道路场景的多变性与复杂性,使得如何快速、准确地检测与识别交通信号灯,并且有效滤除图像中的干扰区域是交通信号灯检测与识别的关键。在图像处理算法设计中,为了提高算法的准确性与时效性,一般只关注局部感兴趣区域而不是整个图像区域。鉴于此,提出一种基于颜色分割与特征匹配相结合的方法,主要分为如下三个步骤:

1. 颜色分割

为了消除噪声,光照等因素的干扰,首先对采集的图像进行直方图均衡化。即:对每一个通道(R,G,B)数据进行直方图均衡化,再合并为一个3通道图像。颜色特征是交通信号灯重要而显著的特征之一。要对交通信号灯进行颜色分割,首先要选择合适的色彩空间。RGB色彩空间中的R、G、B这3个分量之间相关性较高,受光照影响较大,不利于颜色分割。因此,对RGB这3个通道数据进行归一化,即 R = R . / ( R + G + B ) , G = G . / ( R + G + B ) , B = B . / ( R + G + B ) ;然后,统计了不同环境条件下拍摄的交通信号灯的红色、绿色的R,G,B值,确定交通信号灯的颜色阈值。代码如下

void CTrafficLightIdentify::segmentImg(Mat& equaled_img, Mat& redMat, Mat& greenMat, Mat& blueMat, Mat& whiteMat)
{
    Mat square3x3 = Mat(3, 3, CV_8U, cv::Scalar(1));
    Mat diamond = Mat(5, 5, CV_8U, cv::Scalar(1));
    diamond.at<uchar>(0, 0) = 0;
    diamond.at<uchar>(0, 1) = 0;
    diamond.at<uchar>(1, 0) = 0;
    diamond.at<uchar>(4, 4) = 0;
    diamond.at<uchar>(3, 4) = 0;
    diamond.at<uchar>(4, 3) = 0;
    diamond.at<uchar>(4, 0) = 0;
    diamond.at<uchar>(4, 1) = 0;
    diamond.at<uchar>(3, 0) = 0;
    diamond.at<uchar>(0, 4) = 0;
    diamond.at<uchar>(0, 3) = 0;
    diamond.at<uchar>(1, 4) = 0;

    redMat = Mat::zeros(m_inMat.rows, m_inMat.cols, CV_8UC1);
    greenMat = Mat::zeros(m_inMat.rows, m_inMat.cols, CV_8UC1);
    whiteMat = Mat::zeros(m_inMat.rows, m_inMat.cols, CV_8UC1);
    blueMat = Mat::zeros(m_inMat.rows, m_inMat.cols, CV_8UC1);
    yelloMat = Mat::zeros(m_inMat.size(), CV_8UC1);

    Mat hsvMat;
    cvtColor(m_inMat, hsvMat, CV_BGR2HSV);

    int colorH, colorS, colorV;
    int colorB, colorG, colorR;
    float fsumBGR, fRG, fGB, fG, fH, fS;
    bool flagYello;
    unsigned char* outyelloData = (unsigned char*)yelloMat.data;
    unsigned char* outblueData = (unsigned char*)blueMat.data;
    unsigned char* outwhiteData = (unsigned char*)whiteMat.data;
    unsigned char* outredData = (unsigned char*)redMat.data;
    unsigned char* outgreenData = (unsigned char*)greenMat.data;
    unsigned char* rowHSVImgData;
    unsigned char* rowBGRImgData;
    for (int i = 0; i < m_inMat.rows; i++)
    {
        rowHSVImgData = hsvMat.ptr<unsigned char>(i);
        rowBGRImgData = m_inMat.ptr<unsigned char>(i);
        for (int j = 0; j < m_inMat.cols; j++)
        {
            colorH = rowHSVImgData[3 * j];
            colorS = rowHSVImgData[3 * j + 1];
            colorV = rowHSVImgData[3 * j + 2];

            colorB = rowBGRImgData[3 * j];
            colorG = rowBGRImgData[3 * j + 1];
            colorR = rowBGRImgData[3 * j + 2];

            fH = (float)colorH / 180.0;
            fS = (float)colorS / 255.0;

            fsumBGR = colorB + colorG + colorR;
            fRG = (colorR - colorG) / fsumBGR;
            fGB = (colorG - colorB) / fsumBGR;
            fG = colorG / fsumBGR;

            if ((fH >= 0.05 && fH <= 0.2) && (fS >= 0.7))
                *outyelloData = 255;
            if (colorH >= 100 && colorH <= 124 && colorS >= 80 && colorV >= 65)
                *outblueData = 255;
            if (colorB > 230 && colorG > 230 && colorR > 230) 
                (*outwhiteData) = 255;

            if (fRG > 0.16 && fGB < 0.08 && fG < 0.30 && ((colorH>=0&& colorH<=15)|| (colorH >= 140 && colorH <= 180))) {
                (*outredData) = 255;
            }
            if (fRG < -0.1 && fGB > 0.002 && fG > 0.08 && (colorH >= 40 && colorH <= 80)) {
                (*outgreenData) = 255;
            }

            outyelloData++;
            outblueData++;
            outwhiteData++;
            outredData++;
            outgreenData++;
        }
    }
    morphologyEx(yelloMat, yelloMat, MORPH_OPEN, square3x3);
    morphologyEx(yelloMat, yelloMat, MORPH_CLOSE, diamond);

    morphologyEx(whiteMat, whiteMat, MORPH_OPEN, square3x3);
    morphologyEx(whiteMat, whiteMat, MORPH_CLOSE, diamond);

    morphologyEx(redMat, redMat, MORPH_OPEN, square3x3);
    morphologyEx(redMat, redMat, MORPH_CLOSE, diamond);

    morphologyEx(greenMat, greenMat, MORPH_OPEN, square3x3);
    morphologyEx(greenMat, greenMat, MORPH_CLOSE, diamond);

    morphologyEx(blueMat, blueMat, MORPH_OPEN, square3x3);
    morphologyEx(blueMat, blueMat, MORPH_CLOSE, diamond);
}

那么得到的红色分割图像redMat,和绿色分割图像greeMat如下图所示
这里写图片描述

2. 感兴趣区域提取

该步骤的主要目的为对分割的红色通道图像和绿色通道图像,进行联通区域的标定,并提取区域的基本几何特征,比如长度,宽度,长宽比,面积(即白色像素个数)。

3. 信号灯区域判定与识别

该步骤在前一步骤的基础上根据信号灯的特有特征过滤出真正的信号灯区域。本技术使用了3个先验知识:信号灯面积;信号灯形状;信号灯的黑色边框。

  • 信号灯面积
    本技术设置的上下限是10,200;可根据实际情况进行设定,过滤面积过大或过小区域。
  • 信号灯形状
    形状特征是交通信号灯重要而显著的另一特征,尽管气候、道路环境等会对采集的交通信号灯产生不同程度的噪声、褪色及形变,但是交通信号灯的形状和几何尺寸不会发生太大的变化。对于圆形交通信号灯使用圆形度检测,过滤圆形度过低的区域,其中圆形度是指候选区域边缘接近圆形的程度 。圆度的定义为 C i r c l e M e t r i c = / ,该值如果为0.785,则为圆形;设置的阈值为0.5。
  • 信号灯的黑色边框
    交通信号灯在形状上有个显著的特征,即它的灯板是一个黑色矩形框。根据交通信号灯的设计规范,利用该特征可以将交通信号灯的范围提取出来。本技术采用了SVM分类器,进行识别。
    a. 首先根据一些训练图像建立黑色边框正样本,即在工程中用鼠标框住黑色灯框,在该区域内提取颜色直方图;重复操作,得到正样本集;同样的道理,我们在训练图像中其它区域提取负样本集;最后在颜色直方图的特征空间中学习出分割超平面。其中颜色直方图特征提取如下:

    void CTrafficLightIdentify::calcMatROIHist(Mat& inMat, Rect rectROI, MatND& hisMat)
    {
    if (inMat.channels() != 3)
    {
        AfxMessageBox(_T("请输入3通道彩色图像"));
        return;
    }
    Mat img_roi = inMat(rectROI).clone();
    resize(img_roi, img_roi, Size(64, 48));
    Mat result;
    cvtColor(img_roi, result, CV_BGR2HSV);
    MatND histM;
    // Quantize the hue to 30 levels
    // and the saturation to 32 levels
    int hbins = 30, sbins = 32;
    int histSize[] = { hbins, sbins };
    // hue varies from 0 to 179, see cvtColor
    float hranges[] = { 0, 180 };
    // saturation varies from 0 (black-gray-white) to
    // 255 (pure spectrum color)
    float sranges[] = { 0, 256 };
    const float* ranges[] = { hranges, sranges };
    // we compute the histogram from the 0-th and 1-st channels
    int channels[] = { 0, 1 };
    calcHist(&result, 1, channels, Mat(), // do not use mask
        histM, 2, histSize, ranges,
        true, // the histogram is uniform
        false);
    normalize(histM, histM);  //L2 normalization
    hisMat = histM.reshape(0, 1);
    }

    b. 在识别前加载训练好的SVM文件,而后在之前检测出的白色连通区域的上下位置提取一定大小无重叠区域,在该区域内提取颜色直方图特征;
    c. 根据该颜色直方图特征,根据训练的分割超平面预测其类别;
    d. 如果是,则白色区域可进一步判定为信号灯区域;若不是,则过滤掉该区域。
    最终的识别图如下
    这里写图片描述
    最终的工程界面如图

如果在redMat 中有筛选出的白色区域,则该信号灯为红灯;在greeMat中则为同样的道理。后续的方案将考虑使用表示能力更鲁棒的特征描述子,甚至基于卷积神经网络的深度学习方法。

上述框架虽然能达到一定的识别率,但是依然面临两个严峻的挑战:

  • 对视频处理的实时性较差。上述框架对ROI的提取算法涉及到对整个图像的遍历;并且对视频的每一帧进行独立处理,没有考虑帧与帧之间的相关性。
  • 处理不同场景的迁移性较差。上述框架主要基于SVM分类器,而这种传统的机器学习模型的性能主要依赖于训练数据和提取的人工特征。目前的训练数据有限,不能完全表征整个真实数据的分布,导致在其它场景漏检或误检较多;当前的特征主要基于颜色直方图特征,该特征对图像数据较为敏感。比如采用不同的成像传感器或当前采集图像出现极端天气,提取的颜色直方图特征都会给后续的分类器带来误判。

针对上述挑战,对现有框架进行了改进,设计了交通信号灯识别的自主式学习系统来应对实时性和迁移性差的问题。

猜你喜欢

转载自blog.csdn.net/step_forward_ml/article/details/79891535
今日推荐