OpenCV extract area of an image from a vector of squares
Asked Answered
F

1

4

I have an image that contains a square, and I need to extract the area contained in that square. After applying the squares.c script (available in the samples of every OpenCV distribution) I obtain a vector of squares, then I need to save an image for each of them.

The user karlphillip suggested this:

for (size_t x = 0; x < squares.size(); x++) 
{
    Rect roi(squares[x][0].x, squares[x][0].y, 
             squares[x][1].x - squares[x][0].x, 
             squares[x][3].y - squares[x][0].y);
    Mat subimage(image, roi);
}

in order to generate a new Mat called subimage for all the squares detected in the original image

As karl remembered me, the points detected in the image may not represent a perfect square (as you can see in the image above) but the code I just suggested to you assumes they do.

In fact I get this error:

OpenCV Error: Assertion failed (0 <= roi.x && 0 <= roi.width &&
      roi.x + roi.width <= m.cols && 0 <= roi.y && 0 <= roi.height &&
      roi.y + roi.height <= m.rows) in Mat, file /usr/include/opencv/cxmat.hpp, 
      line 187

terminate called after throwing an instance of 'cv::Exception'
what():  /usr/include/opencv/cxmat.hpp:187: error: (-215) 0 <= roi.x && 
       0 <= roi.width && roi.x + roi.width <= m.cols && 0 <= roi.y &&
       0 <= roi.height && roi.y + roi.height <= m.rows in function Mat

Aborted

Suggestion for make the script accept also non perfect squares?

Flite answered 13/10, 2011 at 14:25 Comment(0)
B
11

I feel like I need to clarify a few things about that code.

First, it assumes that the region detected is a perfect square because it ignores some of the points inside squares[x] to create a new Mat.

Second, it also assumes that the points that make the region were detected in the clockwise direction, starting with p0 in the top-left corner of the image:

(p0)  1st----2nd  (p1)
       |      |
       |      |
(p3)  4th----3rd  (p2)

which might not be true for all the regions detected. That means that this code:

Rect roi(squares[x][0].x, squares[x][0].y, 
         squares[x][1].x - squares[x][0].x, 
         squares[x][3].y - squares[x][0].y);

probably will generate a ROI with invalid dimensions, such as negative width and height values, and that's why OpenCV throws a cv::Exception at you on Mat subimage(image, roi);.

What you should do, is write a code that will identify the top-left point of the region and call it p0, then it's nearest neightbor on the right side, p1, then find the bottom-right point of the region and call it p2, and then what's left is p3. After this, assembling the ROI is easy:

Rect roi(p0.x, p0.y, 
         p1.x - p0.x, 
         p3.y - p0.y);

EDIT:

I found an excellent solution while reading the documentation of the v2.3 of OpenCV. It automates the process I described earlier and it make things so much easier and clean. You can use this trick to order the 4 Points in the vector to a meaningful Rect structure:

// Data returned and filled by findSquares(). Check the example squares.cpp for more info on this function.
vector<vector<Point> > squares;

for (size_t i = 0; i < squares.size(); i++)
{
    Rect rectangle = boundingRect(Mat(squares[i]));
    cout << "#" << i << " rectangle x:" << rectangle.x << " y:" << rectangle.y << " " << rectangle.width << "x" << rectangle.height << endl;
}
Blalock answered 13/10, 2011 at 14:49 Comment(12)
while this trick works perfectly with that sample image (img12.imageshack.us/img12/4104/26725680.jpg), if I try with a different image (like img202.imageshack.us/img202/9962/66647480.jpg but it happens with MANY other input images) I continue to receive the same exception...Flite
Moreover, with other images (img401.imageshack.us/img401/9792/64416141.jpg) I get: libpng warning: Image width is zero in IHDR libpng error: Invalid IHDR data OpenCV Error: Bad flag (parameter or structure field) (Unrecognized or unsupported array type) in cvGetMat Error: (-206) Unrecognized or unsupported array type in function cvGetMatFlite
The line that causes the problem is Mat subimage(image, roi);, and the problem is that the dimensions of the detected square (aka. roi) doesn't seem to be right. You need to print these coordinates to the screen and see if they really make sense, because OpenCV thinks they don't.Blalock
@Marco I updated the answer and hopefully made it a little more obvious now.Blalock
I understand the concept...so if I'm not wrong I could take every square, "playing" with the indexes i,j of squares[i][j] and check which of one is the upper left corner and so on...Flite
Humm, in squares[i][j], i will represent one square in the vector of squares and j will be used as index to set of points that make a square, ranging from 0 to 3. If you understood this, then you are right.Blalock
Ok, I'm debugging in this way: printf( "%d", squares[x][i].k ); with i ranging from 0 to 3; and for each i, k varies between x and y. So I obtain this result: [x][0].x = 14 [x][0].y = 89 [x][1].x = 14 [x][1].y = 89 [x][2].x = 14 [x][2].y = 89 [x][3].x = 14 [x][3].y = 89 There is something wrongFlite
You should print all the values of X as well, and take a look at ALL the coordinates detected for ALL squares. Maybe something weird happened during the detection and you are looking at an isolated case. Anyway, what I explained in my answer is correct and from now on it's up to you and your debugging skills.Blalock
@Marco By the way, I just updated the answer with the code that is going to spare you a lot of trouble. It automates the process of identifying Points and organizes them into a Rect structure. This was found on the docs of OpenCV 2.3. That's one more reason for me to stop using the online documentation they have there (which is for the v2.1) and start using the one that ships with OpenCV 2.3.x. I guess now you can accept my answer as the official one.Blalock
thank you so much karl, I really appreciate your help... now it's only matter of improve the results (cause up to now the script detect only few squares)...do you have any advise to give me? in your opinion what part could be improved?Flite
A big secret of the success of this demo is to make sure the Canny result shows all the edges of the object you are looking for. Everything you choose to do needs to be aimed at this single purpose: enhance canny detection. So, add a proper debug, maybe a call to imshow() after Canny() or save the result to the disk for further analysis, and play with the parameters of Canny to improve the detection, or add/remove filters like I did with medianBlur() and dilate(). For instance, if the square you are looking for doesn't show on Canny, it may help if you blur it a little more.Blalock
removing the medianBlur causes a clearer output of Canny, infact it shows a well defined square in every test photo...but then from this to the point in which I extract the image these squares get lost: if I call one imshow after the findContours, it shows an image almost totally blackFlite

© 2022 - 2024 — McMap. All rights reserved.