这是使用OpenCV的minAreaRect函数的方法。它是用C++编写的,但你可能很容易适应,因为几乎只使用了OpenCV函数。
cv::Mat input = cv::imread("../inputData/rectangles.png");
cv::Mat gray;
cv::cvtColor(input,gray,CV_BGR2GRAY);
int threshold = 200;
cv::Mat mask = gray > threshold;
cv::imshow("mask", mask);
std::vector<std::vector<cv::Point> > contours;
cv::findContours(mask, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
for(int i=0; i<contours.size(); ++i)
{
cv::RotatedRect rotatedRect = cv::minAreaRect(contours[i]);
cv::Point2f rect_points[4];
rotatedRect.points( rect_points );
float angle = rotatedRect.angle;
cv::Point2f center = rotatedRect.center;
for(unsigned int j=0; j<4; ++j)
cv::line(input, rect_points[j], rect_points[(j+1)%4], cv::Scalar(0,255,0));
std::stringstream ss; ss << angle;
cv::circle(input, center, 5, cv::Scalar(0,255,0));
cv::putText(input, ss.str(), center + cv::Point2f(-25,25), cv::FONT_HERSHEY_COMPLEX_SMALL, 1, cv::Scalar(255,0,255));
}
导致了这张图片的产生:
如你所见,角度可能不是你想要的(因为它们随机使用较长或较短的线作为参考)。
相反,你可以提取矩形的较长边并手动计算角度。
如果你选择旋转矩形的较长边并从中计算角度,结果会是这样的:
cv::Point2f edge1 = cv::Vec2f(rect_points[1].x, rect_points[1].y) - cv::Vec2f(rect_points[0].x, rect_points[0].y);
cv::Point2f edge2 = cv::Vec2f(rect_points[2].x, rect_points[2].y) - cv::Vec2f(rect_points[1].x, rect_points[1].y);
cv::Point2f usedEdge = edge1;
if(cv::norm(edge2) > cv::norm(edge1))
{
usedEdge = edge2;
}
cv::Point2f reference = cv::Vec2f(1,0);
angle = 180.0f/CV_PI * acos((reference.x*usedEdge.x + reference.y*usedEdge.y) / (cv::norm(reference) *cv::norm(usedEdge)));
给出这个结果,应该是你在寻找的!
编辑:看起来楼主没有使用他发布的输入图像,因为参考矩形的中心会落在图像之外。
使用这个输入(手动重新缩放但可能仍不是最佳选择):
我得到了这些结果(蓝点是由操作提供的参考矩形中心):
与检测结果进行比较:
reference (x,y,angle) detection (x,y,angle)
(320,240,0) (320, 240, 180) // angle 180 is equal to angle 0 for lines
(75,175,90) (73.5, 174.5, 90)
(279,401,170) (279.002, 401.824, 169.992)
(507,379,61) (507.842, 379.75, 61.1443)
(545,95,135) (545.75, 94.25, 135)
(307,79,37) (306.756, 77.8384, 37.1042)
我很想看到真正的输入图像,也许结果会更好。