1. 程式人生 > >Opencv2.4學習::基於形態學處理+基本特徵實現車牌區域提取

Opencv2.4學習::基於形態學處理+基本特徵實現車牌區域提取

基於形態學處理+基本特徵實現車牌區域提取


1、形態學梯度

2、Sobel邊緣檢測


實際上,提取車牌還是那個思路:區域分離->輪廓檢測->特徵判斷


這裡提供這樣一個演算法,來源於《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:垂直方向閉運算結果,填充垂直方向的空洞
  • 實現的效果也是有點驚豔,在沒有用到分類器的情況下可以實現如此 

當然也有侷限性:

  • 受角度影響
  • 要指定車牌大概畫素範圍
  • 若車牌附近有與其連通的地方,則影響效果
  • 車標、或其他類矩形元素會被誤認為是車牌區域