我怎样才能找到最大的四边形

时间:2014-05-16 18:14:03

标签: c++ opencv image-processing contour feature-detection

在这种情况下,如何找到最大的四边形?

在附图中,你可以看到我所拥有的(在左边)和我想要的东西(在rigth中)。 enter image description here

此代码不起作用,因为最大的矩形有十字而不是角。

int GameController::GetIndexOfExternalContour(vector<vector<Point>> contours)
{
    int largest_area=0;
int largest_contour_index=0;

for( int i = 0; i< contours.size(); i++ )           // iterate through each contour. 
{
    double a = contourArea(contours[i], false);     //  Find the area of contour
    if(a > largest_area)
    {
        largest_area = a;
        largest_contour_index = i;                  //Store the index of largest contour
    }
}

3 个答案:

答案 0 :(得分:2)

我想添加convexity defects方法。

找到最大的轮廓, 得到缺陷点, 连接极端。

// stl
#include <algorithm>
#include <iterator>
#include <limits>
using namespace std;

// cv
#include <opencv2/opencv.hpp>
using namespace cv;

int main()
{
    Mat sample = imread("path/to/sample.jpg");
    imshow("window", sample);
    waitKey(0);

    // images to work on
    Mat black = Mat(sample.rows, sample.cols, CV_8UC1, Scalar(0));
    Mat clone = sample.clone();

    // binarization
    Mat gray;
    cvtColor(sample, gray, CV_BGR2GRAY);
    threshold(gray, gray, 127, 255, CV_THRESH_OTSU);

    // find and fill the largest contour
    vector<vector<Point> > contours;
    vector<double> areas;
    findContours(gray, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
    for(unsigned int i = 0; i < contours.size(); i++)
    {
        areas.push_back(abs(contourArea(contours[i])));
    }
    vector<double>::iterator biggest = max_element(areas.begin(), areas.end());
    unsigned int ID = distance(areas.begin(), biggest);
    drawContours(black, contours, ID, Scalar(255), -1);
    imshow("window", black);
    waitKey(0);

    // get convexity defects of thelargest contour
    vector<Point> external = contours[ID];
    vector<int> hull;
    vector<Vec4i> defects;
    convexHull(external, hull);
    convexityDefects(external, hull, defects);

    // show defect points
    for(unsigned int i = 0; i < defects.size(); i++)
    {
        circle(clone, external[defects[i][1]], 1, Scalar(0, 255, 255), 3);
    }
    imshow("window", clone);
    waitKey(0);

    // find extremes
    Point tl, tr, bl, br;
    Point p;
    double d_tl, d_tr, d_bl, d_br;
    double m_tl = numeric_limits<double>::max();
    double m_tr = numeric_limits<double>::max();
    double m_bl = numeric_limits<double>::max();
    double m_br = numeric_limits<double>::max();
    for(unsigned int i = 0; i < defects.size(); i++)
    {
        p = external[defects[i][2]];
        d_tl = (double)sqrt((double)pow((double)(p.x),2) + pow((double)(p.y),2));
        d_tr = (double)sqrt((double)pow((double)(sample.cols - p.x),2) + pow((double)(p.y),2));
        d_bl = (double)sqrt((double)pow((double)(p.x),2) + pow((double)(sample.rows - p.y),2));
        d_br = (double)sqrt((double)pow((double)(sample.cols - p.x),2) + pow((double)(sample.rows - p.y),2));
        if(d_tl < m_tl)
        {
            tl = p;
            m_tl = d_tl;
        }
        if(d_tr < m_tr)
        {
            tr = p;
            m_tr = d_tr;
        }
        if(d_bl < m_bl)
        {
            bl = p;
            m_bl = d_bl;
        }
        if(d_br < m_br)
        {
            br = p;
            m_br = d_br;
        }
    }

    // draw rectangle
    line(sample, tl, tr, Scalar(0, 255, 255), 3);
    line(sample, tr, br, Scalar(0, 255, 255), 3);
    line(sample, br, bl, Scalar(0, 255, 255), 3);
    line(sample, bl, tl, Scalar(0, 255, 255), 3);
    imshow("window", sample);
    waitKey(0);

    return 0;
}

enter image description here

enter image description here

enter image description here

enter image description here

你只需要为最后一步尝试另一种方法(找到极端缺陷)

答案 1 :(得分:0)

我有一个想法:

1)执行Hough变换,并且通过这样做,所有直线将落入变换域中的点。我注意到线条并不是直线,所以请记住在霍夫变换图像上执行平均滤波,或者在执行霍夫变换时降低采样率。

2)分析霍夫变换图像并尝试分别在近水平方向和近垂直方向上找到局部最大点。如果顺利进行,两个方向上应该有5个局部最大点。

3)在每个方向的这些局部最大点中,选择在图像域中转换为线条时导致最大距离的两个点。

4)使用选定的线条,计算线条的交点。它们对应于最大矩形的角。

答案 2 :(得分:0)

基于霍夫的另一种解决方案/解释:

1)预处理 - 在没有“清洁”的情况下应用霍夫通常会导致霍夫域中的大量噪声。您可以进行预处理,例如首先执行平均滤波,然后在边缘检测之后(例如,Canny edge detector更简单,例如Sobel)。也在评论中标明。

2)使用参数化霍夫变换 - H(rho,theta)

3)在H上使用阈值仅留下10个点。

4)在 H 中 - 选择具有最大 rho 的4个点。 ( rho 是其中一个轴,表示在线的参数表示中距图像中心的距离)

5)4个拾取点是图像域中轮廓的4行

作为霍夫变换的替代方案,我想指出角落检测方法 - 哈里斯角点检测器是最常用的(据我所知)(但还有很多其他的) -

open-cv_harris_detector