基于神经网络的车牌定位识别系统

前言

前段时间学习完OpenCV的基础部分后,做了简单的二维码识别与条形码识别,最近做了一下这个车牌识别系统,本来是OpenCv还没有学到机器学习的位置,但是结合之前参加数学建模的时候对神经网络的了解,做了这个车牌字符的训练与识别。

目录:

目录

前言

目录:

处理步骤

1 图像预处理与车牌定位

1.1 灰度处理-滤波-索贝尔求导-二值化

1.2 开操作-去除小的对象

1.3 闭操作-填充小的孔,使车牌特征连在一起

1.4 findContours与轮廓筛选

1.5 将筛选后的矩形在原图上标出,并在灰度图中抠出该矩形位置

2 字符分割

2.1预处理

2.2 分割

3 字符识别

3.1 神经网络训练

3.2  神经网络预测识别字符

源码分享


处理步骤

  1. 图像预处理与车牌定位
  2. 字符分割
  3. 神经网络训练与识别

1 图像预处理与车牌定位

输入图像为:

1.1 灰度处理-滤波-索贝尔求导-二值化

车牌为横向放置,观察车牌可发现车牌类似与条形码,竖向特征较多,所以用Sobel进行X方向求导,并进行自适应二值化

1.2 开操作-去除小的对象

1.3 闭操作-填充小的孔,使车牌特征连在一起

1.4 findContours与轮廓筛选

运用findContours函数寻找轮廓,并做出最小矩形,通过对矩形的筛选,选出我们想要的车牌的位置;

小型汽车车牌尺寸为440mm×140mm,宽高比为3.143;

故:筛选条件:rect1.width/ rect1.height >= 3 && rect1.width / rect1.height <=4

1.5 将筛选后的矩形在原图上标出,并在灰度图中抠出该矩形位置


2 字符分割

2.1预处理

对上一步的结果result进行滤波和二值化

2.2 分割

前段时间刚刚学了分水岭算法,所以起初是准备用分水岭算法实现的结果如下

效果很不理想:

  1. 汉字 “沪” 用分水岭分割智能分割到marks标记的那一部分
  2. K不知道什么原因,溢出到了背景色上

那就换个思路:

观察二值化后的图发现,没有字符的部分是全黑色的,即不存在数值大于0的点

统计每一列数值大于0的点的个数,如下:

00000000061610011823198644613110000019292925977810131918181510400000013100000293030217667911151920201590000008111311111110111113161519181370000071925231613129910131626262011000004162327191512101111111423272013000000112126221511119910142127221600005500 

可以发现,当连续0的个数大于4个时,0序列两侧,即为我们想要的分割线。

分割图:

将分割后的图片输出:


3 字符识别

3.1 神经网络训练

特征向量的定义:将图片分为8*4个方块,计算每一个方块内灰度值的综合除以整张图片灰度值的总和,再按顺序排列成长度为32的行向量,即为特征向量。

这里我只选择了对数字和字母进行训练,每个字符有100个样本,所以输入的训练样本数列为 Mat inputs(34*100, 32, CV_32FC1)

训练输出矩阵为 Mat response(3400, 34, CV_32FC1); 

设置规则:若训练图片为1,则response(x,1)=1,该行其余为0

                  若训练图片为5,则response(x,5)=1,该行其余为0。

设置神经网络过程如下:

	/**************神经网络设置与训练*******/
        cout << "开始训练......." << endl;
	Mat layerSizes = (Mat_<int>(1, 3) << 32, 32, 34); //设置神经网络参数输入层为32,中间层32,输出层34
	
	Ptr<ANN_MLP> bp = ANN_MLP::create(); //创建空模型
	bp->setLayerSizes(layerSizes);       //输入神经元结构
	bp->setTrainMethod(ml::ANN_MLP::BACKPROP, 0.1, 0.1); //训练方法为反向传播方法
	bp->setActivationFunction(ml::ANN_MLP::SIGMOID_SYM); //指定激活函数
	//bp->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 5000, 0.01)); //终止条件
	bp->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER | TermCriteria::EPS, 5000, 0.0001));
	bool trained=bp->train(inputs, ROW_SAMPLE, response);

	cout << "训练结束!" << endl << endl;
	bp->save("bp_trained.xml"); //保存训练好的神经网络

3.2  神经网络预测识别字符

读取训练好的神经网络参数,对输入图片归一化为特征向量后,bp->predict进行预测。

 这里我设置了一个函数,输入字符图片,输出字符

