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?

karlphillip
  • 92,053
  • 36
  • 243
  • 426
Marco L.
  • 1,489
  • 4
  • 17
  • 25

1 Answers1

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;
}
karlphillip
  • 92,053
  • 36
  • 243
  • 426
  • while this trick works perfectly with that sample image (http://img12.imageshack.us/img12/4104/26725680.jpg), if I try with a different image (like http://img202.imageshack.us/img202/9962/66647480.jpg but it happens with MANY other input images) I continue to receive the same exception... – Marco L. Oct 13 '11 at 20:33
  • Moreover, with other images (http://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 cvGetMat` – Marco L. Oct 13 '11 at 20:34
  • 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. – karlphillip Oct 14 '11 at 18:13
  • @Marco I updated the answer and hopefully made it a little more obvious now. – karlphillip Oct 14 '11 at 20:02
  • 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... – Marco L. Oct 15 '11 at 01:02
  • 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. – karlphillip Oct 15 '11 at 02:36
  • 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 wrong – Marco L. Oct 16 '11 at 22:26
  • 1
    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. – karlphillip Oct 17 '11 at 02:34
  • 1
    @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. – karlphillip Oct 17 '11 at 18:07
  • 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? – Marco L. Oct 17 '11 at 20:19
  • 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. – karlphillip Oct 17 '11 at 21:06
  • 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 black – Marco L. Oct 17 '11 at 21:59