书名:《Mastering OpenCV with Practical Computer Vision Projects》
《书中部分其他章节的翻译》:http://blog.csdn.net/raby_gyl/article/details/17472271
电子书下载地址:http://download.csdn.net/detail/xuluhui123/6310819
随书源代码下载地址:http://download.csdn.net/detail/xuluhui123/6310851
程序流程图下载地址:http://download.csdn.net/detail/xuluhui123/6310811
流程图也可参考:http://blog.csdn.net/xuluhui123/article/details/11659915
推荐:采用 SVM 和神经网络车牌识别详细流程图,和代码中部分解释:http://blog.csdn.net/raby_gyl/article/details/11659915
本人英语四级水平,以下翻译只供自己存档和像我一样的初学者参阅。里面错误肯定很多,也有好多英语句子我不太明白的。敬请大家纠正。我最近还要好好的消化一下。还会进行修改的。最近刚看了 opencv
中文版的机器学习篇,opencv 自带的特征数据都是已经做好了的,对于特征数据的生成,矩阵的操作不熟悉的我,还是在脑子里难以形成一个整体的思路。想在网上下载一些实例项目的代码,好像很难,即使找
到了又没有详细的说明,对于一个初学者可能要花好久的时间才能完全整理清楚(还必须查阅相关资料)。最近有幸在 opencv 论坛,看到朋友们推荐的这本书,而且附有源代码,能够运行,看到了效果。这使我
决定要看一看,对于一个不喜欢看英文的我,硬着头皮把第五章看完了。读英文最大的痛苦是句子长,来回的修饰,然而第二大痛苦就是,我们记忆里不怎么好,读完一段话,即使每句话都好像能理解,然而将
的是什么还是云里雾里。其实我感觉最好的方法,就是自己大约理解了,翻译下来,整成中文的,然后推敲一下是不是语义上合理。
<内容>
本章向大家介绍创建一个自动车牌识别应用(ANPR)所需要的步骤。基于不同的情况,有不同的方法和技术。例如,IR camera(红外线摄像机),固定车位置,光亮情况,等等。我们开始构建一个 ANPR 应用,来
检测离车 2-3 米拍的照片中的车牌。在模糊的光线下,并且不是平行与地面而是与车牌的有个小角度的倾斜。
本章的主要目的是向大家介绍图像的分割和特征提取,模式识别的基础,和两个重要的模式识别算法:支持向量机和人工神经网络。在这一章,我们将包含以下内容:
1、ANPR(自动车牌识别)
2、车牌检测
3、车牌识别
ANPR 介绍:
ANRP 也就是众所周知的 ALPR,或者 AVI,或者 CPR,是一种用在光学字符识别的监视方法和其他方法,例如分割和检测来读取车牌号。
在 ANPR 系统中最好的结果是使用一个红外线摄像机,因为检测和 OCR(光学字符识别)分割之前的分割步骤变的简单,干净和错误最小化。这是由于光线法则,最基本的是因为入射角度等于反射角度。当我们看
一个光滑的表面例如一个平面镜,我们能看到这个基本的反射。粗糙表面的反射例如一张纸导致的反射称为漫射或者散射。车牌号的主要部分有一个特殊的特性叫做回复反射。车牌的表面
是用覆盖有成千上万个细小半球的材料做成的。这样会使光线回复反射到光线源,我们从下面的图可以看到:
角度反射
散射或者漫射
回复反射
如果我们使用一个带有红外线投影结构和滤波的摄像机,我们使用带有红外线的摄像机重新获取,将得到一个非常高质量的照片用来分割和随后的检测和识别车牌数字。即不依赖于任何光线环境,如下图所示:
在这一章,我们没有使用红外线摄像,我们使用常规的摄像。我们这样做,以至于我们没有得到最好的结果,得到的是一个更高水平的检测错误和高的错误识别率。这与我们使用红外摄像机所期待的结果截然相
反。然而,两者的步骤是一样的。
每个国家车牌的大小和规格不同,为了获得更好的结果和减少错误,我们知道这些规格是佷有用的。本章使用的算法意图是阐述 ANPR 的基本原理和西班牙车牌,但是我们能把他们扩到任何国家或者规格车牌。
在本章,我将使用来之西班牙的车牌。在西班牙,有三种不同大小和形状的车牌。我们将使用最普通(使用最多)的车牌,其大小是 520*110mm。两种字符(数字和字母)的间距是 41mm。数字和数字之前(或
者字母和字母之间)距离是 14mm。第一组字符含有四个数字。另外一组含有三个字母,其中不包括元音字母:A,E,I,O,U。和 N,Q。所有的字符大小为 45*77。
这些数据对于字符分割很重要,因为我们能够检查两个字符和空格,来核实我们得到是一个字符而没有其它图片部分。如下是一个车牌图。
ANPR 算法
在解释 ANPR 代码之前,我们需要定义算法的主要步骤和任务。ANPR 主要分为两步:车牌的检测和车牌的识别。车牌检测就是检测车牌在整个图像帧中的位置。当一个图像中的车牌检测到时,车牌的分割将交
给接下来的一步——车牌识别。在车牌识别中,我们用 OCR 算法来决定车牌上的字母数字的字符。
在下图我们可以看到两个主要算法的步骤,车牌检测和车牌识别。车牌识别之后,程序将在图像帧中画出检测到的车牌。这个算法能返回坏的结果甚至没有结果(检测不到)。
每个步骤都展示在上边的图中,我们来定义另外三个步骤。他们通常用在模式识别算法中。
1、分割。该步检测和移动图像中每个感兴趣的区域。
2、特征提取。该步提取每个块的一系列的特征。
3、分类。该步从车牌识别步骤或者把图像部分分为有车牌和无车牌的车牌检测的步骤,(上述两个步骤中)提取每个字符。
下图向我们展示了在整个算法中模式识别的步骤。
抛开主要的应用,即该应用的目的是检测和识别一个车牌数字,我们来简单介绍两个不经常被介绍的任务:
1、怎么样训练一个模式识别系统?
2、怎么样来评估这样的一个系统?
然而,这些任务通常比主要应用本身更重要。因为,如果我们不能正确的训练模式识别系统,我们的系统就会失败并且不能正确的工作。不同的模式需要不同类型的训练和评估。我们需要在不同的环境,条件,
带有不同特征,来评估我们的系统,进而得到最好的效果。这两个任务有时一起使用,因为不同的特征能产生不同的结果,这种情况我们会在评估部分看到。
车牌检测
在这一步中,我们需要检测在一个图像帧中所有的车牌。为了做这个任务。我们分为两个主要的步骤:分割和分割分类。特征步骤不在阐述,是因为我们用图像部分作为一个特征矢量。
第一步(分割),我们应用不同的滤波器,形态学操作,轮廓算法,和确认获取图像的这些部分可能有一个车牌。
第二步(分类),我们采用支持向量机(SVM)分类出每个图像部分——我们的特征。在创建主程序之前,我们训练两个不同的类别——有车牌和无车牌。我们采用前向_水平视觉的彩色图像,宽度为 800 像素,从
离车的 2 到 4 米处获取的。这样要求对确保正确的分割很重要。如果我们创建了一个多尺度图像算法,我们能够展示检测。
在下面的图像中,我们展示了车牌检测所包含的所有处理:
1、Sobel 滤波
2、阈值操作
3、闭操作
4、填充区域的掩膜
5、把可能检测到车牌标记为红色(特征图像)
6、SVM 分类后,检测到的车牌
分割
分割是把一幅图像分割成许多部分的过程。这个过程简化图像分析,使特征提取更容易。
车牌分割的一个重要特征是在车牌中的高数量的垂直边缘(就是垂直边缘比较多)(假定照片是从前面拍的,车牌没有旋转,并且没有视觉上的扭曲。这个特征可以用来在分割的第一步(sobel 滤波),来排除那
些没有垂直边缘的区域。
在寻找垂直边缘之前,我们需要把彩色图像转换为灰度图像,因为彩色在我们的任务中没有帮助,并且移除来之相机或者外界的噪声。如果我们不应用去噪方法,我们将得到许多的垂直边缘,将会产生检测失败。
//convert image to gray
Mat img_gray;
cvtColor(input,img_gray,CV_BGR2GRAY);
blur(img_gray,img_gray,Size(5,5));
为了寻找垂直边缘,我们采用 sobel 滤波并且找到一阶垂直方向导数。这个导数是个数学函数,允许我们找到图像上的垂直边缘。Opencv 中 Sobel 函数的定义如下:
void Sobel(InputArray src,OutputArray dst,int ddepth,int xorder,int yoder,int ksize=3,double scale=1,double delta=0,int borderType=BORDER_DEFAULT)
这里,ddepth 是目的图像的深度,xorder 是 x 导数的次序(即 x 的 order 阶导数),yorder 为 y 导数的次序。ksize 核的大小要么是 1,3,5 要么是 7。scale 用在计算导数值,是个可选项。delta 是一个加到结果的可
选项。bordertype 是像素的插值方法。
在本程序中,我们使用 xorder=1,yorder=0,ksize=3;
//寻找垂直方向的线,车牌还很的垂直线密度
Mat img_sobel;
Sobel(img_gray,img_sobel,CV_8U,1,0,3,1,0);
Sobel 滤波后,我们应用一个阈值滤波器来获得一个二值图像,阈值的通过 otsu 方法获得。Ostu 算法需要一个 8 位图像作为输入,该方法自动的决定最佳的阈值。
Mat img_threshold;
threshold(img_sobel,img_threhold,0,255,CV_THRESH_OTSU+CV_THRESH_BINARY);
为了在阈 threshold 函数中定义 ostus 方法,我们使用 CV_THRESH_OTST 值混合参数。则阈值参数被忽略。
(小心:当 CV_THRSH_OTST 被定义,threshold 函数会通过 ostus 算法返回最优阈值)
通过应用一个闭操作,我们能够去掉每个垂直边缘线的空白部分。并且连接有含有边缘数量很多的所有区域。在这一步,我们得到可能的含有车牌的区域。
首先,我们定义用在形态学操作上的结构元。我们将要使用 getstructuringelement 函数来定义一个矩形的结构元,大小为 17*3。这可能和其他图像大小不同。
Mat element=getStructuringElement(MORPH_RECT,Size(17,3));
在 morphologEx 函数中使用上述定义的结构元。
morphologyEx(img_threshold,img_threshold,CV_MOP_CLOSE,element);
应用完这些操作之后,我们的得到了可能含有车牌的区域,大部分的这些区域将没有包含车牌。这些区域用连通部分分析(opencv 中文版 319 页)或者使用 findContours 函数来分开。最后一个函数用不同的方
法和结果来获得一个二值图像的轮廓。我们只需要用任何分层关系和任何多边形近似结果来获得外轮廓。
//寻找可能是车牌的轮廓
vector> contours;
findContours(img_threshold,contours,
CV_RETR_EXERNAL,//获取外轮廓
CV_CHAIN_APPOX_NONE//轮廓的所有像素
);
为了检测每个轮廓,提取轮廓的最小矩形边界框。OpenCV 采用 minAreaRect 函数来完成这个任务。这个函数返回一个旋转矩形类对象:RotatedRect。我们使用 vector 容器迭代器访问每一个轮廓,我们可以得到
旋转的矩行,在分类前做一些初步的确认。
//开始迭代找到的每个以轮廓
vector>::iterator itc=contours.begin();
vector rects;
//去掉没有限制比例(aspect radio 宽高比)和面积的部分
while(itc!=contours.end())
{
//创建对象的矩形边界
RotatedRect mr=minAreaRect(Mat(*itc));
if(!veryfySize(mr))
{
itc=contours.erase(itc);}
else
{
++itc;
rects.push_back(mr);
}
}
我们基于面积和宽高比,对于检查到的区域做一下确认。如果宽高比大于为 520/110=4.727272(车牌宽除以车牌高)(允许带有 40%的误差)和边界在 15 像素和 125 像素高的区域,我们才认为是一个车牌区域。
这些值根据图像的大小和相机的位置进行计算。
bool DetectRegions::verfgSizes(RotateRect candidate)
{
float eror=0.4;
//西班牙车牌的大小 52*11 宽高比为 4.7272
const float aspect=4.7272;
//设置最小和最大区域,其他的将被丢弃.
int min=15*aspect*15;//15*aspect 是宽,宽*高
int max=125*aspect*125;
//仅获取满足 aspec 条件的区域
float rmin=aspect-aspect*error;
float rmax=aspect +aspect*error;
int area=candidate.size.height*candidate.size.width;
float r=(float)candidate.size.width/(float)candidate.size.height;
if(r<1)
r=1/r;
if((areamax)||(rrmax))
{
return false;
}else
{
return true;
}
}
我们利用车牌的白色背景属性可以进一步改善。所有的车牌都有统一的背景颜色。我们可以使用漫水填充算法来获取旋转矩阵的精确修剪。
剪切车牌的第一步是在最后一个旋转矩阵中心的附近得到一些种子,在宽度和高度中得到最小的车牌,用它来产生离中心近的种子。
我们想要选择白色区域,我们需要一些种子,至少有一个种子接触到白色区域。接着对每一个种子,我们使用 floodFill 函数来得到一个掩码图像,用来存储新的最接近的修剪区域。
for(int i=0;i