char disting(Mat inputA)
{
	Ptr<ANN_MLP> bp = ANN_MLP::load("C:/Users/LBJ/Desktop/神经网络训练/OpenCVTest/bp_trained.xml");

	vector<double> sample;

	Mat output;

	division(inputA, sample);
	convers(sample);

	bp->predict(sample, output);
	double min, max;
	Point minLoc, maxLoc;
	minMaxLoc(output, &min, &max, &minLoc, &maxLoc, Mat());
	//cout << endl << maxLoc << endl;
	char re;
	if (maxLoc.x<=9)
	{
		re = '0' + maxLoc.x;
	}
	else if (maxLoc.x <= 17 && maxLoc.x > 9)
	{
		re = '0' + maxLoc.x;
	}
	else if (maxLoc.x > 17 && maxLoc.x<=22)
	{
		re = '0' + maxLoc.x + 8;
	}
	else if (maxLoc.x > 22)
	{
		re = '0' + maxLoc.x + 9;
	}
	return re;
}

   分别输入 分割好的字符图片,输出结果如下:

识别正确。

 

源码分享

神经网路训练源码:

#include<opencv2/opencv.hpp>
#include<opencv2/core/mat.hpp>
#include<iostream>
#include<vector>
#include<opencv2/ml/ml.hpp>

using namespace std;
using namespace cv;
using namespace ml;
int sumMat(Mat inputA);  //求和函数
void division(Mat intput,vector<double>& output); //归一化为8*4的向量
void convers(vector<double>& output);            //归一化为和为1向量

string adress="C:/Users/LBJ/Desktop/神经网络训练/字符训练图片";
vector<string> Adr_id(3400);           //图片地址向量 
vector<vector<double>> divi(3400);     //图片特征向量,每个图片有 4*8=32个特征;

int main()
{	

	
	for (size_t i = 0; i < 34*100; i=i+100)
	{

		for (size_t j = 0; j < 100; j++)
		{
			Adr_id[i + j] = adress + "/" + to_string(i / 100) + "/z" + to_string(i + j + 1) + ".jpg";;
			cout << Adr_id[i + j] << endl;
			Mat src = imread(Adr_id[i + j]);
	        if (!src.data)
			{
				cout << "The iamge is empty" << endl;
				return -1;
			}
			//imshow("src", src);
			cvtColor(src, src, CV_RGB2GRAY);
			division(src, divi[i+j]);
			convers(divi[i + j]);
		}
	
	}

	Mat inputs(3400, 32, CV_32FC1);   //设置训练样本 ,将divi存在input里边
	for (size_t i = 0; i < 3400; i++)
	{
		float* data = inputs.ptr<float>(i);
		for (size_t j = 0; j < 32; j++)
			data[j] = divi[i][j];
	}

	Mat response(3400, 34, CV_32FC1); //设置输出矩阵    
	for (size_t i = 0; i < 3400; i++)
	{
		float* data = response.ptr<float>(i);
		for (size_t j = 0; j < 34; j++)
		{
			if(j==i/100)
				data[j] = 1;
			else
			{
				data[j] = 0;
			}
		}

	}

	cout << endl << endl << response;

	cout << "开始训练。。。。。" << endl;


	/**************神经网络设置与训练*******/
	Mat layerSizes = (Mat_<int>(1, 3) << 32, 32, 34); //设置神经网络参数输入层为32,中间层32,输出层34
	
	Ptr<ANN_MLP> bp = ANN_MLP::create(); //创建空模型
	bp->setLayerSizes(layerSizes);       //输入神经元结构
	bp->setTrainMethod(ml::ANN_MLP::BACKPROP, 0.1, 0.1); //训练方法为反向传播方法
	bp->setActivationFunction(ml::ANN_MLP::SIGMOID_SYM); //指定激活函数
	//bp->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 5000, 0.01)); //终止条件
	bp->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER | TermCriteria::EPS, 10000, 0.0001));
	bool trained=bp->train(inputs, ROW_SAMPLE, response);

	cout << "训练结束!" << endl << endl;
	bp->save("bp_trained.xml"); //保存训练好的神经网络

	
	/**************神经网络预测*******/

	Mat src = imread("C:/Users/LBJ/Desktop/神经网络训练/char3.jpg");
	cvtColor(src, src, CV_BGR2GRAY);
	vector<double> sample;
	division(src, sample);
	convers(sample);
	
	Mat result(Size(1, 34), CV_32FC1);
	bp->predict(sample, result);

	cout << endl << result << endl;

	double min, max;
	Point minLoc, maxLoc;
	minMaxLoc(result, &min, &max, &minLoc, &maxLoc, Mat());
	
	cout << maxLoc;
	getchar();
	return 0;
}

