最近入门Opencv,分配到的任务是识别车牌。作为刚入门的小白,我查阅了大量相关代码,但在学习过程中发现了一些不足(emmm自己垃圾还嫌别人的不好…),例如我搜到的大多是直接用边缘检测加形态学处理来定位车牌,然而在过程中会发现这准确率实在不高,还有一些问题没有考虑到比如说如果图片中的车是斜的,如果定位后不处理一下,切割的时候回把字符切掉。虽然我是小白,但是经过大佬的指点以及自己的摸索,写了一套方法低级但识别率相对高的代码,在此分享一些处理方法,供大家参考。(作为小白我采用的方法真的比较低级,没有用SVM训练或者ANN系统什么的,但是经过我多达30多张图片的测试,个人感觉车牌定位和切割那部分很准,最后的字符识别除去汉字识别率也挺好的。)

车牌定位

hsv处理

网上用的大多数是RGB来识别蓝色,但hsv更符合人眼的颜色识别。
hsv中显示出来大多是比较鲜艳的绿色或者荧光黄色,如下图(下文分享的图片基本以左图作为原图)
**将hsv进行通道分离,提取其中绿色通道,再将较为鲜艳的颜色阈值化,所得到的,是一张黑色为背景,有几处为白色的图片,而车牌就在这几处白色区域当中,所以在将这种图作为模板盖到原图当中,就可显示出车牌。**通过该步可有效的减少干扰,代码如下

  split(srcHsv, channels);threshold(channels.at(1), channels.at(1), 150, 255, THRESH_BINARY);`srcRoi.copyTo(dust, channels.at(1));

运行情况如下
可以看到基本上显示的就是蓝色车牌部分,但是 为了在复杂情况下更加准确的识别,我还加了一步,代码见下

  Mat srcRange;inRange(dust, Scalar(80, 0, 0), Scalar(255, 200, 120), srcRange);//提取原图中蓝色部分

这一步其实很简单,就是提取图片中的蓝色部分(inRange函数真的很有用!!!)。对于上面那张车牌,这一步似乎没什么用,但是如果环境复杂或者干扰比较多的时候这一步可大大提高精准率。如下图,

Sobel边缘检测

此外,为了识别蓝色的车,我还结合了边缘检测
代码如下

 //滤波Mat srcGau;GaussianBlur(src, srcGau, Size(3, 3), 0, 0);cvtColor(srcGau, src_grey, CV_BGR2GRAY);//Sobel水平边缘检测Mat src_sobel;Sobel(src_grey, src_sobel, CV_8UC1, 1, 0, 3);convertScaleAbs(src_sobel, src_sobel);//闭运算Mat srcClose;Mat elementRect = getStructuringElement(MORPH_RECT, Size(25, 25));morphologyEx(src_sobel, srcClose, MORPH_CLOSE, elementRect);imshow("srcClose", srcClose);

所得到的图片如下
可以看到白色车牌部分是白(灰)色了,但是还有很多其他的白(灰)色区域,所以我选择将Sobel边缘检测的与hsv颜色识别结合起来,首先分享一下结合部分相关代码,基本上是前面的处理“加”起来,然后对“加”起来的图片进行形态学处理。(rangeClose是对前面srcRange进行了一些形态学处理后的图片)

//图像相与Mat src_add;bitwise_and(rangeClose, srcClose, src_add);//相与预想==图像二值化Mat add_thre;threshold(src_add, add_thre, 200, 250, THRESH_BINARY);namedWindow("add_thre",WINDOW_NORMAL);imshow("add_thre", add_thre);

运行如下
这里分享一下蓝色车的运行情况

左图是原图;中间那张是不加上边缘检测,仅用hsv识别的效果;右图是hsv结合边缘检测的效果,虽然还有一些干扰,但还是好了很多。(鼓掌)接下来还要对右图进行形态学运算,虽然还会存在干扰可在通过接下来的寻找轮廓环节通过设置轮廓的相关参数来去除这些干扰。

寻找轮廓

找轮廓直接调用opencv里的findContours函数,记得当时被这个函数折磨的很惨,总是异常中断,后来才想起看书的时候说这个函数要求输入的图像为8位单通道,而有时候图片仅有黑白并不意味着是单通道。
在寻找轮廓的时候,考虑到车牌可能是倾斜,后期可能还要旋转车牌(仅仅是车牌!!!而不是整张图片),我用了RotatedRect,以及最小旋转矩形。详细见下

vector<vector<Point>> contours;vector<Vec4i> hierarchy;Point2f rectRotate[4];//存储倾斜矩形顶点float min_x, min_y, max_x, max_y;findContours(close_image, contours, RETR_TREE, CHAIN_APPROX_SIMPLE);

再查找每一个轮廓的最小外接矩形

RotatedRect r = minAreaRect(Mat(contours[i]));

这里要注意的是RotatedRect的长宽并不是我们所想的,所以要对长宽进行一些处理以便筛选,具体代码就不展示了。

车牌轮廓框确定以后,要在原图中将其框出来。因为前面讲倾斜矩形的坐标储存了下来而且是一个矩形,所以我用的是line函数将四个点首尾相连,代码见下

for (int j = 0; j < 4; j++){line(src_copy, rectRotate[j], rectRotate[(j + 1) % 4], Scalar(0, 0, 255), 2, 4);  //绘制最小外接矩形每条边}

运行结果见下图

因为保存了相应坐标,所以可以将车牌区域裁剪出来,见下图

车牌切割

车牌矫正

对于上面原本车牌就很正的图我们只需将框出的区域就单独弄出来进行切割就可以了。但是如果是下面这种图,我们就得将倾斜的矩形车牌的外接矩形框出来,对这个外接矩形进行处理以便后期的切割字符。
查找外接矩形的方法很简单,无非是让四个顶点中x,y最小的值和最大的值,然后框出矩形,这一步很简单,弄出来的效果见下

因为前面将轮廓的最小外接矩形存储在rectRotate中所以这里用rectRotate的angle参数就可以知道矩形的旋转角度,我们通过if判断将旋转角度太大的图片(设置if 是为了提高代码的效率)进行旋转(这里旋转的外接矩形二值化图)。

 if (fabs(angle) > 45)   angle += 90;Point center(src_rect.cols / 2, src_rect.rows / 2);Mat src_rot = getRotationMatrix2D(center, angle, 0.8);Mat warp;warpAffine(src_rect, warp, src_rot, src_rect.size());

效果见下

因为旋转是绕着图片中心旋转的,旋转前后图片的中心没变,且前面有将最小外接矩形的长宽(即车牌的长宽)数据存储下来,所以可以进行相关裁剪提取感兴趣区域即除去图中非车牌区域,处理后图片见下

接下来再用一次前面的hsv通道分离和inRange查找蓝色,得到的图如下

可以看到两边还有两条“黑杠”,甚至在有的图中上下也有黑杠,见下图

所以我们要把这些东西裁剪掉!
这里有多种方法,我用的是根据每行每列像素突变个数来判断这行(列)是不是我所感兴趣的区域(即车牌字符所占据的长方形),可以看到未包括字符在的行或者列那一行(列)要么是白色要么黑色,而字符所在的行(列)存在着黑色变白色或者白色变黑色的像素突变,所以我们可以根据以每行(列)像素突变的个数
是否大于某个值作为条件,找出上下左右边界。代码见下:

for (int j = 0; j < picRange.rows; j++){num = 0;count = 0;uchar* date = picRange.ptr<uchar>(j);//获取第i行首地址for (int i = 0; i < picRange.cols - 1; i++){if (date[i] != date[i + 1])count++;//跃变if (date[i] == 255)num++;//白色个数}if (count > 8 && (num > int(picRange.cols*0.1)) && (num<int(picRange.cols*0.8))){row[k] = j;//记录有效行的行数k++;}}

这一部分是是把行突变像素个数大于某个值的的行数存在数组中下来作为有效行

bool in = false;for (int i = 0; i <= k - 2; i++){if ((row[i] == row[i + 1] - 1) && (row[i + 1] == row[i + 2] - 1)){in = true;row_start = row[i] - 2;//有效行if (row_start < 0) row_start = 0;break;}}

从最上边开始若有限行连续,则认为是上边界
同理也可获得下边界左边界右边界的位置,然后再次获得感兴趣区域见下图

接下来就正式进入切割啦~
上图的二值化图如下

网上切割字符的方法有很多种,最简单的就是自己算比例每隔一段距离裁剪一次,当然这种方法极其不准,还有的是做水平投影,我用的是查找最外层轮廓法

findContours(pic, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);drawContours(picCopy, contours, -1, Scalar(0, 0, 255), 1);

查找的轮廓见下图

有时前期处理的不好还会有别的干扰,例如

可以看到出除了字符以外,还有一些别的白色的区域,所以我们要对轮廓进行筛选,筛选方法与前面筛选车牌差不多,只不过这次是直接用最小矩形将轮廓框起来,然后筛选矩形。这里筛选完矩形以后要把矩形的坐标信息(包括左右)存储下来以便切割

if ((rate > 0.5) && (rectBounding.height > (int(picCopy.rows*0.6)) &&(int(rectBounding.width > 2)) && (rectBounding.x > 0) && (rectBounding.x < picCopy.cols - 7))){int i = 0;Mat roi(picCopy, rectBounding);carOutorder.push_back(roi);xLeft.push_back(rectBounding.x);xRight.push_back(rectBounding.x + rectBounding.width);yUp.push_back(rectBounding.y);yDown.push_back(rectBounding.y + rectBounding.height);rectangle(picCopy, rectBounding, Scalar(0, 255, 0));}

这是 我筛选部分的代码,第六行的代码是直接把单个字符存储在vector中。如果恰好有七个矩形符合条件,则筛选切割字符成功。但是,这时候存储的字符图片并不是按照它在车牌中的顺序存储的,所以要对其进行排序,这时我们存储的矩形坐标信息就派上用场了。

(也不知道为啥这里代码没法用那个代码快,如果有朋友知道就告诉我一下,万分感激!!!!)

有时候一些汉字的轮廓是分开的,比如“沪”等,对于这种情况则补充预语句,如果符合条件的矩形个数为6,则将picCut最左边至第一个切割的字符的左边界划为一个区域。再进行上述切割操作。

最后判断一下,是否有7个矩形块。

字符减法识别

因为模板是20*40的,所以 先用rezise把切割的每一个矩形调整一下大小,但是resize后的图是灰度图,所以resize后要再次二值化。

七个字符,第一个一定是汉字,第二个是字母,剩下五个是汉字与字母的组合。
而我的模板文件中是按照数字、字母、汉字排列的,所以为了提高识别率我让切割的第一个字符只与汉字的模板做减法,第二个字符只与字母的模板做减法,剩下的模板与汉字和字符的模板做减法。每个字符对应与它匹配率最高的模板所对应的字符。
字符减法就是讲两张图对应的像素做减法,为了提高速度,推荐用指针访问。
(这一块代码比较简单就不展示了。)

PS:第一篇博客终于写完了!!!(开心!!!)这学期因为种种原因要学习视觉,学长给的任务是先识别车牌。最开始的时候真的是零基础,连C++也不会只学过C,后来买了一本书当时还打算老老实实把那本书看完再写,后来发现根本来不及。期间也遇到过很多大坑小坑,但是代码弄出来真的很有成就感!!因为本人水平有限,可能有些东西说的不对,希望大家能给予指正。

补一个字符识别的模板:
链接:https://pan.baidu.com/s/1lpKlhhBkKZPaiC_6gIgx8g
提取码:87nv
复制这段内容后打开百度网盘手机App,操作更方便哦

------------------------------------------2022.4.19------------------------------------------
写博客时的源码找不到了 找到了之前的代码改了改又弄了一个链接在下面 方法跟识别效果与上述博客无太大差别 略微改了一下参数什么的
可能之前测试的图太少了 后面多测试了几张图 发现切割的方法可能还要改改
代码大家仅供参考哈 还有很多需要完善的地方 欢迎大家评论一起讨论
之前的链接失效了 重新传一次
链接:https://pan.baidu.com/s/1eyrrUGpEzFPwn_7gerITIQ
提取码:6bbt

Opencv用hsv识别车牌(含源码)相关推荐

  1. 【Matlab行为识别】差影法三维人体姿态行为识别【含源码 277期】

    一.代码运行视频(哔哩哔哩) [Matlab行为识别]差影法三维人体姿态行为识别[含源码 277期] 二.matlab版本及参考文献 1 matlab版本 2014a 2 参考文献 [1] 蔡利梅.M ...

  2. 手把手教你使用LabVIEW OpenCV DNN实现手写数字识别(含源码)

    文章目录 前言 一.OpenCV DNN模块 1.OpenCV DNN简介 2.LabVIEW中DNN模块函数 二.TensorFlow pb文件的生成和调用 1.TensorFlow2 Keras模 ...

  3. 利用OpenCV实现图像修复(含源码链接)

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 前一段时间小白分享过关于图像修复技术介绍的推文(点击可以跳转),有 ...

  4. 【YOLOv5】手把手教你使用LabVIEW ONNX Runtime部署 TensorRT加速,实现YOLOv5实时物体识别(含源码)

    文章目录 前言 一.TensorRT简介 二.准备工作 三.YOLOv5模型的获取 1.下载源码 2.安装模块 3.下载预训练模型 4.转换为onnx模型 四.LabVIEW使用TensorRT加速Y ...

  5. 手把手教你使用LabVIEW OpenCV dnn实现图像分类(含源码)

    文章目录 前言 一.什么是图像分类? 1.图像分类的概念 2.MobileNet简介 二.使用python实现图像分类(py_to_py_ssd_mobilenet.py) 1.获取预训练模型 2.使 ...

  6. 基于OpenCV的图像形状检测(含源码)

    导读 本文给大家分享一个用OpenCV传统方法实现形状检测的小案例. 背景介绍 实例来源:https://github.com/akshaybhatia10/ComputerVision-Projec ...

  7. OpenCV人脸识别LBPH算法源码分析

    1 背景及理论基础 人脸识别是指将一个需要识别的人脸和人脸库中的某个人脸对应起来(类似于指纹识别),目的是完成识别功能,该术语需要和人脸检测进行区分,人脸检测是在一张图片中把人脸定位出来,完成的是搜寻 ...

  8. 面部表情识别3:Android实现表情识别(含源码,可实时检测)

    面部表情识别3:Android实现表情识别(含源码,可实时检测) 目录 面部表情识别3:Android实现表情识别(含源码,可实时检测) 1.面部表情识别方法 2.人脸检测方法 3.面部表情识别模型训 ...

  9. 人脸识别3:C/C++ InsightFace实现人脸识别Face Recognition(含源码)

    人脸识别3:C/C++ InsightFace实现人脸识别Face Recognition(含源码) 目录 1. 前言 2. 项目安装 (1)项目结构 (2)配置开发环境(OpenCV+OpenCL+ ...

最新文章

  1. 面向对象和基于对象的区别
  2. java中capitals,Java GlobalConfiguration.isCapitalMode方法代码示例
  3. HDU 1257 最少拦截系统
  4. 大话ion系列(五)
  5. 第二章 html标记语言,第二章-认识HTML标签
  6. struts2的两个核心配置文件
  7. 在controller类的每个方法执行前调用某个方法_springboot:异步调用@Async
  8. 读书笔记——思维导图带你看《哲学起步》
  9. 坦克大战Java项目四
  10. 嵌入式驱动开发遇到version magic不匹配解决办法
  11. 影响我此生的几首歌(转载)
  12. 爆爆爆!!Deep Mind与Google Brain合并,成立 Google DeepMind 新部门
  13. canvas 实现流星雨特效
  14. C++之struct构造函数
  15. 宠物经济:吃、用、病、葬都是生意
  16. [转fym0121] oracle instant client odbc安装
  17. 从图像超分辨率快速入门pytorch
  18. 全球及中国ICP-OES光谱仪行业产销需求与投资前景预测报告2022~2027年
  19. Mac搭建Flink集群
  20. 利用amWiki轻松打造知识库或技术文档系统

热门文章

  1. 一级建造师全套视频课件下载_重要 ▎一建考试告知承诺制,注册、报考、七大问题最全解答!
  2. 如何把一个PDF文档拆分为多个文档
  3. 【机器学习】在浏览器中训练模型
  4. java考试真题6_JAVA认证考试历年真题:SCJP认证试题解析6
  5. 傅里叶变换是什么?一看就懂,写的超级棒!
  6. Java期末实训作业日历软件设计
  7. 修改VMware虚拟机IP地址为指定IP
  8. oracle+not+logged+on,sqlplus ORA-01012: not logged on
  9. 2023牛客寒假算法集训营3
  10. Kafka KSQL实战