博主渣渣本科一枚,毕业设计选了一个基于OpenCV的车牌识别的题目,在此记下其中用到的一些关键技术备忘,也希望可以给后来人些许启发。
  车牌识别的第一步自然是想办法把车牌从一张图片中提取出来,也就是所谓的车牌定位。目前方法有很多,我采用的是基于边缘检测的车牌定位方案。
  一般来说由于车牌区域有车牌字符的存在,所以会有相当丰富的边缘信息,所以可以求取车牌的边缘图像,然后把所有分布密集的边缘聚合在一起就可以得到一些候选区域,而这些候选区域中就应当包含有我们要找的车牌区域,这时候只要再通过候选区域的长宽比,颜色等信息就可以找到车牌了。
思路讲完了,现在开始正题:


源图像

  这是我在网上随意找的一张图片,为了便于展示处理流程,接下来我讲的所有操作都将以这幅图为例。

1.彩色图转灰度图

  首先要把RGB彩色图像转为灰度图像,这一步无需多讲,OpenCV自带库函数void cvCvtColor( const CvArr* src, CvArr* dst, int code );一行直接搞定,其中src表示输入的源彩色图像,dst存放输出的灰度图像,code选CV_RGB2GRAY得到灰度图像


灰度图

2.对比度增强

  对比度增强这一步是为了让图像中的边缘更加明显。这里采用基于顶帽变换和底帽变换的方法来增强对比度。即:

enhanced(g)=g+Tophat(g)−Bothat(g)

enhanced(g) = g + Tophat(g) - Bothat(g)
  其中, gg表示源图像,Tophat(g)Tophat(g)表示对 gg的顶帽变换,Bothat(g)Bothat(g)表示对 gg的底帽变换,enhanced(g)enhanced(g)表示对比度增强后的图像。
  无论顶帽变换还是底帽变换都可以用OpenCV中的morphologyEx函数实现。主要代码和结果如下。

Mat tophat,blackhat;  //分别用于保存顶帽变换和底帽变换后的图像
Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
morphologyEx(temp, tophat, MORPH_TOPHAT, element, Point(-1, -1)); //这里的temp是源图像
morphologyEx(temp, blackhat, MORPH_BLACKHAT, element, Point(-1, -1));
add(temp, tophat, temp);
subtract(temp, blackhat,temp);


对比度增强后的灰度图

3.边缘检测

  边缘检测有很多办法,比如Sobel,Canny,Laplace等等,我采用的是Sobel算子对图像作水平差分,以求取垂直边缘。因为由于车牌区域的边缘是以垂直边缘为主,一般的背景区域不会有密集的垂直边缘,如果求取全方向上的边缘的话,在背景比较复杂的时候,背景区域也会存在更多边缘,带来干扰。以Canny算子为例,虽然检测效果看起来更好,但是非车牌的背景区域同样检测出了太多无用的边缘,不利于后续处理。


  分别用Canny算子(左图)和Sobel算子(右图)的检测结果

Sobel检测在OpenCV中也很简单,一个函数搞定。在这里我加上了高斯平滑和二值化操作,希望提取出效果更好的边缘。

    GaussianBlur(temp, temp, Size(9, 3), 0, 0); //高斯平滑,这里temp代表待处理图像Sobel(temp, temp, CV_8U, 2, 0, 3); //利用Sobel算子进行二阶水平差分,求取垂直边缘//Scharr(temp, temp, CV_8U, 1, 0, 1, 0, BORDER_DEFAULT);threshold(temp, temp, 0, 255, CV_THRESH_BINARY + CV_THRESH_OTSU);//二值化

  在得到边缘图像之后只要再经过一些形态学操作就可以得到候选区域。这里我们可以先进行开运算,开运算是对图像先腐蚀后膨胀,在腐蚀过程中,可以去除背景中一些细小的杂点,以及比较细的边缘,而车牌区域由于边缘密集所以不容易被完全腐蚀掉,再经过膨胀操作就可以恢复出车牌区域。开运算也可以通过OpenCV库函数morphologyEx实现,这里不再赘述。
  


开运算后的图像

  接着可以再对这些区域完成矩形化处理,(这一步是某篇论文上看来的,记不得出处了( ̄ェ ̄;))
  对于二值图像中的一点P(i,j) :

  • P(i,j)若 为黑点,当他的4邻域中存在至少2个白点,则将其置为白点
  • P(i,j)若 为白点,当他的4邻域全为黑点时,将其置为黑点

