OpenCV學習之路——車牌識別之車牌定位

去年七月份由於學校項目須要開始接觸圖像處理,但那時候只是到網上找車牌識別代碼,而後加入到本身的項目中,不太清楚細節原理。html

如今本身從新一步步實現車牌識別。                                                                                                                      ios

 

車牌識別流程:算法

 

高斯模糊:數組

車牌識別中利用高斯模糊將圖片平滑化,去除干擾的噪聲對後續圖像處理的影響。ide

高斯模糊(GaussianBlur()),也叫高斯平滑。函數

周邊像素的平均值,所謂"模糊",能夠理解成每個像素都取周邊像素的平均值
   
上圖中,2是中間點,周邊點都是1。"中間點"取"周圍點"的平均值,就會變成1。 在數值上,這是一種"平滑化"。在圖形上,就至關於產生"模糊"效果,"中間點"失去細節(上圖右)。顯然, 計算平均值時,取值範圍越大,"模糊效果"越強烈。
左圖分別是原圖、模糊半徑3像素、模糊半徑10像素的效果。模糊半徑越大,圖像就越模糊。從數值角度看,就是數值越平滑。
接下來的問題就是,既然每一個點都要取周邊像素的平均值,那麼應該如何分配權重呢?
若是使用簡單平均,顯然不是很合理,由於圖像都是連續的,越靠近的點關係越密切,越遠離的點關係越疏遠。所以,加權平均更合理,距離越近的點權重越大,距離越遠的點權重越小。
 
OpenCV中函數
void GaussianBlur(InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY=0, int borderType=BORDER_DEFAULT)
參數詳解: 
src:輸入圖片,可使是任意通道數,該函數對通道是獨立處理的,可是深度只能是CV_8U, CV_16U, CV_16S, CV_32F or CV_64F. 
dst:輸出圖片,和輸入圖片相同大小和深度。 
ksize:高斯內核大小。ksize.width和ksize.height容許不相同但他們必須是正奇數。或者等於0,由參數sigma的伺機決定。 
sigmaX:高斯內核在X方向的標準方差。 
sigmaY:高斯內核在Y方向的標準方差。若是sigmaY爲0,他將和sigmaX的值相同,若是他們都爲0,那麼他們由ksize.width和ksize.height計算得出。 
borderType:用於判斷圖像邊界的模式。
1 Mat Gaussian(Mat &img) {
2     Mat out;
3     GaussianBlur(img, out, Size(3, 3),
4         0, 0, BORDER_DEFAULT);
5     return out;
6 
7 }
View Code

原圖:(來自百度)學習

  

 

灰度化:測試

在車牌識別中咱們須要將圖像轉化爲灰度圖像,這樣有利於後續步驟的開展,如Soble算子只能做用於灰度圖像。網站

灰度化,在RGB模型中,若是R=G=B時,則彩色表示一種灰度顏色,其中R=G=B的值叫灰度值,所以,灰度圖像每一個像素只需一個字節存放灰度值(又稱強度值、亮度值),灰度範圍爲0-255。ui

Opencv中函數

void cvtColor(InputArray src,  OutputArray dst,  int code,  int dstCn=0 )

參數詳解:

 src輸入圖像:8位無符號的16位無符號(cv_16uc…)或單精度浮點。
 dst的大小和深度src.

code輸出圖像顏色空間轉換的代碼。

dstCn目標圖像中的信道數;若是參數爲0,則從SRC和代碼自動導出信道的數目。

1 Mat Grayscale(Mat &img) {
2     Mat out;
3     cvtColor(img, out, CV_RGB2GRAY);
4 
5     return out;
6 }
View Code

 

 

Sobel算子(X方向):

車牌定位的核心算法,水平方向上的邊緣檢測,檢測出車牌區域。

主要用於得到數字圖像的一階梯度,常見的應用和物理意義是邊緣檢測。在技術上,它是一個離散的一階差分算子,用來計算圖像亮度函數的一階梯度之近似值。在圖像的任何一點使用此算子,將會產生該點對應的梯度矢量或是其法矢量。

