Opencv2.4學習::基於形態學處理+基本特徵實現車牌區域提取
阿新 • • 發佈:2018-12-24
基於形態學處理+基本特徵實現車牌區域提取
1、形態學梯度
實際上,提取車牌還是那個思路:區域分離->輪廓檢測->特徵判斷
這裡提供這樣一個演算法,來源於《OpenCV影象處理程式設計例項》
步驟如下:
- 邊緣檢測,檢測垂直邊緣,儘量減少橫向的邊緣連通車牌區域----->實現手段:形態學梯度、或者Sobel邊緣檢測的垂直方向,當然也可以用其他邊緣檢測方法
- 對邊緣實現二值化
- 區域填充,填補空洞----->實現手段:閉運算
- 輪廓檢測,找到車牌區域的輪廓----->實現手段:findContours
- 對找到的輪廓進行遍歷,根據車牌的特徵(寬高比、面積比、畫素等)進行篩選
- 輸出
實現程式碼:
#include<opencv2/imgproc/imgproc.hpp> #include<opencv2/core/core.hpp> #include<opencv2/highgui/highgui.hpp> #include<iostream> using namespace std; using namespace cv; Mat getPlate(int width, int height, Mat srcGray) { Mat result; //形態學梯度邊緣檢測 //用Mat(1,2) ,用來檢測出垂直的邊緣 //目的:儘量減少橫向的邊緣連通車牌區域,或者用Sobel邊緣檢測的單方向檢測也可以實現 /*方法1*/ morphologyEx(srcGray, result, MORPH_GRADIENT, Mat(1, 2, CV_8U, Scalar(1))); /*方法2*/ //Mat edgeYMat; ////求y方向的Sobel邊緣 //GaussianBlur(srcGray, srcGray, Size(3, 3),2); //Sobel(srcGray, edgeYMat, CV_16S, 2, 0, 3, 1, 0, BORDER_DEFAULT); //線性變換,轉換輸入陣列元素為8位無符號整形 //convertScaleAbs(edgeYMat, result); imshow("1result", result); //二值化 threshold(result, result, 255 * 0.1, 255, THRESH_BINARY); imshow("2,result", result); //水平方向閉運算 //閉運算:填補空洞 if (width >= 400 && width < 600){ morphologyEx(result, result, MORPH_CLOSE, Mat(1, 25, CV_8U, Scalar(1))); } else if(width>=200&&width<300){ morphologyEx(result, result, MORPH_CLOSE, Mat(1, 20, CV_8U, Scalar(1))); } else if (width>=600){ morphologyEx(result, result, MORPH_CLOSE, Mat(1, 28, CV_8U, Scalar(1))); } else { morphologyEx(result, result, MORPH_CLOSE, Mat(1, 15, CV_8U, Scalar(1))); } imshow("3,result", result); //垂直方向閉運算 if (width >= 400 && width < 600){ morphologyEx(result, result, MORPH_CLOSE, Mat(8, 1, CV_8U, Scalar(1))); } else if (width >= 200 && width<300){ morphologyEx(result, result, MORPH_CLOSE, Mat(6, 1, CV_8U, Scalar(1))); } else if (width >= 600){ morphologyEx(result, result, MORPH_CLOSE, Mat(10, 1, CV_8U, Scalar(1))); } else { morphologyEx(result, result, MORPH_CLOSE, Mat(4, 15, CV_8U, Scalar(1))); } imshow("4,result", result); return result; } int main() { Mat srcImg = imread("F:\\opencv_re_learn\\plate.jpg"); if (!srcImg.data){ cout << "failed to read" << endl; system("pause"); return -1; } Mat srcGray; cvtColor(srcImg, srcGray, CV_BGR2GRAY); Mat result = getPlate(400, 300, srcGray); //連通域檢測 vector<vector<Point>> blue_contours; vector<Rect>blue_rect; findContours(result.clone(), blue_contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(0, 0)); //連通域遍歷,車牌目標提取 for (size_t i = 0; i != blue_contours.size(); i++){ Rect rect = boundingRect(blue_contours[i]); //矩形區域寬高比 double wh_ratio = double(rect.width) / rect.height; //非零畫素點數,即白色畫素點數 int sub = countNonZero(result(rect)); //白色畫素佔比 double ratio = double(sub) / rect.area(); //車牌特徵,條件判斷 if (wh_ratio > 2 && wh_ratio < 8 && rect.height>12 && rect.width > 60 && ratio > 0.4){ rectangle(srcImg, rect, Scalar(0, 0, 255), 2, 8, 0); imshow("rect", srcGray(rect)); waitKey(0); } } imshow("SRCimg", srcImg); //imshow("result", result); waitKey(0); }
實現效果:
上面的:
- 1result:是邊緣檢測的結果,大量減少了橫向邊緣,只保留垂直的邊緣,以減少與車牌區域的聯通
- 2result:二值化
- 3result:水平方向閉運算的結果,填充水平方向的空洞
- 4result:垂直方向閉運算結果,填充垂直方向的空洞
- 實現的效果也是有點驚豔,在沒有用到分類器的情況下可以實現如此
當然也有侷限性:
- 受角度影響
- 要指定車牌大概畫素範圍
- 若車牌附近有與其連通的地方,則影響效果
- 車標、或其他類矩形元素會被誤認為是車牌區域