图像中最大四边形的提取与矩形校正(C++,openCV3.4)

时间:2024-03-15 16:06:27

这个函数作用是从输入的图像中提取检测到的最大矩形区域,但是对矩形的畸变程度鲁棒性还有待改进,
否则矩形区域的提取可能出现问题;
该函数一般可以用于文档扫描等;
图像输入输出示例:图像中最大四边形的提取与矩形校正(C++,openCV3.4)图像中最大四边形的提取与矩形校正(C++,openCV3.4)
代码部分如下:

头文件:

#include<opencv2/opencv.hpp>
using namespace std;
using namespace cv;

extern Mat extractedRect;
int RectSegmentation(const Mat& imgIn);
int PreOperation(const Mat& imgIn);
int InContours(Mat& imgIn);
int largestSquare(const vector<vector<Point>>& squares, vector<Point>& largest_square);																			
int rectExtract(vector<Point>& corners);
double getAngle(Point pt0, Point pt1, Point pt2);

源文件

#include"extract_the_biggest_rectangle.h"

Mat tmpImg, tmpImg2;
Mat grayImg;
vector<Point> largest_square;
static vector<vector<Point>> squaresExp, hullsExp;

double pointDist(Point pt1, Point pt2);
Point2f computeCorner(Vec4i line1, Vec4i line2);
void sortCorners(vector<Point>& points);

int RectSegmentation(const Mat& imgIn)
{
	resize(imgIn, tmpImg, Size(), 0.25, 0.25, 1);
	tmpImg2 = tmpImg.clone();
	PreOperation(tmpImg);
	InContours(grayImg);

	return 0;
}

int PreOperation(const Mat& imgIn)
{
	cvtColor(imgIn, grayImg, COLOR_BGR2GRAY);
	GaussianBlur(grayImg, grayImg, Size(3, 3), 0, 0, BORDER_DEFAULT);														
	return 0;
}

int InContours(Mat& imgIn)
{
	Mat CannyImg;
	int threshLow = 35;
	Canny(imgIn, CannyImg, threshLow, 3 * threshLow, 3);

	dilate(CannyImg, CannyImg, Mat(), Point(-1, -1), 10, 1, Scalar(1));
	erode(CannyImg, CannyImg, Mat(), Point(-1, -1), 10, 1, Scalar(1));

	vector<vector<Point>> contoursExp;
	vector<Vec4i> hierarchyExp;
	findContours(CannyImg, contoursExp, hierarchyExp, RETR_LIST, CHAIN_APPROX_SIMPLE);																			
	Mat contourImg = tmpImg.clone();
	int i = 0;
	for (; i >= 0; i = hierarchyExp[i][0]) {
		drawContours(contourImg, contoursExp, i, Scalar(0, 255, 255), 2, 8, hierarchyExp);
	}
	vector<Point> hull, approx;
	for (size_t i = 0; i < contoursExp.size(); i++)
	{
		convexHull(contoursExp[i], hull);
		approxPolyDP(hull, approx, arcLength(hull, true)*0.02, true);
																	
		if (approx.size() == 4 && fabs(contourArea(Mat(approx))) >= 40000 && isContourConvex(Mat(approx)))	
		{
			double maxCosine = 0;
			for (int j = 2; j < 5; j++) {
				double tmpCosine = fabs(getAngle(approx[j % 4], approx[j - 2], approx[j - 1]));
				maxCosine = MAX(tmpCosine, maxCosine);
			}
			if (maxCosine <= 0.5) {
				squaresExp.push_back(approx);
				hullsExp.push_back(hull);
			}

		}

	}

	int index = largestSquare(squaresExp, largest_square);
	if (largest_square.size() == 0 || index == -1) return -1;
	hull = hullsExp[index];
	approxPolyDP(Mat(hull), approx, 3, true);
	vector<Point> newApprox;
	double maxDistance = arcLength(Mat(approx), true)*0.02;
	for (Point p : approx) {
		if (!(
			pointDist(p, largest_square[0]) > maxDistance&&
			pointDist(p, largest_square[1]) > maxDistance&&
			pointDist(p, largest_square[2]) > maxDistance&&
			pointDist(p, largest_square[3]) > maxDistance
			)) {
			newApprox.push_back(p);
		}

	}

	vector<Vec4i> lines;
	for (int i = 0; i < newApprox.size(); i++) {
		Point p1 = newApprox[i];
		Point p2 = newApprox[(i + 1) % newApprox.size()];
		if (pointDist(p1, p2) > 2 * maxDistance) {
			lines.push_back(Vec4i(p1.x, p1.y, p2.x, p2.y));

			line(grayImg, p1, p2, Scalar(255), 2, 8);
		}
	}

	vector<Point> corners1;
	for (int i = 0; i < lines.size(); i++) {
		Point corner = computeCorner(lines[i], lines[(i + 1) % lines.size()]);
		corners1.push_back(corner);
	}
	for (int i = 0; i < corners1.size(); i++) {
		circle(tmpImg2, corners1[i], 5, Scalar(255, 255, 255), -1);
		line(tmpImg2, corners1[i], corners1[(i + 1) % corners1.size()], Scalar(255, 0, 0), 1, 8);
	}

	imshow("outPutImg", tmpImg2);//

	sortCorners(corners1);
	rectExtract(corners1);
	return 0;
}

