车牌识别(附源代码)

完整项目已上传至github:End-to-end-for-chinese-plate-recognition/License-plate-recognition at master · duanshengliu/End-to-end-for-chinese-plate-recognition · GitHub

 整体思路:

1.利用u-net图像分割得到二值化图像

2.再使用cv2进行边缘检测获得车牌区域坐标,并将车牌图形矫正

3.利用卷积神经网络cnn进行车牌多标签端到端识别

环境:python3.11 tensorflow  opencv4.6

1.车牌定位

首先贴一下图像分割的效果图:

 

我们可以通过图像分割算法对一张输入图片进行分割,分割后的图形其实是对原图中的区域进行的分类标注,例如这里我们可以将原图标注为2类,一类就是车牌区域,还有一类就是无关的背景区域。说到标注图形就不得不说labelme了,我们可以在cmd界面通过命令 pip install labelme 进行labelme库的安装,安装结束在cmd界面输入labelme即可打开lablem软件的标注界面如下

 

 

1. 点击OpenDir ,选择我们准备好的车辆数据集(注意:一定要先把图片全都resize为训练时所需的大小,再进行标注。我们知道图片数据的范围是0-255,背景为黑色0,车牌区域为255,我们需要的是标注好的图片即img_mask中值只有{0,255}这2种,如果我们不先resize,标注完再resize会导致一个大问题,就是数据的值并不是二类,会出现{0,1,10,248,251,255}等类似的多值问题,我在之前就遇到这样的问题,不得已又重新标注了300多张图)

2. 点击左上角File—>将Save Automatically勾选上,点击Change Output Dir选择保存路径,我这里是在桌面D:/desktop/下新建了一个文件夹命名为labelme,在labelm文件夹中新建了一个json文件夹用于保存我们标注的json数据,这里我们Change Output Dir的保存路径就选它,还新建了一个data文件夹用于存放后续转换的图片数据,而待标注图片在pic文件中,存放的都是resize好的512×512的图片,命名格式最好像我这样

 

 

 

3. 准备好上述一切就可以开始标注了,点击软件左侧的   这是画任意多边形的按钮,鼠标左键点击进行标注,最后双击鼠标左键会锁定标注区域,出现如下图界面,第一次标注需输入名称,后续标注就自动显示了,点击ok后标注的线条变为红色,同时json文件夹也会相应保存和pic名字对应的json文件

 

 全部标注结束后,使用如下代码将json数据提取出来并保存到train_image和train_label文件夹中,u-net部分的数据集原作者一共标注了1200多张,最终效果很棒,达到了定位的效果 

 标注好的图片进行分类

把路径改为自己的路径

import os
import cv2
import numpy as np
#将json文件label转换为到data文件夹
n=1200#n为总共标注的图片数
for i in range(n):os.system('labelme_json_to_dataset D:/desktop/labelme/json/%d.json -o D:/desktop/labelme/data/%d_json'%(i,i))
#dst_w=512
#dst_h=512
#dst_shape=(dst_w,dst_h,3)
train_image = 'D:/desktop/labelme/train_image/'
if not os.path.exists(train_image):os.makedirs(train_image)
train_label = 'D:/desktop/labelme/train_label/'
if not os.path.exists(train_label):os.makedirs(train_label)for i in range(n):print(i)img=cv2.imread('D:/desktop/labelme/data/%d_json/img.png'%i)label=cv2.imread('D:/desktop/labelme/data/%d_json/label.png'%i)print(img.shape)label=label/np.max(label[:,:,2])*255label[:,:,0]=label[:,:,1]=label[:,:,2]print(np.max(label[:,:,2]))# cv2.imshow('l',label)# cv2.waitKey(0)print(set(label.ravel()))cv2.imwrite(train_image+'%d.png'%i,img)cv2.imwrite(train_label+'%d.png'%i,label)

 这样一来,标注好的u-net训练图片就准备好了,分别在train_image和train_label文件夹中,一并放在unet_datasets文件夹内,如下图所示

 

接下来是u-net模型搭建和训练,使用tensorflow的keras实现,贴一下我训练u-net用的代码 

 

