【问题标题】:Detect three nested objects of similar shape in an image检测图像中三个形状相似的嵌套对象
【发布时间】:2016-08-10 00:57:25
【问题描述】:

我想通过颜色来检测射箭目标并让它走那么远:

原始图像和分析颜色:

我正在使用 RGB 图像上的颜色范围来获取圆圈。如您所见,图像上还有许多其他的斑点我想去掉。

我尝试比较所有三个图像中的所有形状,以通过 matchShape 找到最佳匹配,但它没有得到我想要的那三个圆圈。

有什么办法可以找出图像上的哪些轮廓/形状/对象实际上是三个圆圈?

输入图像可能有角度/透视,所以我不能使用 hough 来查找圆。

因此我需要的是三个圆的轮廓(外轮廓和内轮廓),因为我需要对这些数据进行进一步处理。

这实际上是该问题的后续:find archery target in image of different perspectives


因为这是一个更具体的问题,所以我创建了一个新问题。我是新来的


Miki 的回答的后续行动。 结果之一如下所示: Contour of blue circle and fitting ellipse 二值图像中的轮廓和原始图像中得到的椭圆是不同的。

我仍然想解决这个问题。我正在考虑一种沿着二进制图像的轮廓进行的算法,并且在轮廓被破坏的任何地方,算法都会使用最后一个已知的半径继续进行,直到找到轮廓的下一个像素。有这样的算法吗?或者也许一种方法可以在特定点拉伸拟合椭圆,直到轮廓的所有部分都被覆盖?

【问题讨论】:

  • 您可以尝试提取轮廓并使用轮廓矩(例如 matchShapes 函数 docs.opencv.org/3.1.0/d5/d45/…)来比较图像中的每个轮廓。希望您会观察到目标轮廓的良好结果和背景的不良结果。
  • @Micka 正如问题中所述,我已经尝试过了。还是你的意思是别的?

标签: java c++ opencv image-processing


【解决方案1】:

您可以查看Circle Hough Transform 算法来查找红色、绿色和蓝色通道中的所有圆形对象,然后匹配它们。

您可以找到实现here 或使用OpenCV realization

【讨论】:

  • 我想我不能使用圆形检测,因为输入图像可能处于不同的角度,甚至不允许我寻找椭圆。
  • 透视扭曲的圆是椭圆!
【解决方案2】:

直接找到该图像中的椭圆可能非常棘手。但是,您可以查看 here 以获取一些信息和代码参考。


在这种情况下,分割 3 种颜色要容易得多:blueredyellow,找到外轮廓,然后拟合对他们来说是一个椭圆。

所以,在这个输入图像上:

因此,您首先将图像转换为 HSV,然后应用一些阈值来恢复蒙版。使用形态学 close 操作将摆脱一些 hole,并链接附近的 blob。

蓝色面具:

红色面具:

黄色面具:

然后,您可以从此掩码中检索外部轮廓,并仅保留最大的(以防您发现一些不属于目标的较小斑点)。

现在你只需要为这些轮廓拟合一个椭圆:

请注意,我也在图像in your other question 上进行了尝试。蓝色目标是变形的,所以不是椭圆,所以在这里拟合椭圆不是一个好的选择:

在这种情况下可能会更好地使用轮廓的凸包,如果遮罩不完美,它会比轮廓本身更健壮(代码如下):

代码:

#include <opencv2/opencv.hpp>
#include <vector>
#include <string>

using namespace std;
using namespace cv;

int main()
{
    // Load image
    Mat3b img = imread("path_to_image");

    // Convert to hsv
    Mat3b hsv;
    cvtColor(img, hsv, COLOR_BGR2HSV);

    // Find masks for different colors
    Mat1b blue_mask;
    inRange(hsv, Scalar(90, 150, 150), Scalar(110, 255, 255), blue_mask);

    Mat1b red_mask;
    inRange(hsv, Scalar(160, 150, 100), Scalar(180, 255, 255), red_mask);

    Mat1b yellow_mask;
    inRange(hsv, Scalar(20, 150, 100), Scalar(30, 255, 255), yellow_mask);

    // Apply morphological close
    Mat1b kernel = getStructuringElement(MORPH_ELLIPSE, Size(11,11));
    morphologyEx(blue_mask, blue_mask, MORPH_CLOSE, kernel);
    morphologyEx(red_mask, red_mask, MORPH_CLOSE, kernel);
    morphologyEx(yellow_mask, yellow_mask, MORPH_CLOSE, kernel);

    // Find largest blob and draw it
    vector<Point> blue_contour, red_contour, yellow_contour;
    {
        vector<vector<Point>> contours;
        findContours(blue_mask.clone(), contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
        blue_contour = *max_element(contours.begin(), contours.end(), [](const vector<Point>& lhs, const vector<Point>& rhs){
            return contourArea(lhs) < contourArea(rhs); });
    }
    {
        vector<vector<Point>> contours;
        findContours(red_mask.clone(), contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
        red_contour = *max_element(contours.begin(), contours.end(), [](const vector<Point>& lhs, const vector<Point>& rhs){
            return contourArea(lhs) < contourArea(rhs); });
    }
    {
        vector<vector<Point>> contours;
        findContours(yellow_mask.clone(), contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
        yellow_contour = *max_element(contours.begin(), contours.end(), [](const vector<Point>& lhs, const vector<Point>& rhs){
            return contourArea(lhs) < contourArea(rhs); });
    }

    // Fit ellipse
    RotatedRect blue_ellipse = fitEllipse(blue_contour);
    RotatedRect red_ellipse = fitEllipse(red_contour);
    RotatedRect yellow_ellipse = fitEllipse(yellow_contour);

    // Draw ellipses
    ellipse(img, blue_ellipse, Scalar(255, 0, 0), 3);
    ellipse(img, red_ellipse, Scalar(0, 0, 255), 3);
    ellipse(img, yellow_ellipse, Scalar(0, 255, 255), 3);

    imshow("Result", img);
    waitKey();

    return 0;
}

凸包代码:

// Get convex hulls
vector<Point> blue_hull, red_hull, yellow_hull;
convexHull(blue_contour, blue_hull);
convexHull(red_contour, red_hull);
convexHull(yellow_contour, yellow_hull);

// Draw convex hulls
drawContours(img, vector < vector<Point> > {blue_hull}, 0, Scalar(255,0,0), 3);
drawContours(img, vector < vector<Point> > {red_hull}, 0, Scalar(0, 0, 255), 3);
drawContours(img, vector < vector<Point> > {yellow_hull}, 0, Scalar(0, 255, 255), 3);

【讨论】:

  • 惊人的解决方案!我用 Java 得到了同样的结果。我仍然想解决最后一张图像的问题,其中拟合椭圆不会覆盖圆形的整个形状。我正在考虑一种沿着二进制图像的轮廓进行的算法,并且在轮廓被破坏的任何地方,算法都会使用最后一个已知的半径继续进行,直到找到轮廓的下一个像素。有这样的算法吗?或者也许一种方法可以在特定点拉伸拟合椭圆,直到轮廓的所有部分都被覆盖?
猜你喜欢
  • 2019-06-22
  • 2016-11-27
  • 2017-10-09
  • 2021-06-28
  • 1970-01-01
  • 2016-01-25
  • 2020-05-04
  • 2014-10-03
相关资源
最近更新 更多