double getAngle(Point pt0, Point pt1, Point pt2)
{
	double dx1 = pt0.x - pt2.x;
	double dy1 = pt0.y - pt2.y;
	double dx2 = pt1.x - pt2.x;
	double dy2 = pt1.y - pt2.y;
	double cos1 = (dx1*dx2 + dy1 * dy2) / sqrt((dx1*dx1 + dy1 * dy1)*(dx2*dx2 + dy2 * dy2) + 1e-10);
	return cos1;
}

int largestSquare(const vector<vector<Point>>& squares, vector<Point>& largest_square)
{
	if (!squares.size()) return -1;
	int maxWidth = 0;
	int maxHeight = 0;
	int maxIndex = 0;
	for (int i = 0; i < squares.size(); i++) {
		Rect rectangleIn = boundingRect(Mat(squares[i]));
		if ((rectangleIn.width >= maxWidth) && (rectangleIn.height >= maxHeight)) {
			maxWidth = rectangleIn.width;
			maxHeight = rectangleIn.height;
			maxIndex = i;
		}
	}
	largest_square = squares[maxIndex];
	return maxIndex;
}

double pointDist(Point pt1, Point pt2) {
	int a = pt1.x - pt2.x;
	int b = pt1.y - pt2.y;
	int c = a * a + b * b;
	return sqrt(c);
}

Point2f  computeCorner(Vec4i line1, Vec4i line2) {
	int x1 = line1[0], y1 = line1[1], x2 = line1[2], y2 = line1[3];
	int x3 = line2[0], y3 = line2[1], x4 = line2[2], y4 = line2[3];
	double d = ((x1 - x2)*(y3 - y4) - (y1 - y2)*(x3 - x4));
	if (d) {
		Point2f cornerIn;
		cornerIn.x = ((x1*y2 - x2 * y1)*(x3 - x4) - (x1 - x2)*(x3*y4 - x4 * y3)) / d;
		cornerIn.y = ((x1*y2 - x2 * y1)*(y3 - y4) - (y1 - y2)*(x3*y4 - x4 * y3)) / d;
		return cornerIn;
	}
	else
		return Point2f(-1, -1);
}

int rectExtract(vector<Point>& corners)
{
	sortCorners(corners);
	Point2f p0 = corners[0];
	Point2f p1 = corners[1];
	Point2f p2 = corners[2];
	Point2f p3 = corners[3];
	double space0 = pointDist(p0, p1);
	double space1 = pointDist(p1, p2);
	double space2 = pointDist(p2, p3);
	double space3 = pointDist(p3, p0);
	double imgWidth = space1 > space3 ? space1 : space3;
	double imgHeight = space0 > space2 ? space0 : space2;
	//Add the perspective correction
	double paraFix1 = (space3 / space1) > 1 ? (space3 / space1) : (space1 / space3);
	double paraFix2 = (space2 / space0) > 1 ? (space2 / space0) : (space0 / space2);
	if (paraFix1 > paraFix2) imgHeight = imgHeight * paraFix1;
	else imgWidth = imgWidth * paraFix2;

	if (imgWidth < imgHeight) {
		double temp = imgWidth;
		imgWidth = imgHeight;
		imgHeight = temp;
		Point2f tempPoint = p0;
		p0 = p1;
		p1 = p2;
		p2 = p3;
		p3 = tempPoint;//rotate the Image
	}

	vector<Point2f> cornersIn;
	cornersIn.push_back(p0);
	cornersIn.push_back(p1);
	cornersIn.push_back(p2);
	cornersIn.push_back(p3);
	Mat imgRect(imgHeight * 2, imgWidth * 2, CV_8UC3);
	vector<Point2f> rect_pts;
	rect_pts.push_back(Point2f(0, imgRect.rows));
	rect_pts.push_back(Point2f(0, 0));
	rect_pts.push_back(Point2f(imgRect.cols, 0));
	rect_pts.push_back(Point2f(imgRect.cols, imgRect.rows));

	Mat transmtx;
	transmtx = getPerspectiveTransform(cornersIn, rect_pts);
	warpPerspective(tmpImg, imgRect, transmtx, imgRect.size());
	cout << "size" << (int)(imgRect.rows) << (int)(imgRect.cols) << endl;
	imshow("outPutimg", imgRect);
	imwrite("outputImg.jpg", imgRect);
	return 0;
}

void sortCorners(vector<Point>& points) {
	if (points.size() == 0) return;
	Point pl = points[0];
	int index = 0;
	for (int i = 1; i < points.size(); i++)
	{
		Point point = points[i];
		if (pl.x > point.x)
		{
			pl = point;
			index = i;
		}
	}
	points[index] = points[0];
	points[0] = pl;
	Point lp = points[0];
	for (int i = 1; i < points.size(); i++)
	{
		for (int j = i + 1; j<points.size(); j++)
		{
			cv::Point point1 = points[i];
			cv::Point point2 = points[j];
			if ((point1.y - lp.y*1.0) / (point1.x - lp.x)>(point2.y - lp.y*1.0) / (point2.x - lp.x))
			{
				cv::Point temp = point1;
				points[i] = points[j];
				points[j] = temp;
			}
		}
	}

}