def unet_train():height = 512width = 512path = 'D:/desktop/unet_datasets/'input_name = os.listdir(path + 'train_image')n = len(input_name)print(n)X_train, y_train = [], []for i in range(n):print("正在读取第%d张图片" % i)img = cv2.imread(path + 'train_image/%d.png' % i)label = cv2.imread(path + 'train_label/%d.png' % i)X_train.append(img)y_train.append(label)X_train = np.array(X_train)y_train = np.array(y_train)def Conv2d_BN(x, nb_filter, kernel_size, strides=(1, 1), padding='same'):x = layers.Conv2D(nb_filter, kernel_size, strides=strides, padding=padding)(x)x = layers.BatchNormalization(axis=3)(x)x = layers.LeakyReLU(alpha=0.1)(x)return xdef Conv2dT_BN(x, filters, kernel_size, strides=(2, 2), padding='same'):x = layers.Conv2DTranspose(filters, kernel_size, strides=strides, padding=padding)(x)x = layers.BatchNormalization(axis=3)(x)x = layers.LeakyReLU(alpha=0.1)(x)return xinpt = layers.Input(shape=(height, width, 3))conv1 = Conv2d_BN(inpt, 8, (3, 3))conv1 = Conv2d_BN(conv1, 8, (3, 3))pool1 = layers.MaxPooling2D(pool_size=(2, 2), strides=(2, 2), padding='same')(conv1)conv2 = Conv2d_BN(pool1, 16, (3, 3))conv2 = Conv2d_BN(conv2, 16, (3, 3))pool2 = layers.MaxPooling2D(pool_size=(2, 2), strides=(2, 2), padding='same')(conv2)conv3 = Conv2d_BN(pool2, 32, (3, 3))conv3 = Conv2d_BN(conv3, 32, (3, 3))pool3 = layers.MaxPooling2D(pool_size=(2, 2), strides=(2, 2), padding='same')(conv3)conv4 = Conv2d_BN(pool3, 64, (3, 3))conv4 = Conv2d_BN(conv4, 64, (3, 3))pool4 = layers.MaxPooling2D(pool_size=(2, 2), strides=(2, 2), padding='same')(conv4)conv5 = Conv2d_BN(pool4, 128, (3, 3))conv5 = layers.Dropout(0.5)(conv5)conv5 = Conv2d_BN(conv5, 128, (3, 3))conv5 = layers.Dropout(0.5)(conv5)convt1 = Conv2dT_BN(conv5, 64, (3, 3))concat1 = layers.concatenate([conv4, convt1], axis=3)concat1 = layers.Dropout(0.5)(concat1)conv6 = Conv2d_BN(concat1, 64, (3, 3))conv6 = Conv2d_BN(conv6, 64, (3, 3))convt2 = Conv2dT_BN(conv6, 32, (3, 3))concat2 = layers.concatenate([conv3, convt2], axis=3)concat2 = layers.Dropout(0.5)(concat2)conv7 = Conv2d_BN(concat2, 32, (3, 3))conv7 = Conv2d_BN(conv7, 32, (3, 3))convt3 = Conv2dT_BN(conv7, 16, (3, 3))concat3 = layers.concatenate([conv2, convt3], axis=3)concat3 = layers.Dropout(0.5)(concat3)conv8 = Conv2d_BN(concat3, 16, (3, 3))conv8 = Conv2d_BN(conv8, 16, (3, 3))convt4 = Conv2dT_BN(conv8, 8, (3, 3))concat4 = layers.concatenate([conv1, convt4], axis=3)concat4 = layers.Dropout(0.5)(concat4)conv9 = Conv2d_BN(concat4, 8, (3, 3))conv9 = Conv2d_BN(conv9, 8, (3, 3))conv9 = layers.Dropout(0.5)(conv9)outpt = layers.Conv2D(filters=3, kernel_size=(1, 1), strides=(1, 1), padding='same', activation='relu')(conv9)model = models.Model(inpt, outpt)model.compile(optimizer='adam',loss='mean_squared_error',metrics=['accuracy'])model.summary()print(np.max(X_train))print(np.max(y_train))print(X_train.shape)model.fit(X_train, y_train, epochs=100, batch_size=15)#epochs和batch_size看个人情况调整,batch_size不要过大,否则内存容易溢出#我11G显存也只能设置15-20左右,我训练最终loss降低至250左右,acc约95%左右model.save('unet.h5')print('unet.h5保存成功!!!')

