Opencv----切边

问题描述:提取一幅图像中的最大矩形区域。

注意:图像可能是倾斜的,要先进行旋转校正。

代码实现主要分为两块:一是实现图像旋转校正;一是实现提取目标矩形区域。

旋转校正代码实现

Mat correctImg(Mat src)
{
	Mat gray, gauss;
	cvtColor(src, gray, COLOR_BGR2GRAY);
	GaussianBlur(gray, gauss, Size(5, 5), 0, 0);
	//canny检测
	Mat edge;
	Canny(gauss, edge, 80, 240, 3);
	//直线检测
	vector<Vec4i>lines;
	HoughLinesP(edge, lines, 1, 3.1415 / 180, 200, 100, 0);
	vector<double>slope;
	for (size_t i = 0; i < lines.size(); i++)
	{
		//计算斜率
		double k = (lines[i][3] - lines[i][1]) / (lines[i][2] - lines[i][0] + 0.00001);
		if (k >= 0)
		{
			slope.push_back(k);
		}
		cout << "斜率:" << k << endl;
	}
	//计算图像旋转角度
	double mean_k = accumulate(slope.begin(), slope.end(), 0.0) / slope.size();
	cout << "平均斜率:" << mean_k << endl;
	double angle = atan2(accumulate(slope.begin(), slope.end(), 0.0), slope.size()) * 180 / 3.14;
	cout << "偏移角:" << angle << endl;
	//旋转变换
	Point2f center(src.cols / 2, src.rows / 2);
	Mat rotm = getRotationMatrix2D(center, angle, 1.0);
	Mat dst;
	warpAffine(src, dst, rotm, src.size(), INTER_LINEAR, 0, Scalar(255, 255, 255));
	imshow("correct", dst);
	imwrite("correct.jpg", dst);
	return dst;
}

代码解释:

(1)霍夫曼直线检测算法在opencv里有两个api函数,比较常用的是这个HoughLinesP函数,因为这个函数的返回值是直线两个端点的坐标,比较符合使用习惯。另一个函数HoughLines的返回值是直线的两个极坐标参数。

(2)for循环中为什么使用size_t定义而不是int,size_t是unsigned int,比int更加稳定。

(3)计算斜率时为什么分母上加个0.00001,防止分母为0,产生数据溢出。

(4)利用反正切函数atan2计算出的是弧度,还应乘以180°/Π才是真正的角度。

提取目标矩形的代码实现

