Bug in RotatedRect::boundingRect() method (Feature #3396)


Added by Hamed Habibi Aghdam over 11 years ago. Updated over 9 years ago.


Status:Open Start date:2013-11-25
Priority:Low Due date:
Assignee:- % Done:

50%

Category:core
Target version:2.4.10 Estimated time:0.50 hour
Difficulty:Easy Pull request:

Description

I tried to get the smallest bounding box for a rotated rectangle. However, the result of RotatedRect::boundingRect() method is not correct all time and in some cases it return rectangles much bigger than the original one.

I checked the source code for of this method. There is a mathematical solution to this problem. I implemented and tested it. Given the major and minor axis as well as the amount of the rotation of the RotatedRect, the smallest bounding box can be calculated as follow:

///////////////////////////////////////////////////////////////////

float t1 = atan(-(majorAxe*sin(degree))/(minorAxe*cos(degree)));
float t2 = t1 + 3.1415;

float maxX = x + majorAxe*cos(t1)*cos(degree)-minorAxe*sin(t1)*sin(degree);
float minX = x + majorAxe*cos(t2)*cos(degree)-minorAxe*sin(t2)*sin(degree);

t1 = atan((minorAxe*cos(degree))/(majorAxe*sin(degree)));
t2 = t1 + 3.1415;

float maxY = y + minorAxe*sin(t1)*cos(degree)+majorAxe*cos(t1)*sin(degree);
float minY = y + minorAxe*sin(t2)*cos(degree)+majorAxe*cos(t2)*sin(degree);
if (minY > maxY) {
float temp = minY;
minY = maxY;
maxY = temp;
}
if (minX > maxX) {
float temp = minX;
minX = maxX;
maxX = temp;
}

Rect r(minX,minY,maxX-minX,maxY-minY);

///////////////////////////////////////////////////////
To speed up the computations, the code can be optimized to avoid redundant sin and cos calls. Also, the code can be more optimized by utilizing simple Trigonometric equations. So the more optimized code can be as follows:

float c_degree = cos(degree);
float s_degree = sin(degree);
float t1 = atan(-(majorAxe*s_degree)/(minorAxe*c_degree));
float c_t1 = cos(t1);
float s_t1 = sin(t1);
float w1 = majorAxe*c_t1*c_degree;
float w2 = minorAxe*s_t1*s_degree;
float maxX = x + w1-w2;
float minX = x - w1+w2;

t1 = atan((minorAxe*c_degree)/(majorAxe*s_degree));
c_t1 = cos(t1);
s_t1 = sin(t1);

w1 = minorAxe*s_t1*c_degree;
w2 = majorAxe*c_t1*s_degree;
float maxY = y + w1+w2;
float minY = y - w1-w2;

if (minY > maxY) {
float temp = minY;
minY = maxY;
maxY = temp;
}

if (minX > maxX) {
float temp = minX;
minX = maxX;
maxX = temp;
}

Rect r(minX,minY,maxX-minX,maxY-minY);


Associated revisions

Revision ff095bbb
Added by Alexander Alekhin over 10 years ago

Merge pull request #3396 from akarsakov:ocl_pow_fix

History

Updated by Hamed Habibi Aghdam over 11 years ago

I changed the opencv code and tested it. it works:

Rect RotatedRect::boundingRect() const {
float degree = angle*3.1415/180;
float majorAxe = this->size.width/2;
float minorAxe = this->size.height/2;
float x = this->center.x;
float y =this->center.y;
float c_degree = cos(degree);
float s_degree = sin(degree);
float t1 = atan(-(majorAxe*s_degree)/(minorAxe*c_degree));
float c_t1 = cos(t1);
float s_t1 = sin(t1);
float w1 = majorAxe*c_t1*c_degree;
float w2 = minorAxe*s_t1*s_degree;
float maxX = x + w1-w2;
float minX = x - w1+w2;
/*#ifdef _DEBUG
printf("major=%f minor=%f x=%f y=%f deg=%f",majorAxe,minorAxe,x,y,degree);
#endif*/
t1 = atan((minorAxe*c_degree)/(majorAxe*s_degree));
c_t1 = cos(t1);
s_t1 = sin(t1);
w1 = minorAxe*s_t1*c_degree;
w2 = majorAxe*c_t1*s_degree;
float maxY = y + w1+w2;
float minY = y - w1-w2;
if (minY > maxY) {
float temp = minY;
minY = maxY;
maxY = temp;
}
if (minX > maxX) {
float temp = minX;
minX = maxX;
maxX = temp;
}
Rect r(minX,minY,maxX-minX+1,maxY-minY+1);
return r;
}

Updated by Steven Puttemans over 11 years ago

Are you up for submitting a pull request? It will help the adaptation of the repository alot! Some hints on how to do so can be found here: code.opencv.org/projects/opencv/wiki/How_to_contribute! By submitting a pull request, the review process becomes more easier and build problems on different operating systems can be tested.

If you are not up for it, then give a shout so that someone can pick up your work and submit a pull.

  • Assignee set to Hamed Habibi Aghdam
  • % Done changed from 0 to 50
  • Target version set to 2.4.8
  • Status changed from New to Open

Updated by Daniil Osokin over 11 years ago

  • Category set to core

Updated by Alexander Smorkalov about 11 years ago

  • Target version changed from 2.4.8 to 2.4.9

Updated by Daniil Osokin about 11 years ago

Hamed, please, attach a test sample with wrong bounding rect, otherwise this ticket will be closed.

  • Affected version changed from 2.4.7 (latest release) to 2.4.8 (latest release)

Updated by Hamed Habibi Aghdam about 11 years ago

Daniil,

I just saw your message. I will attach a picture of the wrong bounding rect in the next 4 days.

Regards,

Updated by Hamed Habibi Aghdam about 11 years ago

Using following:

Mat mm = Mat::zeros(220,220,CV_8UC1);
RotatedRect rect2(Point(101.33,109.84),Size2f(177.04,109.25),73.71);

Rect r = rect2.boundingRect();
ellipse(mm,rect2,Scalar(255,255,255),2);
rectangle(mm,r,Scalar(255,255,255),2);
imshow("test",mm);
waitKey(0);

We get the following result:

However, after I changed the source code to the one that I mentioned before we can get the following result:

You can also test the previous code and the modified code on following RotatedRect:

RotatedRect rect1(Point(144.52,89.47),Size2f(186.35,138.56),68.28);

Updated by Alexander Smorkalov almost 11 years ago

  • Target version changed from 2.4.9 to 2.4.10

Updated by Vadim Pisarevsky almost 10 years ago

RotatedRect::boundingBox is (as the name implies) finds bounding box for a rotated rectangle, not for a rotated ellipse. The proposed algorithm should be put into a separate function. I change it from bug to a feature request.

  • Tracker changed from Bug to Feature
  • Priority changed from Normal to Low
  • Assignee deleted (Hamed Habibi Aghdam)

Updated by Maksim Shabunin over 9 years ago

Issue has been transferred to GitHub: https://github.com/Itseez/opencv/issues/4651

Also available in: Atom PDF