上述代码关键部分是要获取车牌四边形的四个顶点,一开始只使用cont中坐标到外接矩形四个端点的距离,发现对于倾斜度很高的车牌效果可能不佳,见下图,可以观察到,计算得到的4个黄色坐标中,左右有2个黄色点并不处在四边形的顶点位置,这样矫正效果大打折扣,同时也会影响后续的识别效果

 

发现上述问题后,我又想了个方法就是加入了上述的point_to_line_distance函数,即还计算坐标点到上下两条边的距离,并添加了权重,经过调整权重设置为0.975倍的点线距离,0.025点到端点距离时整体效果较佳,最终矫正效果如下图: 

 

矫正效果大大改善后,识别率也将大大提高。

最终运行后上述代码后,提取的license文件夹中的车牌图如下:

 

 2.车牌识别

我们的输入图片就是上述的宽240,高80的车牌图片,要实现车牌的端到端识别,显然是多标签分类问题,每张输入图片有7个标签,模型输出前的结构都是可以共享的,只需将输出修改为7个即可,7个输出对应了7个loss,总loss就是7个loss的和,使用keras可以很方便地实现,训练cnn的代码如下

 

def cnn_train():char_dict = {"京": 0, "沪": 1, "津": 2, "渝": 3, "冀": 4, "晋": 5, "蒙": 6, "辽": 7, "吉": 8, "黑": 9, "苏": 10,"浙": 11, "皖": 12, "闽": 13, "赣": 14, "鲁": 15, "豫": 16, "鄂": 17, "湘": 18, "粤": 19, "桂": 20,"琼": 21, "川": 22, "贵": 23, "云": 24, "藏": 25, "陕": 26, "甘": 27, "青": 28, "宁": 29, "新": 30,"0": 31, "1": 32, "2": 33, "3": 34, "4": 35, "5": 36, "6": 37, "7": 38, "8": 39, "9": 40,"A": 41, "B": 42, "C": 43, "D": 44, "E": 45, "F": 46, "G": 47, "H": 48, "J": 49, "K": 50,"L": 51, "M": 52, "N": 53, "P": 54, "Q": 55, "R": 56, "S": 57, "T": 58, "U": 59, "V": 60,"W": 61, "X": 62, "Y": 63, "Z": 64}# 读取数据集path = 'home/cnn_datasets/'  # 车牌号数据集路径(车牌图片宽240,高80)pic_name = sorted(os.listdir(path))n = len(pic_name)X_train, y_train = [], []for i in range(n):print("正在读取第%d张图片" % i)img = cv2.imdecode(np.fromfile(path + pic_name[i], dtype=np.uint8), -1)  # cv2.imshow无法读取中文路径图片,改用此方式label = [char_dict[name] for name in pic_name[i][0:7]]  # 图片名前7位为车牌标签X_train.append(img)y_train.append(label)X_train = np.array(X_train)y_train = [np.array(y_train)[:, i] for i in range(7)]  # y_train是长度为7的列表,其中每个都是shape为(n,)的ndarray,分别对应n张图片的第一个字符,第二个字符....第七个字符# cnn模型Input = layers.Input((80, 240, 3))  # 车牌图片shape(80,240,3)x = Inputx = layers.Conv2D(filters=16, kernel_size=(3, 3), strides=1, padding='same', activation='relu')(x)x = layers.MaxPool2D(pool_size=(2, 2), padding='same', strides=2)(x)for i in range(3):x = layers.Conv2D(filters=32 * 2 ** i, kernel_size=(3, 3), padding='valid', activation='relu')(x)x = layers.Conv2D(filters=32 * 2 ** i, kernel_size=(3, 3), padding='valid', activation='relu')(x)x = layers.MaxPool2D(pool_size=(2, 2), padding='same', strides=2)(x)x = layers.Dropout(0.5)(x)x = layers.Flatten()(x)x = layers.Dropout(0.3)(x)Output = [layers.Dense(65, activation='softmax', name='c%d' % (i + 1))(x) for i in range(7)]  # 7个输出分别对应车牌7个字符,每个输出都为65个类别类概率model = models.Model(inputs=Input, outputs=Output)model.summary()model.compile(optimizer='adam',loss='sparse_categorical_crossentropy',  # y_train未进行one-hot编码,所以loss选择sparse_categorical_crossentropymetrics=['accuracy'])# 模型训练print("开始训练cnn")model.fit(X_train, y_train, epochs=35)  # 总loss为7个loss的和model.save('cnn.h5')print('cnn.h5保存成功!!!')

 

