OpenCV:如何检测特定颜色的线条?

时间:2016-03-08 11:42:20

标签: c++ opencv feature-extraction hough-transform color-detection

我正在开发一个小型OpenCV项目,以从手机摄像头中检测某种颜色的线条

总之希望:

  1. 将输入图像转换为特定颜色的图像(例如,来自特定上限和下限的红色)
  2. 将Hough线变换应用于生成的图像,以便仅检测该特定颜色的线
  3. 在原始图像上叠加检测到的行
  4. 这些是我想要使用的功能,但不能确定如何填补缺失的部分。

      

    这是从 CvVideoCamera

    的实例处理图像时从智能手机应用调用的processImage函数
    - (void)processImage:(Mat&)image;
    {
    cv::Mat orig_image = image.clone();
    
    cv::Mat red_image = ?? 
    
    // Apply houghes transformation to detect lines between a minimum length and a maximum length (I was thinking of using the CV_HOUGH_PROBABILISTIC method..)
    // Comment.. see below..
    
      

    我无法将documentation here理解为C ++   方法签名没有方法字段

    vector<Vec2f> lines;
    
      

    来自官方文件:

         

    C ++:void HoughLines(InputArray image,OutputArray lines,double rho,double theta,int threshold,double srn = 0,double stn = 0)

    HoughLines(dst, lines, 1, CV_PI/180, 100, 0, 0 ); 
    
      

    取自示例代码,尚未正确理解其工作原理..

         

    (例如theta的用法是什么?如何给出不同的角度   反映线检测?)

    for( size_t i = 0; i < lines.size(); i++ )
    {
    
      

    在这里,我应该只考虑超过一定大小的行...(不知道如何)

    }
    
      

    然后我应该将结果行添加到原始图像(不知道如何),以便它们可以显示在屏幕上。

    非常感谢任何帮助。

2 个答案:

答案 0 :(得分:6)

您可以使用HSV色彩空间提取色调信息。

以下是一些带注释的代码,如果有任何问题可以随意提问:

int main(int argc, char* argv[])
{
    cv::Mat input = cv::imread("C:/StackOverflow/Input/coloredLines.png");

    // convert to HSV color space
    cv::Mat hsvImage;
    cv::cvtColor(input, hsvImage, CV_BGR2HSV);

    // split the channels
    std::vector<cv::Mat> hsvChannels;
    cv::split(hsvImage, hsvChannels);

    // hue channels tells you the color tone, if saturation and value aren't too low.

    // red color is a special case, because the hue space is circular and red is exactly at the beginning/end of the circle.
    // in literature, hue space goes from 0 to 360 degrees, but OpenCV rescales the range to 0 up to 180, because 360 does not fit in a single byte. Alternatively there is another mode where 0..360 is rescaled to 0..255 but this isn't as common.
    int hueValue = 0; // red color
    int hueRange = 15; // how much difference from the desired color we want to include to the result If you increase this value, for example a red color would detect some orange values, too.

    int minSaturation = 50; // I'm not sure which value is good here...
    int minValue = 50; // not sure whether 50 is a good min value here...

    cv::Mat hueImage = hsvChannels[0]; // [hue, saturation, value]

    // is the color within the lower hue range?
    cv::Mat hueMask;
    cv::inRange(hueImage, hueValue - hueRange, hueValue + hueRange, hueMask);

    // if the desired color is near the border of the hue space, check the other side too:
    // TODO: this won't work if "hueValue + hueRange > 180" - maybe use two different if-cases instead... with int lowerHueValue = hueValue - 180
    if (hueValue - hueRange < 0 || hueValue + hueRange > 180)
    {
        cv::Mat hueMaskUpper;
        int upperHueValue = hueValue + 180; // in reality this would be + 360 instead
        cv::inRange(hueImage, upperHueValue - hueRange, upperHueValue + hueRange, hueMaskUpper);

        // add this mask to the other one
        hueMask = hueMask | hueMaskUpper;
    }

    // now we have to filter out all the pixels where saturation and value do not fit the limits:
    cv::Mat saturationMask = hsvChannels[1] > minSaturation;
    cv::Mat valueMask = hsvChannels[2] > minValue;

    hueMask = (hueMask & saturationMask) & valueMask;

    cv::imshow("desired color", hueMask);

    // now perform the line detection
    std::vector<cv::Vec4i> lines;
    cv::HoughLinesP(hueMask, lines, 1, CV_PI / 360, 50, 50, 10);

    // draw the result as big green lines:
    for (unsigned int i = 0; i < lines.size(); ++i)
    {
        cv::line(input, cv::Point(lines[i][0], lines[i][1]), cv::Point(lines[i][2], lines[i][3]), cv::Scalar(0, 255, 0), 5);
    }


    cv::imwrite("C:/StackOverflow/Output/coloredLines_mask.png", hueMask);
    cv::imwrite("C:/StackOverflow/Output/coloredLines_detection.png", input);

    cv::imshow("input", input);
    cv::waitKey(0);
    return 0;
}

使用此输入图像:

enter image description here

将提取此“红色”颜色(调整hueValuehueRange以检测不同的颜色):

enter image description here

和HoughLinesP从掩码中检测到这些行(同样适用于HoughLines):

enter image description here

这是另一组非线条图像......

enter image description here

enter image description here

enter image description here

关于您的不同问题:

  1. HoughLines和HoughLinesP有两个函数。 HoughLines不提取行长度,但您可以通过再次检查在后处理中计算它,边缘掩码(HoughLines输入)的哪些像素对应于提取的行。

  2. 参数:

    图像 - 边缘图像(应该清楚?) 线 - 由角度和位置给出的线,没有长度或者......它们被无限长的解释 rho - 累加器分辨率。越大越好,在线条略微变形的情况下越稳定,但提取线的位置/角度越不准确 阈值 - 假阳性越少越好,但你可能会错过一些线条 θ - 角度分辨率:可以检测到越小的线条(取决于方向)越多。如果线条的方向不适合角度步长,则可能无法检测到线条。例如,如果您CV_PI/180将以分辨率检测到,如果您的行有0.5°(例如33.5°)方向,则可能会错过。

  3. 我对所有参数都不太确定,也许你不得不看一下有关霍夫线检测的文献,或者其他人可以在这里添加一些提示。

    如果您改为使用cv::HoughLinesP,则会检测到包含起点和终点的线段,这更容易理解,您可以从cv::norm(cv::Point(lines[i][0], lines[i][1]) - cv::Point(lines[i][2], lines[i][3]))

    计算线长

答案 1 :(得分:1)

我不会显示代码,而是显示一些技巧的步骤。

假设您要检测道路车道(白色或浅黄色并具有某些特定属性的线条)。

原始图片(我添加一些额外的行来制作噪音)

enter image description here

步骤1:删除不需要使用的部分图像,节省CPU使用率(简单但有用)

enter image description here

第2步:转换为灰色图像

enter image description here

第3步:阈值

根据线条颜色使用阈值,颜色将变为白色,其他颜色将变为黑色

enter image description here

第4步:使用等高线查找对象的界限

enter image description here

步骤5:在上一步中使用Fitline和Contours输入以返回线的方程

Fitline返回(x0,y0)和向量v =(a,b)

enter image description here

第6步:使用线条方程式,您可以在任意线条中绘制

enter image description here