void findROI(Mat image, Mat dst)
{
	Mat gray, gauss, edge;
	cvtColor(image, gray, COLOR_BGR2GRAY);
	GaussianBlur(gray, gauss, Size(5, 5), 0, 0);
	Canny(gauss, edge, 80, 240, 3);
	//寻找轮廓
	vector<vector<Point>>contours;
	vector<Vec4i>hie;
	findContours(edge, contours, hie, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
	//定义最小矩形
	int minW = 0.5*image.cols;
	int minH = 0.5*image.rows;
	Mat drawImage = Mat::zeros(image.size(), CV_8UC3);
	Rect bbox;
	cout << "image的宽:" << image.cols << "," << "image的高:" << image.rows << endl;
	for (size_t i = 0; i < contours.size(); i++)
	{
		//寻找最小外接矩形
		RotatedRect minRect = minAreaRect(contours[i]);
		cout << "minRect的宽:" << minRect.size.width << "," << "minRect的高:" << minRect.size.height << endl;
		//如果最小外接矩形大于定义的最小矩形尺寸,则符合条件
		if (minRect.size.width > minW && minRect.size.height > minH && minRect.size.width < (image.cols - 5))
		{
			Point2f pts[4];
			minRect.points(pts);
			bbox = minRect.boundingRect();
			for (int j = 0; j < 4; j++)
			{
				//画出模板矩形
				line(drawImage, pts[j], pts[(j + 1) % 4], Scalar(0, 0, 255), 2, 8, 0);
			}
		}
	}
	imshow("drawImage", drawImage);
	//bbox的长宽大于0则表明找到了矩形区域
	if (bbox.width > 0 && bbox.height > 0)
	{
		dst = image(bbox);
		imshow("轮廓", dst);
		imwrite("roi.jpg", dst);
	}
	return;
}

代码解释:

(1)定义一个最小矩形尺寸,为了过滤图片中较小轮廓的干扰。

(2)在画矩形窗口模板时,(pts+1)%4是为了防止数据溢出。

(3)如果图像轮廓大于定义的最小矩形尺寸,则说明符合要求,这个最小矩形尺寸则是看情况指定的,还有一个最小外接矩形的宽不小于图像宽-5,这是为了防止图像轮廓不至于太过夸张,当然这种情况很少出现。

运行结果:

完整代码:

#include<opencv.hpp>
#include<iostream>
#include<numeric>
using namespace std;
using namespace cv;

Mat correctImg(Mat src);//图像校正
void findROI(Mat image, Mat dst);//寻找目标区域
int main()
{
	//加载图像
	Mat src = imread("E:\\open CV\\VS\\project\\切边\\切边\\1.jpg");
	if (src.empty())
	{
		cout << "no image!" << endl;
		return -1;
	}
	imshow("src", src);
	
	Mat dst = correctImg(src);
	
	Mat roi;
	findROI(dst, roi);
	
	waitKey(0);
	return 0;
}
Mat correctImg(Mat src)
{
	Mat gray, gauss;
	cvtColor(src, gray, COLOR_BGR2GRAY);
	GaussianBlur(gray, gauss, Size(5, 5), 0, 0);
	//canny检测
	Mat edge;
	Canny(gauss, edge, 80, 240, 3);
	//直线检测
	vector<Vec4i>lines;
	HoughLinesP(edge, lines, 1, 3.1415 / 180, 200, 100, 0);
	vector<double>slope;
	for (size_t i = 0; i < lines.size(); i++)
	{
		//计算斜率
		double k = (lines[i][3] - lines[i][1]) / (lines[i][2] - lines[i][0] + 0.00001);
		if (k >= 0)
		{
			slope.push_back(k);
		}
		cout << "斜率:" << k << endl;
	}
	//计算图像旋转角度
	double mean_k = accumulate(slope.begin(), slope.end(), 0.0) / slope.size();
	cout << "平均斜率:" << mean_k << endl;
	double angle = atan2(accumulate(slope.begin(), slope.end(), 0.0), slope.size()) * 180 / 3.14;
	cout << "偏移角:" << angle << endl;
	//旋转变换
	Point2f center(src.cols / 2, src.rows / 2);
	Mat rotm = getRotationMatrix2D(center, angle, 1.0);
	Mat dst;
	warpAffine(src, dst, rotm, src.size(), INTER_LINEAR, 0, Scalar(255, 255, 255));
	imshow("correct", dst);
	imwrite("correct.jpg", dst);
	return dst;
}
void findROI(Mat image, Mat dst)
{
	Mat gray, gauss, edge;
	cvtColor(image, gray, COLOR_BGR2GRAY);
	GaussianBlur(gray, gauss, Size(5, 5), 0, 0);
	Canny(gauss, edge, 80, 240, 3);
	//寻找轮廓
	vector<vector<Point>>contours;
	vector<Vec4i>hie;
	findContours(edge, contours, hie, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
	//定义最小矩形
	int minW = 0.5*image.cols;
	int minH = 0.5*image.rows;
	Mat drawImage = Mat::zeros(image.size(), CV_8UC3);
	Rect bbox;
	cout << "image的宽:" << image.cols << "," << "image的高:" << image.rows << endl;
	for (size_t i = 0; i < contours.size(); i++)
	{
		//寻找最小外接矩形
		RotatedRect minRect = minAreaRect(contours[i]);
		cout << "minRect的宽:" << minRect.size.width << "," << "minRect的高:" << minRect.size.height << endl;
		//如果最小外接矩形大于定义的最小矩形尺寸,则符合条件
		if (minRect.size.width > minW && minRect.size.height > minH && minRect.size.width < (image.cols - 5))
		{
			Point2f pts[4];
			minRect.points(pts);
			bbox = minRect.boundingRect();
			for (int j = 0; j < 4; j++)
			{
				//画出模板矩形
				line(drawImage, pts[j], pts[(j + 1) % 4], Scalar(0, 0, 255), 2, 8, 0);
			}
		}
	}
	imshow("drawImage", drawImage);
	//bbox的长宽大于0则表明找到了矩形区域
	if (bbox.width > 0 && bbox.height > 0)
	{
		dst = image(bbox);
		imshow("轮廓", dst);
		imwrite("roi.jpg", dst);
	}
	return;
}

 

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值