ANN神经网络车牌识别 - (车牌识别3)
本章节视频讲解如下:
本章节将会带领大家如何基于ANN神经网络来实现车牌识别。
ANN神经网络的训练、测试、训练图片的制作,都已经在前两节讲了,为了广大网友能够更好的学习如何实现车牌识别,本章节将会给大家分别提供基于C#、C++、Python三种开发语言的车牌识别项目案例,该案例博主认为再优化优化,应该完全具备一定的商业价值,并非B站那种免费的、基于模板匹配的车牌识别案例。
这里博主要说明一下,由于样本训练比较耗时,博主这里每个车牌字符的样本只制作了100个,由于样本数的限制,本章节的案例并不能100%的将所有情况下的车牌都识别出来,如果想要提高识别成功率,童鞋们可以自行增加训练样本数,或者采用如yolo等其他识别方案。
博主列举了当前车牌识别方案中用到的几个重要的opencv相关技术,如下:
1、均值滤波
2、腐蚀
3、膨胀
4、轮廓查找
5、颜色查找
6、ANN神经网络
7、透视变换
8、轮廓提取
1、效果演示
在讲解车牌识别的流程之前,我们先来看下最终的识别效果(C#版本,无加速)

2、车牌识别流程讲解
下面我们来讲解一下当前车牌识别方案中重要的几个操作流程。
步骤1:
首先准备一张用于测试的图片(非原图,原图请在项目工程目录下获取)

接着使用Blur方法对图像进行滤波,然后直接进行对图像进行颜色识别,识别的颜色空间设置成车牌的颜色,识别效果如下,我们可以看到,通过颜色识别,我们可以很容易的将车牌所在位置查找出来。

步骤2:
通过颜色识别,将车牌识别出来之后,接着采用轮廓查找,并对轮廓采用面积判断、长宽比例判断的方式将车牌单独过滤出来。过滤的关键代码如下:
//绘制出最小面积的包围矩形
RotatedRect rect = Cv2.MinAreaRect(contours[i]);
int area = (int)(rect.Size.Width * rect.Size.Height);
float smin = rect.Size.Width > rect.Size.Height ? rect.Size.Height : rect.Size.Width;
float smax = rect.Size.Width > rect.Size.Height ? rect.Size.Width : rect.Size.Height;
float a = smin / smax;
// 进行轮廓的尺寸过滤,如果图片尺寸有变化,area的面积区间也要相应的做调整
if ((a > (0.3 - 0.08)) && (a < (0.3 + 0.14)) && area > 20000 && area < 900000)
{
/* 符合条件的区域 */
}
经过过滤后,将符合条件的轮廓绘制出来,结果如下(图中红色方框区域):

步骤3:
经过前面的操作,我们已经把车牌的轮廓位置给找到了。接下来,直接将轮廓单独裁剪出来,裁剪函数“Perspective_Crop()”,具体实现方法,请直接查看工程源码。
裁剪结果如下:

步骤4:
考虑到不可能每个识别图片的车牌角度都是正的,所以需要使用透视变换将车牌纠正。纠正方法如下:
1、缩小图像,留出一点空间用于透视变换。缩小结果如下:

2、再次使用颜色识别,将车牌区域单独提取处理,目的是为了剔除除车牌以外的其他干扰,为透视变换查找变换坐标做准备。结果如下:

3、查找透视变换的起始4个坐标点,查找方法过程演示如下。通过对图像四个角进行倾斜扫描,就可以找到透视变换的起点4个坐标了。

4、找到透视变换的4个坐标后,就可以对图像进行拉伸操作,起点为前面查找的4个坐标,目标点,则为整个车牌的4个角区域。

拉伸结果如下,从结果上看,此时车牌相对来说已经变的比较正了。

步骤5:
由于我们在进行ANN神经网络训练时,采用的模板为黑白二值图,所以,我们需要将纠正后的车牌进行二值化,这样才能保证和训练时用的模板保持一致。
二值化后的结果如下:

步骤6:
前期工作都准备就绪之后,接着就是将图像中的字符一个个裁剪出来,博主这里,裁剪的规格由于是固定的,所以裁剪之前,需要使用“Resize”将图像的尺寸统一。博主这里是直接将宽度固定,高度按比例缩放,之所以这么做,是因为在裁剪时,博主是按固定宽度进行裁剪的,高度的话,就直接使用图像原始高度,所以图像的宽度必须统一固定才行。
图像缩放算法如下:
double bl = (double)547 / (double)CropImg.Cols;
Cv2.Resize(CropImg, CropImg, new Size(CropImg.Cols * bl, CropImg.Rows * bl), 0, 0, InterpolationFlags.Area);
图像裁剪区域表如下:
Point[] moves = new Point[] {
new Point(1,86),
new Point(78,165),
new Point(175 + 75 * 0, 190 + 75 * 1),
new Point(165 + 75 * 1, 190 + 75 * 2),
new Point(165 + 75 * 2, 180 + 75 * 3),
new Point(165 + 75 * 3, 175 + 75 * 4),
new Point(165 + 75 * 4, 175 + 75 * 5)
};
将需要裁剪的区域通过不同颜色绘制出来,绘制结果如下:

步骤7:
将轮廓裁剪出来后,如数字和字母中,其实还是有很对干扰的,如下图:

我们可以看到,在字母“S”的周边存在很多白色干扰,这个其实是不利于我们进行识别的,所以需要剔除。剔除办法其实也很简单,就是对图像进行轮廓识别,同时保留面积最大的区域,其他区域全部用黑色进行填充。这种方法也不能100%完全剔除干扰,有时车牌上的固定用铆钉有时也会挡住字符,影响识别。(博主认为,只要有足够多的样本,并且在训练样本中将铆钉也算进去进行训练,也许能解决铆钉的干扰)。
如果有铆钉干扰,差不多是下面这种样子(铆钉和字符重叠),这样子的干扰,不太好滤除:

最后干扰过滤后的结果如下:

如果是汉字部分,就没办法用这种方法了,所以,汉字部分只能使用简单的面积过滤,将些较小的干扰剔除,但也没办法保证100%剔除干净。
步骤8:
到了这一步,车牌的字符基本已经全部提取出来,剩下的,就是直接将提取的图像采用前面章节讲到的方法,直接使用ANN神经网络进行识别。(如果图像看着比较正常,但是还是识别不出来,十有八九就是训练样本种类太少了,没有把当前的图像样式包含进去,增加样本数,重新训练xml文件即可解决)
最终识别结果如下,总共耗时0.23秒。

3、ANN神经网络训练说明
博主在测试过程中,发现前面第一个章节中提供的车牌训练样本在当前项目中识别成功率很低,原因在于训练的样本和当前工程生成的识别图像不一致导致的。为了提高识别成功率,博主针对当前项目,重新制作了训练样本,大家可以直接在下面链接中下载。
CSDN下载地址:车牌识别源码下载页
部分样本图片展示如下:


在车牌识别项目源码中,C#工程采用的是C#训练的xml文件,C++和Python工程采用C++训练的xml文件,原因在于,C#版本的xml文件在C++工程中的识别成功率有点低(具体原因可能兼容问题,也有可能是训练样本过少的问题,毕竟训练样本才100张)
C#版本的训练配置如下:
汉字部分的配置:
public int CLASSSUM = 11; // 图片共有11类
public int IMAGE_ROWS = 70; // 统一图片高度
public int IMAGE_COLS = 40; // 统一图片宽度
public int IMAGESSUM = 100; // 每一类图片张数
public string[] dirNum = new string[] {
"川", "赣", "贵", "桂", "鲁", "闽", "苏", "皖", "湘", "渝", "浙"
};
public float[,] trainingData = new float[11 * 100, 40 * 70]; // 每一行一个训练图片
public float[,] labels = new float[11 * 100, 11]; // 训练样本标签
public string outName = "Chanese.xml";
数字和字母部分的配置:
public int CLASSSUM = 34; // 图片共有(24+10)类
public int IMAGE_ROWS = 70; // 统一图片高度
public int IMAGE_COLS = 40; // 统一图片宽度
public int IMAGESSUM = 100; // 每一类图片张数
public string[] dirNum = new string[] {
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
"A", "B", "C", "D", "E", "F", "G", "H", "J", "K",
"L", "M", "N", "P", "Q", "R", "S", "T", "U", "V",
"W", "X", "Y", "Z" };
public float[,] trainingData = new float[34 * 100, 40 * 70]; // 每一行一个训练图片
public float[,] labels = new float[34 * 100, 34]; // 训练样本标签
public string outName = "EnglishAndNumber.xml";
C++版本的训练配置如下:
汉字部分的配置:
#define CLASSSUM 11 // 图片共有(24+10)类
#define IMAGE_ROWS 30 // 统一图片高度
#define IMAGE_COLS 30 // 统一图片宽度
#define IMAGESSUM 100 // 每一类图片张数
std::string dirNum[CLASSSUM] = { "川", "赣", "贵", "桂", "鲁", "闽", "苏", "皖", "湘", "渝", "浙" };
float trainingData[CLASSSUM * IMAGESSUM][IMAGE_ROWS * IMAGE_COLS] = { { 0 } }; // 每一行一个训练图片
float labels[CLASSSUM * IMAGESSUM][CLASSSUM] = { { 0 } }; // 训练样本标签
string outName = "Chanese.xml";
数字和字母部分的配置:
#define CLASSSUM 34 // 图片共有(24+10)类
#define IMAGE_ROWS 30 // 统一图片高度
#define IMAGE_COLS 30 // 统一图片宽度
#define IMAGESSUM 100 // 每一类图片张数
std::string dirNum[CLASSSUM] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };
float trainingData[CLASSSUM * IMAGESSUM][IMAGE_ROWS * IMAGE_COLS] = { { 0 } }; // 每一行一个训练图片
float labels[CLASSSUM * IMAGESSUM][CLASSSUM] = { { 0 } }; // 训练样本标签
string outName = "EnglishAndNumber.xml";
当前项目ANN神经网络训练核心代码以及xml文件下载如下:
站内下载地址:车牌识别源码下载页
运行方法:将代码替换到车牌识别第一讲的项目工程中即可
4、各版本源码下载
说明:
以下三个版本运行逻辑、函数名字完全一致,C#版本带有部分调试代码,C++和python调试代码相对较少,算是比较干净的。
由于测试图片和视频文件较大,博主把它放在了CSDN上,供大伙儿免费下载,每个工程源码中都提供了一张测试图片。
车牌识别源码下载页