反复执行以上操作至图像不再变化。不过这不操作略微费时,实际操作时可以先对图像求取水平和垂直投影,然后只对投影值大于一定阈值的区域进行矩形化,这样可以省下部分时间开销。附上结果和部分代码:


候选区域矩形化

//参数src为待处理源图像
//参数dst用于存储处理后的图像
//x,y两个数组存放水平和垂直投影值
void rectanglize(IplImage *src, IplImage * dst,int * x,int * y)    //矩形化
{double temp = 0;CvScalar white, black;white.val[0] = 255;black.val[0] = 0;for (int i = 1; i < src->height - 1; i++){for (int j = 1; j < src->width - 1; j++){if (y[i]>15 && x[j]>5){temp = cvGet2D(src, i, j - 1).val[0] + cvGet2D(src, i, j + 1).val[0] + cvGet2D(src, i - 1, j).val[0] + cvGet2D(src, i + 1, j).val[0];if (255 == cvGet2D(src, i, j).val[0]){if (0 == temp)cvSet2D(dst, i, j, black);         //如果该白点周围都是黑点,则设置改点为黑点elsecvSet2D(dst, i, j, white);   //反之则为白点}else{if (temp >= 255 * 2)cvSet2D(dst, i, j, white);         //如果该黑点周围存在两个及以上的白点,则设置改点为白点elsecvSet2D(dst, i, j, black);   //反之则为黑点}}}}//将图像边缘设置为黑色for (int i = 0; i < src->height; i++){cvSet2D(dst, i, 0, black);cvSet2D(dst, i, src->width - 1, black);}for (int j = 0; j < src->width; j++){cvSet2D(dst, 0, j, black);cvSet2D(dst, src->height-1, j, black);}
}

4.区域筛选

  最后一步,区域筛选,从所有候选区域选出车牌区域,这一步就比较简单啦,比较明显的判据就是车牌的长宽比,过长或者过宽的区域都不会车牌。直接上代码,这一步需要说明下,src是要判断的图像,用cvFindContours函数可以找出图像中所有连通域,用CvSeq类型的双向链表表示,接着只要遍历链表节点,移除不合适的节点就可以完成筛选了。但是不知道怎么回事,用OpenCV自带的cvSeqRemove函数貌似无法删除节点,自己写了一个删除节点的函数还是不行,移植别人删除节点的代码还是不行,当时快把我搞疯了(╯°Д°)╯︵┻━┻,所以我自己建了一个结构体plateinfo构成链表来存放车牌在图像中的位置。

//plateinfo定义
typedef struct plateinfo    //车牌信息
{CvRect rect;      //车牌位置IplImage * plateregion; //车牌区域//IplImage * plateregion = cvCreateImage(cvSize(132, 42), IPL_DEPTH_8U, 1); //车牌区域PlateChar * plate_num = NULL; //车牌号struct plateinfo * next = NULL;
}Plate;
void screen(IplImage * src)
{//IplImage* dst = cvCreateImage(cvGetSize(src), 8, 3);CvMemStorage * storage = cvCreateMemStorage(0);CvSeq * temp = 0;int totals = cvFindContours(src, storage, &temp, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0));cout << "find " << totals << " contours" << endl;int total = totals;//plate = contours;double minarea = 100;double templarea;int flag = 1;/*int * flag=new int [totals];memset(flag, 0,totals * 4);*///temp = plate;for (int i = 0; temp != NULL; temp = temp->h_next,i++){templarea = fabs(cvContourArea(temp));//cout << templarea << endl;if (templarea < minarea) //除去面积过小的区域{cvSeqRemove(temp, 0);//mySeqRemove(&temp);totals--;continue;}CvRect aRect = cvBoundingRect(temp, 0);if (float(aRect.width / aRect.height)<2 || float(aRect.width / aRect.height)>4) //除去高比例过大和过小的区域{cvSeqRemove(temp, 0);//mySeqRemove(&temp);totals--;continue;}if (0 == colorScreen(temp))  //除去颜色不符合的区域{cvSeqRemove(temp, 0);totals--;continue;}//flag[i] = 1;if (1 == flag){aRect.x -= int(aRect.width*0.1);//aRect.y -= int(aRect.height*0.1);aRect.width = int(aRect.width*1.2);aRect.height = int(aRect.height*1.2);firstplate->rect = aRect;flag = 0;}   else{aRect.x -= int(aRect.width*0.1);//aRect.y -= int(aRect.height*0.1);aRect.width = int(aRect.width*1.2);aRect.height = int(aRect.height*1.2);Plate * newplate = new Plate;newplate->rect = aRect;constructPlateList(firstplate, newplate);}/*CvScalar red = CV_RGB(255, 0, 0);cvDrawContours(source, contours, red, red, 0, 1, 8);*/}cout << "find " << totals << " plates" << endl;if (0 == totals){delete(firstplate);firstplate = NULL;cout << "find no plate" << endl;exit(0);}//int count = 0;//for (int i = 0; i < total; i++)//{//  cout << flag[i]<<endl;//  if (0 == flag[i])//  {//      //  }//      //mySeqRemove(&plate, count);//  else//  {//      CvScalar red = CV_RGB(255, 0, 0);//      cvDrawContours(source, plate, red, red, 0, 1, 8);//      count++;//  }//}}

  另外我在筛选过程中加上了颜色判断步骤colorScreen,一般来说长宽比已经足够判断出车牌了,而且后期字符分割时可以再做一次判断:如果分不出7个字符,那么同样不是车牌。所以这一步本身不是特别重要,而且由于不同图像光照明暗的变化有所不同,难以选取出统一的阈值。所以我把判断条件放的特别宽,这一步只是单纯为了保险,不能作为唯一判据。送上代码:
  

bool colorScreen(CvSeq * contour)   //根据颜色判断待选区域是否为车牌
{CvRect rect = cvBoundingRect(contour, 0);double area = fabs(cvContourArea(contour));CvMat * temp = cvCreateMatHeader(rect.height, rect.width,CV_8UC3);cvGetSubRect(source, temp, rect);IplImage * _tempimg = cvGetImage(temp, cvCreateImageHeader(cvSize(rect.width, rect.height), IPL_DEPTH_8U,3));IplImage * tempimg = cvCreateImage(cvSize(rect.width, rect.height), IPL_DEPTH_8U, 3);cvCopy(_tempimg, tempimg);  //需将_tempimg复制到tempimg中,对_tempimg操作会改变原图//cvCvtColor(tempimg, tempimg, CV_BGR2HSV); //颜色空间由RGB转到HSVrgb2hsi(tempimg, tempimg); //颜色空间由RGB转到HSICvScalar color;CvScalar black = CV_RGB(0, 0, 0);CvScalar red = CV_RGB(255, 0, 0);double blue = 0;double white = 0;for (int i = 0; i < tempimg->height; i++){for (int j = 0; j < tempimg->width; j++){color = cvGet2D(tempimg, i, j);//color.val[0]为H,val.[1]为S,val.[2]为V//if (color.val[0]>90 && color.val[0]<120 && color.val[1]>130)//HSV方案if (color.val[0]>255 * 190 / 360 && color.val[0]<255 * 260 / 360 && color.val[1]>255 * 30 / 100)//HSI方案{blue++;//cvSet2D(source, i, j, black);}//if (color.val[1] < 100 && color.val[2]>180)//HSV方案if (color.val[1] < 255 * 30 / 100 && color.val[2]>255 * 60 / 100)//HSI方案{white++;//cvSet2D(source, i, j, red);}}}//cout << "area= " << area << " blue: "<<blue<<" white:"<<white<<" height and width : "<<tempimg->height<<","<<tempimg->width<<endl;/*cvReleaseImage(&tempimg);cvReleaseMat(&temp);*//*showpic("source", source);cvWaitKey(0);*/if ((blue + white) > (tempimg->width*temp->height*0.2) && blue / (white + 1) > 1 && blue / (white + 1) < 12)return 1;   else return 0;
}

车牌识别关键技术-车牌定位相关推荐

  1. 安卓Android、iOS移动端车牌识别OCR技术原理

    核心内容:移动端车牌识别.安卓端车牌识别.Android车牌识别.iOS端车牌识别.OCR识别技术 一.安卓Android.iOS移动端车牌识别OCR技术识别流程 安卓Android.iOS移动端车牌 ...

  2. 基于神经网络的车牌识别,卷积神经网络车牌识别

    现在很多工程项目都用车牌识别系统,我想问一下车牌识别系统的原理是什么? 核心算法:从六个步骤来提取我们抓拍的车牌信息,第一:图像捕捉采集.第二:车牌定位.第三:预处理.第四:字符分割.第五:字符识别. ...

  3. PDA车牌识别/手持机车牌识别SDK—应用处理

    核心技术:Android车牌识别.ios车牌识别 本地离线识别可保存车牌号码.方便,快捷,精准提高前端人员工作效率.增强C端用户产品体验价值. 目前,不仅是军用项目会用到PDA,很多民用项目使用PDA ...

  4. 移动端扫描车牌识别,新能源车牌OCR识别已研发

    移动端扫描车牌自动识别技术的技术核心:移动端车牌识别,手机端车牌识别,手持端车牌识别,Android端车牌识别,ios车牌识别,统统都是以OCR识别技术为基础,车牌定位凹凸字体处理二值化字符切割一系列 ...

  5. 智能驾驶 车牌检测和识别(三)《CRNN和LPRNet实现车牌识别(含车牌识别数据集和训练代码)》

    智能驾驶 车牌检测和识别(三)<CRNN和LPRNet实现车牌识别(含车牌识别数据集和训练代码)> 目录 智能驾驶 车牌检测和识别(三)<CRNN和LPRNet实现车牌识别(含车牌识 ...

  6. 易泊安卓车牌识别以及IOS车牌识别

    易泊安卓车牌识别以及IOS车牌识别 随着经济水平的不断提升,汽车数量的爆炸式激增为汽车管理带来了一定的困难.现在,车牌识别已经成为每个城市的车辆管理重点工作之一,有效.准确.及时的车牌识别可以方便警务 ...

  7. 计算机应用的时间识别的,计算机人工智能识别关键技术及运用

    刘丽娜 摘   要:互联网的信息时代,通过计算机和人工智能识别技术相结合,在人们的日常生活中不断的进行运用,让人们与科技紧密相连,真正的体验科学发展给生活带来的乐趣.人工智能识别技术可以满足所有行业的 ...

  8. 车牌识别系统服务器安装,车牌识别系统安装流程及注意事项

    原标题:车牌识别系统安装流程及注意事项 一套完整的车牌识别系统包含车牌识别一体机.停车场收费显示屏.智能道闸.车牌识别软件等.这些设备的安装是否规范,很大程度上影响着整套车牌识别系统的工作性能. 在车 ...

  9. 车牌识别EasyPR(2)——车牌颜色定位与偏斜扭转

    本篇文章介绍EasyPR里新的定位功能:颜色定位与偏斜扭正.让我们先看一下示例图片,这幅图片中的车牌通过颜色的定位法进行定位并从偏斜的视角中扭正为正视角(请看右图的左上角). 图1 新版本的定位效果 ...

最新文章

  1. ArcGIS的许可文件问题
  2. Bootstrap学习-其它内置组件
  3. 【虚拟化实战】Cluster设计之一资源池
  4. nohup /dev/null 21 含义详解
  5. linux svn启动和关闭(转)
  6. 苹果a10处理器_【突然】苹果宣布2019款iPad降价 发布不到半年最高降500
  7. leetcode刷题日记-课程表 III
  8. 取得客户端的机器名,域名,登陆用户名...转
  9. T2Admin 完美集成 RDP报表(含:菜单、权限系统)
  10. 无需ROOT卸载或禁用vivo Z5手机的内置软件
  11. python实现英文新闻摘要自动提取_Python实现英文新闻摘要自动提取
  12. 偏差方差分解中为什么第三行第六行为零
  13. python框架知乎_我正在学习python的flask框架?为什么样知乎没有选择 Ruby
  14. linux修改分区自检,linux tune2fs命令取消大分区开机自检
  15. 《UE4蓝图完全学习》笔记
  16. 迷惑新手的IOS开发问题
  17. 正确理解差异的“专业意义”与“统计学意义”
  18. JavaScript知识点-周2.md
  19. cpp enum enum class
  20. RabbitMQ Server简介和安装教程

热门文章

  1. 学会Python好找工作吗?这就告诉你答案
  2. halcon calculate_lines_gauss_parameters算子详解
  3. 7、【WebGIS实战】专题篇——API key
  4. java spring+mybatis整合实现爬虫之《今日头条》搞笑动态图片爬取
  5. 关于数据结构中的叶节点和二度节点的关系(通俗的理解)。
  6. java 短信_java实现发送手机短信
  7. Pattern Recognition期刊投稿经验
  8. [转]Kaldi命令词识别
  9. 深入了解物联网,这几个物联网技术了解吗?
  10. 【夜读】做好这6件事,让人受益一生