车牌识别:HyperLPR车牌识别代码解析

article/2025/9/10 20:42:31

首先声明,这只是本人自己对HyperLPR代码的看法解析可能会有错还请多多谅解。
先贴上HyperLPR源码的链接HyperLPR
其中最有用的其实就是HyperLPRLite.py这个代码文件,原来Github上的使用教程可能有点老了不太适用,这边附上一个简单的demo使用,创建一个新的文件夹然后把HyperLPRLite.py放进文件夹,再在新文件夹里面创建一个demo.py文件,如下(记得要把xml,两个h5文件一起放进去)

import sys
from PIL import ImageFont
from PIL import Image
from PIL import ImageDraw
import HyperLPRLite as pr
import cv2
import numpy as np
import time
import importlibdef visual_draw_position(grr):max=0model = pr.LPR("model/cascade.xml","model/model12.h5","model/ocr_plate_all_gru.h5")for pstr,confidence,rect in model.SimpleRecognizePlateByE2E(grr):if 1:max=confidencegrr = drawRectBox(grr, rect, pstr+" "+str(round(confidence,3)))print("车牌号:")print(pstr)print("置信度")print(confidence)cv2.imshow("image",grr)cv2.waitKey(0)test_image = cv2.imread("C://Users//dell//Desktop//Cars//30.jpg")
visual_draw_position(test_image)

以上就可以简单的使用HyperLPR这个开源的车牌识别库

现在进入正题我们来看一下HyperLPRLite.py这个重要文件里面写的内容
接下来我会按照代码执行的顺序来解读各个函数的作用
其实这份代码总共分为三大部分:粗定位,精定位,车牌信息识别
1、车牌粗定位

    def computeSafeRegion(self,shape,bounding_rect):    #该函数是初步的框出车牌的大概位置属于粗定位出一个保险的包含车牌的大小top = bounding_rect[1] # ybottom  = bounding_rect[1] + bounding_rect[3] # y +  hleft = bounding_rect[0] # xright =   bounding_rect[0] + bounding_rect[2] # x +  wmin_top = 0max_bottom = shape[0]min_left = 0max_right = shape[1]if top < min_top:top = min_topif left < min_left:left = min_leftif bottom > max_bottom:bottom = max_bottomif right > max_right:right = max_rightreturn [left,top,right-left,bottom-top]def cropImage(self,image,rect):    #框出车牌识别的区域x, y, w, h = self.computeSafeRegion(image.shape,rect)return image[round(y):y+h,round(x):x+w]#返回所有车牌的boxdef detectPlateRough(self,image_gray,resize_h = 720,en_scale =1.08 ,top_bottom_padding_rate = 0.05):if top_bottom_padding_rate>0.2:print("error:top_bottom_padding_rate > 0.2:",top_bottom_padding_rate)exit(1)height = image_gray.shape[0]padding =    int(height*top_bottom_padding_rate)scale = image_gray.shape[1]/float(image_gray.shape[0])image = cv2.resize(image_gray, (int(scale*resize_h), resize_h))image_color_cropped = image[padding:resize_h-padding,0:image_gray.shape[1]]image_gray = cv2.cvtColor(image_color_cropped,cv2.COLOR_RGB2GRAY)watches = self.watch_cascade.detectMultiScale(image_gray, en_scale, 2, minSize=(36, 9),maxSize=(36*40, 9*40))cropped_images = []for (x, y, w, h) in watches:x -= w * 0.14w += w * 0.28y -= h * 0.15h += h * 0.3cropped = self.cropImage(image_color_cropped, (int(x), int(y), int(w), int(h)))cropped_images.append([cropped,[x, y+padding, w, h]])return cropped_images