該算子包含兩組3x3的矩陣,分別爲橫向及縱向,將之與圖像做平面卷積,便可分別得出橫向及縱向的亮度差分近似值。若是以A表明原始圖像, Gx及Gy分別表明經橫向及縱向邊緣檢測的圖像,其公式以下:
圖像的每個像素的橫向及縱向梯度近似值可用如下的公式結合,來計算梯度的大小。
可用如下公式計算梯度方向。
在以上例子中,若是以上的角度Θ等於零,即表明圖像該處擁有縱向邊緣,左方較右方暗。
 
OpenCV中函數:
void Sobel(InputArray src, OutputArray dst, int ddepth, int xorder, int yorder, int ksize=3, double scale=1, double delta=0, int borderType=BORDER_DEFAULT )

參數:
src: 源圖像。
dst:相同大小和相同數量的通道的目標圖像。
ddepth:目標圖像的深度。
xorder:階導數的X.
yorder:階導數的Y.
ksize:擴展Sobel算子–大小。它必須是1, 3, 5,或者7。
scale計算衍生值的可選刻度因子。默認狀況下,不該用縮放。看到getderivkernels()詳情。
delta :可選的delta值,在將它們存儲在DST以前添加到結果中。
bordertype:像素外推方法。

convertScaleAbs()——先縮放元素再取絕對值,最後轉換格式爲8bit型。

 1 Mat Sobel(Mat &img) {
 2     Mat out;
 3     Mat grad_x, grad_y;
 4     Mat abs_grad_x, abs_grad_y;
 5 
 6     //X方向
 7     //Sobel(img, grad_x, CV_16S, 1, 0, 3, 1, 1, BORDER_DEFAULT);
 8     //convertScaleAbs(grad_x, abs_grad_x);
 9     Sobel(img, img, CV_16S, 1, 0, 3, 1, 1, BORDER_DEFAULT);
10     convertScaleAbs(img, out);
11 
12     //Y方向
13     //Sobel(img, grad_y, CV_16S, 0, 1, 3, 1, 1, BORDER_DEFAULT);
14     //convertScaleAbs(grad_y, abs_grad_y);
15     //convertScaleAbs(img, out);
16 
17     //合併
18     //addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, out);
19 
20     return out;
21 }
View Code

 

 

二值化:

進一步對圖像進行處理,強化目標區域,弱化背景。

圖像的二值化,就是將圖像上的像素點的灰度值設置爲0或255,也就是將整個圖像呈現出明顯的只有黑和白的視覺效果

OpenCV中函數

double threshold(InputArray src, OutputArray dst, double thresh, double maxVal, int thresholdType)

參數:
src源陣列(單通道,32位浮點8位)。
dst:相同大小和類型的目標數組。
thresh門限閾值。
Maxval:最大值使用的thresh_binary和thresh_binary_inv閾值類型。
thresholdtype:閾值型,以下。

 THRESH_BINARY  當前點值大於閾值時,取Maxval,也就是第四個參數,下面再不說明,不然設置爲0

 THRESH_BINARY_INV 當前點值大於閾值時,設置爲0,不然設置爲Maxval

 THRESH_TRUNC 當前點值大於閾值時,設置爲閾值,不然不改變

 THRESH_TOZERO 當前點值大於閾值時,不改變,不然設置爲0

 THRESH_TOZERO_INV  當前點值大於閾值時,設置爲0,不然不改變

1 Mat TwoValued(Mat &img) {
2     Mat out;
3     threshold(img, out, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);
4     //threshold(img, out, 100, 255, CV_THRESH_BINARY);
5 
6     return out;
7 }
View Code

 

 

閉操做:

閉操做能夠將目標區域連成一個總體,便於後續輪廓的提取。

