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:垂直方向闭运算结果,填充垂直方向的空洞
  • 实现的效果也是有点惊艳,在没有用到分类器的情况下可以实现如此 

当然也有局限性:

  • 受角度影响
  • 要指定车牌大概像素范围
  • 若车牌附近有与其连通的地方,则影响效果
  • 车标、或其他类矩形元素会被误认为是车牌区域

猜你喜欢

转载自blog.csdn.net/dieju8330/article/details/84348634