基于OpenCV3.0的车牌识别系统设计(二)--车牌提取

news/2024/11/29 12:52:28/

写在前面的话

上一篇开篇博文写好之后找女朋友看了一下,希望她提一点建设性建议。结果她很委婉的告诉我,写的还行就是太表面了,告诉我要注意细节的描述与具体的实现过程与原理等等。其实我只是想骗她看一下增加一下点击量,顺便知道我写的博客新手能不能看懂而已。结果她告诉我,她那么聪明当然能看懂,别人就未必能看懂了!!!吼吼吼,信了她的邪,什么时候都不忘记赞美一下她自己的小妖精。上一篇写的不好的话请各位见谅,吾理小子文笔有点差,但是我会尽自己的最大努力把自己懂的东西分享出来,希望对你有用。为了讲述清楚车牌识别的每一个环节,吾理小子决定把写好的工程源码重新解剖,在每一个环节中贴出该部分完整的代码,新手可直接拿去运行观看实际效果。各位感动吗!

进入正题,上次重点介绍了车牌识别的流程,本文来详细聊一下车牌提取的实现过程。

车牌提取方法

车牌提取通常也称为车牌定位,其目的是从含有车牌的图像中找到车牌区域。车牌定位的重要性不言而喻,作为车牌识别的第一个步骤,车牌区域的提取成功与否是完成车牌识别的基础也是首要决定因素。车牌提取的方法有很多,不同的分类方法有不同的叫法。通过对常见的几种方法的归纳总结,见下图:(编辑一张图片,喷出一口鲜血)

本文着重讲解基于颜色信息的定位方法。上图中可以看到基于彩色信息的定位方法具有准确、快速、精确等优点,对于新手也更加容易理解。通过对质量较高的原始图像进行相应处理之后,定位出车牌区域。现象明显,一气呵成。有助与新手继续研究下去。

到目前为止,关于车牌定位的问题国内外众多学者提出了很多方法,适用条件不尽相同,各有利弊。弄懂了基本的颜色定位之后,可以尝试结合其它的方法来提升车牌定位的性能。

车牌特点

说完车牌定位的方法之后,需要对我们提取的对象本身的特点进行说明。车牌的大小固定,字体格式是由国家相关部门统一规定,与一般常见的字体格式都不一样。据说是由常见的宋体字修改而来,一般民用的车牌包含数字0-9,还有全国26个省市的简称,总共是36个字符。车牌的颜色以及字体颜色如下:

蓝牌白字:普通小型车(其中包括政府机关专用号段、政法部门警车以外的行政用车)的牌照
           黄牌黑字:大型车辆、摩托车、驾校教练车牌照
           黑牌白字:涉外车辆牌照,式样和蓝牌基本相同
                  白牌:政法部门(公安、法院、检察院、国安、司法)警车、武警部队车辆、解放军军车的牌照都是白牌
                  警车:公安警车的牌照样式为[某·A1234警],除“警”为红字外其他的都是黑字,一共4位数字,含义与普通牌照相同

我们研究的目标是普通民用小型车,也就是蓝牌白字车牌。说到这里,顺便说明一下蓝牌白字车牌的颜色信息,对于正常曝光的图像而言,蓝色车牌的三个通道值大约为Blue=138,Green=63,Red=23。除了颜色信息外,车牌形状为矩形,具有固定的长宽比3:1。知道车牌的这些信息后,在使用颜色信息定位时可以做为限制条件,这样可以提高车牌定位的准确性。

车牌提取的步骤

讲完车牌提取的方法和车牌本身的特点之后,接下来仔细说明基于颜色信息的定位方法各个环节处理效果图。

第一步:读取待处理图像

首先,读取待处理的彩色图像,判断图像是否读取成功,成功时显示原始图像。最后打印图像的长和宽,方便对图像的尺寸有一个了解。