閉操做可以使輪廓線更光滑,但與開操做相反的是,閉操做一般消彌狹窄的間斷和長細的鴻溝,消除小的空洞,並填補輪廓線中的斷裂。

使用結構元素B對集合A進行閉操做,定義爲
這個公式代表, 使用結構元素B對集合A的閉操做就是用B對A進行膨脹,而後用B對結果進行腐蝕
 
OpenCV中函數
void  morphologyEx (InputArray  src, OutputArray  dst, int  op, InputArray  element, Point  anchor=Point(-1,-1), int  iterations=1, int  borderType=BORDER_CONSTANT, const Scalar&  borderValue=morphologyDefaultBorderValue()  )

參數:
src:源圖像。
dst:相同大小和類型的目標圖像。
element內核類型    用getStructuringElement函數獲得。
OP:
能夠是如下形式之一的形態學操做的類型:
morph_open -開啓操做
morph_close -閉合操做
morph_gradient -形態學梯度
morph_tophat「頂帽」
morph_blackhat -「黑帽」
iterations侵蝕和膨脹的次數被應用。
bordertype–像素外推方法。
bordervalue–邊界值在一個恆定的邊界狀況。默認值有特殊含義。

關注前4個參數便可,後面用默認參數。

1 Mat Close(Mat &img) {
2     Mat out;
3     //Mat element(5, 5, CV_8U, cv::Scalar(1));
4     Mat element = getStructuringElement(MORPH_RECT, Size(17, 5));
5     morphologyEx(img, out, cv::MORPH_CLOSE, element);
6 
7     return out;
8 }
View Code

 

 

取輪廓:

將前面處理的車牌目標區域提取出來。

相關函數:

查找輪廓:

void findContours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset=Point())

image: 輸入的 8-比特、單通道圖像. 非零元素被當成 1, 0 象素值保留爲 0 - 從而圖像被當作二值的。爲了從灰度圖像中獲得這樣的二值圖像,可使用 cvThreshold, cvAdaptiveThreshold 或 cvCanny. 本函數改變輸入圖像內容。 

storage :獲得的輪廓的存儲容器 
first_contour :輸出參數:包含第一個輸出輪廓的指針 
header_size :若是 method=CV_CHAIN_CODE,則序列頭的大小 >=sizeof(CvChain),不然 >=sizeof(CvContour) . 
mode
提取模式. 
CV_RETR_EXTERNAL - 只提取最外層的輪廓 
CV_RETR_LIST - 提取全部輪廓,而且放置在 list 中 
CV_RETR_CCOMP - 提取全部輪廓,而且將其組織爲兩層的 hierarchy: 頂層爲連通域的外圍邊界,次層爲洞的內層邊界。 
CV_RETR_TREE - 提取全部輪廓,而且重構嵌套輪廓的所有 hierarchy 
method :
逼近方法 (對全部節點, 不包括使用內部逼近的 CV_RETR_RUNS). 
CV_CHAIN_CODE - Freeman 鏈碼的輸出輪廓. 其它方法輸出多邊形(定點序列). 
CV_CHAIN_APPROX_NONE - 將全部點由鏈碼形式翻譯(轉化)爲點序列形式 
CV_CHAIN_APPROX_SIMPLE - 壓縮水平、垂直和對角分割,即函數只保留末端的象素點; 
CV_CHAIN_APPROX_TC89_L1, 
CV_CHAIN_APPROX_TC89_KCOS - 應用 Teh-Chin 鏈逼近算法. CV_LINK_RUNS - 經過鏈接爲 1 的水平碎片使用徹底不一樣的輪廓提取算法。僅有 CV_RETR_LIST 提取模式能夠在本方法中應用. 
offset :
每個輪廓點的偏移量. 當輪廓是從圖像 ROI 中提取出來的時候,使用偏移量有用,由於能夠從整個圖像上下文來對輪廓作分析. 
函數 cvFindContours 從二值圖像中提取輪廓,而且返回提取輪廓的數目。指針 first_contour 的內容由函數填寫。它包含第一個最外層輪廓的指針,若是指針爲 NULL,則沒有檢測到輪廓(好比圖像是全黑的)。其它輪廓能夠從 first_contour 利用 h_next 和 v_next 連接訪問到。 在 cvDrawContours 的樣例顯示如何使用輪廓來進行連通域的檢測。輪廓也能夠用來作形狀分析和對象識別 - 見CVPR2001 教程中的 squares 樣例。該教程能夠在 SourceForge 網站上找到。 