这个地方的重点是detectPlateRough()函数,首先开始的时候会对图像进行一些插补还有调整图像大小比例操作(其实个人认为这些不是很必要),然后核心的一部就是这个Cascade级联分类器的应用,这才是核心。这里的级联分类器是基于Haar+Adaboost构成的。很重要的就是之前的cascade.xml文件,这个文件里面存放的都是车牌的Haar特征。
在这里我们采用了cascade.xml 检测模型——目前效果最好的cascad级联检测模型。然后使用OpenCV的detectMultiscale的方法来对图像进行滑动窗口遍历寻找车牌,实现粗定位。detectMultiscale函数为多尺度多目标检测:多尺度:通常搜索目标的模板尺寸大小是固定的,但是不同图片大小不同,所以目标对象的大小也是不定的,所以多尺度即不断缩放图片大小(缩放到与模板匹配),通过模板滑动窗函数搜索匹配;同一副图片可能在不同尺度下都得到匹配值,所以多尺度检测函数detectMultiscale是多尺度合并的结果。

简单来讲就是cascade.xml这个文件是通过很多的正样本车牌图片和负样本非车牌图片然后经过一个exe的程序可以创建出一个cascade.xml文件其中文件里Haar特征数据已经过Adaboost处理。最后通过这个xml文件就可以训练出一个级联分类器分类器的判别车牌标准是通过计算好多车牌特征后得出的一个阈值,大于这个阈值判别为车牌。这就是这个函数大概的功能,其中有一些框选图像中车牌的部分的功能比较基础此处不讲。
在这里插入图片描述
推荐原作者的讲解:HyperLPR车牌识别技术算法之车牌粗定位与训练

2、车牌精定位

    def model_finemapping(self):input = Input(shape=[16, 66, 3])  # change this shape to [None,None,3] to enable arbitraty shape inputx = Conv2D(10, (3, 3), strides=1, padding='valid', name='conv1')(input)x = Activation("relu", name='relu1')(x)x = MaxPool2D(pool_size=2)(x)x = Conv2D(16, (3, 3), strides=1, padding='valid', name='conv2')(x)x = Activation("relu", name='relu2')(x)x = Conv2D(32, (3, 3), strides=1, padding='valid', name='conv3')(x)x = Activation("relu", name='relu3')(x)x = Flatten()(x)output = Dense(2,name = "dense")(x)output = Activation("relu", name='relu4')(output)model = Model([input], [output])return modeldef finemappingVertical(self,image,rect):resized = cv2.resize(image,(66,16))resized = resized.astype(np.float)/255#cv2.imshow("wd", resized)res_raw= self.modelFineMapping.predict(np.array([resized]))[0]#这里会返回两个值print(self.modelFineMapping.predict(np.array([resized])))res  =res_raw*image.shape[1]res = res.astype(np.int)H,T = resH-=3if H<0:H=0T+=2;if T>= image.shape[1]-1:T= image.shape[1]-1rect[2] -=  rect[2]*(1-res_raw[1] + res_raw[0])rect[0]+=res[0]cv2.imshow("ww",image)image = image[:,H:T+2]cv2.imshow("wd",image)image = cv2.resize(image, (int(136), int(36)))return image,rect

model12.h5这是这部分左右边界回归模型
这里的精定位其实就是切掉原来上面粗定位出来车牌的多余部分,这里用到了一个CNN网络来实现左右边届切除点的预测也就是self.modelFineMapping.predict()这个函数他会返回两个值两个小数值,左右边界分别要切除多少。其实这部分我有点迷,我不太懂如何利用CNN网络来训练出得到两个切除边界的值,因为一般往往CNN网络用来做图像中分类较多,至少对我来说我很少看到CNN拿来做回归。所以在这一部分我不是很看懂。
这里附上原作者的:HyperLPR车牌识别技术算法之车牌精定位

