Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OpenCV C++ - Rectangle detection which has irregular side

enter image description here

Hi.. I have problem with rectangle detection which has irregular side (not straight) like figure above. actually with method houghline can detect lines on the rectangle with some parameter configuration. After compute intersect and get 4 corner, I can rotate it to normal position.

But if I change the image with another rectangle (different size and still has irregular side), I need to reconfigure the parameters again. This is because the line is not detected on four sides, beside that the line can be more than 4.

is there any other method beside houghline which is more simple (does not require reconfiguration / difficult configuration)?

like image 231
stranger Avatar asked Oct 27 '14 08:10

stranger


2 Answers

Try this:

1.Run findCountours on the image.

2.Apply approxPolyDP to approximate the contour to a rectangle. The contour sides will be a lot more regular.

3.Segment the rectangular contours using moments and/or geometry.

like image 20
Vasanth Avatar answered Sep 28 '22 10:09

Vasanth


This way is to compute the rotated rectangle that holds all your rectangle pixels.

Maybe you can combine that with vasanth's answer, so you can first approximate the polynome to get a regular border and afterwards extract the rotated rectangle with cv::minAreaRect

Here's my code:

int main()
{
    cv::Mat input = cv::imread("../inputData/RotatedRect.png");

    // convert to grayscale (you could load as grayscale instead)
    cv::Mat gray;
    cv::cvtColor(input,gray, CV_BGR2GRAY);

    // compute mask (you could use a simple threshold if the image is always as good as the one you provided)
    cv::Mat mask;
    cv::threshold(gray, mask, 0, 255, CV_THRESH_BINARY_INV | CV_THRESH_OTSU);

    // find contours (if always so easy to segment as your image, you could just add the black/rect pixels to a vector)
    std::vector<std::vector<cv::Point>> contours;
    std::vector<cv::Vec4i> hierarchy;
    cv::findContours(mask,contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

    /// Draw contours and find biggest contour (if there are other contours in the image, we assume the biggest one is the desired rect)
    // drawing here is only for demonstration!
    int biggestContourIdx = -1;
    float biggestContourArea = 0;
    cv::Mat drawing = cv::Mat::zeros( mask.size(), CV_8UC3 );
    for( int i = 0; i< contours.size(); i++ )
    {
        cv::Scalar color = cv::Scalar(0, 100, 0);
        drawContours( drawing, contours, i, color, 1, 8, hierarchy, 0, cv::Point() );

        float ctArea= cv::contourArea(contours[i]);
        if(ctArea > biggestContourArea)
        {
            biggestContourArea = ctArea;
            biggestContourIdx = i;
        }
    }

    // if no contour found
    if(biggestContourIdx < 0)
    {
        std::cout << "no contour found" << std::endl;
        return 1;
    }

    // compute the rotated bounding rect of the biggest contour! (this is the part that does what you want/need)
    cv::RotatedRect boundingBox = cv::minAreaRect(contours[biggestContourIdx]);
    // one thing to remark: this will compute the OUTER boundary box, so maybe you have to erode/dilate if you want something between the ragged lines



    // draw the rotated rect
    cv::Point2f corners[4];
    boundingBox.points(corners);
    cv::line(drawing, corners[0], corners[1], cv::Scalar(255,255,255));
    cv::line(drawing, corners[1], corners[2], cv::Scalar(255,255,255));
    cv::line(drawing, corners[2], corners[3], cv::Scalar(255,255,255));
    cv::line(drawing, corners[3], corners[0], cv::Scalar(255,255,255));

    // display
    cv::imshow("input", input);
    cv::imshow("drawing", drawing);
    cv::waitKey(0);

    cv::imwrite("rotatedRect.png",drawing);

    return 0;
}

giving this result:

enter image description here

like image 75
Micka Avatar answered Sep 28 '22 12:09

Micka