最终,训练集上准确率acc1(即车牌省份字符)为97%,其余字符均为99%左右,本地测试集准确率为97%,识别效果较佳。

最后放一下整体的效果图

 

转载于

基于u-net,cv2以及cnn的中文车牌定位,矫正和端到端识别软件_车牌图像增强-CSDN博客

 

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

11.1 Go 标准库的组成

💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

内网穿透方法有哪些?路由器端口映射外网和软件方案步骤

公网IP和私有IP不能互相通讯。我们通常在局域网内部署服务器和应用,当需要将本地服务提供到互联网外网连接访问时,由于本地服务器本身并无公网IP,就无法实现。这时候就需要内网穿透技术,即内网映射,内网IP端口映射到外…

iOS 18 照片应用程序增加了新分类用于查找二维码、收据、插图等

苹果照片应用 苹果在iOS 18的照片应用中引入了全新的设计,将图库和推荐部分合并为一个更集成的多合一视图。重新设计的实用工具部分经过全面改造,使得查找不同类型的图片更加容易。 实用工具 在iOS 17中,照片应用的实用工具部分只包括导入…

【代码随想录】【算法训练营】【第36天】[452]用最少数量的箭引爆气球 [435]无重叠区间 [763]划分字母区间

前言 思路及算法思维,指路 代码随想录。 题目来自 LeetCode。 day 36,周三,最难坚持的一天~ 题目详情 [452] 用最少数量的箭引爆气球 题目描述 452 用最少数量的箭引爆气球 解题思路 前提:区间可能重叠 思路:…

自动控制:滑模控制(Sliding Mode Control, SMC)

自动控制:滑模控制(Sliding Mode Control, SMC) 滑模控制(Sliding Mode Control, SMC)是一种在处理非线性系统时非常有效的控制技术。它通过驱动系统状态达到并保持在特定的滑模面附近,来实现控制目标。本文将介绍滑模控制的基本…

潞晨训推一体机,画出大模型到企业的一条龙路线图

最近跟一位企业的CIO交流,对方关于大模型的认知让我惊呆了,他说,“听说做私域大模型要两千万的软件投入和两千万的算力投入,我们公司没有这个预算”。 于是我问道:“那如果按照你们公司的数据基础和业务场景&#xff0…

功能测试 之 单模块测试----轮播图、登录、注册

单功能怎么测? 需求分析 拆解测试点 编写用例 1.轮播图 (1)需求分析 位置:后台--页面--广告管理---广告列表(搜索index页面增加广告位2) 操作完成后需要点击admin---更新缓存,前台页面刷新生效 (2)拆解…

感受光子芯片中试线,如何点亮未来计算与通信的革命之路(2024青岛智能装备与通信技术展)

光子芯片中试线:点亮未来计算与通信的革命之路 在新一代信息技术的浪潮中,光子芯片以其低能耗、高速度的特点备受瞩目。首条光子芯片中试线的建立,标志着我国在光电子领域的重大突破,同时也为即将到来的量子计算时代奠定了坚实基…

Fantasy Icons Megapack(梦幻盔甲宝石图标魔法道具图标集)