繪製輪廓:

void drawContours(InputOutputArray image, InputArrayOfArrays contours, int contourIdx, const Scalar& color, intthickness=1, int lineType=8, InputArray hierarchy=noArray(), int maxLevel=INT_MAX, Point offset=Point() )

 

相關參數參考——http://www.opencv.org.cn/opencvdoc/2.3.2/html/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html?highlight=drawcontours#cv.DrawContours

 

漫水填充算法:

int floodFill(InputOutputArray image, Point seed, Scalar newVal, Rect* rect=0, Scalar loDiff=Scalar(), ScalarupDiff=Scalar(), int flags=4 )

 

 

 

相關參數參考——http://www.opencv.org.cn/opencvdoc/2.3.2/html/modules/imgproc/doc/miscellaneous_transformations.html?highlight=floodfill#cv.FloodFill

 

尺寸判斷函數

 1 bool verifySizes(RotatedRect mr)
 2 {
 3     float error = 0.3;
 4     //Spain car plate size: 52x11 aspect 4,7272
 5     //China car plate size: 440mm*140mm,aspect 3.142857
 6     float aspect = 3.142857;
 7     //Set a min and max area. All other patchs are discarded
 8     int min= 1*aspect*1; // minimum area
 9     int max= 2000*aspect*2000; // maximum area
10     //int min = 44 * 14 * m_verifyMin; // minimum area
11     //int max = 44 * 14 * m_verifyMax; // maximum area
12                                      //Get only patchs that match to a respect ratio.
13     float rmin = aspect - aspect*error;
14     float rmax = aspect + aspect*error;
15 
16     int area = mr.size.height * mr.size.width;
17     float r = (float)mr.size.width / (float)mr.size.height;
18     if (r < 1)
19     {
20         r = (float)mr.size.height / (float)mr.size.width;
21     }
22 
23     if ((area < min || area > max) || (r < rmin || r > rmax))
24     {
25         return false;
26     }
27     else
28     {
29         return true;
30     }
31 }
View Code

 

均衡直方圖:

 1 Mat histeq(Mat in)
 2 {
 3     Mat out(in.size(), in.type());
 4     if (in.channels() == 3) {
 5         Mat hsv;
 6         vector<Mat> hsvSplit;
 7         cvtColor(in, hsv, CV_BGR2HSV);
 8         split(hsv, hsvSplit);
 9         equalizeHist(hsvSplit[2], hsvSplit[2]);
10         merge(hsvSplit, hsv);
11         cvtColor(hsv, out, CV_HSV2BGR);
12     }
13     else if (in.channels() == 1) {
14         equalizeHist(in, out);
15     }
16 
17     return out;
18 
19 }
View Code

