MATLAB实现的车牌定位系统
看完《数字图像处理后》,做的图像识别的入门级项目,代码在: https://github.com/zhoulukuan/Plate-Location ,论文都可以在知网里找到,我就不贴了。具体项目的一些简介可以看github,大致上讲,可以分为颜色检测、区域操作、车牌检测算法和夜晚下的Retinex算法四个环节。
颜色检测
选用颜色检测的好处是在于定位比较精准。选中图片中特定的颜色区域,然后每个区域单独拿出来检测,可以提高检测速度。相对于对图片灰度化,然后进行算法增强与补偿的思路来说,颜色检测对图片信息的利用更加充分。缺点也很明显,就是对于某些强光或者异物(如污泥)引发的车牌颜色变化没有很好的检测能力。
颜色检测部分我较多地参考了:
陈昌涛.基于彩色和黑白纹理分析的车牌定位方法[D].重庆:重庆大学,2008
主要思路是在HSV模式下计算颜色与标准颜色相似度。从colorDetection.m文件中,可以看到,针对区域和非区域、白天和夜晚、各种颜色的参数都是不同。这里区域和非区域的差别可能比较难理解。具体来说,就是区域颜色检测是为了框出特定颜色区域,寻找车牌可能的所在区域的;而非区域则是为了检测特定的颜色边缘,为寻找某种颜色的边缘点做投影准备的。因此非区域阈值可以设置得窄一点,区域阈值可以设置大一点。
其实最主要的,设定区域和非区域参数的原因是区域的话需要进行一系列的形态学操作。我的步骤是:去除小区域——膨胀——进一步去除小区域。第一个去除小区域是排除大量干扰点,这里的面积阈值可以设置得比较小。随后的膨胀操作,是为了将车牌在颜色检测中得到的可能有断裂情况的车牌不同区域连成一体。最后,再次去除小区域的面积阈值可以设置大一些,是为了进一步过滤区域,减小工作量。不一步到位的原因是,若先去除区域,则可能将车牌断裂的小区域去除,区域不完整;若先膨胀,则可能引入大量不同区域连在一起造成的干扰。
区域操作
区域操作主要是我发现直接进行车牌检测干扰比较多,所以设置了很多检验条件(虽然好像也没什么卵用)。区域提取主要是依靠regionprops函数实现的。
一开始实际上我打算使用一个区域归并算法,顺便替代掉上面说的膨胀操作,因为膨胀可能会把车牌和周边区域连起来,引入干扰,这个算法在这里有提到:
陈寅鹏,丁晓青.复杂车辆图像中的车牌定位与字符分割方法[J].红外与激光工程
不过实际使用效果很差,大概因为我的样本库比较杂和刁钻,有很多倾斜度过高的图片,所以垂直重合率很低。我还是在代码里写了这个算法,真实的效果不得而知。
另外就是根据区域里蓝色点的比例之类的,区域大小之类的做做基本的排除工作了。
车牌检测
投影点选择
很多论文提到的思路都是差分绝对值投影,我个人感觉是不太靠谱的,因为对信息的利用率还是太低了。相比较而言,第一篇论文提到的提取白色区域和蓝色区域,计算边缘点然后做与,得到蓝白边缘点,然后计算蓝白交界点做投影就会更好一些。
边缘提取的话我用的是colorLP方法和OTSU阈值化方法,使用纵向sobel算子应该也可以,感觉差距不是很大。不过我只求了蓝色边缘点然后做投影,原因还是样本库太杂的问题。给我的样本库很多网上搜集的小图片,可能车牌大小还不到40*20这样子,打开一看都是像素点,白色的文字基本和蓝色背景混成一堆,颜色混叠了……
实际看了下,蓝色边缘和蓝白边缘区别不是很大的,不过真的图片尺寸理想的话,最好的还是用蓝白边缘交界。顺带一说,我感觉用白色边缘点膨胀然后与蓝色边缘点相与比较好,原论文的算法,检测出的点真的太少,可能这样会更准确一些?不过我感觉车牌纹理特征体现的不明显就是了……
计算具体区域
个人觉得这部分应该是整个项目最复杂的部分。简单说下思路:
水平方向,检测连续多波峰特征,指的是车牌纹理蓝色边缘点投影会连续出现多个高峰:
1、获取函数所有极大值,极大值最小间隙为图片宽/20,用极大值序列中第三高的峰值的0.6作为阈值。之所以选择第三高的峰值来获取阈值是因为在运行时发现区域两边会有几率出现0~2个干扰,如果干扰是峰值极大的情况,很可能会影响判断。而极大值最小间隙的存在是为了防止相近的峰被误检测。假设车牌最小宽度占总区域的1/3,则七个字符,每个字符高峰的距离为1/20。
2、获取大于阈值的所有峰值及其位置。依次检索每个峰,判断某个峰的位置和它前一个峰是否接近,若接近则认定检索峰和前面的峰属于同一个可能的“连续多波峰区域”;否则则认为检索峰属于新的“连续多波峰区域”。
比如我们检索到大于阈值的有8个峰,前7个峰都很接近,第8个峰离第7个较远,最后的区域划分是[1 7]和[8 8],代表1-7峰属于一个“连续多波峰区域”,而8峰则是一个单独的“连续多波峰区域”。
距离是否足够接近是由需要判断的峰和前面最后一个峰的距离与平均距离的比值判断的。
3、将包含最多峰数的区域视为车牌可能存在区域。若该区域所含峰数过少或宽度过短,则直接判断不含车牌;否则,从该区域的第一个峰向左检索,若某点接近零且该点的左面不含有大于阈值的峰,则该点视为车牌开始坐标;从该区域的最后一个峰向右检索,遇到的第一个接近零的点视为车牌结束坐标。接近零代表该点值小于峰值阈值的5%。
之所以开始坐标还要有左面不含有大于阈值的峰值要求,是因为在调试时发现,若左边的开始坐标仅以接近零为条件,程序会将车牌“XX·XXXXX”中的点作为开始位置。不过考虑中间的点确实有时候太小,会把车牌隔断,我后来把最大区域所含峰过少就判断不含车牌改成了,最大区域所含峰或者最大和次大区域所含峰之和过小,一定程度上也减小了这个问题。
垂直方向:
1、获取函数的最大值及其位置,设置最大值5%为阈值。
2、从最大值位置向左检索,若某点接近零且该点的左面不含有大于阈值的峰,则该点视为开始点;从最大值位置向右检索,若某点接近零且该点的右面不含有大于阈值的峰,则该点视为结束点。
波峰要求同样是为了防止检索到的车牌不完整而设置的。
3、若垂直方向的宽度比例过短,则直接判断不含车牌;否则,结合水平方向的宽度进行车牌判断。
含有车牌条件:
宽度大于40,高度大于20,长宽比在0.7~6之间。这个阈值和各种论文比起来很大了,主要原因当然还是样本库……写到这里真是满满的怨念。
夜晚下的Retinex算法
最后就是考虑到夜晚下车牌太暗所希望做的补偿算法,这个具体参考: https://github.com/Vespa314/Retinex ,将的很清楚,代码也写的很棒,我的代码也是用了这里的。不过不知道为什么,好像这个代码使用后得到的图像再转成HSV模式时,部分图像会提示函数错误,而且还是工具箱的函数使用错误,让我有点摸不着头脑,也是我目前没有找出来的一个bug。
关于白天和夜晚的衔接,我的也比较粗暴。直接先检测白天的条件,没有的话再检测夜晚……主要是关于白天和夜晚不是很好判断。我最早使用的是灰度化图片的平均值,标准差平均值,OTSU阈值和灰度平均值比值来判断的,效果其实还是不错的。更加复杂精准的方法,我看过用模式的聚类来做的,也属于我知识的空白区吧,以后有机会再做改进。最后使用的这个方法也是勉勉强强能用,夜晚识别率也只有一半左右,没办法识别强光。
总结
想想,自己做的还是比较惨淡的。很多更加有效精准的方法不能用,碍于样本库的多样复杂,检测率一直没办法提高。有时候还确实有卡着的感觉,好像忙活了半天也就改改参数,没什么实质化的进步。感觉自己还是对特征挖掘不够充分,掌握方法不多,也很不足,说到底还是个刚入门的。希望自己以后继续深入学习能找到改进的方法吧。
请发表评论