Mat OriginalImg ;
OriginalImg = imread("TestPhoto (1).jpg", IMREAD_COLOR);//读取原始彩色图像if (OriginalImg.empty())  //判断图像对否读取成功{cout << "错误!读取图像失败\n";return -1;}imshow("原图", OriginalImg); //显示原始图像cout << "Width:" << OriginalImg.rows << "\tHeight:" << OriginalImg.cols << endl;//打印图像长宽

运行效果

第二步:图像尺寸变换

读取原图像后,可以看到原图像像素较高,对于车牌识别而言,过高的分辨率对识别结果效果太大的帮助,反而会影响识别的速度,也就是系统实时性会变差。所以在这里对尺寸进行统一变换,在保证输入图像的长宽比不变的情况下,将图像的长度变成640,相应的宽度可以通过计算得到。

    Mat ResizeImg; if (OriginalImg.cols > 640)resize(OriginalImg, ResizeImg, Size(640, 640* OriginalImg.rows / OriginalImg.cols));imshow("尺寸变换图", ResizeImg);

上图和原图的区别就是尺寸不一致。后续的处理步骤都是在缩放之后的图像基础上来做,适当分辨率的图像有助于提高系统的响应速度,也就是实时性。

第三步:基于颜色信息二值化

基于颜色的二值化处理就是通过颜色信息将图像二值化,上面已经提到了正常曝光的车牌各个通道的颜色信息大约是Blue=138,Green=63,Red=23。但是颜色信息有一定的偏差,因此在二值化时放宽颜色条件,然后再通过其他特点来精确寻找车牌区域。本文设置各个通道的偏差值为50。程序如下,注意观察颜色信息以及偏差值在程序中的体现。