int sumMat(Mat inputA)
{
	int sum=0;
	for (size_t i = 0; i < inputA.cols; i++)
	{
		for (size_t j = 0; j < inputA.rows; j++)
		{
			sum += inputA.at<uchar>(i, j);
		}
	}
	return sum;

}

void division(Mat intput, vector<double>& output)
{
	
	for (size_t i = 0; i < intput.rows; i=i+4)
	{
		for (size_t j = 0;  j < intput.cols;  j=j+4)
		{
			
			output.push_back(sumMat(intput(Rect(j, i, 4, 4))));
		}

	}


}
void convers(vector<double>& output)
{
	double sum = 0;
	for (size_t k = 0; k < output.size(); k++)
	{

		sum += output[k];
	}
	for (size_t k = 0; k < output.size(); k++)
	{
		output[k] = output[k] / sum;

	}

}

车牌预处理--字符分割与识别源码:

#include<opencv2/opencv.hpp>
#include<opencv2/core/mat.hpp>
#include<iostream>
#include<vector>
#include<opencv2/ml/ml.hpp>
using namespace std;
using namespace cv;
using namespace ml;

int sumMat(Mat inputA);
void division(Mat intput, vector<double>& output); //归一化为8*4的向量
void convers(vector<double>& output);            //归一化为和为1向量
char disting(Mat inputA);                       //图片识别,输入图片,输出识别的字符

Mat src, dst;