所有图标都具备高质量,并以专业水平实施。任何幻想风格游戏的上佳选择。 - 可更新的超级资源包; - 每个图标的大小均为 256x256 像素 (PNG); - 总计 2672 个独一无二的图标; - 所有图标均具有透明背景。 超级资源包内置&#xff1…

Linux常⽤服务器构建-samba

目录 1. 介绍 2. 安装 3. 配置 3.1 创建存放共享⽂件的路径 3.2 创建samba账户 4 重启samba 5. 访问共享⽂件 5.1 mac下访问⽅式 5.2 windows下访问⽅式 1. 介绍 Samba 是在 Linux 和 UNIX 系统上实现 SMB 协议的⼀个免费软件,能够完成在 windows 、 mac 操作系统…

卡塔尔.巴林:海外媒体投放-宣发.发稿效果显著提高

引言 卡塔尔和巴林两国积极采取措施,通过海外媒体投放和宣发,将本国的商业新闻和相关信息传达给更广泛的受众。在这一过程中,卡塔尔新闻网、巴林商业新闻和摩纳哥新闻网等媒体起到了关键作用。通过投放新闻稿,这些国际化的媒体平…

力扣148. 排序链表

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。 示例 1: 输入:head [4,2,1,3] 输出:[1,2,3,4] 示例 2: 输入:head [-1,5,3,4,0] 输出:[-1,0,3,4,5] 示例 3&…

人工气候老化曝露暴晒风电叶片用涂层涂料的老化耐候性能研究

关键词:太阳光模拟器、紫外光模拟器、高低温试验箱、太阳辐射光照测试系统 通过研究风电叶片用​ 氟碳涂料老化性能评价方法,对制定适合我国国情的风电叶片涂料检测方法和技术评价指标具有重要意义。 1 实验部分 1.1 试验材料 收集国内外三家知名风电…

谷粒商城实战(036 k8s集群学习2-集群的安装)

Java项目《谷粒商城》架构师级Java项目实战,对标阿里P6-P7,全网最强 总时长 104:45:00 共408P 此文章包含第343p-第p345的内容 k8s 集群安装 kubectl --》命令行操作 要进入服务器 而且对一些不懂代码的产品经理和运维人员不太友好 所以我们使用可视化…

(三十九)Vue之集中式的状态管理机制Vuex

目录 概念vuex的核心概念State(状态)Getters(获取器)Mutations(突变)Actions(动作) 搭建vuex环境基本使用getters的使用 上一篇:(三十八)Vue之插槽…

记Windows环境下JDK安装配置

写在文章开头 这是笔者非常早期接触Java时写的文章,为方便每次系统重装时能够快速完成JDK解压版安装配置遂用此文记录了一下整个过程。 Hi,我是 sharkChili ,是个不断在硬核技术上作死的 java coder ,是 CSDN的博客专家 &#x…

docker拉取镜像失败超时的解决方法,docker配置国内镜像源

更换国内源 创建或修改 /etc/docker/daemon.json 文件 安装docker后一般只有 /etc/docker 这个目录 下面并没有 daemon.json 文件 我们直接创建 : vim /etc/docker/daemon.json {"registry-mirrors" : ["https://registry.docker-cn.com"…

基于springboot实现入校申报审批系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现入校申报审批系统演示 摘要 传统办法管理信息首先需要花费的时间比较多,其次数据出错率比较高,而且对错误的数据进行更改也比较困难,最后,检索数据费事费力。因此,在计算机上安装入校申报审批系统软…

如何通过“小猪APP分发”轻松实现应用分发

你是否也在为应用分发发愁? 还记得那些日子吗?你花费了大量的时间和精力开发了一款出色的应用,但却在分发和推广环节遇到了瓶颈。是的,无论你的应用多么优秀,如果不能顺利分发给用户,那一切都是徒劳的。别…

Unity Protobuf+RPC+UniTask

远程过程调用(RPC)协议详解 什么是RPC协议RPC的基本原理RPC的关键组件RPC的优缺点Protobuf函数绑定CallEncodeRecvDecodeSocket.Send和Recv项目地址 什么是RPC协议 远程过程调用(Remote Procedure Call,简称RPC)是一种…