Antialiased polygon fill doesn't respect area borders

I want to draw a quadrilateral which vertices doesn't coincide with pixel boundaries. The simplest case is a square with all 4 vertices located exactly in the middle of pixels. Here is my code doing it:

cv::Point2f A(5.5,2.5), B(6.5, 2.5), C(6.5,3.5), D(5.5, 3.5);
cv::Point points[4] = {A, B, C, D};
const cv::Point *contours[1] = {points};
int lengths[1] = {4};

cv::Mat p(10, 10, CV_8U, cv::Scalar(0));
cv::fillPoly(p, contours, lengths, 1, cv::Scalar(100), CV_AA, 0);
std::cout << p << std::endl;


In the output image I expected 4 pixels with the value of 25%, i.e. total value of 100 (because the area of the square is exactly 1). But the real output looks like this:

[  0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
0,   0,   0,   0,   0,  19, 100,  22,   0,   0;
0,   0,   0,   0,   0,  36, 100,  40,   0,   0;
0,   0,   0,   0,   0,  20,  83,  22,   0,   0;
0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
0,   0,   0,   0,   0,   0,   0,   0,   0,   0]


Is this a bug in the library or do I use it in a wrong way? And how to draw a simple 1x1 antialiased square?

UPDATE: After removing CV_AA flag the output of

 cv::fillPoly(p, contours, lengths, 1, cv::Scalar(100));


looks like this:

[  0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
0,   0,   0,   0,   0,   0, 100,   0,   0,   0;
0,   0,   0,   0,   0,   0, 100,   0,   0,   0;
0,   0,   0,   0,   0,   0, 100,   0,   0,   0;
0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
0,   0,   0,   0,   0,   0,   0,   0,   0,   0]

edit retag close merge delete

I think that is filling with degradation, because of the CV_AA flag. But I do not understand why the (2,6) pixel is 100, too.

( 2014-10-29 05:59:48 -0600 )edit

I updated the posting: without CV_AA flag I have 3 full (100%) pixels instead of expected four pixels at 25% each.

( 2014-10-29 07:13:33 -0600 )edit
1. This is in debug?
2. Have you tried cv::fillPoly(p, contours, lengths, 0, cv::Scalar(100));? Normally, the first index is 0, not 1.
3. If you change the points to integer, is it the same? IMHO it should be 1 pixel or 4.
( 2014-10-29 08:31:16 -0600 )edit

1) Yes, I run debug version, should it be any difference?

2) I don't understand your item "2": of course I set 4th parameter of cv::fillPoly(...) to 1, not to 0, because the documentation says: "ncontours – Number of contours that bind the filled region", and I have ONE contour, not ZERO contours.

3) I removed ".5" from all the coordinates: cv::Point2f A(5,2), B(6, 2), C(6,3), D(5, 3); The result differs a bit, but still a very wrong one: 0, 10, 10, 0 // 10, 100, 100, 12 // 10, 85, 87, 12 // 0, 12, 12, 0 //

( 2014-10-29 09:48:38 -0600 )edit

Sort by » oldest newest most voted

This is because Point is Point2i, an int type, and your floats are rounded to ints implicitly in the constructor. In your case, all points are halfway between two ints, and the default rounds to the nearest even int. Thus the polygon actually being rendered is degenerate, and only touches 3 pixels:

[(6,2), (6,2), (6,4), (6,4)]


fillPoly won't take floats; however, you can use the shift parameter to get a similar effect.

Modifying your example to multiply the points by 2 (a bit shift of 1) when converting the Point2f array to Point2i:

cv::Point points[4] = { A*2, B*2, C*2, D*2 };


And then the fillPoly command looks like:

cv::fillPoly(p, contours, lengths, 1, cv::Scalar(100), 0, 1);


Resulting in:

[  0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
0,   0,   0,   0,   0,   0, 100, 100,   0,   0;
0,   0,   0,   0,   0,   0, 100, 100,   0,   0;
0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
0,   0,   0,   0,   0,   0,   0,   0,   0,   0]


and with CV_AA,

[  0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
0,   0,   0,   0,   0,   4,  10,   6,   0,   0;
0,   0,   0,   0,   0,  57, 100,  62,   0,   0;
0,   0,   0,   0,   0,  60,  87,  63,   0,   0;
0,   0,   0,   0,   0,   6,  12,   6,   0,   0;
0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
0,   0,   0,   0,   0,   0,   0,   0,   0,   0]


...still a weird blob, but closer to being proportional.

more

Ok, the problem is this one: the 4th parameter of fillPoly is the position of the contour to fill, which in your case is 0, because you have only one contour (for verification just put 2 contours, to see that you ar filling the second). Just change like this:

cv::fillPoly(p, contours, lengths, 0, cv::Scalar(100));


If you run under Debug, the initial values are all the same, so if you call the function for the element on position 1 you are attempting to fill the poly on the position "after last element" (because you have just one element). If you will run this in Release mode, you will always get different values and on different positions in the matrix. In C++ the first element is always on the position 0. You have seen that if you changed the contour values, you are getting new different values in matrix, this is because the element "after the last element" has changed. In fact you are not using vectors, to see that if you are calling the contours[contours.size()] (which in your case is contours[1]) you are getting an error. This is a problem of using pointers and arrays, please try to use vectors and other types of STL (like smart pointers) for better seeing the problem.

more

What? Why -1? Am I wrong about the index? in my case it always throwing exception if I am trying to give an index greater than the size of the vector...

( 2014-11-04 07:38:37 -0600 )edit

4th parameter to fillPoly is not index, it's the number of contours. Passing 0 will draw nothing.

( 2019-10-10 18:32:03 -0600 )edit

Stats

Asked: 2014-10-29 05:14:41 -0600

Seen: 1,569 times

Last updated: Oct 10 '19