OpenCV实例(八)车牌字符识别技术(二)字符识别
车牌字符识别技术(二)字符识别
- 1.字符识别原理及其发展阶段
- 2.字符识别方法
- 3.英文、数字识别
- 4.车牌定位实例
1.字符识别原理及其发展阶段
匹配判别是字符识别的基本思想,与其他模式识别的应用非常类似。字符识别的基本原理就是对字符图像进行预处理、模式表达、判别和字典学习。
字符识别一般可分为三个阶段:
第一阶段为初级阶段,主要是应用一维图像的处理方法实现对二维图像的识别。此阶段主要涉及相关函数的构造以及特征向量的抽取。目前,该阶段的字符识别方法仍然在匹配方法的庞大家族中扮演着很重要的角色。
第二阶段为对基础理论进行相关研究的阶段。细化思想、链码法以及对一些离散图形上的拓扑性研究在这一阶段进行,其中细化思想主要用于结构的分析,链码法主要用于边界的表示。本阶段实现了抽取大范围的孔、凹凸区域、连通性以及抽取局部特征等算法,同时还实现了对K-L展开法“特征抽取理论”作为核心相关工作的研究。
第三阶段为发展阶段。本阶段在依据实际系统的要求以及设备难以提供的条件的基础上提出更为复杂的技术,主要研究工作是将技术与实际结合起来。另外,在以构造解析法以及相关法为主的基础上,许多各具特色且不同类的实用系统得以研究出来。

2.字符识别方法
目前字符识别方法主要有基于神经网络的识别方法、基于特征分析的匹配方法和基于模板的匹配方法。
(1)基于神经网络的识别方法
基于神经网络的识别方法主要包括4个步骤:预处理样本字符、提取字符的特征、对神经网络进行训练、神经网络接受经过相关预处理和特征提取的字符并对这些字符进行识别。
(2)基于特征分析的匹配方法
基于特征分析的匹配方法,主要利用特征平面来进行字符匹配。与其他匹配方法进行比较可知,它不但对噪声具有不明显的反应,而且可以获得效果更好的字符特征。
(3)基于模板的匹配方法
基于模板的匹配方法也是字符识别的一种方法,主要权衡输入模式与标准模式之间的相似程度。因此,从结果来看,输入模式的类别其实也是标准模式,单从与输入模式相似度的程度来讲,这里提到的标准模式最高。对于离散输入模式分类的实现,此方法所起的作用非常明显也非常奏效。
组成汽车牌照的字符大约有50个汉字、20多个英文字符和10个阿拉伯数字,相对而言,字符数比较少,所以可以使用模板匹配法识别这些字符。其中,用于匹配的模板的标准形式可由前面所述的字符制作而成。与其他的字符识别的方法进行比较可知,模板匹配法具有相对来说较为简单的识别过程和较快的字符识别速度,只不过准确率不是很高。

3.英文、数字识别
目前,小波识别法、模板匹配法与神经网络法等常被作为汽车牌照字符识别的主要方法。数字字符是在汽车牌照的字符集中具有最小规模、最简单结构的子集。虽然字母字符相对于数字字符而言并不复杂,但是单从字符的结构上来讲,不难看出车牌字符集中的数字字符要相对简单一些。一般采用模板匹配法来识别字母字符以及数字字符,只是有时采用模板匹配法不一定能取得理想的识别效果,例如字符存在划伤破损、褪色、污迹等问题时。本章采用的匹配模式为两级模板匹配,首先通过一级模板实现对字母数字字符的匹配,然后基于边缘霍斯多夫距离对一级模板匹配不成功的字符进行匹配。
真实的汽车图像的采集主要通过CCD工业相机进行的,输入的汽车牌照的字符图像在经过汽车牌照的定位以及汽车牌照内字符的分割之后形成,其中约有50%的高质量的字符包含在3000个字符组成的字符集中,剩下的汽车牌照内的字符质量都有一定程度的降低。相较于传统的模板匹配法和基于细化图像霍斯多夫距离的模板匹配法,准确率在基于边缘霍斯多夫距离的模板匹配识别方法中表现得更高(为98%,字符的错误识别率只有2%)。
4.车牌定位实例
测试照片:

