车牌识别项目——当前技术(1)

不多说别的了,现在简单总结一下几个模块的技术问题和我的感想。

2. 目前使用的技术

车牌识别的模块虽然有时候不那么清楚,但大体还是划分为三个部分:定位,分割和识别。这中间涉及很多无法被包含在这三个模块中的重要功能,比如去噪、二值化、倾斜校正。当前是深度学习火爆的年代,我也从网络上看到过不分割直接识别的深度学习方法,与连笔手写体的识别有些类似之处。

因为导师对新技术有些抵触(主要因为它对硬件的需求),所以我还是采用比较传统的技术来做,模块划分也采用定位、分割、识别的传统划分。

2.1. 车牌定位

目前采用的是竖直边缘加数学形态学的方法。在提取候选区域后,再使用先验知识(车牌区域形状,字符个数,位置等等)进行筛选。

2.1.1 竖直边缘和数学形态学

这种方法网上有各种讲解和实现,在开源的车牌识别库EasyPR和OpenALPR中也都有比较不错的实现,原理我这里不再多说。事实上所有技术的原理我今天都不再多说了,因为都是各种基本的套路。

这种方法的好处是简单和有效,在比较好的参数下,对大多数车牌都能定位到车牌区域。因此初期一直考虑在这种方法基础上进行修改。

这种方法有几个难以解决的缺陷,导致参数的调整十分繁琐:

  1. 对使用场景必须具有很好的了解。比如车牌的大体位置,车牌在整个场景中所占百分比等等。为了使车牌内容能够完全融合,必须要知道字符竖直边缘之间的最大距离。由于中国车牌在第二第三个字符之间有一个时隐时现的白点,而第二和第三个字符的差异可能也会导致它们之间的距离比较远,因此形态学SE形状的选择十分棘手。
  2. 对异常光照敏感。夜间补光灯会打在车体上,不管什么颜色的车,都会产生亮白的光斑,车牌周围的区域极易与车牌融合在一起,可能会产生很大的区域。
  3. 永远达不到精确,有时候会包含很多车牌周围的背景。

在对这种方法的各种试验,包括调整参数,根据先验进行筛选,等等之后,因为鲁棒性不好,环境情况复杂,我开始考虑其他的思路。

2.1.2 目标识别思路

在国外的开源车牌项目OpenALPR中,使用的是Cascade Classifier进行定位,整体思路是目标识别中常用的Haar-like或LBP特征配合Cascade Adaboost分类器,在多尺度上识别场景中的目标物体。OpenALPR中使用的是LBP特征,有关这种特征网上也能查到很多介绍,跟HOG,Haar之类的差不多,都不复杂。使用LBP比Haar好的地方是训练速度会快很多,因为全部是整数。
图像特征提取三大法宝:HOG特征,LBP特征,Haar特征
这种方法之所以去尝试,是因为最近获得了2000多张车牌照片,感觉在这些数据的支持下应该能去尝试机器学习的方法。

这种方法我从上一周就开始尝试,但是取得的效果不尽如人意。我想主要因为以下几个方面的问题:

  • 正样本是用EasyPR的定位方法获取的,没有完全获取到所有的车牌,而且获取到的车牌经过了倾斜校正。某些正样本不包含完整的车牌区域,例如汉字缺失。
  • 负样本中混有一些包含了正样本的区域。因为太多而没有仔细去筛选。
  • 对整个方法各种不了解,没有好好去决定参数和选取样本。

昨天一天我用imageclipper工具标定了这些数据,把所有的车牌区域都截取了出来。这个工具虽然简单但是很好用,应该是专为标数据而生的,在windows下面编译有问题,直接在这里可以下到binary。这种近似苦力的工作虽然让人难受,但是大量数据也是训练分类器所必须的。对于负样本,就在场景图片中随机截取不包含车牌的背景就好。在网上看到的经验中,对正负样本的个数众说纷纭,有说1比5,有说1比3,也有说2比1。这种事情还是自己去尝试一下比较好。

在最新一轮的训练中,我使用了正样本2300张,负样本6000张。

正样本例子:
车牌识别项目——当前技术(1)_第1张图片

负样本例子:
车牌识别项目——当前技术(1)_第2张图片

