opencv 表格識別之表格透視矯正(一)

asiwxy發表於2018-10-07

 這個專案的目的是讓使用者選擇模板表格(未填寫的表格)中的多個或者一個小格子,然後將掃描(注意:是掃描的表格)傳入的填寫好的表格對應的格子提取出來,對格子中的選擇進行處理。

在對錶格進行處理之前,我們應該先將傳入的填寫好的表格對照著模板表格進行矯正。一下提供了一種矯正表格的方法,這種方法效果還行,但是當運用到一張紙上有多個的表格的時候,只能對其中最大的表格進行處理,所以下一篇文章,將給出一種能進行多個表格矯正的方法。

        Mat src = imread("9.jpg");
	Mat srcClone = src.clone();


	// 檢查是否為灰度圖,如果不是,轉化為灰度圖
	Mat gray;
	if (src.channels() == 3) {
		cvtColor(src, gray, CV_BGR2GRAY);
	}
	else {
		gray = src;
	}


	//轉化為二值圖 
	Mat bw;
	adaptiveThreshold(~gray, bw, 255, CV_ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, -2);

	
        //形態學
	Mat Structure = getStructuringElement(MORPH_RECT, Size(3, 3));

	// Apply morphology operations
	erode(bw, bw, Structure, Point(-1, -1));
	dilate(bw, bw, Structure, Point(-1, -1));

閾值化的結果如下:

閾值化

 

//提取輪廓 
	vector<Vec4i> hierarchy;
	std::vector<std::vector<cv::Point> > contours;
	cv::findContours(bw, contours, hierarchy, CV_RETR_LIST, CHAIN_APPROX_SIMPLE);// , Point(0, 0));

	//逼近多邊形
	vector<vector<Point> > contours_poly(contours.size());

	int indexMax = 0;
	double areaMax = 0;
	for (size_t i = 0; i < contours.size(); i++)
	{
		//面積
		double area = contourArea(contours[i]);

		//篩選可能存在且不代表表的單獨的行的行。
		if (area < 20) // value is randomly chosen
			continue;

		Scalar color(0, 255, 0);
		drawContours(srcClone, contours, i, color, 2, 8);

		//逼近區域成為一個形狀
		approxPolyDP(Mat(contours[i]), contours_poly[i], 10, true);


		//不知為何,加了四個點的篩選之後,就沒有最大的框了
		//if (contours_poly[i].size() != 4)
		//	continue;

		//找出面積最大的四邊形
		if (area > areaMax)
		{
			areaMax = area;
			indexMax = i;
		}
	}

在原圖上畫出所有的輪廓,效果如下:

在原圖上畫出所有的輪廓

 

	Scalar color(0, 0, 255);
	drawContours(srcClone, contours, indexMax, color, 2, 8);
	imshow("contou1", srcClone);


	Mat polyPic = Mat::zeros(src.size(), CV_8UC3);
	drawContours(polyPic, contours, indexMax, color, 2, 8);

	imshow("Contour2", polyPic);
        vector<int>  hull;
	convexHull(contours_poly[indexMax], hull, false);    //檢測該輪廓的凸包

	for (int j = 0; j < hull.size(); j++)
	{
		circle(src, contours_poly[indexMax][j], 10, Scalar(255, 0, 0));
	}

在原圖上找出最大的輪廓:

原圖最大輪廓

提取出最大輪廓:

最大輪廓

繪製凸包:

hull

        //矯正後的座標
        Mat outPic = Mat(Size(800, 800), src.type());
	Point2f srcPoints[4], dstPoints[4];
	dstPoints[0] = Point2f(0, 0);
	dstPoints[1] = Point2f(outPic.cols, 0);
	dstPoints[2] = Point2f(outPic.cols, outPic.rows);
	dstPoints[3] = Point2f(0, outPic.rows);
        
        //排序
	bool sorted = false;
	int n = 4;
	while (!sorted)
	{
		for (int i = 1; i < n; i++)
		{
			sorted = true;
			if (contours_poly[indexMax][i - 1].x > contours_poly[indexMax][i].x)
			{
				swap(contours_poly[indexMax][i - 1], contours_poly[indexMax][i]);
				sorted = false;
			}
		}
		n--;
	}

	if (contours_poly[indexMax][0].y < contours_poly[indexMax][1].y)
	{
		srcPoints[0] = contours_poly[indexMax][0];
		srcPoints[3] = contours_poly[indexMax][1];
	}
	else
	{
		srcPoints[0] = contours_poly[indexMax][1];
		srcPoints[3] = contours_poly[indexMax][0];
	}
	if (contours_poly[indexMax][2].y < contours_poly[indexMax][3].y)
	{
		srcPoints[1] = contours_poly[indexMax][2];
		srcPoints[2] = contours_poly[indexMax][3];
	}
	else
	{
		srcPoints[1] = contours_poly[indexMax][3];
		srcPoints[2] = contours_poly[indexMax][2];
	}
	
	Mat transMat = getPerspectiveTransform(srcPoints, dstPoints);    //得到變換矩陣
	warpPerspective(src, outPic, transMat, outPic.size()); //進行座標變換

矯正後的影像:

結果

 

由於程式碼過於簡單,處理過的影像也比較粗糙。

參考:https://blog.csdn.net/xingchenbingbuyu/article/details/50783585

相關文章