3、车牌信息识别

    def fastdecode(self,y_pred):results = ""confidence = 0.0table_pred = y_pred.reshape(-1, len(chars)+1)res = table_pred.argmax(axis=1)for i,one in enumerate(res):if one<len(chars) and (i==0 or (one!=res[i-1])):results+= chars[one]confidence+=table_pred[i][one]confidence/= len(results)return results,confidencedef model_seq_rec(self,model_path):width, height, n_len, n_class = 164, 48, 7, len(chars)+ 1  #输入层为164*48*3的tensor,类别有len(chars)+1个rnn_size = 256input_tensor = Input((164, 48, 3))x = input_tensorbase_conv = 32for i in range(3):                                    #一共做了三次卷积池化x = Conv2D(base_conv * (2 ** (i)), (3, 3))(x)     #卷积层x = BatchNormalization()(x)                       #统一量纲防止网络失衡x = Activation('relu')(x)                         #采用RELU激活函数x = MaxPooling2D(pool_size=(2, 2))(x)             #池化层conv_shape = x.get_shape()x = Reshape(target_shape=(int(conv_shape[1]), int(conv_shape[2] * conv_shape[3])))(x)x = Dense(32)(x)                                      #全连接层x = BatchNormalization()(x)x = Activation('relu')(x)gru_1 = GRU(rnn_size, return_sequences=True, kernel_initializer='he_normal', name='gru1')(x)gru_1b = GRU(rnn_size, return_sequences=True, go_backwards=True, kernel_initializer='he_normal', name='gru1_b')(x)gru1_merged = add([gru_1, gru_1b])gru_2 = GRU(rnn_size, return_sequences=True, kernel_initializer='he_normal', name='gru2')(gru1_merged)gru_2b = GRU(rnn_size, return_sequences=True, go_backwards=True, kernel_initializer='he_normal', name='gru2_b')(gru1_merged)x = concatenate([gru_2, gru_2b])x = Dropout(0.25)(x)                                   #正则化,扔掉25%的隐层神经元x = Dense(n_class, kernel_initializer='he_normal', activation='softmax')(x)  #再一次全连接,最后使用softmax输出类别数的网络base_model = Model(inputs=input_tensor, outputs=x)base_model.load_weights(model_path)return base_modeldef recognizeOne(self,src):x_tempx = srcx_temp = cv2.resize(x_tempx,( 164,48))x_temp = x_temp.transpose(1, 0, 2)y_pred = self.modelSeqRec.predict(np.array([x_temp]))y_pred = y_pred[:,2:,:]return self.fastdecode(y_pred)

此处的车牌字符信息识别事实上是采用了OCR光学字符识别技术也就是在不分割字符的前提下能够识别出车牌一共七个字符。当然传统的车牌字符识别就是先分割字符然后再逐一使用分类算法进行识别。这种方法有个好处就是,仅仅需要较少的字符样本即可用于分类器的训练。在光照,相机条件好的情况下也能取得较好的效果。现在大多数商业车牌识别软件采用的也是这种方法。但是在某些恶劣的自然情况下,车牌字符的分割和识别变得尤其的困难,传统的方法并不能取得很好的结果。这时候我们就能考虑下是否能整体一起识别。当然我们注意到车牌字符的数量是固定的,对于中国车牌而言,车牌一直是7个字符。我们就可以采用多标签分类的方法直接输出多个标签。
在这里插入图片描述

但是这种方法有个很大的缺点,就是需要大量的样本,样本需要涵盖所有的省份和地区。这就为样本的收集造成的极大的困难

这里采用CNN+GRU的方法来实现
GRU:(我个人理解CNN网络是初步的对字符进行分类,而GRU实际是并不常用在分类上,GRU更多用在预测处理上,我个人认为这里运用了双向GRU是为了在车牌图片较为模糊时,也可以大概预测出一个字符类别就是经过G双向GRU后可以更好的预测)
GRU(Gated Recurrent Unit) 是由 Cho, et al. (2014) 提出,是LSTM的一种变体。GRU的结构与LSTM很相似,LSTM有三个门,而GRU只有两个门且没有细胞状态,简化了LSTM的结构。而且在许多情况下,GRU与LSTM有同样出色的结果。GRU有更少的参数,因此相对容易训练且过拟合问题要轻一点。
下图展示了GRU的网络结构,GRU的网络结构和LSTM的网络结构很相似,LSTM中含有三个门结构和细胞状态,而GRU只有两个门结构:更新门和重置门,分别为图中的z_t和r_t,结构上比LSTM简单。把GRU看着LSTM的变体,相当于取消了LSTM中的cell state,只使用了hidden state,并且使用update gate更新门来替换LSTM中的输入们和遗忘门,取消了LSTM中的输出门,新增了reset gate重置门。这样做的好处是在达到LSTM相近的效果下,GRU参数更少,训练的计算开销更小,训练速度更快。