在有数据之后,使用opencv的createsamples和traincascade命令进行训练。在opencv的官方文档中有详细说明。 为了快速生成样本描述文件、生成不包含正样本的负样本区域、生成训练参数,我写了一些python脚本来辅助进行训练。这些脚本都很简单,很容易修改。

在opencv提供的训练程序中, opencv_createsamples.exe这个程序可以指定一张图片,生成多个包含该图片的正样本vec文件;也可以根据正样本描述文件来生成vec文件。如果是前者,那么正样本会被进行一定的变换之后,随机放置在一些负样本上,从而增加正样本的个数。因此此时还需要指定负样本描述文件。根据opencv官方文档中的描述,这种方法通常适用于纯刚性物体,例如opencv的徽标。如果是后者,那么就不会对正样本进行变形。

如果用第一种方法建立.vec文件,那么多个车牌将会生成多个.vec文件。我的python脚本中有一个merge_vec.py文件,可以用来合并这些成为单独的.vec文件。

我尝试了这种方法之后发现,由于会把正样本放置在负样本中,因此定位的结果中会带有很多不包含车牌的区域。因此我认为这种方法并不适合我们的应用。

另外,在训练过程中,还有以下几个问题需要注意:

  • 负样本描述文件中的路径。在opencv_createsample.exe中,你需要填写由图片目录开始的相对路径。例如,你把图片存在neg/img中,描述文件中的每行你需要填写img/filename。而在opencv_traincascade.exe中,你需要填写由当前目录开始的相对路径,例如neg/img/filename。这应当属于opencv设计当中的问题。
  • 正负样本个数的比例,这个前面已经提到过了,众说纷纭。
  • opencv_traincascade.exe的numPos和numNeg参数,需要合理地填写,如果填写的参数大于实际的样本个数,会在运行时或训练中报错。这些参数都要填地稍低于实际样本个数。
  • 负样本中一定不能包含车牌区域,甚至是一部分也不可以。我的python脚本中的gen_neg.py可以根据imageclipper得到的结果,在生成负样本时避开正样本区域。

虽然我经历过的分类或回归问题并不多,但是也总结了一个很重要的点,那就是最重要的不是用什么模型(Adaboost, SVM, ANN),而是使用的数据,即特征。至少在提到的这些传统的方法里面(我没接触过深度网络),最核心的还是提取的特征,这些特征是否能够区分类别。

在目标识别的方法中,除了Haar-like+Adaboost的方法,还有HOG+SVM等方法,我查到过的一些模型选择和特征提取方面的记录:
行人检测(haar+adaboost 与 hog+SVM)
Is Haar Cascade the only available technique for image recognition in OpenCV
For car detection, which one is better: Haar like feature adaBoost based detector or HoG-SVM based detector?

以下内容为8月19日编辑

经过这次重新训练,整个定位器的精度已经好了很多。
虽然还是有如下图这样字符偏向一侧的情况,但是考虑到这是纯分类器的输出,已经很不错了,至少比一开始的数学形态学定位要好了很多。在后面的过程中还会根据情况进行一些调整。

8月19日定位测试结果
车牌识别项目——当前技术(1)_第3张图片

这种定位方法最好的地方是,输出的非车牌区域真的很少。也可能是负样本个数比较多的原因?
另外,我指定的级联分类器层数是17,但是训练到12层就报错了,不知道是什么原因。今天我要把整个过程再来一遍。查看一下中间是否有问题。

经过上述初步试验,我认为haar-like+adaboost的方法可取。
之前逛知乎的时候搜opencv这个话题,发现很多人在真正做工程时是不用这个库,而是很多东西要去自己实现的。这几天读opencv关于cascade classifier这方面的文档,发现使用的技术已经是10几年前论文中的技术。从论文到工程,再到开源工程,这个路线是太漫长了。我们这样缺乏技术能力的团队,也很难具有快速的论文转换为工程的能力。不去深挖整个技术,我认为是很难取得好的效果的。

Anyway,我要去阅读一下关于Adaboost以及Cascade Classifier的这几篇文章,主要看看能否去修改一下训练器中使用的特征。

Learning Multi-scale Block Local Binary Patterns for Face Recognition

Rapid Object Detection Using a Boosted Cascade of Simple Features

An Extended Set of Haar-like Features for Rapid Object Detection

你可能感兴趣的:(技术,opencv,c++,机器学习)