總體代碼:

 

  1 void Contour(Mat &img, Mat &out) {
  2     RNG rng(12345);
  3 
  4     vector< Mat > contours(1000);
  5     vector<Vec4i> hierarchy(1000);
  6     findContours(img, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
  7 
  8 
  9     vector< Mat >::iterator itc = contours.begin();
 10     vector<RotatedRect> rects;
 11     int t = 0;
 12     while (itc != contours.end()) {
 13         //Create bounding rect of object
 14         RotatedRect mr = minAreaRect(Mat(*itc));
 15         //large the rect for more
 16         if (!verifySizes(mr)) {
 17             itc = contours.erase(itc);
 18         }
 19         else {
 20             ++itc;
 21             rects.push_back(mr);
 22         }
 23    }
 24     
 25     cv::Mat result;
 26     img.copyTo(result);
 27     for (int i = 0; i< contours.size(); i++)
 28     {
 29         drawContours(result, contours, i, Scalar(0, 0, 255), 2, 8, vector<Vec4i>(), 0, Point());
 30         //drawContours(result, contours, i, Scalar(255), 2);
 31     }
 32 
 33     //imshow("MASK11", result);
 34 
 35     for (int i = 0; i < rects.size(); i++) {
 36         circle(result, rects[i].center, 3, Scalar(0, 255, 0), -1);
 37 
 38         float minSize = (rects[i].size.width < rects[i].size.height) ? rects[i].size.width : rects[i].size.height;
 39         //minSize = minSize - minSize*0.5;
 40 
 41         srand(time(NULL));
 42         Mat mask;
 43         mask.create(out.rows + 2, out.cols + 2, CV_8UC1);
 44         mask = Scalar::all(0);
 45         int loDiff = 30;
 46         int upDiff = 30;
 47         int connectivity = 4;
 48         int newMaskVal = 255;
 49         int NumSeeds = 10;
 50         Rect ccomp;
 51         int flags = connectivity + (newMaskVal << 8) + CV_FLOODFILL_FIXED_RANGE + CV_FLOODFILL_MASK_ONLY;
 52 
 53         for (int j = 0; j < NumSeeds; j++) {
 54             Point seed;
 55             seed.x = rects[i].center.x + rand() % (int)minSize - (minSize / 2);
 56             seed.y = rects[i].center.y + rand() % (int)minSize - (minSize / 2);
 57             circle(result, seed, 1, Scalar(0, 255, 255), -1);
 58             int area = floodFill(out, mask, seed, Scalar(255, 0, 0), &ccomp, Scalar(loDiff, loDiff, loDiff), Scalar(upDiff, upDiff, upDiff), flags);
 59         }
 60 
 61         //imshow("MASK", mask);
 62         
 63         vector<Point> pointsInterest;
 64         Mat_<uchar>::iterator itMask = mask.begin<uchar>();
 65         Mat_<uchar>::iterator end = mask.end<uchar>();
 66         for (; itMask != end; ++itMask)
 67             if (*itMask == 255)
 68                 pointsInterest.push_back(itMask.pos());
 69 
 70         RotatedRect minRect = minAreaRect(pointsInterest);
 71 
 72         if (verifySizes(minRect)) {
 73             // rotated rectangle drawing   
 74             Point2f rect_points[4]; minRect.points(rect_points);
 75             for (int j = 0; j < 4; j++)
 76                 line(result, rect_points[j], rect_points[(j + 1) % 4], Scalar(0, 0, 255), 1, 8);
 77 
 78             //Get rotation matrix  
 79             float r = (float)minRect.size.width / (float)minRect.size.height;
 80             float angle = minRect.angle;
 81             if (r < 1)
 82                 angle = 90 + angle;
 83             Mat rotmat = getRotationMatrix2D(minRect.center, angle, 1);
 84 
 85             //Create and rotate image  
 86             Mat img_rotated;
 87             warpAffine(out, img_rotated, rotmat, out.size(), CV_INTER_CUBIC);//實現旋轉
 88 
 89             //Crop image  
 90             Size rect_size = minRect.size;
 91             if (r < 1)
 92                 swap(rect_size.width, rect_size.height);
 93             Mat img_crop;
 94             getRectSubPix(img_rotated, rect_size, minRect.center, img_crop);
 95 
 96             Mat resultResized;
 97             resultResized.create(33, 144, CV_8UC3);
 98             resize(img_crop, resultResized, resultResized.size(), 0, 0, INTER_CUBIC);;
 99 
100             ////Equalize croped image  
101             Mat grayResult;
102             cvtColor(resultResized, grayResult, CV_BGR2GRAY);// CV_RGB2GRAY
103             blur(grayResult, grayResult, Size(3, 3));
104             grayResult = histeq(grayResult);
105 
106             if (1) {
107                 stringstream ss(stringstream::in | stringstream::out);
108                 ss << "haha" << "_" << i << ".jpg";
109                 imwrite(ss.str(), grayResult);
110             }
111 
112         }
113     }
114 }
View Code

 

主函數:

 1 int main() {
 2     Mat img;
 3     Mat out;
 4     //Mat result;
 5 
 6     //載入圖片  
 7     img = imread("test1.jpg");//, CV_LOAD_IMAGE_GRAYSCALE);
 8     img.copyTo(out);
 9     //imshow ("原始圖", img);
10 
11     img = Gaussian(img);
12     //imshow ("高斯模糊", img);
13 
14     img = Grayscale(img);
15     //imshow("灰度化", img);
16 
17     img = Sobel(img);
18     //imshow("Sobel_X", img);
19 
20     img = TwoValued(img);
21     //imshow("二值化", img);
22 
23     img = Close(img);
24     //imshow("閉操做", img);
25 
26     //
27     Contour(img, out);
28 
29     waitKey(0);
30     cvDestroyAllWindows();
31 }
View Code

 

 

 車牌定位部分完整代碼:

 

  1 /*------------------------------ - 程序介紹------------------------------*/
  2 //版本:VS2017 + Opencv2.4.9
  3 //描述:OpenCV學習之路——車牌識別之車牌定位
  4 /*-----------------------------------------------------------------------*/
  5 
  6 
  7 #include "opencv.hpp"
  8 #include "opencv2/highgui/highgui.hpp"
  9 #include "opencv2/imgproc/imgproc.hpp"
 10 #include "opencv2\core\core.hpp"
 11 #include "vector"
 12 #include "iostream"
 13 #include "time.h"
 14 
 15 using namespace cv;
 16 using namespace std;
 17 
 18 Mat histeq(Mat in)
 19 {
 20     Mat out(in.size(), in.type());
 21     if (in.channels() == 3) {
 22         Mat hsv;
 23         vector<Mat> hsvSplit;
 24         cvtColor(in, hsv, CV_BGR2HSV);
 25         split(hsv, hsvSplit);
 26         equalizeHist(hsvSplit[2], hsvSplit[2]);
 27         merge(hsvSplit, hsv);
 28         cvtColor(hsv, out, CV_HSV2BGR);
 29     }
 30     else if (in.channels() == 1) {
 31         equalizeHist(in, out);
 32     }
 33 
 34     return out;
 35 
 36 }
 37 
 38 //! 對minAreaRect得到的最小外接矩形,用縱橫比進行判斷
 39 bool verifySizes(RotatedRect mr)
 40 {
 41     float error = 0.3;
 42     //Spain car plate size: 52x11 aspect 4,7272
 43     //China car plate size: 440mm*140mm,aspect 3.142857
 44     float aspect = 3.142857;
 45     //Set a min and max area. All other patchs are discarded
 46     int min= 1*aspect*1; // minimum area
 47     int max= 2000*aspect*2000; // maximum area
 48     //int min = 44 * 14 * m_verifyMin; // minimum area
 49     //int max = 44 * 14 * m_verifyMax; // maximum area
 50                                      //Get only patchs that match to a respect ratio.
 51     float rmin = aspect - aspect*error;
 52     float rmax = aspect + aspect*error;
 53 
 54     int area = mr.size.height * mr.size.width;
 55     float r = (float)mr.size.width / (float)mr.size.height;
 56     if (r < 1)
 57     {
 58         r = (float)mr.size.height / (float)mr.size.width;
 59     }
 60 
 61     if ((area < min || area > max) || (r < rmin || r > rmax))
 62     {
 63         return false;
 64     }
 65     else
 66     {
 67         return true;
 68     }
 69 }
 70 
 71 Mat Gaussian(Mat &img) {
 72     Mat out;
 73     GaussianBlur(img, out, Size(3, 3),
 74         0, 0, BORDER_DEFAULT);
 75     return out;
 76 
 77 }
 78 
 79 Mat Grayscale(Mat &img) {
 80     Mat out;
 81     cvtColor(img, out, CV_RGB2GRAY);
 82 
 83     return out;
 84 }
 85 
 86 Mat Sobel(Mat &img) {
 87     Mat out;
 88     Mat grad_x, grad_y;
 89     Mat abs_grad_x, abs_grad_y;
 90 
 91     //X方向
 92     //Sobel(img, grad_x, CV_16S, 1, 0, 3, 1, 1, BORDER_DEFAULT);
 93     //convertScaleAbs(grad_x, abs_grad_x);
 94     Sobel(img, img, CV_16S, 1, 0, 3, 1, 1, BORDER_DEFAULT);
 95     convertScaleAbs(img, out);
 96 
 97     //Y方向
 98     //Sobel(img, grad_y, CV_16S, 0, 1, 3, 1, 1, BORDER_DEFAULT);
 99     //convertScaleAbs(grad_y, abs_grad_y);
100     //convertScaleAbs(img, out);
101 
102     //合併
103     //addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, out);
104 
105     return out;
106 }
107 
108 Mat TwoValued(Mat &img) {
109     Mat out;
110     threshold(img, out, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);
111     //threshold(img, out, 100, 255, CV_THRESH_BINARY);
112 
113     return out;
114 }
115 
116 Mat Close(Mat &img) {
117     Mat out;
118     //Mat element(5, 5, CV_8U, cv::Scalar(1));
119     Mat element = getStructuringElement(MORPH_RECT, Size(17, 5));
120     morphologyEx(img, out, cv::MORPH_CLOSE, element);
121 
122     return out;
123 }
124 
125 
126 void Contour(Mat &img, Mat &out) {
127     RNG rng(12345);
128 
129     vector< Mat > contours(1000);
130     vector<Vec4i> hierarchy(1000);
131     findContours(img, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
132 
133 
134     vector< Mat >::iterator itc = contours.begin();
135     vector<RotatedRect> rects;
136     int t = 0;
137     while (itc != contours.end()) {
138         //Create bounding rect of object
139         RotatedRect mr = minAreaRect(Mat(*itc));
140         //large the rect for more
141         if (!verifySizes(mr)) {
142             itc = contours.erase(itc);
143         }
144         else {
145             ++itc;
146             rects.push_back(mr);
147         }
148    }
149     
150     cv::Mat result;
151     img.copyTo(result);
152     for (int i = 0; i< contours.size(); i++)
153     {
154         drawContours(result, contours, i, Scalar(0, 0, 255), 2, 8, vector<Vec4i>(), 0, Point());
155         //drawContours(result, contours, i, Scalar(255), 2);
156     }
157     //imshow("畫輪廓", out);
158 
159     for (int i = 0; i < rects.size(); i++) {
160         circle(result, rects[i].center, 3, Scalar(0, 255, 0), -1);
161 
162         float minSize = (rects[i].size.width < rects[i].size.height) ? rects[i].size.width : rects[i].size.height;
163         //minSize = minSize - minSize*0.5;
164 
165         srand(time(NULL));
166         Mat mask;
167         mask.create(out.rows + 2, out.cols + 2, CV_8UC1);
168         mask = Scalar::all(0);
169         int loDiff = 30;
170         int upDiff = 30;
171         int connectivity = 4;
172         int newMaskVal = 255;
173         int NumSeeds = 10;
174         Rect ccomp;
175         int flags = connectivity + (newMaskVal << 8) + CV_FLOODFILL_FIXED_RANGE + CV_FLOODFILL_MASK_ONLY;
176 
177         for (int j = 0; j < NumSeeds; j++) {
178             Point seed;
179             seed.x = rects[i].center.x + rand() % (int)minSize - (minSize / 2);
180             seed.y = rects[i].center.y + rand() % (int)minSize - (minSize / 2);
181             circle(result, seed, 1, Scalar(0, 255, 255), -1);
182             int area = floodFill(out, mask, seed, Scalar(255, 0, 0), &ccomp, Scalar(loDiff, loDiff, loDiff), Scalar(upDiff, upDiff, upDiff), flags);
183         }
184         imshow("漫水填充", mask);
185         
186         vector<Point> pointsInterest;
187         Mat_<uchar>::iterator itMask = mask.begin<uchar>();
188         Mat_<uchar>::iterator end = mask.end<uchar>();
189         for (; itMask != end; ++itMask)
190             if (*itMask == 255)
191                 pointsInterest.push_back(itMask.pos());
192 
193         RotatedRect minRect = minAreaRect(pointsInterest);
194 
195         if (verifySizes(minRect)) {
196             // rotated rectangle drawing   
197             Point2f rect_points[4]; minRect.points(rect_points);
198             for (int j = 0; j < 4; j++)
199                 line(result, rect_points[j], rect_points[(j + 1) % 4], Scalar(0, 0, 255), 1, 8);
200 
201             //Get rotation matrix  
202             float r = (float)minRect.size.width / (float)minRect.size.height;
203             float angle = minRect.angle;
204             if (r < 1)
205                 angle = 90 + angle;
206             Mat rotmat = getRotationMatrix2D(minRect.center, angle, 1);
207 
208             //Create and rotate image  
209             Mat img_rotated;
210             warpAffine(out, img_rotated, rotmat, out.size(), CV_INTER_CUBIC);//實現旋轉
211 
212             //Crop image  
213             Size rect_size = minRect.size;
214             if (r < 1)
215                 swap(rect_size.width, rect_size.height);
216             Mat img_crop;
217             getRectSubPix(img_rotated, rect_size, minRect.center, img_crop);
218 
219             Mat resultResized;
220             resultResized.create(33, 144, CV_8UC3);
221             resize(img_crop, resultResized, resultResized.size(), 0, 0, INTER_CUBIC);;
222 
223             ////Equalize croped image  
224             Mat grayResult;
225             cvtColor(resultResized, grayResult, CV_BGR2GRAY);// CV_RGB2GRAY
226             blur(grayResult, grayResult, Size(3, 3));
227             grayResult = histeq(grayResult);
228 
229             if (1) {
230                 stringstream ss(stringstream::in | stringstream::out);
231                 ss << "haha" << "_" << i << ".jpg";
232                 imwrite(ss.str(), grayResult);
233             }
234 
235         }
236     }
237 }
238 
239 
240 int main() {
241     Mat img;
242     Mat out;
243     //Mat result;
244 
245     //載入圖片  
246     img = imread("test1.jpg");//, CV_LOAD_IMAGE_GRAYSCALE);
247     img.copyTo(out);
248     //imshow ("原始圖", img);
249     img = Gaussian(img);
250     imshow ("高斯模糊", img);
251 
252     img = Grayscale(img);
253     imshow("灰度化", img);
254 
255     img = Sobel(img);
256     imshow("Sobel_X", img);
257 
258     img = TwoValued(img);
259     imshow("二值化", img);
260 
261     img = Close(img);
262     imshow("閉操做", img);
263 
264     Contour(img, out);
265 
266     waitKey(0);
267     cvDestroyAllWindows();
268 }
View Code

 

總結:

車牌定位部分到此告一段落,但這方面的研究還要繼續。

本算法僅適用於特定的圖片,對於另外一些場合的圖片須要從新進行參數測試以判斷準確率。須要對算法進一步完善,實現複雜背景下車牌的識別。

同時想要獲得更準確的車牌區域,須要進行SVM訓練。

 

 注:

註解部分來自百度百科和http://www.opencv.org.cn

學習自——http://www.cnblogs.com/subconscious/p/3979988.html

相關文章
相關標籤/搜索