Question

I am willing to get a plain rectangle out of a warped Image.

For example, once I have this sort of Image: enter image description here

... I would like to crop the area correspondent to the following rectangle: enter image description here

... BUT my code is extracting this bigger frame: enter image description here

MY CODE IS SHOWN bellow:

int main(int argc, char** argv) {
    cv::Mat img = cv::imread(argv[1]);

    // Convert RGB Mat to GRAY
    cv::Mat gray;
    cv::cvtColor(img, gray, CV_BGR2GRAY);

    // Store the set of points in the image before assembling the bounding box
    std::vector<cv::Point> points;
    cv::Mat_<uchar>::iterator it = gray.begin<uchar>();
    cv::Mat_<uchar>::iterator end = gray.end<uchar>();
    for (; it != end; ++it) {
        if (*it)
            points.push_back(it.pos());
    }

    // Compute minimal bounding box
    Rect box = cv::boundingRect(cv::Mat(points));

    // Draw bounding box in the original image (debug purposes)
    cv::Point2f vertices[4];
    vertices[0] = Point2f(box.x, box.y  +box.height);
    vertices[1] = Point2f(box.x, box.y);
    vertices[2] = Point2f(box.x+ box.width, box.y);
    vertices[3] = Point2f(box.x+ box.width, box.y   +box.height);

    for (int i = 0; i < 4; ++i) {
        cv::line(img, vertices[i], vertices[(i + 1) % 4], cv::Scalar(0, 255, 0), 1, CV_AA);
        cout << "==== vertices (x, y) = " << vertices[i].x << ", " << vertices[i].y << endl;
    }

    cv::imshow("box", img);
    cv::imwrite("box.png", img);

    waitKey(0);

    return 0;
}

Any ideas on how to find the rhombus corners and try to reduce them to a smaller rectangle?

Was it helpful?

Solution

The most difficult part of this problem is actually finding the locations of the rhombus corners. If the images in your actual usage are much different from your example, this particular procedure for finding the rhombus corners may not work. Once this is achieved, you can sort the corner points by their distance from the center of the image. You are looking for points closest to the image center.

First, you must define a functor for the sort comparison (this could be a lambda if you can use C++11):

struct CenterDistance
{
    CenterDistance(cv::Point2f pt) : center(pt){}

    template <typename T> bool operator() (cv::Point_<T> p1, cv::Point_<T> p2) const
    {
        return cv::norm(p1-center) < cv::norm(p2-center);
    }

    cv::Point2f center;
};

This doesn't actually need to be a templated operator(), but it makes it work for any cv::Point_ type.

For your example image, the image corners are very well defined, so you can use a corner detector like FAST. Then, you can use cv::convexHull() to get the exterior points, which should be only the rhombus corners.

int main(int argc, char** argv) {
    cv::Mat img = cv::imread(argv[1]);

    // Convert RGB Mat to GRAY
    cv::Mat gray;
    cv::cvtColor(img, gray, CV_BGR2GRAY);

    // Detect image corners
    std::vector<cv::KeyPoint> kpts;
    cv::FAST(gray, kpts, 50);

    std::vector<cv::Point2f> points;
    cv::KeyPoint::convert(kpts, points);
    cv::convexHull(points, points);

    cv::Point2f center(img.size().width / 2.f, img.size().height / 2.f);
    CenterDistance centerDistance(center);
    std::sort(points.begin(), points.end(), centerDistance);

    //The two points with minimum distance are what we want
    cv::rectangle(img, points[0], points[1], cv::Scalar(0,255,0));

    cv::imshow("box", img);
    cv::imwrite("box.png", img);

    cv::waitKey(0);

    return 0;
}

Note that you can use cv::rectangle() instead of constructing the drawn rectangle from lines. The result is:

Box result

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top