车牌矫正

一、前言

汽车车牌识别设备往往都是固定于红绿灯的支架或者是小区、学校入口的一侧来采集获取车牌图像,由于采集图像设备的安装位置不固定以及不同车辆车牌悬挂的高度也不确定等因素,这些外部因素都有可能导致车牌在一定程度上发生倾斜。发生倾斜的车牌如果不进行矫正,将会影响后续的字符划分处理。所以,对车牌进行倾斜矫正处理是字符划分算法得以成功的前提。车牌倾斜主要存在三种情形,分别是水平、垂直和包括水平、垂直倾斜两种情况的混合倾斜。本文所研究的算法中只对水平方向倾斜情况进行校正。本文采用基于透视变换的倾斜校正算法来判断车牌是否需要倾斜校正以及对车牌进行校正操作,整个矫正车牌及划分字符的流程如图3-1所示。

二、基于透视变换的倾斜矫正算法

透视变换的本质是将图像投影到一个新的视平面,也被称为投影映射[4]。透视变换的通用变换公式为式 (3-1):

其中,(u,v)代表原始图像的坐标,
代表变换矩阵,透视变化后的图像横、纵坐标分别如式 (3-2)和式 (3-3)所示:

给定原始图像的四个顶点坐标和变换后图像的四个顶点坐标,即可求得变换矩阵。求得变换矩阵后,需要求出倾斜状态下车牌的四个顶点坐标,具体步骤如下:首先定位出车牌位置,判断车牌是否倾斜。车牌倾斜主要有逆时针倾斜和顺时针倾斜的两种情况。本文只说明逆时针倾斜情况。逆、顺时针情况可以简化成如图3-2和图3-3的模型。

如图3-1所示,根据求得A、E、F、G的坐标(Top_Point、Right_Point、Bottom_Point、Left_Point),在图3-2中,则是根据▲ABC~▲ADE来计算出A、E、F、G点的坐标。本文只说明图3-1中G点的求解步骤如下:
求解G点的横坐标,如式(3-4)所示:

依据三角形相似原理,写出相似等式,如式(3-5)所示:

求解得到G的纵坐标值,如式(3-6)所示:

求解后,原始图像中车牌的4个顶点均已知。根据上文车牌特征分析可知,车牌尺寸为440×140,故变换后的车牌4个顶点依次为(0,140)、(440,0)、(0,0)、 (440,140)。根据变换矩阵与变换前后对应顶点关系,利用透视变换倾斜纠正算法对车牌进行矫正,矫正前后实验结果分别如图3-4和图3-5所示。

三、跳变次数法去除车牌边框和铆钉


车牌倾斜矫正之后,车牌边框和用于固定车牌的铆钉会影响字符分割操作的结果,因此,去除车牌边框和铆钉是车牌字符分割算法能否取得成功的关键步骤。本文采用跳变次数法[5][6]去除边框和铆钉。跳变次数法去除边框及铆钉的原理及流程如图3-6。

由于字符颜色与车牌底色不同,因此车牌区域中存在大量的颜色突变信息,这就为跳变次数法的实施创造了条件。所谓的跳变是指由字符区域过渡到非字符区域或者由非字符区域过渡到字符区域。具体做法是从上至下扫描出二值化后的车牌图像,找到跳变次数超过预先设定阈值的第一行,且之后连续3行跳变次数均超过阈值,则认定该行为车牌字符上边界;同理,从下至上扫描车牌图像,找到突变次数超过预先设定阈值的一行,且之后连续3行突变次数均超过阈值,则该行为车牌字符下边界。在确定车牌上下边界的前提下,接着确定车牌字符的左边界、右边界。因为车牌中可能存在数字1,数字1的跳变次数为2,为了防止滤除掉阿拉伯数字1,设定跳变次数的阈值为2。具体做法是从左至右扫描车牌图像,依次统计每列的跳变次数。找到跳变次数超过阈值的第一行,且之后连续3列跳变次数均超过阈值,则该行为车牌字符左边界;同理,找到车牌字符右边界。去除前后效果分别如图3-8和图3-9所示。