这里在CNN的训练模型后加上的是双向GRU
在这里插入图片描述
以上就是我对于HyperLPR代码的理解,如有不足之处,请勿喷!蟹蟹!


http://chatgpt.dhexx.cn/article/S9T8IEkQ.shtml

相关文章

opencv + svm实现车牌识别(附完整代码)

一、实验目的 通过一张含有车牌的车的照片&#xff0c;分割出车牌并识别出图片上车的车牌号 二、具体内容 车牌定位车牌字符分割车牌字符识别 三、实验过程 1.车牌定位 具体过程&#xff1a; 1.灰度转换&#xff1a;将彩色图片转换为灰度图像&#xff0c;常见的RGB像素平均…

Java车辆牌照识别

Java车辆牌照识别 大家好&#xff0c;近期想做一个Java的车牌识别功能&#xff08;借助第三方百度云车牌识别API&#xff09;&#xff0c;大致搜索了一下相关的博客文档&#xff0c;没有看到有相对完整详细的文章&#xff0c;于是准备写一篇稍微详细一点的文章说明&#xff0c…

基于opencv的车牌识别解析与代码

Jeremy Lin HQU 车牌识别太出名了&#xff0c;我也就花几天来了解下这个系统&#xff0c;并结合opencv进行实现。下面是一些介绍&#xff1a; 车辆牌照识别&#xff08;License Plate Recognition&#xff0c;LPR&#xff09;技术作为交通管理自动化的重要手段和车辆检测系统的…

基于matlab的车牌识别

20221126 新增 首先说一下这个工程的思路&#xff0c;很多朋友妄想直接拿着工程用&#xff0c;那是不可能的&#xff0c;自己学去叭&#xff0c;我是先将车牌号预处理之后&#xff0c;整个图片干净一点之后&#xff0c;进行每个字符的切割&#xff0c;但是是很投机取巧的方法&a…

车牌识别步骤及部分代码

目录(?)[-] 车牌预处理字符分割归一化处理细化处理字符特征提取神经网络训练车牌图像识别结果测试 1.车牌预处理 车牌预处理过程的好坏直接影响到车牌图像进行后期处理过程&#xff0c;比如车牌字符分割等。车牌预处理也是尽可能的消除噪声&#xff0c;减少后期处理带来的不必…

车牌识别(毕业设计+代码)

简介与效果 用python3opencv3做的中国车牌识别&#xff0c;包括算法和客户端界面&#xff0c;只有2个文件&#xff0c;一个是界面代码&#xff0c;一个是算法代码&#xff0c;点击即可出结果&#xff0c;方便易用&#xff01; 大致的UI界面如下&#xff0c;点击输入图片&#…

真香!用Python检测和识别车牌(附代码)

车牌检测与识别技术用途广泛&#xff0c;可以用于道路系统、无票停车场、车辆门禁等。这项技术结合了计算机视觉和人工智能。 本文将使用Python创建一个车牌检测和识别程序。该程序对输入图像进行处理&#xff0c;检测和识别车牌&#xff0c;最后显示车牌字符&#xff0c;作为…

写好 API 接口文档,想清楚这几点

我在开始一个新的接口之前&#xff0c;需要进行以下判断&#xff1a; 请求协议是不是 HTTP、https? 请求体和响应格式是什么&#xff08;XML、JSON、FormData、Raw&#xff09;? API 是不是 RESTful 风格&#xff1f; 如果上面三个问题的答案都清楚了&#xff0c;就可以开…

API调用,API传参,面向对接开发,你真的会写接口文档吗?

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e2; &#x1f497; 你正在阅读 【梦想橡皮擦】 的博客 &#x1f44d; 阅读完毕&#xff0c;可以点点小手赞一下 &#x1f33b; 发现错误&#xff0c;直接评论区中指正吧 &#x1f4c6; 橡皮擦的第…

接口接口文档

