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 np


def 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 img

def dobinaryzation(img):
    '''
    二值化处理函数
    '''
    maxi=float(img.max())
    mini=float(img.min())
    
    x=maxi-((maxi-mini)/2)
    #二值化,返回阈值ret  和  二值化操作后的图像thresh
    ret,thresh=cv2.threshold(img,x,255,cv2.THRESH_BINARY)
    #返回二值化后的黑白图像
    return thresh

def 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,-1
    for i in range(len(block)):
        b=afterimg[block[i][0][1]:block[i][0][3],block[i][0][0]:block[i][0][2]]
        #BGR转HSV
        hsv=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=0
        for m in mask:
            w1+=m/255
        
        w2=0
        for n in w1:
            w2+=n
            
        #选出最大权值的区域
        if w2>maxweight:
            maxindex=i
            maxweight=w2
            
    return 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=16
    h=w=r*2+1
    kernel=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,img

def 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_show

def deal_license(licenseimg):
    '''
    车牌图片二值化
    '''
    #车牌变为灰度图像
    gray_img=cv2.cvtColor(licenseimg,cv2.COLOR_BGR2GRAY)
    
    #均值滤波  去除噪声
    kernel=np.ones((3,3),np.float32)/9
    gray_img=cv2.filter2D(gray_img,-1,kernel)
    
    #二值化处理
    ret,thresh=cv2.threshold(gray_img,120,255,cv2.THRESH_BINARY)
    
    return thresh


def find_end(start,arg,black,white,width,black_max,white_max):
    end=start+1
    for 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=m
            break
    return end
                

if __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]  #263
    width=thresh.shape[1]   #400
    #print('height',height)
    #print('width',width)
    white_max=0
    black_max=0
    #计算每一列的黑白像素总和
    for i in range(width):
        line_white=0
        line_black=0
        for j in range(height):
            if thresh[j][i]==255:
                line_white+=1
            if thresh[j][i]==0:
                line_black+=1
        white_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=True
    if black_max<white_max:
        arg=False
    
    n=1
    start=1
    end=2
    while 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=n
            end=find_end(start,arg,black,white,width,black_max,white_max)
            n=end
            if end-start>5:
                cj=thresh[1:height,start:end]
                cv2.imshow('cutlicense',cj)
                cv2.waitKey(0)
    
    
    cv2.waitKey(0)
    cv2.destroyAllWindows()

输出结果:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/70685.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

实时时钟+闹钟

在江科大实时时钟的基础上添加闹钟的配置&#xff0c;参考http://t.csdn.cn/YDlYy。 实现功能 &#xff1a;每隔time秒蜂鸣器响一次、设置闹钟的年月日时分秒&#xff0c;到时间蜂鸣器响。 前三个函数没有变&#xff0c;添加 void RTC_AlarmInit(void) 闹钟的中断配置void…

【算法题】螺旋矩阵I (求解n阶螺旋矩阵问题)

一、问题的提出 螺旋矩阵是一种常见的矩阵形式&#xff0c;它的特点是按照螺旋的方式排列元素。n阶螺旋矩阵是指矩阵的大小为nn&#xff0c;其中n为正整数。 二、解决的思路 当N1时&#xff0c;矩阵为; 当N2时&#xff0c;矩阵为; 当N>2(N为偶数如N4)时&#xff0c;矩阵…

Arduino ESP32 v2 使用记录:开发环境搭建

文章目录 目的开发环境搭建程序下载测试使用VS Code进行开发批量烧录固件到模块中总结 目的 在之前的文章 《使用Arduino开发ESP32&#xff08;01&#xff09;&#xff1a;开发环境搭建》 中介绍了使用Arduino开发ESP32的开发环境搭建内容&#xff0c;只不过当时的 Arduino co…

Django进阶

1.orm 1.1 基本操作 orm&#xff0c;关系对象映射。 类 --> SQL --> 表 对象 --> SQL --> 数据特点&#xff1a;开发效率高、执行效率低&#xff08; 程序写的垃圾SQL &#xff09;。 编写ORM操作的步骤&#xff1a; settings.py&#xff0c;连…

Metasploitable2靶机漏洞复现

一、信息收集 nmap扫描靶机信息 二、弱口令 1.系统弱口令 在Kali Linux中使用telnet远程连接靶机 输入账号密码msfadmin即可登录 2.MySQL弱口令 使用mysql -h 靶机IP地址即可连接 3.PostgreSQL弱密码登录 输入psql -h 192.168.110.134 -U postgres 密码为postgres 输入\…

线性代数(二) 矩阵及其运算

前言 行列式det(A) 其实表示的只是一个值 ∣ a b c d ∣ a d − b c \begin{vmatrix} a & b\\ c & d\end{vmatrix} ad -bc ​ac​bd​ ​ad−bc&#xff0c;其基本变化是基于这个值是不变。而矩阵表示的是一个数表。 定义 矩阵与线性变换的关系 即得 ( a 11 a 12…

数据结构——单链表的实现(c语言版)

前言 单链表作为顺序表的一种&#xff0c;了解并且熟悉它的结构对于我们学习更加复杂的数据结构是有一定意义的。虽然单链表有一定的缺陷&#xff0c;但是单链表也有它存在的价值&#xff0c; 它也是作为其他数据结构的一部分出现的&#xff0c;比如在图&#xff0c;哈希表中。…

java之junit Test

JUnit测试简介 1.什么是单元测试 单元测试是针对最小的功能单元编写测试代码Java程序最小的功能单元是方法单元测试就是针对单个Java方法的测试 2.测试驱动开发 3.单元测试的好处 确保单个方法运行正常如果修改了方法代码&#xff0c;只需确保其对应的单元测试通过测试代码…

【深度学习】【风格迁移】Visual Concept Translator,一般图像到图像的翻译与一次性图像引导,论文

General Image-to-Image Translation with One-Shot Image Guidance 论文&#xff1a;https://arxiv.org/abs/2307.14352 代码&#xff1a;https://github.com/crystalneuro/visual-concept-translator 文章目录 Abstract1. Introduction2. 相关工作2.1 图像到图像转换2.2. Di…

使用chatGPT生成提示词,在文心一言生成装修概念图

介绍 家是情感的港湾&#xff0c;而家居装修则是将情感融入空间的艺术。如何在有限的空间里展现个性与美感&#xff0c;成为了现代人关注的焦点。而今&#xff0c;随着人工智能的发展&#xff0c;我们发现了一个新的创意助手——ChatGPT&#xff0c;它不仅为我们带来了更多可能…

nodejs+vue+elementui招聘求职网站系统的设计与实现-173lo

&#xff08;1&#xff09;管理员的功能是最高的&#xff0c;可以对系统所在功能进行查看&#xff0c;修改和删除&#xff0c;包括企业和用户功能。管理员用例如下&#xff1a; 图3-1管理员用例图 &#xff08;2&#xff09;企业关键功能包含个人中心、岗位类型管理、招聘信息…

C语言每日一题:16:数对。

思路一&#xff1a;基本思路 1.x,y均不大于n&#xff0c;就是小于等于n。 2.x%y大于等于k。 3.一般的思路使用双for循环去遍历每一对数。 代码实现&#xff1a; #include <stdio.h> int main() {int n 0;int k 0;//输入scanf("%d%d", &n, &k);int x…

【深度学习注意力机制系列】—— ECANet注意力机制(附pytorch实现)

ECANet&#xff08;Efficient Channel Attention Network&#xff09;是一种用于图像处理任务的神经网络架构&#xff0c;它在保持高效性的同时&#xff0c;有效地捕捉图像中的通道间关系&#xff0c;从而提升了特征表示的能力。ECANet通过引入通道注意力机制&#xff0c;以及在…

【Plex】FRP内网穿透后 App无法使用问题

能搜索到这个文章的&#xff0c;应该都看过这位同学的分析【Plex】FRP内网穿透后 App无法使用问题_plex frp无效_Fu1co的博客-CSDN博客 这个是必要的过程&#xff0c;但是设置之后仍然app端无法访问&#xff0c;原因是因为网络端口的问题 这个里面的这个公开端口&#xff0c;可…

STM32 F103C8T6学习笔记1:开发环境与原理图的熟悉

作为一名大学生&#xff0c;学习单片机有一段时间了&#xff0c;也接触过嵌入式ARM的开发&#xff0c;但从未使用以及接触过STM32C8T6大开发使用&#xff0c;于是从今日开始&#xff0c;将学习使用它~ 本文介绍STM32C8T6最小系统开发环境搭建注意问题&#xff0c;STM32C8T6单片…

WPF上位机9——Lambda和Linq

Lambda Linq 操作集合 使用类sql形式查询 Linq To SQL

微服务学习笔记-基本概念

微服务是一种经过良好架构设计的分布式架构方案。根据业务功能对系统做拆分&#xff0c;每个业务功能模块作为独立项目开发&#xff0c;称为一个服务。 微服务的架构特征&#xff1a; 单一职责&#xff1a;微服务拆分粒度更小&#xff0c;每一个服务都对应唯一的业务能力&…

Vue实现详细界面里面有一个列表

目录 Vue实现详细界面里面有一个列表 理一下思路&#xff1a; 效果如下&#xff1a; 1、 主页面正常写 2、详细界面(重点) 3、详细界面里面的列表(重点) 要点&#xff1a; Vue实现详细界面里面有一个列表 理一下思路&#xff1a; 1、首先需要这条数据的主键id&#xff…

SpringSpringBoot常用注解

目录 一、核心注解二、Spring Bean 相关2.1 Autowired2.2 Component, Repository, Service, Controller2.3 RestController 与 Controller2.4 Configuration 与 Component2.5 Scope 三、处理常见的 HTTP 请求类型3.1 GET 请求3.2 POST 请求3.3 PUT 请求3.4 DELETE 请求3.5 PATC…

【Python】背景及环境搭建

文章目录 了解计算机一、Python背景知识一、Python环境搭建 努力经营当下 直至未来明朗 了解计算机 示例&#xff1a;使用电脑访问B站 1&#xff09; 本地的计算机会给B站服务器发送一个网络请求&#xff08;如&#xff1a;谁&#xff0c;想看哪个视频&#xff09; 2&#xf…