OpenCV 计算物体外切正矩形

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);
}

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值