代码实例:
# -*- coding: utf-8 -*-import cv2
import numpy as npdef stretch(img):'''图像拉伸函数'''maxi=float(img.max())mini=float(img.min())for i in range(img.shape[0]):for j in range(img.shape[1]):img[i,j]=(255/(maxi-mini)*img[i,j]-(255*mini)/(maxi-mini))return imgdef dobinaryzation(img):'''二值化处理函数'''maxi=float(img.max())mini=float(img.min())x=maxi-((maxi-mini)/2)#二值化,返回阈值ret 和 二值化操作后的图像threshret,thresh=cv2.threshold(img,x,255,cv2.THRESH_BINARY)#返回二值化后的黑白图像return threshdef find_rectangle(contour):'''寻找矩形轮廓'''y,x=[],[]for p in contour:y.append(p[0][0])x.append(p[0][1])return [min(y),min(x),max(y),max(x)]def locate_license(img,afterimg):'''定位车牌号'''contours,hierarchy=cv2.findContours(img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)#找出最大的三个区域block=[]for c in contours:#找出轮廓的左上点和右下点,由此计算它的面积和长度比r=find_rectangle(c)a=(r[2]-r[0])*(r[3]-r[1]) #面积s=(r[2]-r[0])*(r[3]-r[1]) #长度比block.append([r,a,s])#选出面积最大的3个区域block=sorted(block,key=lambda b: b[1])[-3:]#使用颜色识别判断找出最像车牌的区域maxweight,maxindex=0,-1for i in range(len(block)):b=afterimg[block[i][0][1]:block[i][0][3],block[i][0][0]:block[i][0][2]]#BGR转HSVhsv=cv2.cvtColor(b,cv2.COLOR_BGR2HSV)#蓝色车牌的范围lower=np.array([100,50,50])upper=np.array([140,255,255])#根据阈值构建掩膜mask=cv2.inRange(hsv,lower,upper)#统计权值w1=0for m in mask:w1+=m/255w2=0for n in w1:w2+=n#选出最大权值的区域if w2>maxweight:maxindex=imaxweight=w2return block[maxindex][0]def find_license(img):'''预处理函数'''m=400*img.shape[0]/img.shape[1]#压缩图像img=cv2.resize(img,(400,int(m)),interpolation=cv2.INTER_CUBIC)#BGR转换为灰度图像gray_img=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)#灰度拉伸stretchedimg=stretch(gray_img)'''进行开运算,用来去除噪声'''r=16h=w=r*2+1kernel=np.zeros((h,w),np.uint8)cv2.circle(kernel,(r,r),r,1,-1)#开运算openingimg=cv2.morphologyEx(stretchedimg,cv2.MORPH_OPEN,kernel)#获取差分图,两幅图像做差 cv2.absdiff('图像1','图像2')strtimg=cv2.absdiff(stretchedimg,openingimg)#图像二值化binaryimg=dobinaryzation(strtimg)#canny边缘检测canny=cv2.Canny(binaryimg,binaryimg.shape[0],binaryimg.shape[1])'''消除小的区域,保留大块的区域,从而定位车牌'''#进行闭运算kernel=np.ones((5,19),np.uint8)closingimg=cv2.morphologyEx(canny,cv2.MORPH_CLOSE,kernel)#进行开运算openingimg=cv2.morphologyEx(closingimg,cv2.MORPH_OPEN,kernel)#再次进行开运算kernel=np.ones((11,5),np.uint8)openingimg=cv2.morphologyEx(openingimg,cv2.MORPH_OPEN,kernel)#消除小区域,定位车牌位置rect=locate_license(openingimg,img)return rect,imgdef cut_license(afterimg,rect):'''图像分割函数'''#转换为宽度和高度rect[2]=rect[2]-rect[0]rect[3]=rect[3]-rect[1]rect_copy=tuple(rect.copy())rect=[0,0,0,0]#创建掩膜mask=np.zeros(afterimg.shape[:2],np.uint8)#创建背景模型 大小只能为13*5,行数只能为1,单通道浮点型bgdModel=np.zeros((1,65),np.float64)#创建前景模型fgdModel=np.zeros((1,65),np.float64)#分割图像cv2.grabCut(afterimg,mask,rect_copy,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_RECT)mask2=np.where((mask==2)|(mask==0),0,1).astype('uint8')img_show=afterimg*mask2[:,:,np.newaxis]return img_showdef deal_license(licenseimg):'''车牌图片二值化'''#车牌变为灰度图像gray_img=cv2.cvtColor(licenseimg,cv2.COLOR_BGR2GRAY)#均值滤波 去除噪声kernel=np.ones((3,3),np.float32)/9gray_img=cv2.filter2D(gray_img,-1,kernel)#二值化处理ret,thresh=cv2.threshold(gray_img,120,255,cv2.THRESH_BINARY)return threshdef find_end(start,arg,black,white,width,black_max,white_max):end=start+1for m in range(start+1,width-1):if (black[m] if arg else white[m])>(0.98*black_max if arg else 0.98*white_max):end=mbreakreturn endif __name__=='__main__':img=cv2.imread('car.jpg',cv2.IMREAD_COLOR)#预处理图像rect,afterimg=find_license(img)#框出车牌号cv2.rectangle(afterimg,(rect[0],rect[1]),(rect[2],rect[3]),(0,255,0),2)cv2.imshow('afterimg',afterimg)#分割车牌与背景cutimg=cut_license(afterimg,rect)cv2.imshow('cutimg',cutimg)#二值化生成黑白图thresh=deal_license(cutimg)cv2.imshow('thresh',thresh)cv2.imwrite("cp.jpg",thresh)cv2.waitKey(0)#分割字符'''判断底色和字色'''#记录黑白像素总和white=[]black=[]height=thresh.shape[0] #263width=thresh.shape[1] #400#print('height',height)#print('width',width)white_max=0black_max=0#计算每一列的黑白像素总和for i in range(width):line_white=0line_black=0for j in range(height):if thresh[j][i]==255:line_white+=1if thresh[j][i]==0:line_black+=1white_max=max(white_max,line_white)black_max=max(black_max,line_black)white.append(line_white)black.append(line_black)print('white',white)print('black',black)#arg为true表示黑底白字,False为白底黑字arg=Trueif black_max<white_max:arg=Falsen=1start=1end=2while n<width-2:n+=1#判断是白底黑字还是黑底白字 0.05参数对应上面的0.95 可作调整if(white[n] if arg else black[n])>(0.02*white_max if arg else 0.02*black_max):start=nend=find_end(start,arg,black,white,width,black_max,white_max)n=endif end-start>5:cj=thresh[1:height,start:end]cv2.imshow('cutlicense',cj)cv2.waitKey(0)cv2.waitKey(0)cv2.destroyAllWindows()
输出结果:

