简体   繁体   中英

Extract corner (extreme corner points) of quadrangle from black/white image using OpenCV C++

As part of a bigger project, I need to extract the extreme bottom corners of a quadrangle. I have an image and a corresponding binary Mat with 1s where the image is white (the image) and 0 where black (the background).

I've found ways to find the extreme left, right, bottom and top points but they may not give the points I want as the quadrangles are not perfectly rectangular.

https://www.pyimagesearch.com/2016/04/11/finding-extreme-points-in-contours-with-opencv/

Finding Top Left and Bottom Right Points (C++)

Finding extreme points in contours with OpenCV C++

The only way I can think of doing it is not very good. I'm hoping you guys can think of a better way than to just cycle though the matrix for the most bottom row then most left point and then keep points within a certain radius from that most bottom and left point.

And the same for the right, but this is not very computationally efficient.

示例四边形

This is an example quadruple and the corners of interest.

The ideal output is two Mats, similar to the original one, that have 1s only in the region of interest and 0s everywhere.

Any and all help will be greatly appreciated!!

There are at least two possible approaches. Both assume that you've extracted the corners:

  1. Use approxPolyDP function to approximate the contour and get 4 vertices of a quadrangle.

2.Fit rectangle to the contour and then find nearest points in your contour to the bottom vertices of this rectangle.

// bin -  your binarized image
std::vector<std::vector<cv::Point2i>> contours;
cv::findContours(bin, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
int biggestContourIdx = -1;
double biggestContourArea = 0;
for (int i = 0; i < contours.size(); ++i)
{
    auto area = cv::contourArea(contours[i]);
    if (area > biggestContourArea)
    {
        biggestContourArea = area;
        biggestContourIdx = i;
    }
}
//first solution:
std::vector<cv::Point2i> approx;
cv::approxPolyDP(contours[biggestContourIdx], approx, 30, true);
auto mean = cv::mean(approx);
std::vector<cv::Point2i> bottomCorners;

for (auto p : approx)
{
    if (p.y > mean[1]) bottomCorners.push_back(p);
}

//second solution:
auto rect = cv::minAreaRect(cv::Mat(contours[biggestContourIdx]));
auto center = rect.center;
Point2f rect_points[4];
rect.points(rect_points);
std::vector<cv::Point2i> bottomRectCorners;
std::vector<double> distances(2, std::numeric_limits<double>::max());
for (int i = 0; i < 4; ++i)
{
    if (rect_points[i].y > center.y)
        bottomRectCorners.push_back(rect_points[i]);
}
bottomCorners.clear();
bottomCorners.resize(2);
for (auto p : contours[biggestContourIdx])
{
    for (int i = 0; i < distances.size(); ++i)
    {
        auto dist = cv::norm(p - bottomRectCorners[i]);
        if (dist < distances[i])
        {
            distances[i] = dist;
            bottomCorners[i] = p;
        }
    }
}

Results of both approaches: red - first method, green second 两种方法的结果

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM