cv::Point2f GetPointAfterRotate(cv::Point2f InputPt, cv::Point2f center, float angle)
{
cv::Point2f preturn;
preturn.x = (InputPt.x - center.x)*cos(-angle) - (InputPt.y - center.y)*sin(-angle) + center.x;
preturn.y = (InputPt.x - center.x)*sin(-angle) + (InputPt.y - center.y)*cos(-angle) + center.y;
return preturn;
}
float getOirentation(std::vector<cv::Point> & pts, cv::Point2f & pos, cv::Mat & img)
{
//Construct a buffer used by the pca analysis
cv::Mat data_pts = cv::Mat(pts.size(), 2, CV_32FC1);
for (int i = 0; i < data_pts.rows; ++i)
{
data_pts.at<float>(i, 0) = pts[i].x;
data_pts.at<float>(i, 1) = pts[i].y;
}
//Perform PCA analysis
cv::PCA pca_analysis(data_pts, cv::Mat(), CV_PCA_DATA_AS_ROW);
//Store the position of the object
pos = cv::Point2f(pca_analysis.mean.at<float>(0, 0),
pca_analysis.mean.at<float>(0, 1));
//Store the eigenvalues and eigenvectors
std::vector<cv::Point2f> eigen_vecs(2);
std::vector<float> eigen_val(2);
for (int i = 0; i < 2; ++i)
{
eigen_vecs[i] = cv::Point2d(pca_analysis.eigenvectors.at<float>(i, 0),
pca_analysis.eigenvectors.at<float>(i, 1));
eigen_val[i] = pca_analysis.eigenvalues.at<float>(i, 0);
}
// Draw the principal components
//在轮廓/图像中点绘制小圆
//circle(img, pos, 3, CV_RGB(255, 0, 255), 2);
计算出直线,在主要方向上绘制直线
//line(img, pos, pos + 0.02 * Point2f(eigen_vecs[0].x * eigen_val[0], eigen_vecs[0].y * eigen_val[0]) , CV_RGB(255, 255, 0));
//line(img, pos, pos + 0.02 * Point2f(eigen_vecs[1].x * eigen_val[1], eigen_vecs[1].y * eigen_val[1]) , CV_RGB(0, 255, 255));
return atan2(eigen_vecs[0].y, eigen_vecs[0].x);
}
// 应该是绘制结果与处理分开,绘制结果中记得利用 fFactor 反向计算坐标
void FindMinRect(cv::Mat & cvInImgGray, std::vector<std::vector<cv::Point2i> > & OutContours, std::vector<cv::RotatedRect> & rotateRects)
{
cv::Mat cvImgGray;
cv::Mat cvImgGray2;
float fFactor = 1.0;
if (std::max(cvInImgGray.cols, cvInImgGray.rows) > 500)
{
fFactor = 850.0 / std::max(cvInImgGray.cols, cvInImgGray.rows);
cv::resize(cvInImgGray, cvImgGray, cvSize(0,0), fFactor, fFactor);
}
else
{
cvInImgGray.copyTo(cvImgGray);
}
cvInImgGray.copyTo(cvImgGray2);
// 要求必须是二值图片
if (cvImgGray.type() != 0)
{
cv::cvtColor(cvImgGray, cvImgGray, cv::COLOR_BGR2GRAY);
}
//阈值处理
cv::threshold(cvImgGray, cvImgGray, 150, 255, CV_THRESH_BINARY);
//寻找轮廓
std::vector<std::vector<cv::Point2i> > contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(cvImgGray, contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
//轮廓分析,找到
for (size_t i = 0; i < contours.size(); ++i)
{
//计算轮廓大小
double area = contourArea(contours[i]);
//去除过小或者过大的轮廓区域(科学计数法表示)
if (area < 1e2 || 1e5 < area) continue;
//获得轮廓的角度
cv::Point2f pos ;
float dOrient = getOirentation(contours[i], pos, cvImgGray);
//得到原图轮廓
std::vector<cv::Point2i> Pts;
for (int j = 0; j < contours[i].size(); j++)
{
cv::Point2f Pt;
Pt.x = float(contours[i][j].x) / fFactor;
Pt.y = float(contours[i][j].y) / fFactor;
Pts.push_back(Pt);
}
OutContours.push_back(Pts);
//转换轮廓,并获得极值
for (size_t j = 0; j<contours[i].size(); j++)
contours[i][j] = GetPointAfterRotate(contours[i][j], (cv::Point)pos, dOrient);
//轮廓最小外接矩形
cv::Rect RectMin = boundingRect(contours[i]);
// 计算矩形几何中心
cv::Point2f rectPtCent = cv::Point2f(0,0);
cv::Point2f rectPts[4];
rectPts[0] = RectMin.tl();
rectPts[1] = RectMin.tl() + cv::Point2i(RectMin.width, 0);
rectPts[2] = RectMin.tl() + cv::Point2i(0, RectMin.height);
rectPts[3] = RectMin.br();
for (size_t j = 0; j<4; j++)
rectPtCent += GetPointAfterRotate(rectPts[j], pos, -dOrient); // 得到旋转后的矩形四个坐标点
rectPtCent.x /= 4;
rectPtCent.y /= 4;
// 得到旋转矩形
cv::RotatedRect rotateRect = cv::RotatedRect(rectPtCent, RectMin.size(), dOrient/3.1415926*180);
// 得到原图旋转矩形
rectPtCent.x /= fFactor;
rectPtCent.y /= fFactor;
cv::Size sizeRect = RectMin.size();
sizeRect.width /= fFactor;
sizeRect.height /= fFactor;
cv::RotatedRect rotateRectBig = cv::RotatedRect(rectPtCent, sizeRect, dOrient / 3.1415926 * 180);
rotateRects.push_back(rotateRectBig);
//绘制轮廓
//drawContours(cvImgGray2, OutContours, -1, CV_RGB(255, 0, 0), 2, 8/*, hierarchy, 0*/);
//绘制矩形
//cv::Point2f rect_points3[4];
//rotateRectBig.points(rect_points3);
//for (size_t j = 0; j < 4; j++)
//{
// cv::Point2f pt1 = rect_points3[j];
// cv::Point2f pt2 = rect_points3[(j + 1) % 4];
// cv::line(cvImgGray2, pt1, pt2, cv::Scalar(0, 255, 255), 2);
//}
//得出结果
//char cbuf[255];
//double fshort = std::min(RectMin.width, RectMin.height);
//double flong = std::max(RectMin.width, RectMin.height);
//sprintf_s(cbuf, "第%d个轮廓,长度%.2f,宽度%.2f像素\n", i, flong, fshort);
//std::cout << cbuf << std::endl;
//cv::namedWindow("img", cv::WINDOW_NORMAL);
//cv::imshow("img", cvImgGray2);
//cv::waitKey();
}
}
int main(int argc, char** argv)
{
//读入图像,转换为灰度
Mat img = imread("1.png");
cMyFunc func;
std::vector<std::vector<cv::Point2i> > OutContours;
std::vector<cv::RotatedRect> rotateRects;
func.FindMinRect(img, OutContours, rotateRects);
// 绘制结果
Mat matDraw;
img.copyTo(matDraw);
cv::RNG rng(12345);
for (int i = 0; i < rotateRects.size(); i++)
{
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
drawContours(matDraw, OutContours, i, color, 2);
cv::Point2f rect_points3[4];
rotateRects[i].points(rect_points3);
for (size_t j = 0; j < 4; j++)
{
cv::Point2f pt1 = rect_points3[j];
cv::Point2f pt2 = rect_points3[(j + 1) % 4];
cv::line(matDraw, pt1, pt2, color, 2);
}
}
imwrite("matDraw.jpg", matDraw);
}