OpenCV的fitEllipse()有时会返回完全错误的省略号

Yuk*_*awa 15 c++ opencv image-processing

我的目标是识别图像中存在的所有形状.这个想法是:

  1. 提取轮廓
  2. 使每个轮廓适合不同的形状
  3. 正确的形状应该是具有最接近轮廓区域的区域.

示例图片: 在此输入图像描述

我用它fitEllipse()来找到轮廓最合适的椭圆,但结果有点乱: 在此输入图像描述

可能正确的椭圆用蓝色填充,边界椭圆用黄色填充.可能不正确的轮廓用绿色填充,(错误的)边界椭圆是青色.

正如您所看到的,在第一行中限定三角形的椭圆看起来非常适合最佳拟合.第三行中三角形的边界椭圆似乎不是最合适的,但仍然可以作为拒绝不正确椭圆的标准.

但我无法理解为什么剩下的三角形在其轮廓之外完全具有边界椭圆.最坏的情况是最后一行中的第三个三角形:椭圆是完全错误的,但碰巧有一个区域靠近轮廓的区域,因此三角形被错误地识别为椭圆.

我想念什么吗?我的代码:

#include <iostream>
#include <opencv/cv.h>
#include <opencv/highgui.h>

using namespace std;
using namespace cv;

void getEllipses(vector<vector<Point> >& contours, vector<RotatedRect>& ellipses) {
    ellipses.clear();
    Mat img(Size(800,500), CV_8UC3);
    for (unsigned i = 0; i<contours.size(); i++) {
        if (contours[i].size() >= 5) {
            RotatedRect temp = fitEllipse(Mat(contours[i]));
            if (area(temp) <= 1.1 * contourArea(contours[i])) {
                //cout << area(temp) << " < 1.1* " << contourArea(contours[i]) << endl;
                ellipses.push_back(temp);
                drawContours(img, contours, i, Scalar(255,0,0), -1, 8);
                ellipse(img, temp, Scalar(0,255,255), 2, 8);
                imshow("Ellipses", img);
                waitKey();
            } else {
                //cout << "Reject ellipse " << i << endl;
                drawContours(img, contours, i, Scalar(0,255,0), -1, 8);
                ellipse(img, temp, Scalar(255,255,0), 2, 8);
                imshow("Ellipses", img);
                waitKey();
            }
        }
    }
}

int main() {
    Mat img = imread("image.png", CV_8UC1);
    threshold(img, img, 127,255,CV_THRESH_BINARY);
    vector<vector<Point> > contours;
    vector<Vec4i> hierarchy;
    findContours(img, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
    vector<RotatedRect> ellipses;
    getEllipses(contours, ellipses);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Mic*_*cka 13

请记住,这fitEllipse不是边界椭圆的计算,而是假设点位于椭圆上的最小二乘优化.

我不能告诉你为什么它在最后一行的3个三角形上失败但是在上面一行的三角形上"起作用",但我看到的一件事是,最后一行中的所有3个三角形都适合于一个rotateRect用angle 0.可能最小的方形配件在那里失败了.

但我不知道openCV实现中是否存在错误,或者算法无法处理这些情况.使用此算法:http://www.bmva.org/bmvc/1995/bmvc-95-050.pdf

我的建议是,只有fitEllipse在你确定这些点真的属于椭圆时才使用.fitLine如果你有随机数据点,你不会假设得到合理的结果.您可能想要查看的其他功能是:minAreaRectminEnclosingCircle

如果您使用RotatedRect temp = minAreaRect(Mat(contours[i]));而不是fitEllipse您将得到这样的图像:

在此输入图像描述

也许你甚至可以使用这两种方法并拒绝在两个版本中都失败的所有省略号并接受两个版本中都接受的所有省略,但是在那些不同的版本中进一步调查?


kar*_*lip 5

如果您遇到问题cv::fitEllipse(),本文将讨论一些方法,以最大限度地减少在cv::RotatedRect没有任何进一步测试的情况下直接绘制时发生的错误.事实证明cv::fitEllipse()并不完美,可能会出现问题中提到的问题.

现在,还不完全清楚项目的约束是什么,但解决这个问题的另一种方法是根据轮廓的面积分离这些形状:

在此输入图像描述

这种方法在这种特殊情况下非常简单而有效:圆形区域在1300-1699之间变化,三角形区域在1-1299之间变化.

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>

int main()
{
    cv::Mat img = cv::imread("input.png");
    if (img.empty())
    {
        std::cout << "!!! Failed to open image" << std::endl;
        return -1;
    }

    /* Convert to grayscale */

    cv::Mat gray;
    cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);

    /* Convert to binary */

    cv::Mat thres;
    cv::threshold(gray, thres, 127, 255, cv::THRESH_BINARY);

    /* Find contours */

    std::vector<std::vector<cv::Point> > contours;
    cv::findContours(thres, contours, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE);

    int circles = 0;
    int triangles = 0;
    for (size_t i = 0; i < contours.size(); i++)
    {
        // Draw a contour based on the size of its area:
        //  - Area > 0 and < 1300 means it's a triangle;
        //  - Area >= 1300 and < 1700 means it's a circle;

        double area = cv::contourArea(contours[i]);
        if (area > 0 && area < 1300)
        {
            std::cout << "* Triangle #" << ++triangles << " area: " << area << std::endl;
            cv::drawContours(img, contours, i, cv::Scalar(0, 255, 0), -1, 8); // filled (green)
            cv::drawContours(img, contours, i, cv::Scalar(0, 0, 255), 2, 8); // outline (red)
        }
        else if (area >= 1300 && area < 1700)
        {
            std::cout << "* Circle #" << ++circles << " area: " << area << std::endl;
            cv::drawContours(img, contours, i, cv::Scalar(255, 0, 0), -1, 8); // filled (blue)
            cv::drawContours(img, contours, i, cv::Scalar(0, 0, 255), 2, 8); // outline (red)
        }
        else
        {
            std::cout << "* Ignoring area: " << area << std::endl;
            continue;
        }

        cv::imshow("OBJ", img);
        cv::waitKey(0);
    }   

    cv::imwrite("output.png", img);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

您可以调用其他函数来绘制更精确的形状轮廓(边框).


A.E*_*rew 5

在调用中更改cv::CHAIN_APPROX_SIMPLEcv::CHAIN_APPROX_NONEtocv::findContours()给了我更合理的结果。

我们会得到一个更好的椭圆近似,轮廓中包含更多点,但我仍然不确定为什么简单链近似的结果如此之差。有关差异的解释,请参阅opencv 文档

似乎在使用 时cv::CHAIN_APPROX_SIMPLE,三角形的相对水平的边缘几乎完全从轮廓中移除。

在此处输入图片说明

至于您的最佳拟合分类,正如其他人所指出的,仅使用该区域将为您提供您观察到的结果,因为根本不考虑定位。