【问题标题】:OpenCV: how to detect lines of a specific colour?OpenCV:如何检测特定颜色的线条?
【发布时间】:2016-06-22 08:11:44
【问题描述】:

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

总之想:

  1. 将输入图像转换为特定颜色的图像(例如,特定上下范围的红色)
  2. 对生成的图像应用霍夫线变换,使其仅检测特定颜色的线
  3. 在原始图像上叠加检测到的线条

这些是我想使用但不确定如何填充缺失位的功能。

这是在处理来自 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++ )
{

这里我应该只考虑超过一定大小的线条..(不知道怎么做)

}

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

任何帮助将不胜感激。

【问题讨论】:

  • 要获得线条的大小,请使用概率霍夫变换。它只会检测到超过一定长度的行。
  • 你能贴几张图片吗?解释起来会更容易、更清楚。
  • 您的“红色图像”的问题是您找不到功能,因为您自己不了解任务。 “红色”可能不是单一的 RGB 值,而是许多相关的颜色。您想删除所有其他色调并用黑色替换它们。
  • @MSalters 我对 Red inRange of a lower and upper treshold 感兴趣..
  • @mm24:色彩空间是 3 维的。您需要一个上、下、左、右、前和后阈值。

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


【解决方案1】:

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

这里有一些cmets的代码,如果有任何问题欢迎提问:

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;
}

使用此输入图像:

将提取这种“红色”颜色(调整hueValue 和hueRange 以检测不同的颜色):

并且 HoughLinesP 从掩码中检测到这些行(应该与 HoughLines 类似地使用):

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

关于您的不同问题:

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

  2. 参数:

    image - 边缘图像(应该清晰吗?) 线条 - 由角度和位置给出的线条,没有长度或某物。它们被无限长地解释 rho - 累加器分辨率。越大,在略微扭曲的线条的情况下应该越健壮,但提取线条的位置/角度越不准确 阈值 - 越大误报越少,但你可能会错过一些行 theta - 角度分辨率:越小,可以检测到的不同线越多(取决于方向)。如果您的线的方向不适合角度步长,则可能无法检测到该线。例如,如果您CV_PI/180 将检测到1° 分辨率,如果您的线路具有0.5°(例如33.5°)方向,则可能会被错过。

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

如果你改为使用cv::HoughLinesP,将检测到具有起点和终点的线段,这更容易解释,你可以从cv::norm(cv::Point(lines[i][0], lines[i][1]) - cv::Point(lines[i][2], lines[i][3]))计算线段长度

【讨论】:

  • 谢谢!我现在要试试你的代码..我怎样才能在相机上显示图像?我正在使用“- (void)processImage:(Mat&)image”函数来处理输入帧图像。想将检测到的图像叠加到它上面。
  • 只需将我的代码中的 input 重命名为 orig_image 并确保 orig_image 是 BGR 格式。
  • 我的代码有一个问题,我猜...如果目标颜色选择在 180 附近,if(out target +/- range of range) 代码将不起作用。我认为它必须是 2 种情况,一种用于溢出,一种用于下溢......不幸的是我没有时间在 atm 测试它..
  • 你好,代码看起来很棒,我很欣赏详细的解释。我已经开始测试它,但不得不切换任务,明天将重新开始。一旦我测试过,我会告诉你我是怎么上去的并接受答案。同时也非常感谢您最后的评论。
【解决方案2】:

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

假设您要检测道路车道(它们是白色或浅黄色的线,具有一些特定的属性)。

原图(我添加了一些额外的线条来制造噪音)

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

第 2 步:转换为灰度图像

第 3 步:阈值

根据你的线条颜色使用阈值,颜色会变成白色,其他的会变成黑色

第 4 步:使用轮廓找到对象的边界

第 5 步:在上一步中使用带轮廓的 Fitline 来返回直线方程

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

第 6 步:使用直线方程,您可以画出任何您想要的直线

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-08-17
    • 2018-01-09
    • 1970-01-01
    • 2016-09-06
    • 2014-06-25
    • 1970-01-01
    • 2014-03-15
    • 2018-01-01
    相关资源
    最近更新 更多