使用OpenCV进行多颜色对象检测

我试图检测两个白色墙壁上的红色墙壁和白色方块的图像中的位置,红色顶部和白色“柱子”:

在此处输入图像描述

我的方法是进行阈值处理以找到我现在可以从输出中轻松检测到的红色墙壁:

在此处输入图像描述

现在我的问题是检测白色方块的位置,但考虑到白色墙壁,这更难。 如果我基于白色的阈值,我仍然在白色方柱之间保留不需要的白色墙壁。

任何帮助将不胜感激。

一种方法是使用cv::inRange()对输入图像进行阈值 处理

 cv::Mat image = cv::imread(argv[1]); if (image.empty()) { std::cout << "!!! Failed imread()" << std::endl; return -1; } cv::Mat red_image; cv::inRange(image, cv::Scalar(40, 0, 180), cv::Scalar(135, 110, 255), red_image); //cv::imwrite("out1.png", red_image); 

产出

在此处输入图像描述

我们可以使用cv::findContours来检索阈值图像的轮廓,以便能够为它们创建边界框, 这是一种描述的技术 :

 std::vector > contours; std::vector hierarchy; cv::findContours( red_image, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0) ); std::vector > contours_poly( contours.size() ); std::vector boundRect( contours.size() ); for( int i = 0; i < contours.size(); i++ ) { cv::approxPolyDP( cv::Mat(contours[i]), contours_poly[i], 3, true ); boundRect[i] = cv::boundingRect( cv::Mat(contours_poly[i]) ); } // Debug purposes: draw bonding rects //cv::Mat tmp = cv::Mat::zeros( red_image.size(), CV_8UC3 ); //for( int i = 0; i< contours.size(); i++ ) // rectangle( tmp, boundRect[i].tl(), boundRect[i].br(), cv::Scalar(0, 255, 0), 2, 8, 0 ); //cv::imwrite("out2.png", tmp); 

输出

在此处输入图像描述

上图中显示的所有矩形都存储在boundRect向量中的cv::Rect对象中。 每个矩形由2个相反的cv::Point对象组成,因此我们迭代此向量以创建仅由cv::Point对象组成的新向量:

 // Two opposite cv::Point can be used to draw a rectangle. // Iterate on the cv::Rect vector and retrieve all cv::Point // and store them in a cv::Point vector. std::vector rect_points; for( int i = 0; i < contours.size(); i++ ) { rect_points.push_back(boundRect[i].tl()); rect_points.push_back(boundRect[i].br()); } //cv::Mat drawing = cv::Mat::zeros( red_image.size(), CV_8UC3 ); cv::Mat drawing = image.clone(); 

找到白色方块的逻辑是:假设彼此相距25x25的2个像素定义了一个白色方块:

 // Draw a rectangle when 2 points are less than 25x25 pixels of // distance from each other for( int i = 0; i < rect_points.size(); i++ ) { for( int j = 0; j < rect_points.size(); j++ ) { if (i == j) continue; int x_distance = (rect_points[i].x - rect_points[j].x); if (x_distance < 0) x_distance *= -1; int y_distance = (rect_points[i].y - rect_points[j].y); if (y_distance < 0) y_distance *= -1; if ( (x_distance < 25) && (y_distance < 25) ) { std::cout << "Drawing rectangle " << i << " from " << rect_points[i] << " to " << rect_points[j] << " distance: " << x_distance << "x" << y_distance << std::endl; cv::rectangle( drawing, rect_points[i], rect_points[j], cv::Scalar(255, 50, 0), 2 ); break; } } } //cv::imwrite("out3.png", drawing); cv::imshow("white rectangles", drawing); cv::waitKey(); 

输出

在此处输入图像描述

这个算法非常原始,并且错过了底部的2个白色方块,因为它们下面没有红色的墙,只有它们在它们之上。

所以我留给你改进这种方法:)

祝好运。

场景中的所有重要信息似乎都在提取的红条的二值化图片中。 我会尝试忽略此步骤的原始图片,并仅使用矩形的几何图形来查找它们之间的区域。

例如,您可以调用findContours来获取示例图片中的8个blob。 如果检查质心之间的直线上的点,则返回最小值到pointPolygonTest的点是其中一个白点(或至少接近)的中心点。

您可以使用有关场景的已知信息以及您将遇到的图像。 例如,您可以将轮廓分组为“左”和“右”条,仅在某些轮廓之间进行线搜索。 但是,如果你需要更加不可知。 你的输入,你或多或少地从阈值图片得到的一切(场景方向,墙壁数量,矩形的厚度……)应该是非常可能的。