void SegMeasure::measure2(cv::Mat &mask, cv::Mat &img)
{
// 1、原图画一个分割的绿色边界,轮廓排序
mask.convertTo(mask, CV_8U);
std::vector<std::vector<cv::Point>> contours;
findsortContours(mask, contours, cv::CHAIN_APPROX_NONE);
if (contours.size() < 1)
return;
cv::drawContours(img, contours, 0, cv::Scalar(0, 255, 0), 1); //从大到小排序轮廓
// 2、计算面积最大的轮廓外接矩形,返回的排序从大到小,不用排序了
std::vector<cv::Point> max_contour = contours[0]; //
// 计算最小外接矩形
cv::RotatedRect rect = cv::minAreaRect(max_contour);
cv::Point2f vertices[4];//vertices[0] = (100, 50);四个点,画图时候
rect.points(vertices);
// 在原图中画出矩形框和边界线
cv::Scalar color = cv::Scalar(0, 0, 255);
for (int i = 0; i < 4; i++) { //四个点,依次连接画直线,最小矩形
cv::line(img, vertices[i], vertices[(i + 1) % 4], color, 2);//连接最后的点时候3, (3+1)/4=0,和第一个相连
}
// 计算每条边的中点,默认顺时针或者逆时针,两个对点,哪个长哪个就是长轴的两个点
cv::Point2f midpoints[4];
midpoints[0] = (vertices[0] + vertices[1]) / 2;
midpoints[1] = (vertices[1] + vertices[2]) / 2;
midpoints[2] = (vertices[2] + vertices[3]) / 2;
midpoints[3] = (vertices[3] + vertices[0]) / 2;
// 确定中点坐标,边上的4个中点 是左右范围边界
float long_d = cv::norm(midpoints[0]- midpoints[2]);
float short_d = cv::norm(midpoints[1]- midpoints[3]);
cv::Point2f mid_short[2]; // 移动直线的左右边界坐标,通过中心点在两个中位线上的移动进行实现
cv::Point2f mid_long[2]; // 移动直线的左右边界坐标,通过中心点在两个中位线上的移动进行实现
mid_short[0] = midpoints[1]; //you x y
mid_short[1] = midpoints[3];
mid_long[0] = midpoints[0]; //you x y
mid_long[1] = midpoints[2];
if (long_d<short_d) {
mid_short[0] = midpoints[0];
mid_short[1] = midpoints[2];
mid_long[0] = midpoints[1];
mid_long[1] = midpoints[3];
}
// cv::line(img, mid_short[0], mid_short[1], cv::Scalar(255,255 , 0), 3);//是4个中点没错了
cv::line(img, mid_long[0], mid_long[1], cv::Scalar(255,255 , 0), 3);
// 3、计算最小矩形长宽方向的线段,矩形的中心点和长宽方向,来计算长宽方向的线段,斜率和截距,从而计算出线段的方程
// 计算长边和短边的线段长度
cv::Point2f center = rect.center; // 矩形中心
float angle = rect.angle; // 矩形的旋转角度
cv::Size2f rect_size = rect.size; // 矩形的尺寸
// 将矩形旋转角度限制在-45到45度范围内
if (angle < -45.0)
{
angle += 90.0;
std::swap(rect_size.width, rect_size.height);
}
// 3.1、最小矩形长的计算
float k_long = tan(angle * CV_PI / 180.0); // 长边的斜率,为tan(theta)
float b_long = center.y - k_long * center.x; // 长边的截距,为y-kx
cv::Point2f left_long_point(rect.center.x - rect_size.width / 2, k_long * (rect.center.x - rect_size.width / 2) + b_long);
cv::Point2f right_long_point(rect.center.x + rect_size.width / 2, k_long * (rect.center.x + rect_size.width / 2) + b_long);
float long_edge_length = cv::norm(right_long_point - left_long_point); // 计算长边线段长度
// cv::line(img, left_long_point, right_long_point, cv::Scalar(255,0 , 0), 2);
// 3.2、最小矩形宽的计算, 短轴的计算
float k = tan(angle * CV_PI / 180.0 + CV_PI / 2.0); //斜率
float b = center.y - k * center.x; //截距
cv::Point2f top(center.x - rect_size.height / 2.0 / fabs(k), center.y - rect_size.height / 2.0);
cv::Point2f bottom(center.x + rect_size.height / 2.0 / fabs(k), center.y + rect_size.height / 2.0);
float short_edge_length = cv::norm(right_long_point - left_long_point); // 计算长边线段长度
// cv::line(img, top, bottom, cv::Scalar(0, 0, 255), 2);// 限制在矩形框内的短轴方向直线,画的是中心点的坐标
// 关键是 找中心点,沿着短斜率平移,先得到矩形框内的多条直线进行遍历
// 短轴中心点左右移动,这个中心点在,长轴直线上,直接代新的x值就行了,最右边的短轴,中心点(x1+x2)/2
// 下面是画纵轴方向矩形框内多条直线
int l_left = int(mid_long[0].x);
int r_right = int(mid_long[1].x);
if (l_left>r_right){
int t = l_left;
l_left = r_right;
r_right = t;
}
// 大一点的线段等间隔画直线,小一点的的线段
float tt = r_right-l_left;
int interval = int(tt * 0.05); // 矩形框左右中心点,水平方向x 长度,从左到右,间隔10移动,这里间隔多少按照长度比例来0.05%, 这种处理小的采样多,大的采样少
if (tt<=60 && tt>3){
interval = 2;
}
else if(tt<=3){
int interval = 1;
}
std::cout<<l_left<<std::endl <<r_right<<std::endl;
int ii = l_left;
while( ii < r_right){
float new_center_x = ii;
float new_center_y = k_long *new_center_x +b_long;// x 带入长轴直线,得到 y ,得到两个点的坐标
// 将中心点,带入短轴的方向
cv::Point2f top(new_center_x - rect_size.height / 2.0 / fabs(k), new_center_y - rect_size.height / 2.0);
cv::Point2f bottom(new_center_x + rect_size.height / 2.0 / fabs(k), new_center_y + rect_size.height / 2.0);
// cv::line(img, top, bottom, cv::Scalar(255,255 , 0), 2); //每条矩形内部的线画出来
ii = ii + interval;