Opencv3.2之简单项目:边缘检测、轮廓查找、轮廓筛选、绘制最小外接矩、尺寸测量和尺寸标注

版权声明:凡本人原创,转发请注明出处,谢谢! https://blog.csdn.net/qq_41248872/article/details/89203928

废话不多说,直接上栗子!

1.边缘检测、轮廓查找、轮廓筛选、绘制最小外接矩、尺寸测量和标注尺寸,代码如下: 

//开发环境:VS2015+OPENCV3.2

#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>

using namespace cv;
using namespace std;

#define  PHY_LEN   72.0      //参照物较长边边长的物理长度

//文本绘制
void PaintText(Mat& img, char* text, Point origin)
{
	int fontface = CV_FONT_HERSHEY_SIMPLEX;   //字体
	double fontscale = 0.5;                   //尺寸因子,值越大文字越大
	Scalar textcolor = Scalar(0, 0, 255);    //文本颜色
	int thickness = 2;  //线宽
	int linetype = 8;   //线型

						//int baseline;

						////获取文本框的长宽
						//Size text_size = getTextSize(text, fontface, fontscale, thickness, &baseline);

	putText(img, text, origin, fontface, fontscale, textcolor, thickness, linetype);
}

void fineMinAreaRect(Mat &threshold_output, Mat &src)
{
	bool flag = false;   //查找比例尺标志
	double  pp = 0.0;    //比例因子:每像素代表的实际物理尺寸        

	vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;
	//寻找轮廓,输入必须为二值图像
	findContours(threshold_output, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE, Point(0, 0)); //CV_CHAIN_APPROX_SIMPLE

																											  //第一轮筛选:移除过短的轮廓  
	int cmin = 100; //最小轮廓长度  
	int cmax = 5000; //最大轮廓长度
	vector<vector<Point>>::const_iterator itc = contours.begin();
	while (itc != contours.end())
	{
		if (itc->size() < cmin || itc->size() > cmax)
			itc = contours.erase(itc);
		else
			++itc;
	}

	//第二轮筛选:剔除面积小于指定阈值的轮廓
	//计算轮廓的面积  
	for (int i = 0; i < (int)contours.size(); i++)
	{
		double g_dConArea = fabs(contourArea(contours[i], true));
		cout << "轮廓面积: " << g_dConArea << endl;
	}

	//剔除小面积轮廓
	//vector <vector<Point>>::const_iterator itc = contours.begin();
	itc = contours.begin();
	for (; itc != contours.end();)
	{
		double g_dConArea = contourArea(*itc);
		if (g_dConArea < 200)
		{
			itc = contours.erase(itc);
		}
		else
		{
			++itc;
		}
	}

	//对每个找到的轮廓创建可倾斜的边界框
	vector<RotatedRect> minRect(contours.size());
	for (int i = 0; i < contours.size(); i++)
	{
		minRect[i] = minAreaRect(Mat(contours[i]));
	}

	//绘出轮廓及其可倾斜的边界框
	//Mat drawing = Mat::zeros(threshold_output.size(), CV_8UC3);

	//先遍历寻找参照物,计算出比例因子
	for (int i = 0; i < contours.size(); i++)
	{
		Point2f rect_points[4];
		minRect[i].points(rect_points);

		//寻找比例尺,四顶点均位于左半图
		if (!flag && rect_points[0].x < src.cols / 2 && rect_points[1].x < src.cols / 2 && rect_points[2].x < src.cols / 2
			&& rect_points[3].x < src.cols / 2)
		{
			flag = true;
			double length1 = sqrt(pow((rect_points[1].x - rect_points[0].x), 2) + pow((rect_points[1].y - rect_points[0].y), 2));
			double length2 = sqrt(pow((rect_points[2].x - rect_points[1].x), 2) + pow((rect_points[2].y - rect_points[1].y), 2));
			if (length1 <= length2) {
				pp = PHY_LEN / length2;
				break;
			}
			else {
				pp = PHY_LEN / length1;
				break;
			}
		}
	}

	double length;          //矩形边长
	Point paint_point;      //中点位置
	char text[20] = { 0 };  //文本字符串数组	

							//再遍历绘制外接矩形
	for (int i = 0; i< contours.size(); i++)
	{
		Scalar color = Scalar(255, 255, 255);  //白色轮廓线
											   //绘制轮廓
		drawContours(src, contours, i, color, 1, 8, vector<Vec4i>(), 0, Point());
		Point2f rect_points[4];
		minRect[i].points(rect_points);

		//for (int j = 0; j < 4; j++) {
		//	line(drawing, rect_points[j], rect_points[(j + 1) % 4], Scalar(0, 255, 0), 2, 8);  //绿色矩形框
		//}

		//分别绘制矩形的四条边	
		//第一条边
		line(src, rect_points[0], rect_points[1], Scalar(0, 255, 0), 2, 8);  //绿色矩形框		
		length = pp* (sqrt(pow((rect_points[1].x - rect_points[0].x), 2) + pow((rect_points[1].y - rect_points[0].y), 2)));  //计算长度
		sprintf(text, "%.3lfmm", length);   //转为字符串	

		paint_point.x = (rect_points[0].x + rect_points[1].x) / 2;    //寻找中点
		paint_point.y = (rect_points[0].y + rect_points[1].y) / 2;
		PaintText(src, text, paint_point);   //文本标注

											 //第二条边
		line(src, rect_points[1], rect_points[2], Scalar(255, 0, 0), 2, 8);  //蓝色矩形框
		length = pp* (sqrt(pow((rect_points[2].x - rect_points[1].x), 2) + pow((rect_points[2].y - rect_points[1].y), 2)));
		sprintf(text, "%.3lfmm", length);
		paint_point.x = (rect_points[1].x + rect_points[2].x) / 2;
		paint_point.y = (rect_points[1].y + rect_points[2].y) / 2;
		PaintText(src, text, paint_point);

		//第三、四条边
		line(src, rect_points[2], rect_points[3], Scalar(0, 255, 0), 2, 8);
		line(src, rect_points[3], rect_points[0], Scalar(255, 0, 0), 2, 8);

	}

	//结果显示
	imshow("测量结果", src);
}