unsigned char pixelB, pixelG, pixelR;  //记录各通道值
unsigned char DifMax = 50;             //基于颜色区分的阈值设置
unsigned char B = 138, G = 63, R = 23; //各通道的阈值设定,针对与蓝色车牌
Mat BinRGBImg = ResizeImg.clone();  //二值化之后的图像
int i = 0, j = 0;
for (i = 0; i < ResizeImg.rows; i++)   //通过颜色分量将图片进行二值化处理
{for (j = 0; j < ResizeImg.cols; j++){pixelB = ResizeImg.at<Vec3b>(i, j)[0]; //获取图片各个通道的值pixelG = ResizeImg.at<Vec3b>(i, j)[1];pixelR = ResizeImg.at<Vec3b>(i, j)[2];if (abs(pixelB - B) < DifMax && abs(pixelG - G) < DifMax && abs(pixelR - R) < DifMax){                                           //将各个通道的值和各个通道阈值进行比较BinRGBImg.at<Vec3b>(i, j)[0] = 255;     //符合颜色阈值范围内的设置成白色BinRGBImg.at<Vec3b>(i, j)[1] = 255;BinRGBImg.at<Vec3b>(i, j)[2] = 255;}else{BinRGBImg.at<Vec3b>(i, j)[0] = 0;        //不符合颜色阈值范围内的设置为黑色BinRGBImg.at<Vec3b>(i, j)[1] = 0;BinRGBImg.at<Vec3b>(i, j)[2] = 0;}}
}
imshow("基于颜色信息二值化", BinRGBImg);        //显示二值化处理之后的图像

第四步:形态学处理

基于颜色信息二值化的图像效果还是挺不错的,可以看到车牌区域基本完整,其他地方有一些细小的干扰,接下来进行形态学处理,消除小区域干扰。形态学闭操作——先膨胀后腐蚀,是图像的基本操作之一,其特点是填充细小空间,连接临近物体和平滑边界,不同矩形窗的大小会有不同的结果。(形态学基本知识可参考数字图像处理相关书籍)

Mat BinOriImg;     //形态学处理结果图像
Mat element = getStructuringElement(MORPH_RECT, Size(3, 3)); //设置形态学处理窗的大小
dilate(BinRGBImg, BinOriImg, element);     //进行多次膨胀操作
dilate(BinOriImg, BinOriImg, element);
dilate(BinOriImg, BinOriImg, element);erode(BinOriImg, BinOriImg, element);      //进行多次腐蚀操作
erode(BinOriImg, BinOriImg, element);
erode(BinOriImg, BinOriImg, element);
imshow("形态学处理后", BinOriImg);        //显示形态学处理之后的图像

第五步:增加限制条件寻找车牌区域并框选车牌

从上图中可以看到车牌的基本位置已经能够确定,接下来通过其他特点来进一步确定车牌区域。处理思路如下:

1.寻找各个空白区域外轮廓并计算面积;

2.为各个空白区域增加外接矩形并计算面积;

3.通过外轮廓面积与外接矩形的比值,判断区域的矩形度;

4.进一步判断长宽比;

5.满足全部条件确定车牌区域

double length, area, rectArea;     //定义轮廓周长、面积、外界矩形面积
double rectDegree = 0.0;           //矩形度=外界矩形面积/轮廓面积
double long2Short = 0.0;           //体态比=长边/短边
CvRect rect;           //外界矩形
CvBox2D box, boxTemp;  //外接矩形
CvPoint2D32f pt[4];    //矩形定点变量
double axisLong = 0.0, axisShort = 0.0;        //矩形的长边和短边
double axisLongTemp = 0.0, axisShortTemp = 0.0;//矩形的长边和短边
double LengthTemp;     //中间变量
float  angle = 0;      //记录车牌的倾斜角度
float  angleTemp = 0;
bool   TestPlantFlag = 0;  //车牌检测成功标志位
cvtColor(BinOriImg, BinOriImg, CV_BGR2GRAY);   //将形态学处理之后的图像转化为灰度图像
threshold(BinOriImg, BinOriImg, 100, 255, THRESH_BINARY); //灰度图像二值化
CvMemStorage *storage = cvCreateMemStorage(0);
CvSeq * seq = 0;     //创建一个序列,CvSeq本身就是一个可以增长的序列,不是固定的序列
CvSeq * tempSeq = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);
int cnt = cvFindContours(&(IplImage(BinOriImg)), storage, &seq, sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
//第一个参数是IplImage指针类型,将MAT强制转换为IplImage指针类型
//返回轮廓的数目 
//获取二值图像中轮廓的个数
cout << "number of contours   " << cnt << endl;  //打印轮廓个数
for (tempSeq = seq; tempSeq != NULL; tempSeq = tempSeq->h_next)
{length = cvArcLength(tempSeq);       //获取轮廓周长area = cvContourArea(tempSeq);       //获取轮廓面积if (area > 800 && area < 50000)     //矩形区域面积大小判断{rect = cvBoundingRect(tempSeq, 1);//计算矩形边界boxTemp = cvMinAreaRect2(tempSeq, 0);  //获取轮廓的矩形cvBoxPoints(boxTemp, pt);              //获取矩形四个顶点坐标angleTemp = boxTemp.angle;                 //得到车牌倾斜角度axisLongTemp = sqrt(pow(pt[1].x - pt[0].x, 2) + pow(pt[1].y - pt[0].y, 2));  //计算长轴(勾股定理)axisShortTemp = sqrt(pow(pt[2].x - pt[1].x, 2) + pow(pt[2].y - pt[1].y, 2)); //计算短轴(勾股定理)if (axisShortTemp > axisLongTemp)   //短轴大于长轴,交换数据{LengthTemp = axisLongTemp;axisLongTemp = axisShortTemp;axisShortTemp = LengthTemp;}elseangleTemp += 90;rectArea = axisLongTemp * axisShortTemp;  //计算矩形的面积rectDegree = area / rectArea;     //计算矩形度(比值越接近1说明越接近矩形)long2Short = axisLongTemp / axisShortTemp; //计算长宽比if (long2Short > 2.2 && long2Short < 3.8 && rectDegree > 0.63 && rectDegree < 1.37 && rectArea > 2000 && rectArea < 50000){Mat GuiRGBImg = ResizeImg.clone();TestPlantFlag = true;             //检测车牌区域成功for (int i = 0; i < 4; ++i)       //划线框出车牌区域cvLine(&(IplImage(GuiRGBImg)), cvPointFrom32f(pt[i]), cvPointFrom32f(pt[((i + 1) % 4) ? (i + 1) : 0]), CV_RGB(255, 0, 0));imshow("提取车牌结果图", GuiRGBImg);    //显示最终结果图box = boxTemp;angle = angleTemp;axisLong = axisLongTemp;axisShort = axisShortTemp;cout << "倾斜角度:" << angle << endl;}}
}

框选结果如下图所示。这部分牵扯到的变量较多,主要看思路,不必纠结为什么设置这么多变量,因为后面需要用到这些变量。

不知道细心的小伙伴有没有发现,其中有一个变量是倾斜角度。其实这个变量保存的是车牌区域的倾斜角度,通过观察原图中车牌的位置和姿态,和输出的倾斜角度的值可以明白这个值表达的含义。后续步骤就是车牌的倾斜矫正。各位首先有一个大致的了解。

车牌提取效果展示

下面展示一些提取车牌区域的效果图。

车牌提取源码

说完各个步骤的具体操作,最后贴上设计到的源码。各位自行验证改进!


#include <iostream>
#include <opencv2\opencv.hpp>using namespace std;
using namespace cv;int main(int,char *argv[])
{Mat OriginalImg;OriginalImg = imread("TestPhoto (1).jpg", IMREAD_COLOR);//读取原始彩色图像if (OriginalImg.empty())  //判断图像对否读取成功{cout << "错误!读取图像失败\n";return -1;}
//	imshow("原图", OriginalImg); //显示原始图像cout << "Width:" << OriginalImg.rows << "\tHeight:" << OriginalImg.cols << endl;//打印长宽Mat ResizeImg; if (OriginalImg.cols > 640)resize(OriginalImg, ResizeImg, Size(640, 640 * OriginalImg.rows / OriginalImg.cols));imshow("尺寸变换图", ResizeImg);unsigned char pixelB, pixelG, pixelR;  //记录各通道值unsigned char DifMax = 50;             //基于颜色区分的阈值设置unsigned char B = 138, G = 63, R = 23; //各通道的阈值设定,针对与蓝色车牌Mat BinRGBImg = ResizeImg.clone();  //二值化之后的图像int i = 0, j = 0;for (i = 0; i < ResizeImg.rows; i++)   //通过颜色分量将图片进行二值化处理{for (j = 0; j < ResizeImg.cols; j++){pixelB = ResizeImg.at<Vec3b>(i, j)[0]; //获取图片各个通道的值pixelG = ResizeImg.at<Vec3b>(i, j)[1];pixelR = ResizeImg.at<Vec3b>(i, j)[2];if (abs(pixelB - B) < DifMax && abs(pixelG - G) < DifMax && abs(pixelR - R) < DifMax){                                           //将各个通道的值和各个通道阈值进行比较BinRGBImg.at<Vec3b>(i, j)[0] = 255;     //符合颜色阈值范围内的设置成白色BinRGBImg.at<Vec3b>(i, j)[1] = 255;BinRGBImg.at<Vec3b>(i, j)[2] = 255;}else{BinRGBImg.at<Vec3b>(i, j)[0] = 0;        //不符合颜色阈值范围内的设置为黑色BinRGBImg.at<Vec3b>(i, j)[1] = 0;BinRGBImg.at<Vec3b>(i, j)[2] = 0;}}}imshow("基于颜色信息二值化", BinRGBImg);        //显示二值化处理之后的图像Mat BinOriImg;     //形态学处理结果图像Mat element = getStructuringElement(MORPH_RECT, Size(3, 3)); //设置形态学处理窗的大小dilate(BinRGBImg, BinOriImg, element);     //进行多次膨胀操作dilate(BinOriImg, BinOriImg, element);dilate(BinOriImg, BinOriImg, element);erode(BinOriImg, BinOriImg, element);      //进行多次腐蚀操作erode(BinOriImg, BinOriImg, element);erode(BinOriImg, BinOriImg, element);imshow("形态学处理后", BinOriImg);        //显示形态学处理之后的图像double length, area, rectArea;     //定义轮廓周长、面积、外界矩形面积double rectDegree = 0.0;           //矩形度=外界矩形面积/轮廓面积double long2Short = 0.0;           //体态比=长边/短边CvRect rect;           //外界矩形CvBox2D box, boxTemp;  //外接矩形CvPoint2D32f pt[4];    //矩形定点变量double axisLong = 0.0, axisShort = 0.0;        //矩形的长边和短边double axisLongTemp = 0.0, axisShortTemp = 0.0;//矩形的长边和短边double LengthTemp;     //中间变量float  angle = 0;      //记录车牌的倾斜角度float  angleTemp = 0;bool   TestPlantFlag = 0;  //车牌检测成功标志位cvtColor(BinOriImg, BinOriImg, CV_BGR2GRAY);   //将形态学处理之后的图像转化为灰度图像threshold(BinOriImg, BinOriImg, 100, 255, THRESH_BINARY); //灰度图像二值化CvMemStorage *storage = cvCreateMemStorage(0);CvSeq * seq = 0;     //创建一个序列,CvSeq本身就是一个可以增长的序列,不是固定的序列CvSeq * tempSeq = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);int cnt = cvFindContours(&(IplImage(BinOriImg)), storage, &seq, sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);//第一个参数是IplImage指针类型,将MAT强制转换为IplImage指针类型//返回轮廓的数目 //获取二值图像中轮廓的个数cout << "number of contours   " << cnt << endl;  //打印轮廓个数for (tempSeq = seq; tempSeq != NULL; tempSeq = tempSeq->h_next){length = cvArcLength(tempSeq);       //获取轮廓周长area = cvContourArea(tempSeq);       //获取轮廓面积if (area > 800 && area < 50000)     //矩形区域面积大小判断{rect = cvBoundingRect(tempSeq, 1);//计算矩形边界boxTemp = cvMinAreaRect2(tempSeq, 0);  //获取轮廓的矩形cvBoxPoints(boxTemp, pt);              //获取矩形四个顶点坐标angleTemp = boxTemp.angle;                 //得到车牌倾斜角度axisLongTemp = sqrt(pow(pt[1].x - pt[0].x, 2) + pow(pt[1].y - pt[0].y, 2));  //计算长轴(勾股定理)axisShortTemp = sqrt(pow(pt[2].x - pt[1].x, 2) + pow(pt[2].y - pt[1].y, 2)); //计算短轴(勾股定理)if (axisShortTemp > axisLongTemp)   //短轴大于长轴,交换数据{LengthTemp = axisLongTemp;axisLongTemp = axisShortTemp;axisShortTemp = LengthTemp;}elseangleTemp += 90;rectArea = axisLongTemp * axisShortTemp;  //计算矩形的面积rectDegree = area / rectArea;     //计算矩形度(比值越接近1说明越接近矩形)long2Short = axisLongTemp / axisShortTemp; //计算长宽比if (long2Short > 2.2 && long2Short < 3.8 && rectDegree > 0.63 && rectDegree < 1.37 && rectArea > 2000 && rectArea < 50000){Mat GuiRGBImg = ResizeImg.clone();TestPlantFlag = true;             //检测车牌区域成功for (int i = 0; i < 4; ++i)       //划线框出车牌区域cvLine(&(IplImage(GuiRGBImg)), cvPointFrom32f(pt[i]), cvPointFrom32f(pt[((i + 1) % 4) ? (i + 1) : 0]), CV_RGB(255, 0, 0));imshow("提取车牌结果图", GuiRGBImg);    //显示最终结果图box = boxTemp;angle = angleTemp;axisLong = axisLongTemp;axisShort = axisShortTemp;cout << "倾斜角度:" << angle << endl;}}}waitKey();return 0;}

好啦,本节就说到这里。下一篇开始说车牌的倾斜矫正,各位小伙伴期待吗?

 

 


http://www.ppmy.cn/news/210441.html

相关文章

C++中文车牌识别检测系统源码

下载地址&#xff1a;C中文车牌识别检测系统源码 其目标是成为一个简单、高效、准确的非限制场景(unconstrained situation)下的车牌识别库。 相比于其他的车牌识别系统&#xff0c;EasyPR有如下特点&#xff1a; 它基于openCV这个开源库。这意味着你可以获取全部源代码&…

【车牌识别】模板匹配新能源、轿车、货车车牌识别【含GUI Matlab源码 2169期】

⛄一、模板匹配车牌识别简介 1 系统整体设计 车牌识别系统包括4个步骤:车牌定位 (包括识别图像中的车牌位置并将其分割) 、图像处理、字符分割与字符识别, 如图1所示。车牌定位的主要功能是从图像中可能包含车牌的候选区域中定位车牌区域;图像处理的功能是强化车牌关键特征;字…

智能停车场车牌识别系统(一)

前段时间练习过的一个小项目&#xff0c;今天再看看&#xff0c;记录一下~开发工具准备&#xff1a; 开发工具&#xff1a;PyCharmPython内置模块&#xff1a;os、time、datetime第三方模块&#xff1a;pygame、opencv-python、pandas、matplotlib、baidu-aip pygame模块&…

【云原生-K8s】k8s可视化管理界面安装配置及比较【Kuboard篇】

总览 安装了k8s控制面板&#xff0c;方便日常的问题处理&#xff0c;查看资源状态信息&#xff0c;也可以增加子账号进行开放给其他人员使用&#xff0c;减少命令操作&#xff0c;提升工作效率 前置条件 须有一个正常使用的k8s集群附k8s v1.23版本搭建&#xff1a;https://…

在线合成车牌照片【模拟车牌,用于车牌识别项目测试】

1、服务发布地址&#xff1a; http://new.hdsxsc.com:10086/server.php?cphm冀DSX888&cpys0 2、传参说明&#xff1a; cphm&#xff1a;车牌号码【仅限合规的车牌号码规则】 cpys&#xff1a;车牌颜色&#xff1a; 0蓝色&#xff0c;1黄色&#xff0c;2白色&#xff0c…

HyperLPR3车牌识别-五分钟搞定: 中文车牌识别光速部署与使用

简介 HyperLPR在2023年初已经更新到了v3的版本&#xff0c;该版本与先前的版本一样都是用于识别中文车牌的开源图像算法项目&#xff0c;最新的版本的源码可从github中提取&#xff1a;https://github.com/szad670401/HyperLPR 支持多种类型车牌 快速安装 使用Python平台可以直…

车牌字母编号(转)

本文链接&#xff1a;http://user.qzone.qq.com/122022063/blog/1201669012 本文由 雪山飞狐 发表在&#xff1a; 雪山飞狐的Qzone 广东省&#xff08;粤&#xff09;粤A 广州&#xff0c;粤B 深圳&#xff0c;粤C 珠海&#xff0c;粤D 汕头&#xff0c;粤E 佛山&#xff…

车牌识别-基于模板匹配

基于模板匹配的车牌识别 一、设计思路二、功能模块1、GUI界面创建2、图片选择3、车牌粗定位4、灰度化5、倾斜矫正6、二值化和第一次形态学处理7、精确定位8、第二次形态学处理9、字符分割10、归一化切割后的字符以及模板11、字符匹配12、语音播报13、退出系统和关于按钮 三、总…