ANPR算法(车牌自动识别系统)
1.车牌检测
在整个视频帧中检测到车牌的位置。
2.车牌识别
当在图像中检测到车牌时,使用OCR(光学字符识别)算法来识别车牌上的字母和数字。
1.1车牌检测
这一步要检测当前帧中的所有车牌,我们将其分为两个主要步骤,分割和分类。
在第一步(分割)中,将应用不同的滤波器,形态学算子,轮廓算法来验证图像中可能包含的车牌的部分。
在第二步(分类)中,将对每个图像块(特征)应用SVM分类器进行分类。先训练两个不同的类:车牌和非车牌。
1.1.1分割
车牌分割的一个重要特征是:假设图像是正面拍的,车牌没有旋转,则车牌会有大量的垂直边缘,首次分割的时候,可以利用这个特征来深处没有任何垂直边缘的区域。
在找到垂直边缘之前,需要将彩色图像转换为灰度图像,并消除可能由相机或其他因素产生的噪点,利用5x5高斯模糊去噪。
//转化为灰度图像
Mat img_gray;
cvtColor(input,img_gray,CV_BGR2GRAY);
blur(img_gray,img_gray,Size(5,5));
为了找到垂直边缘,采用Sobel滤波器对水平方向(x)求一阶导数,这个导数是一个数学导数,可以在图像上找到垂直边缘,函数定义如下:
CV_EXPORTS_W void Sobel( InputArray src, OutputArray dst, int ddepth,
int dx, int dy, int ksize = 3,
double scale = 1, double delta = 0,
int borderType = BORDER_DEFAULT );
这里ddepth是目标图像深度,xorder是对x求导的阶数,yorder是对y求导的阶数,ksize表示kernel的大小,取值为1,3,5,7(默认3),scale是计算导数值的可选因子,delta是加入结果的可选值(默认0),borderType是橡树内插方法。
这里采用xorder=1,yorder=0,ksize=3。
//寻找垂直边缘
Mat img_sobel;
Sobel(img_gray,img_sobel,CV_8U,1,0,3,1,0);
Sobel滤波器后,采用阈值滤波器来获得二值图像,阈值通过Otsu算法得到(Otsu算法通过输入一个8位图像,自动获取图像的最优阈值)
//阈值图像
Mat img_threshold;
threshold(img_sobel,img_threshold,0,255,CV_THRESH_OTSU+CV_THRESH_BINARY;
通过使用一个闭形态学算子,可以去除每条垂直边缘线之间的空白区,并将边缘数目较多的区域连接在一起,在此步骤中,可能包含车牌区域。
先定义形态学算子中使用的结构元素,这里定义具有17x3大小的结构矩形元素,其他图像可能元素尺寸不一样
Mat element = getStructuringElement(MORPH_RECT,Size(17,3));
然后,在闭形态学算子中通过morphologyEx函数使用这个结构元素。
morphologyEx(img_threshold,img_threshold,CV_MOP_CLOSE_element);
上述操作后,得到了包含车牌的区域,但是大多数区域并不包含插排,可以使用findContours函数来拆分这些区域,下面这个函数用来获取二进制图像的轮廓。
// Find contours of possibles plates
vector<vector<Point>> contours;
findContours(img_threshold,
contours, // a vector of contours
cv::RETR_EXTERNAL, // retrieve the external contours
cv::CHAIN_APPROX_NONE); // all pixels of each contours
可用minAreaRect函数对检测到的每一个轮廓,提取最小面积的边界矩形。
// Remove patch that are no inside limits of aspect ratio and area.
while (itc != contours.end()) {
// Create bounding rect of object
RotatedRect mr = minAreaRect(Mat(*itc));
if (!verifySizes(mr)) {
itc = contours.erase(itc);
} else {
++itc;
rects.push_back(mr);
}
}
根据区域面积和纵横比,对检测到的区域进行基本验证,若纵横比约为520/110=4.727272,那么认为该区域是个车牌,误差范围位40%,车牌区域高度误差在15~125个像素,这些参数因为图像的大小,相机的位置而不同。
bool DetectRegions::verifySizes(RotatedRect mr)
{
float error = 0.4;
// Spain car plate size: 52x11 aspect 4,7272
float aspect = 4.7272;
// Set a min and max area. All other patchs are discarded
int min = 15 * aspect * 15; // minimum area
int max = 125 * aspect * 125; // maximum area
// Get only patchs that match to a respect ratio.
float rmin = aspect - aspect * error;
float rmax = aspect + aspect * error;
int area = mr.size.height * mr.size.width;
float r = (float)mr.size.width / (float)mr.size.height;
if (r < 1)
r = (float)mr.size.height / (float)mr.size.width;
if ((area < min || area > max) || (r < rmin || r > rmax)) {
return false;
} else {
return true;
}
}
所有的车牌都有白色的背景,为了得到精确的裁剪,可使用漫水填充算法来获取旋转的矩形。
for (int i = 0; i < rects.size(); i++) {
// For better rect cropping for each posible box
// Make floodfill algorithm because the plate has white background
// And then we can retrieve more clearly the contour box
circle(result, rects[i].center, 3, Scalar(0, 255, 0), -1);
// get the min size between width and height
float minSize =