一、接口简介 API&#xff08;Application Programming Interface&#xff09;即应用程序接口&#xff0c;可以任务是一个软件组件或一个Web服务与外界进行交互的接口&#xff0c;在这里接口可以和API划等号。 接口可以简单的理解为一个黑盒子&#xff0c;从一边输入参数&…

【编程规范】 后端API接口设计编写与文档编写参考

文章目录 0 统一规范0.1 理清业务流程0.2 定义前后端开发的接口规范0.3 定义接口文档 1 后端接口编写1.0 后端接口介绍1.0.1 接口交互1.0.2 返回格式1.0.3 CODE状态码1.0.4 Message&#xff08;Msg&#xff09;1.0.5 DATA 1.1 数据校验与异常处理1.1.1 参数校验设计1.1.2 全局异…

如何根据接口文档,写一个接口类(举例说明)

直接先贴图&#xff08;文中的url做了修改&#xff09;&#xff1a; 代码如下&#xff1a; FeignClient(name "Z_KPI_API",url "${Z.KPI.url:http://11.11.111.111:8080}",fallback ZApiClientFallback.class) public Interface ZApiClient{PostMappin…

Postman写接口文档

文章目录 参考首先了解什么是postman那么后端如何用postman编写接口文档&#xff0c;并且让前端或者其他开发人员也一起同步协作呢&#xff1f;1. 注册登录postman2. 创建项目工作区3. 编写接口文档4. 添加协作人&#xff08;这一步也可以在创建工作区之初就操作&#xff09;5.…

怎么写一份好的接口文档?

编写一份优秀的接口文档会让软件开发中变得更加轻松&#xff0c;更有效率。这可是关键任务&#xff0c;写得好不仅可以帮助开发人员更好地理解和使用 API 接口&#xff0c;还可以提高整个团队的协作效率。 大家可以在线感受一下优秀的接口文档是怎样的&#xff1a;https://pets…

接口文档的使用

请求参数&#xff08;(GET方法就是Query参数&#xff0c;POST方法就是 Body参数) 在 axios中通过headers选项设置Headers请求头参数 在 axios中通过data选项设置Body请求体参数 在 axios中通过params选项设置Query参数 如果有 Query 参数&#xff0c;axios 会在内部把这个对象…

接口文档包含哪些内容?怎么才能写好接口文档?十年测试老司机来告诉你

目录 接口文档结构 参数说明 示例 错误码说明 语言基调通俗易懂 及时更新与维护 总结 那么我们该如何写好一份优秀的接口文档呢&#xff1f; 接口文档结构 首先我们要知道文档结构是什么样子的。接口文档应该有清晰明确的结构&#xff0c;以便开发人员能快速定位自己需…

详解接口文档的编写

正规的团队合作或者是项目对接&#xff0c;接口文档是非常重要的&#xff0c;一般接口文档都是通过开发人员写的。一个工整的文档显得是非重要。下面我总结下自己看到的优秀接口文档。 一、背景介绍 接口&#xff1a;API API&#xff08;Application Programming Interface&…

如何写好API接口文档

日常项目开发的过程中&#xff0c;接口文档是必不可少的。后端工程师与前端工程师之间需要接口文档来定义数据传输协议、系统对外暴露接口需要文档来说明、系统之间相互调用需要文档来记录接口协议等等。对于一个完整的项目&#xff0c;接口文档是至关重要的。那我们如何写好一…

什么是接口文档

一、什么是接口文档&#xff1f; 在项目开发中&#xff0c;web项目的前后端分离开发&#xff0c;APP开发&#xff0c;需要由前后端工程师共同定义接口&#xff0c;编写接口文档&#xff0c;之后大家都根据这个接口文档进行开发&#xff0c;到项目结束前都要一直维护。 二、为…

如何正确规范写接口文档

前言 正规的团队合作或者是项目对接&#xff0c;接口文档是非常重要的&#xff0c;一般接口文档都是通过开发人员写的。一个工整的文档显得是非重要。下面我将我看到的一篇接口文档做一个总结 开始吧&#xff01;&#xff01;&#xff01; 接口1&#xff1a; 查询排重接口 接…