四、字符细化操作

去除边框及铆钉后,图像上仍有一些残留的噪声点,这些残留的噪声点会对垂直投影法划分字符操作产生不良影响。因此,对去除边框和铆钉后的图像进行形态学腐蚀处理是非常有必要而且是至关重要的,腐蚀处理在去掉车牌字符区域残留噪声的同时,也可以对字符进行细化,形态学腐蚀处理效果如图3-10所示。

五、垂直投影法分割字符


去除边框、铆钉,以及对字符进行细化处理后,接下来需要将车牌字符区域划分为单个字符。本文采取基于垂直投影法的字符划分方法[7][8]来划分车牌字符区域,依据车牌特点可知,每个字符之间都存在一定的纯黑区域。二值化后,字符区域为白色,车牌中的非字符区域为黑色。
划分单个字符的原理及具体流程如图3-11所示。首先计算每列中白色像素点个数,垂直投影后得到直方图,如图3-12所示,并以此来判断各个字符的起始位置。然后从左至右扫描投影直方图,找到存在白色像素点的第一列,则认定为该列是车牌第一个字符的左边界。若上一列存在白色像素点,而下一列是不存在白色像素点的黑色区域,则认定该列为第一个字符的右边界,同理,便可分割出其余6个车牌字符左右边界。投影得到的图像如图3-12所示,划分出的单个字符如图3-13所示。

六、字符图像归一化处理

由于从采集图像中提取出的车牌大小不完全相同,这就导致了划分出来的单个字符的尺寸可能不同,为了能够适应已经训练好的车牌字符识别网络,需要对划分出的单个车牌字符进行归一化处理,使得从采集图像中定位出的车牌在字符分割操作完成后,所获取的车牌单个字符图像大小都为2020像素[2],如图3-11所示。这里至于为什么都归一化为2020像素,是因为我的神经网络的输入都是20*20像素的。

七、代码实现

```python
for rect in car_contours:rect = (rect[0], (rect[1][0]+20, rect[1][1]+5), rect[2])box = cv2.boxPoints(rect)#图像矫正   cv2.getAffineTransform(pos1,pos2),其中两个位置就是变换前后的对应位置关系。输出的就是仿射矩阵M,最后这个矩阵会被传给函数 cv2.warpAffine() 来实现仿射变换if rect[2] > ANGLE: #正角度new_right_point_x = vertices[0, 0]new_right_point_y = int(vertices[1, 1] - (vertices[0, 0] - vertices[1, 0]) / (vertices[3, 0] - vertices[1, 0]) * (vertices[1, 1] - vertices[3, 1]))new_left_point_x = vertices[1, 0]new_left_point_y = int(vertices[0, 1] + (vertices[0, 0] - vertices[1, 0]) / (vertices[0, 0] - vertices[2, 0]) * (vertices[2, 1] - vertices[0, 1]))point_set_1 = np.float32([[440, 0], [0, 0], [0, 140], [440, 140]])elif rect[2] < ANGLE: #负角度new_right_point_x = vertices[1, 0]new_right_point_y = int(vertices[0, 1] + (vertices[1, 0] - vertices[0, 0]) / (vertices[3, 0] - vertices[0, 0]) * (vertices[3, 1] - vertices[0, 1]))new_left_point_x = vertices[0, 0]new_left_point_y = int(vertices[1, 1] - (vertices[1, 0] - vertices[0, 0]) / (vertices[1, 0] - vertices[2, 0]) * (vertices[1, 1] - vertices[2, 1]))point_set_1 = np.float32([[0, 0], [0, 140], [440, 140], [440, 0]])new_box = np.array([(vertices[0, 0], vertices[0, 1]), (new_left_point_x, new_left_point_y), (vertices[1, 0], vertices[1, 1]),(new_right_point_x, new_right_point_y)])point_set_0 = np.float32(new_box)mat = cv2.getPerspectiveTransform(point_set_0, point_set_1)dst = cv2.warpPerspective(img, mat, (440, 140))cv_show('dst',dst)#-------------------------------字符分割-------------------------------------
plate_original = dst.copy()
img_aussian = cv2.GaussianBlur(dst,(5,5),1)
# cv_show('img_aussian',img_aussian)
#中值滤波
dst = cv2.medianBlur(img_aussian,3)# 对车牌进行精准定位
img_B = cv2.split(dst)[0]
img_G = cv2.split(dst)[1]
img_R = cv2.split(dst)[2]
for i in range(dst.shape[:2][0]):for j in range(dst.shape[:2][1]):if abs(img_B[i,j] - Blue) < THRESHOLD and abs(img_G[i,j] - Green) <THRESHOLD and abs(img_R[i,j] - Red) < THRESHOLD:dst[i][j][0] = 0dst[i][j][1] = 0dst[i][j][2] = 0else:dst[i][j][0] = 255dst[i][j][1] = 255dst[i][j][2] = 255
# cv_show('dst',dst)# 灰度化
gray = cv2.cvtColor(dst, cv2.COLOR_BGR2GRAY)
cv_show('gray',gray)#-------------------------------跳变次数去掉铆钉和边框----------------------------------
times_row = []  #存储哪些行符合跳变次数的阈值
for row in range(LICENSE_HIGH): # 按行检测 白字黑底pc = 0for col in range(LICENSE_WIDTH):if col != LICENSE_WIDTH-1:if gray[row][col+1] != gray[row][col]:pc = pc + 1times_row.append(pc)
print("每行的跳变次数:",times_row)#找车牌的下边缘-从下往上扫描
row_end = 0
row_start = 0
for row in range(LICENSE_HIGH-2):if times_row[row] < 16:continueelif times_row[row+1] < 16:continueelif times_row[row+2] < 16:continueelse:row_end = row + 2
print("row_end",row_end)#找车牌的上边缘-从上往下扫描
i = LICENSE_HIGH-1
row_num = [] #记录row_start可能的位置
while i > 1:if times_row[i] < 16:i = i - 1continueelif times_row[i-1] < 16:i = i - 1continueelif times_row[i-2] < 16:i = i - 1continueelse:row_start = i - 2row_num.append(row_start)i = i - 1
print("row_num",row_num)#确定row_start最终位置
for i in range(len(row_num)):if i != len(row_num)-1:if abs(row_num[i] - row_num[i+1])>3:row_start = row_num[i]
print("row_start",row_start)times_col = [0]
for col in range(LICENSE_WIDTH):pc = 0for row in range(LICENSE_HIGH):if row != LICENSE_HIGH-1:if gray[row,col] != gray[row+1,col]:pc = pc + 1times_col.append(pc)
print("每列的跳变次数",times_col)
# 找车牌的左右边缘-从左到右扫描
col_start = 0
col_end = 0
for col in range(len(times_col)):if times_col[col] > 2:col_end = col
print('col_end',col_end)j = LICENSE_WIDTH-1
while j >= 0:if times_col[j] > 2:col_start = jj = j-1
print('col_start',col_start)# 将车牌非字符区域变成纯黑色
for i in range(LICENSE_HIGH):if i > row_end or i < row_start:gray[i] = 0
for j in range(LICENSE_WIDTH):if j < col_start or j > col_end:gray[:,j] = 0
cv_show("res",gray)
# plate_binary = gray.copy()
for i in range(LICENSE_WIDTH-1,LICENSE_WIDTH):gray[:,i] = 0# 字符细化操作
specify = cv2.erode(gray,kernel,iterations=2)
cv_show("specify",specify)
plate_specify = specify.copy()#---------------------------垂直投影法切割字符-------------------------
lst_heise = []  #记录每一列中的白色像素点数量
for i in range(LICENSE_WIDTH):pc = 0for j in range(LICENSE_HIGH):if specify[j][i] == 255:pc = pc + 1lst_heise.append(pc)
# print("lst_heise",lst_heise)
a = [0 for i in range(0,LICENSE_WIDTH)]
for j in range(0, LICENSE_WIDTH):  # 遍历一列for i in range(0, LICENSE_HIGH):  # 遍历一行if specify[i, j] == 255:  # 如果该点为白点a[j] += 1  # 该列的计数器加一计数specify[i, j] = 0  # 记录完后将其变为黑色# print (j)
for j in range(0, LICENSE_WIDTH):  # 遍历每一列for i in range((LICENSE_HIGH - a[j]), LICENSE_HIGH):  # 从该列应该变白的最顶部的点开始向最底部涂白specify[i, j] = 255
plt.imshow(specify,cmap=plt.gray())
plt.show()
cv_show("touying",specify)#开始找字符的边界
in_block = False #用来指示是否遍历到字符区
startIndex = 0
endIndex = 0
threshold = 10
index = 0
char_Image = [] # 存放一个个分割后的字符
for i in range(LICENSE_WIDTH):if lst_heise[i] != 0 and in_block == False: # 表示进入有白色像素点的区域in_block = TruestartIndex = iprint("start", startIndex)elif lst_heise[i] == 0 and in_block == True: # 表示进入纯黑区域,且纯黑区域前面是字符区域endIndex = iin_block = Falseprint("end", endIndex)if endIndex < startIndex:endIndex = 440if endIndex - startIndex > 10:res = plate_specify[row_start:row_end,startIndex:endIndex]index = index + 1res = cv2.resize(res, (20, 20), interpolation=cv2.INTER_LANCZOS4)  # 分割出的各个字符进行归一化char_Image.append(res)cv_show("res", res)print("char_Image的长度:{}".format(len(char_Image)))
# print(char_Image)
char_Image = np.array(char_Image)
# print(char_Image.shape[0])
plate_char = np.hstack((char_Image[0],char_Image[1],char_Image[2],char_Image[3],char_Image[4],char_Image[5],char_Image[6]))
cv2.imshow("plate",plate_char)
cv2.waitKey(0)
cv2.destroyAllWindows()

到这里,车牌矫正的相关步骤都已讲解完毕,剩下的就是关于深度学习的知识了,之后我会陆续分享深度学习的基础知识。

汽车车牌识别系统实现(三)-- 车牌矫正+字符分割+代码实现相关推荐

  1. c 语言车牌识别系统课题设计,车牌识别系统的设计--课程设计报告.doc

    车牌识别系统的设计--课程设计报告 目录 一.摘要:3 二.设计目的和意义:3 2.1.设计目的:3 2.2.设计意义:3 三.设计原理:3 四.详细设计步骤:3 4.1.提出总体设计方案:4 4.2 ...

  2. 车牌识别系统电脑当服务器,车牌识别系统数据库的安装方法

    如果觉得本信息对你在以后的工作中有帮助,请关注本公众号并转发出去!谢谢! 车牌识别系统安装SQL Server 2000数据库的参考步骤 一.安装SQL Server 2000 注意:如果您的一卡通管 ...

  3. java车牌识别系统_基于jsp的车牌识别系统-JavaEE实现车牌识别系统 - java项目源码...

    基于jsp+servlet+pojo+mysql实现一个javaee/javaweb的车牌识别系统, 该项目可用各类java课程设计大作业中, 车牌识别系统的系统架构分为前后台两部分, 最终实现在线上 ...

  4. spring boot + maven + opencv 车牌识别系统,包含车牌检测、车牌号识别训练

    yx-image-recognition 介绍 这是一个基于spring boot + maven + opencv 实现的图像识别及训练的Demo项目 包含车牌识别.人脸识别等功能,贯穿样本处理.模 ...

  5. python车牌识别系统开源代码_汽车牌照识别系统【YOLO+MLP】

    车牌识别系统可以自动检测并识别图像中的车辆牌照,其算法主要包括牌照定位.牌照分割.字符识别等步骤.本文将给出一种基于深度学习的车牌识别系统方案. 要快速掌握开发人工智能系统的技能,推荐汇智网的 机器学 ...

  6. 毕业设计 : 车牌识别系统实现【全网最详细】 - opencv 卷积神经网络 机器学习 深度学习

    文章目录 0 简介 1 车牌识别原理和流程 1.1 车牌定位 1.2 基于图形图像学的定位方法. 1.3 基于机器学习的定位方法. 1.4 字符分割 1.5 字符识别 2 基于机器学习的车牌识别 2. ...

  7. EasyPR--中文车牌识别系统 开发详解(开源)

    人工智能AI与大数据技术实战  公众号: weic2c 一个开源的中文车牌识别系统, Git地址为:https://github.com/liuruoze/EasyPR. 我给它取的名字为EasyPR ...

  8. 【OpenCV+Qt】使用车牌识别系统EasyPR识别车牌号

    EasyPR是一个中文的开源车牌识别系统,其车牌识别划分为了两个过程:即车牌检测(Plate Detection)和字符识别(Chars Recognition)两个过程: 车牌检测(Plate De ...

  9. pkr车牌识别系统服务器,车牌识别系统车牌录入的操作步骤

    录入车牌是很简单的,车牌识别系统基本都需要配合电脑用,打开录入界面后在车牌信息一栏输入车牌号码就行了.有些车牌识别系统是不用录入车牌前面的汉字的,有些车牌识别系统需要将车牌前面的汉字录入. 非常多商场 ...

最新文章

  1. 走进Java 7模块系统
  2. OpenJ_Bailian 4148 生理周期
  3. 面试精讲之面试考点及大厂真题 - 分布式专栏 08 Redis中有哪些数据结构及底层实现原理
  4. Fragment过度动画分析一
  5. 计算机主机系统总线,全国2009年10月高等教育自学考试计算机应用基础试题及答案...
  6. 探索哪个进程使磁盘I/O升高
  7. 优质编程网站推荐(适合学习和查资料)
  8. 在EnableQ中定义逻辑关系
  9. 罗振宇2021跨年演讲6:山村小学的豆腐课到底在玩啥?
  10. 什么是递归?递归的理解
  11. 七夕送什么礼物最实用?送人绝对不会出错的礼物值得买
  12. 2021-06-19:交错字符串。 有三个字符串s1,s2,s3。判断s3是否由s1和s2交错组成的。比如s1=“abc“,s2=“123“,s3=“12ab3c“,应该返回true,因为s3去掉12
  13. 轻松停车入位 淑女节女司机5大用车指南
  14. MacOS解压rar文件
  15. vc++实现avi文件的操作
  16. 社交电商海外崛起:小程序助力打造超级App
  17. Excel VBA Sheets和Worksheets的区别
  18. 软件测试 前置条件是什么意思,软件测试用例生成中前置条件分析.doc
  19. 快速视频Seeking(视频帧搜索)
  20. 全新第八代智能英特尔® 酷睿™ 处理器经全面优化

热门文章

  1. 监控神器-普罗米修斯Prometheus
  2. POJ 2352 Starts POJ 2418 Cows
  3. Jackson--使用/教程/示例
  4. 独上高楼,望断天涯处
  5. 本周AI热点回顾:天津大学火速解聘学术不端教授,利用职权为女儿铺路;人脸识别第一案判决...
  6. Linux的简单使用
  7. 鹤城杯2021 Web
  8. MapReduce and Parallel DBMSs Friends or Foes
  9. Linux 脚本部署应用宝,应用宝:支付接入流程相关
  10. signature=6e522504557107558aa08bc03de24226,Java Signature.initVerify方法代碼示例