int main()
{
	src = imread("C:/Users/LBJ/Desktop/车牌识别/1 - 副本.jpg");
	if (!src.data)
	{
		cout << "The iamge is empty" << endl;
		return -1;
	}

	imshow("input_imge", src);
	
	/****************灰度处理,提取边缘,二值化********/
	Mat src_gray,Gblur;
	cvtColor(src, src_gray, CV_BGR2GRAY);
	GaussianBlur(src_gray, Gblur, Size(3, 3), 0, 0);
	Mat canny;

	Sobel(Gblur, canny, CV_32F, 1, 0, 3);
	convertScaleAbs(canny, canny);
	medianBlur(canny, canny,5);
	threshold(canny, canny, 40, 255, THRESH_BINARY | THRESH_OTSU);
	imshow("canny", canny);

	Mat open;
	Mat structure = getStructuringElement(MORPH_RECT, Size(5, 5), Point(-1, -1));
	morphologyEx(canny, open, CV_MOP_OPEN, structure, Point(-1, -1), 1);
	imshow("open", open);

	Mat close;
	structure = getStructuringElement(MORPH_RECT, Size(13, 9 ), Point(-1, -1));
	morphologyEx(open, close, CV_MOP_CLOSE, structure, Point(-1, -1), 1);
	imshow("close", close);
	//
	/*************提取轮廓************/
	vector<vector<Point>> contours;
	vector<Vec4i> hierachy;
	findContours(close, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
	//
	///**************筛选出长宽比为3.14左右,矩形度高的轮廓*************/

	Rect rect1,rect;
	dst = Mat::zeros(src.size(), CV_8UC3);
	RNG rng(12345);


	for (size_t i = 0; i < contours.size(); i++)
	{
		rect1 = boundingRect(contours[i]);
		
		if ( rect1.width/ rect1.height >= 3 && rect1.width / rect1.height <=4)
		{
			rect = rect1;
	      
		}
	
		Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
		drawContours(dst, contours, i, color, 2, 8, hierachy, 0, Point(0, 0));
	}
//	imshow("dst", dst);

	Mat result;
	result = src_gray(Rect(rect));
	rectangle(src, rect, Scalar(0, 255, 0), 2);
	imshow("output_img", src);
	imshow("Result", result);

/*************车牌分割**************/


	medianBlur(result, result, 3);
	threshold(result, result, 180, 255, THRESH_BINARY );
	imshow("thresh", result);

	//cout << "rows" << endl;
	vector<int> row_count(result.rows);
	for (size_t i = 0; i < result.rows; i++)
	{
		row_count[i] = 0;
		for (size_t j = 0; j < result.cols; j++)
		{
			if (result.at<uchar>(i, j) > 0)
			{
				row_count[i]++;
			}

		}
		//cout << row_count[i];
	}
	cout << endl;

	int high, low;
	for (size_t i = 0; i < result.rows; i++)
	{
		if (row_count[i] != 0)
		{
			high = i; break;
		}
	}
	for (size_t i = result.rows -1 ; i >0; i--)
	{
		if (row_count[i] != 0)
		{
			low = i; break;
		}
	}
	result = result.rowRange(high, low);

	vector<int> count(result.cols),limit;
	for (size_t i = 0; i < result.cols; i++)
	{
		count[i] = 0;
		for (size_t j = 0; j < result.rows; j++)
		{
			if (result.at<uchar>(j,i)>0)
			{
				count[i]++;
			}

		}
		cout << count[i];
	}

	cout << endl;


	int k;
	for (size_t i = 0; i < result.cols; i++)
	{
	
		if (count[i]==0)
		{
			k = i; //储存一下i的起始值
			int zeros=0;
			while (count[i] == 0)
			{
				i++;
				zeros++;

				if (i>= result.cols)
				{
					break;
				}
			}
			//cout << zeros<<", ";

			if (zeros >= 4)
			{
				limit.push_back(k);
				limit.push_back(i);
			}
		}

	}

	Mat char1, char2, char3, char4, char5, char6, char7;
	char1 = result.colRange(limit[1]-2, limit[2]+2);
	char2 = result.colRange(limit[3]-2, limit[4]+2);
	char3 = result.colRange(limit[7]-2, limit[8]+2);
	char4 = result.colRange(limit[9]-2, limit[10]+2);
	char5 = result.colRange(limit[11]-2, limit[12]+2);
	char6 = result.colRange(limit[13]-2, limit[14]+2);
	char7 = result.colRange(limit[15]-2, limit[16]+2);
	resize(char1, char1, Size(16, 32));
	resize(char2, char2, Size(16, 32));
	resize(char3, char3, Size(16, 32));
	resize(char4, char4, Size(16, 32));
	resize(char5, char5, Size(16, 32));
	resize(char6, char6, Size(16, 32));
	resize(char7, char7, Size(16, 32));
	imshow("char1", char1);
	imshow("char2", char2);
	imshow("char3", char3);
	imshow("char4", char4);
	imshow("char5", char5);
	imshow("char6", char6);
	imshow("char7", char7);


/**************字符识别****************///读取训练好的神经网络参数
	cout << disting(char2) << " "
		<< disting(char3) << " "
		<< disting(char4) << " "
		<< disting(char5) << " "
		<< disting(char6) << " "
		<< disting(char7) << " ";

	waitKey(0);
	return 0;
}

int sumMat(Mat inputA)
{
	int sum = 0;
	for (size_t i = 0; i < inputA.cols; i++)
	{
		for (size_t j = 0; j < inputA.rows; j++)
		{
			sum += inputA.at<uchar>(i, j);
		}
	}
	return sum;

}

void division(Mat intput, vector<double>& output)
{

	for (size_t i = 0; i < intput.rows; i = i + 4)
	{
		for (size_t j = 0; j < intput.cols; j = j + 4)
		{

			output.push_back(sumMat(intput(Rect(j, i, 4, 4))));
		}

	}


}
void convers(vector<double>& output)
{
	double sum = 0;
	for (size_t k = 0; k < output.size(); k++)
	{

		sum += output[k];
	}
	for (size_t k = 0; k < output.size(); k++)
	{
		output[k] = output[k] / sum;

	}

}

char disting(Mat inputA)
{
	Ptr<ANN_MLP> bp = ANN_MLP::load("C:/Users/LBJ/Desktop/神经网络训练/OpenCVTest/bp_trained.xml");

	vector<double> sample;

	Mat output;

	division(inputA, sample);
	convers(sample);

	bp->predict(sample, output);
	double min, max;
	Point minLoc, maxLoc;
	minMaxLoc(output, &min, &max, &minLoc, &maxLoc, Mat());
	//cout << endl << maxLoc << endl;
	char re;
	if (maxLoc.x<=9)
	{
		re = '0' + maxLoc.x;
	}
	else if (maxLoc.x <= 17 && maxLoc.x > 9)
	{
		re = '0' + maxLoc.x;
	}
	else if (maxLoc.x > 17 && maxLoc.x<=22)
	{
		re = '0' + maxLoc.x + 8;
	}
	else if (maxLoc.x > 22)
	{
		re = '0' + maxLoc.x + 9;
	}
	return re;

	

}

 

 

 

 

 

 

 

       

  • 3
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值