int main()
{
	Mat src, src_gray, edge;

	src = imread("F:/opencv_workspace/img/greein33.jpg");  //加载 	
	resize(src, src, Size(src.cols / 2, src.rows / 2));  //等比例缩放,必须等比例缩放,否则尺寸错误
	imshow("原始图片", src);

	GaussianBlur(src, src, Size(3, 3), BORDER_DEFAULT);  //高斯模糊
														 //blur(src, src, Size(3, 3)); //均值滤波
														 //medianBlur(src, src, 3);   //中值滤波
														 //imshow("滤波效果", src);

														 //对比度增强(很重要,否则部分背光区域边缘轮廓无法找到)
	Mat kernel = (Mat_<int>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
	filter2D(src, src, -1, kernel, Point(-1, -1), 0);

	//定义核
	Mat element = getStructuringElement(MORPH_RECT, Size(7, 7));
	//进行形态学操作
	morphologyEx(src, src, MORPH_OPEN, element);   //开运算:可以用来消除小物体、在纤细点处分离物体、平滑较大物体的边界的同时并不明显改变其面积
	imshow("开运算", src);


	//int s = 3 * 2 + 1;
	//Mat structureElement = getStructuringElement(MORPH_RECT, Size(s, s), Point(-1, -1));
	//dilate(src, src, structureElement, Point(-1, -1), 1);   //膨胀											
	//erode(src, src, structureElement);    //腐蚀


	cvtColor(src, src_gray, CV_BGR2GRAY);  //转为灰度图像

	Canny(src_gray, edge, 30, 90, 3);  //边缘检测,得到的是二值图像
	imshow("边缘检测", edge);

    //形态学闭运算
    //进行形态学操作
    morphologyEx(edge, edge, MORPH_CLOSE, element);

	//threshold(src_gray, edge, 175, 255, CV_THRESH_BINARY);  //二值化
	//imshow("二值化效果", edge);	

	fineMinAreaRect(edge, src);  //寻找最小外接矩形

	waitKey(0);
	return(0);
}



运行效果:

 

2.同例1,只是将轮廓筛选改为了只保留最大面积的轮廓,代码如下:

#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>

using namespace cv;
using namespace std;

#define  PHY_LEN   114.0      //参照物较长边边长的物理长度

//文本绘制
void PaintText(Mat& img, char* text, Point origin)
{
	int fontface = CV_FONT_HERSHEY_SIMPLEX;   //字体
	double fontscale = 0.5;                   //尺寸因子,值越大文字越大
	Scalar textcolor = Scalar(0, 0, 255);    //文本颜色
	int thickness = 2;  //线宽
	int linetype = 8;   //线型

						//int baseline;

						////获取文本框的长宽
						//Size text_size = getTextSize(text, fontface, fontscale, thickness, &baseline);

	putText(img, text, origin, fontface, fontscale, textcolor, thickness, linetype);
}

void fineMinAreaRect(Mat &threshold_output, Mat &src)
{
	bool flag = false;   //查找比例尺标志
	double  pp = 0.0;    //比例因子:每像素代表的实际物理尺寸        

	vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;
	//寻找轮廓,输入必须为二值图像
	findContours(threshold_output, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE, Point(0, 0)); //CV_CHAIN_APPROX_SIMPLE

 //   //第一轮筛选:移除过短的轮廓  
	//int cmin = 100; //最小轮廓长度  
	//int cmax = 5000; //最大轮廓长度
	//vector<vector<Point>>::const_iterator itc = contours.begin();
	//while (itc != contours.end())
	//{
	//	if (itc->size() < cmin || itc->size() > cmax)
	//		itc = contours.erase(itc);
	//	else
	//		++itc;
	//}

	vector<vector<Point>>::const_iterator itc = contours.begin();
	//计算轮廓的面积  
	for (int i = 0; i < (int)contours.size(); i++)
	{
		double g_dConArea = fabs(contourArea(contours[i], true));
		cout << "轮廓面积: " << g_dConArea << endl;
	}

	//遍历查找最大面积轮廓
	itc = contours.begin();
	double MaxArea = fabs(contourArea(*itc));
	for (itc = contours.begin()+1; itc != contours.end();)
	{
		double g_dConArea = fabs(contourArea(*itc));

		if (g_dConArea > MaxArea)
		{
			MaxArea = g_dConArea;	
			itc = contours.erase(itc-1);
			itc++;
		}
		else {
			itc = contours.erase(itc);
		}
	}
	
	//查看检测到的最大轮廓面积
	for (int i = 0; i < (int)contours.size(); i++)
	{
		double max_dConArea = fabs(contourArea(contours[i], true));
		cout << "最大轮廓面积: " << max_dConArea << endl;
	}

	//对每个找到的轮廓创建可倾斜的边界框
	vector<RotatedRect> minRect(contours.size());
	for (int i = 0; i < contours.size(); i++)
	{
		minRect[i] = minAreaRect(Mat(contours[i]));
	}

	//绘出轮廓及其可倾斜的边界框
	//Mat drawing = Mat::zeros(threshold_output.size(), CV_8UC3);

	//先遍历寻找参照物,计算出比例因子
	for (int i = 0; i < contours.size(); i++)
	{
		Point2f rect_points[4];
		minRect[i].points(rect_points);

		//寻找比例尺,四顶点均位于左半图
		if (!flag && rect_points[0].x < src.cols / 2 && rect_points[1].x < src.cols / 2 && rect_points[2].x < src.cols / 2
			&& rect_points[3].x < src.cols / 2)
		{
			flag = true;
			double length1 = sqrt(pow((rect_points[1].x - rect_points[0].x), 2) + pow((rect_points[1].y - rect_points[0].y), 2));
			double length2 = sqrt(pow((rect_points[2].x - rect_points[1].x), 2) + pow((rect_points[2].y - rect_points[1].y), 2));
			if (length1 <= length2) {
				pp = PHY_LEN / length2;
				break;
			}
			else {
				pp = PHY_LEN / length1;
				break;
			}
		}
	}

	double length;          //矩形边长
	Point paint_point;      //中点位置
	char text[20] = { 0 };  //文本字符串数组	

							//再遍历绘制外接矩形
	for (int i = 0; i< contours.size(); i++)
	{
		Scalar color = Scalar(255, 255, 255);  //白色轮廓线
											   //绘制轮廓
		drawContours(src, contours, i, color, 1, 8, vector<Vec4i>(), 0, Point());
		Point2f rect_points[4];
		minRect[i].points(rect_points);

		//for (int j = 0; j < 4; j++) {
		//	line(drawing, rect_points[j], rect_points[(j + 1) % 4], Scalar(0, 255, 0), 2, 8);  //绿色矩形框
		//}

		//分别绘制矩形的四条边	
		//第一条边
		line(src, rect_points[0], rect_points[1], Scalar(0, 255, 0), 2, 8);  //绿色矩形框		
		length = pp* (sqrt(pow((rect_points[1].x - rect_points[0].x), 2) + pow((rect_points[1].y - rect_points[0].y), 2)));  //计算长度
		sprintf(text, "%.3lfmm", length);   //转为字符串	

		paint_point.x = (rect_points[0].x + rect_points[1].x) / 2;    //寻找中点
		paint_point.y = (rect_points[0].y + rect_points[1].y) / 2;
		PaintText(src, text, paint_point);   //文本标注

		//第二条边
		line(src, rect_points[1], rect_points[2], Scalar(255, 0, 0), 2, 8);  //蓝色矩形框
		length = pp* (sqrt(pow((rect_points[2].x - rect_points[1].x), 2) + pow((rect_points[2].y - rect_points[1].y), 2)));
		sprintf(text, "%.3lfmm", length);
		paint_point.x = (rect_points[1].x + rect_points[2].x) / 2;
		paint_point.y = (rect_points[1].y + rect_points[2].y) / 2;
		PaintText(src, text, paint_point);

		//第三、四条边
		line(src, rect_points[2], rect_points[3], Scalar(0, 255, 0), 2, 8);
		line(src, rect_points[3], rect_points[0], Scalar(255, 0, 0), 2, 8);

	}

	//结果显示
	imshow("测量结果", src);
}

int main()
{
	Mat src, src_gray, edge;

	src = imread("F:/opencv_workspace/img/m_img/m5.jpg");  //加载 	
	//resize(src, src, Size(src.cols / 2, src.rows / 2));  //等比例缩放,必须等比例缩放,否则尺寸错误
	imshow("原始图片", src);

	GaussianBlur(src, src, Size(3, 3), BORDER_DEFAULT);  //高斯模糊
	//blur(src, src, Size(3, 3)); //均值滤波
	//medianBlur(src, src, 3);   //中值滤波
	//imshow("滤波效果", src);

							   //对比度增强(很重要,否则部分背光区域边缘轮廓无法找到)
	Mat kernel = (Mat_<int>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
	filter2D(src, src, -1, kernel, Point(-1, -1), 0);

	//定义核
	Mat element = getStructuringElement(MORPH_RECT, Size(7, 7));
	//进行形态学操作
	morphologyEx(src, src, MORPH_OPEN, element);   //开运算:可以用来消除小物体、在纤细点处分离物体、平滑较大物体的边界的同时并不明显改变其面积
	//imshow("开运算",src);

	
	//int s = 3 * 2 + 1;
	//Mat structureElement = getStructuringElement(MORPH_RECT, Size(s, s), Point(-1, -1));
	//dilate(src, src, structureElement, Point(-1, -1), 1);   //膨胀											
	//erode(src, src, structureElement);    //腐蚀
										

	cvtColor(src, src_gray, CV_BGR2GRAY);  //转为灰度图像

	Canny(src_gray, edge, 30, 90, 3);  //边缘检测,得到的是二值图像
	//imshow("边缘检测", edge);

    //形态学闭运算
    //进行形态学操作
    morphologyEx(edge, edge, MORPH_CLOSE, element);

											 //threshold(src_gray, edge, 175, 255, CV_THRESH_BINARY);  //二值化
											 //imshow("二值化效果", edge);	

	fineMinAreaRect(edge, src);  //寻找最小外接矩形

	waitKey(0);
	return(0);
}



运行效果:

猜你喜欢

转载自blog.csdn.net/qq_41248872/article/details/89203928