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

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章