相关文章:
OpenCV实例(八)车牌字符识别技术(二)字符识别
车牌字符识别技术(二)字符识别 1.字符识别原理及其发展阶段2.字符识别方法3.英文、数字识别4.车牌定位实例 1.字符识别原理及其发展阶段 匹配判别是字符识别的基本思想,与其他模式识别的应用非常类似。字符识别的基本原理就是对字符图像进行…...
svn文章五:问题排查与修复 - 出了问题怎么办?SVN故障排除与修复指南
文章五:问题排查与修复 - “出了问题怎么办?SVN故障排除与修复指南” 概述:在使用SVN时,难免会遇到一些问题和错误。在这篇文章中,我们将教您如何进行故障排查和修复,保护您的SVN仓库和数据安全。 1. 引言…...
国产开源ambari之DataSophon部署
介绍 DataSophon致力于快速实现部署、管理、监控以及自动化运维大数据云原生平台,帮助您快速构建起稳定、高效、可弹性伸缩的大数据云原生平台。 主要特性有: 快速部署,可快速完成300个节点的大数据集群部署兼容复杂环境,极少的依赖使其很容易适配各种复杂环境监控指标全面丰…...
面试之快速学习STL- vector
1. vector底层实现机制刨析: 简述:使用三个迭代器表示的:  这也就解释了,为什么 vector 容器在进行扩容后,与其相关的指针、引用以及迭代器可能会失效的原因。 insert 整体向后移 erase 整体向前移…...
LeetCode_03Java_1572. 矩阵对角线元素的和
给你一个正方形矩阵 mat,请你返回矩阵对角线元素的和。 请你返回在矩阵主对角线上的元素和副对角线上且不在主对角线上元素的和。 输入:mat [[1,2,3],[4,5,6],[7,8,9]] 输出:25 解释:对角线的和为:1 5 9 3 7 2…...
系统架构设计师---职责及与其他角色的关系区别
一. 系统架构设计师的职责如下: 系统架构设计师是系统或产品线的设计责任人,是一个负责理解和管理并最终确认和评估非功能性系统需求(比如软件的可维护性、性能、复用性、可靠性、有效性和可测试性等),给出 开发规范,搭建系统实现的核心构架,对整个软件架构、关键构件、…...
【Visual Studio Code】--- Win11 C盘爆满 修改 Code 插件数据和缓存的保存路径
Win11 C盘爆满 修改 Code 插件数据和缓存的保存路径 一、概述二、修改 Code 插件数据和缓存的保存路径 一、概述 一个好的文章能够帮助开发者完成更便捷、更快速的开发。书山有路勤为径,学海无涯苦作舟。我是秋知叶i、期望每一个阅读了我的文章的开发者都能够有所成…...
mapbox-gl中mvt、pbf 矢量切片 feature id bug
1.版本:mapbox-gl.js 2.13.0,pbf采用 postgis生成 2.调用矢量切片时,采用如下方法查询矢量切片要素,报错 map.on(mousemove, function(e) { var bbox = [ [e.point.x - 5, e.point.y - 5], [e.point…...
206、仿真-51单片机锂电池蓄电池电压电流加按键控制开关状态Proteus仿真设计(程序+Proteus仿真+配套资料等)
毕设帮助、开题指导、技术解答(有偿)见文未 目录 一、硬件设计 二、设计功能 三、Proteus仿真图 四、程序源码 资料包括: 需要完整的资料可以点击下面的名片加下我,找我要资源压缩包的百度网盘下载地址及提取码。 方案选择 单片机的选择 方案一&a…...
【Realtek sdk-3.4.14b】RTL8197F+RTL8812F欧洲屏蔽5G天气雷达信道DFS信道120、124、128方法
需求描述 对于欧洲国家来说,默认支持DFS信道,但是有三个信道比较特殊,是天气雷达信道,如下图所示120、124、128,天气雷达信道有个特点就是在信号可以发射之前需要检测静默15min,如果信道自动选择到了天气雷达信道,就会有15min的时间无法连接到WiFi热点,严重影响用户体验…...
【嵌入式学习笔记】嵌入式入门7——IIC总线协议
1.IIC简介 IIC即Inter Integrated Circuit,集成电路总线,是一种同步,串行,半双工通信总线。 IIC总线协议——总线就是传输数据通道,协议就是传输数据的规则,有以下特点: 由时钟线SCL和数据线S…...
你永远想象不到有多折磨的 Android 开发 react-native gradle*!¥%#
很难过,拿到项目运行不起来,错误报告研究几天没研究明白,改代码,装gradle,忙和好久还是一个样,也不知道是码的问题还是什么,一开始 后面装完gradle,不报错了,但是也跑不起…...
关于STM32 hal printf重定向 “FILE“ is undefined
> 关于STM32 hal printf重定向,及报错。“FILE” is undefined 增加以下内容: #include "string.h" #include "stdio.h" #pragma import(__use_no_semihosting) 标准库需要的支持函数 struct __…...
“深入剖析JVM内部机制:理解Java虚拟机的工作原理“
标题:深入剖析JVM内部机制:理解Java虚拟机的工作原理 介绍: Java虚拟机(JVM)是Java语言的核心组件,负责将Java源代码转换为可以在计算机上运行的机器码。了解JVM的内部机制对于开发人员来说非常重要&#…...
939. 最小面积矩形;2166. 设计位集;2400. 恰好移动 k 步到达某一位置的方法数目
939. 最小面积矩形 核心思想:枚举矩形的右边那条边的两个点,并用一个哈希表存储相同纵坐标的最近出现的列的列数,不断更新最近出现的左边那条边。 2166. 设计位集 核心思想:这题主要是时间复杂度的优化,用一个flag来标记当前翻转…...
GPT垂直领域相关模型 现有的开源领域大模型
对于ToC端来说,广大群众的口味已经被ChatGPT给养叼了,市场基本上被ChatGPT吃的干干净净。虽然国内大厂在紧追不舍,但目前绝大多数都还在实行内测机制,大概率是不会广泛开放的(毕竟,各大厂还是主盯ToB、ToG市…...
学习Vue:slot使用
在Vue.js中,组件高级特性之一是插槽(Slots)。插槽允许您在父组件中插入内容到子组件的特定位置,从而实现更灵活的组件复用和布局控制。本文将详细介绍插槽的使用方法和优势。 什么是插槽? 插槽是一种让父组件可以向子…...
【Linux】Shell脚本之流程控制语句 if判断、for循环、while循环、case循环判断 + 实战详解[⭐建议收藏!!⭐]
👨🎓博主简介 🏅云计算领域优质创作者 🏅华为云开发者社区专家博主 🏅阿里云开发者社区专家博主 💊交流社区:运维交流社区 欢迎大家的加入! 🐋 希望大家多多支…...
【数据结构】“栈”的模拟实现
💐 🌸 🌷 🍀 🌹 🌻 🌺 🍁 🍃 🍂 🌿 🍄🍝 🍛 🍤 📃个人主页 :阿然成长日记 …...
12 注册登录
12 注册登录 整体概述 使用数据库连接池实现服务器访问数据库的功能,使用POST请求完成注册和登录的校验工作。 本文内容 介绍同步实现注册登录功能,具体涉及到流程图、载入数据库表、提取用户名和密码、注册登录流程与页面跳转的代码实现。 流程图&a…...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...
蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...
CSS | transition 和 transform的用处和区别
省流总结: transform用于变换/变形,transition是动画控制器 transform 用来对元素进行变形,常见的操作如下,它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...
