圖像矯正技術(一)基於輪廓提取的矯正算法

要進行圖像矯正,至少需要具備以下知識儲備:

  • 輪廓提取技術
  • 霍夫變換知識
  • ROI感興趣區域知識

下面以人民幣矯正、發票矯正、文本矯正爲例,一步步剖析如何實現圖像矯正。

首先分析如何矯正人民幣。

比如我們要矯正這張人民幣,思路應該是怎麼樣的?

 首先分析這張圖的特點。

在這張圖裏,人民幣有一定的傾斜角度,但是角度不大;人民幣的背景是黑色的,而且人命幣的邊緣比較明顯。

沒錯,我們抓住人民幣的邊緣比較明顯來做文章!我們可以先把人民幣的輪廓找出來(找出來的輪廓當然是一個大大的矩形),然後用矩形去包圍它,得到他的旋轉角度,然後根據得到的角度進行旋轉,就可以實現矯正了!

詳細的總結處理步驟:

  1. 圖片灰度化
  2. 閾值二值化
  3. 檢測輪廓
  4. 尋找輪廓的包圍矩陣,並且獲取角度
  5. 根據角度進行旋轉矯正
  6. 對旋轉後的圖像進行輪廓提取
  7. 對輪廓內的圖像區域摳出來,成爲一張獨立圖像

我們把該矯正算法命名爲基於輪廓提取的矯正算法,因爲其關鍵技術是通過輪廓來獲取旋轉角度。

#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>

using namespace cv;
using namespace std;

//第一個參數:輸入圖片名稱: 第二參數:輸出圖片名稱
void GetContoursPic(const char* pSrcFileName, const char* pDstFileName)
{
	Mat srcImg = imread(pSrcFileName);
	imshow("原始圖", srcImg);

	Mat gray, binImg;
	//灰度化
	cvtColor(srcImg, gray, COLOR_RGB2GRAY);
	imshow("灰度圖", gray);
	//二值化
	threshold(gray, binImg, 100, 200, CV_THRESH_BINARY);
	imshow("二值化", binImg);

	vector<vector<Point>>contours;
	vector<Rect>boundRect(contours.size());

	//注意第5個參數爲CV_RETR_EXTERNAL,只檢索外框
	findContours(binImg, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);//找輪廓
	cout << contours.size() << endl;
	for (int i = 0; i < contours.size(); i++)
	{
		//需要獲取的座標
		CvPoint2D32f rectpoint[4];
		CvBox2D rect = minAreaRect(Mat(contours[i]));
		cvBoxPoints(rect, rectpoint);//獲取4個頂點座標

		//與水平線的角度
		float angle = rect.angle;
		cout << angle << endl;

		int line1 = sqrt((rectpoint[1].y - rectpoint[0].y)*(rectpoint[1].y - rectpoint[0].y) + (rectpoint[1].x - rectpoint[0].x)*(rectpoint[1].x - rectpoint[0].x));
		int line2 = sqrt((rectpoint[3].y - rectpoint[0].y)*(rectpoint[3].y - rectpoint[0].y) + (rectpoint[3].x - rectpoint[0].x)*(rectpoint[3].x - rectpoint[0].x));

		//rectangle(binImg,rectpoint[0],rectpoint[3],Scalar(255),2);
		//面積太小的直接pass
		if (line1*line2 < 600)
		{
			continue;
		}
		//爲了讓正方形橫着放,所以旋轉角度是不一樣的,豎放的,給他加90度,翻過來
		if (line1 > line2)
		{
			angle = 90 + angle;
		}
		//新建一個感興趣的區域圖,大小跟原圖一樣大
		Mat RoiSrcImg(srcImg.rows, srcImg.cols, CV_8UC3);//注意這裏必須選CV_8UC3
		RoiSrcImg.setTo(0);//顏色都設置爲黑色
		//對得到的輪廓填充一下
		drawContours(binImg, contours, -1, Scalar(255), CV_FILLED);

		//摳圖到RoiSrcImg
		srcImg.copyTo(RoiSrcImg, binImg);

		//再顯示一下看看,除了感興趣的區域,其他部分都是黑色的了
		namedWindow("RoiSrcImg", 1);
		imshow("RoiSrcImg", RoiSrcImg);

		//創建一個旋轉後的圖像
		Mat RatationedImg(RoiSrcImg.rows, RoiSrcImg.cols, CV_8UC1);
		RatationedImg.setTo(0);
		//對RoiSrcImg進行旋轉
		Point2f center = rect.center;//中心點
		Mat M2 = getRotationMatrix2D(center, angle, 1);//計算旋轉加縮放的變換矩陣
		warpAffine(RoiSrcImg, RatationedImg, M2, RoiSrcImg.size(), 1, 0, Scalar(0));//仿射變換
		imshow("旋轉之後", RatationedImg);
		imwrite("r.jpg", RatationedImg);//將矯正後的圖片保存下來

	}

	//#if 1
	//		code
	//#endif爲測試程序

#if 1
	//對ROI區域進行摳圖
	//對旋轉後的圖片進行輪廓提取
	vector<vector<Point>>contours2;
	Mat raw = imread("r.jpg");
	Mat SecondFindImg;
	//SecondFindImg.setTo(0);
	cvtColor(raw, SecondFindImg, COLOR_BGR2GRAY);
	threshold(SecondFindImg, SecondFindImg, 80, 200, CV_THRESH_BINARY);
	findContours(SecondFindImg, contours2, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
	//cout<<"sec contour:"<<contours2.size()<<endl;

	for (int j = 0; j < contours2.size(); j++)
	{
		//這時候其實就是一個長方形了,所以獲取rect
		Rect rect = boundingRect(Mat(contours2[j]));
		//面積太小的輪廓直接pass,通過設置過濾面積大小,可以保證只拿到外框
		if (rect.area() < 600)
		{
			continue;
		}
		Mat dstImg = raw(rect);
		imshow("dst", dstImg);
		//imwrite(pDstFileName, dstImg);
	}

#endif
}

void main()
{
	GetContoursPic("E:\\OpenCV\\ImageCorrection\\2.jpg", "FinalImage,jpg");
	waitKey();
}

效果一次如下:

原始圖

二值化圖

旋轉矯正後

 

將人民幣區域摳出來

該算法效果還是很不錯的,那趕緊試試其他圖片,我們把傾斜的發票圖像拿去試試

原始圖

 

傾斜矯正後

 

最後把目標區域摳出來,成爲單獨的照片。

 

 

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