【车牌识别和动态称重系统】(二)基于树莓派+HyperLPR的中文车牌识别

引言

【车牌识别和动态称重系统】(二)基于树莓派+HyperLPR的中文车牌识别_第1张图片
HyperLRP是一个开源的、基于深度学习高性能中文车牌识别库,由北京智云视图科技有限公司开发,支持PHP、C/C++、Python语言,Windows/Mac/Linux/Android/IOS平台。与较为流行的开源的EasyPR相比,它的检测速度和鲁棒性和多场景的适应性都要好于目前开源的EasyPR,HyperLPR可以识别多种中文车牌包括白牌,新能源车牌,使馆车牌,教练车牌,武警车牌等。

  • TODO
    支持多种车牌以及双层
    支持大角度车牌
    轻量级识别模型
  • 特性
    速度快 720p,单核 Intel 2.2G CPU (MaBook Pro 2015)平均识别时间低于100ms
    基于端到端的车牌识别无需进行字符分割
    识别率高,卡口场景准确率在95%-97%左右
    轻量,总代码量不超1k行

目录

  • 引言
  • 一、开发环境配置
    • ⭕️Win
    • ⭕️树莓派
  • 二、代码解析
    • demo.py(单张图片检测部分)
      • 1、输入模块
      • 2、输入图片识别处理
        • HyperLPRLite.py LPR类中的int()函数
          • - cv2.CascadeClassifier()函数
          • - model_finemapping()函数:车牌精定位
          • - model_seq_rec()函数:ocr识别
      • 3、输出参数
        • HyperLPRLite.py LPR类中的SImpleRecognizePlateByE2E()函数
          • - detectPlateRough()函数:车牌粗定位
          • - filemappingVertical()函数
          • - recognizeOne函数
        • hyperlpr.pipline.py 中的drawRectBox()函数


一、开发环境配置

⭕️Win

1、在Anaconda所建环境的命令行中输入

pip3 install hyperlpr -i https://mirrors.aliyun.com/pypi/simple/

2、下载整个开源库文件
https://gitee.com/zeusees/HyperLPR/tree/master/

把hyperlpr_py3并改名为hyperlpr复制到Anaconda3安装路径\envs\环境名\lib\python3.6\site-packages,与原目录下的hyperlpr合并

3、新建开发文件
将开源库中的Font、model、HyperLprGUI.py、HyperLprLite.py、demo.py拷到此目录中,创建一个Images的目录,放置待识别车牌的车辆照片,命名为plate1.jpg、plate2.jpg、plate3.jpg。

4、测试代码demo.py(单张图片检测部分)

from hyperlpr.pipline import drawRectBox
import HyperLPRLite as pr
import cv2
import numpy as np
grr = cv2.imread("./Images/plate3.png")
model = pr.LPR("model/cascade.xml","model/model12.h5","model/ocr_plate_all_gru.h5")
for pstr,confidence,rect in model.SimpleRecognizePlateByE2E(grr):
        if confidence>0.7:
            image = drawRectBox(grr, rect, pstr+" "+str(round(confidence,3)))
            print("plate_str:")
            print(pstr)
            print("plate_confidence")
            print(confidence)

cv2.imshow("image",image)
cv2.waitKey(0)

⭕️树莓派

1、在命令行里输入sudo pip3 install hyperlpr
2、将上面的hyperlpr_py3并改名为hyperlpr拷贝到树莓派/home/pi/.local/lib/python3.7/site-packages

二、代码解析

demo.py(单张图片检测部分)

demo中总的流程分为:
1)利用cascade进行车牌定位
2)对粗定位的车牌进行左右边界回归,去除车牌两边多余的部分
3)将精定位的车牌送入CRNN网络进行字符识别

1、输入模块

from hyperlpr.pipline import drawRectBox
import HyperLPRLite as pr
import cv2

Github : https://github.com/icepoint666/HyperLPR

Forked from zeusees/HyperLPR 略加改动

只需要三个代码文件:

  • multi_demo.py
  • demo.py
  • HyperLPRLite.py

2、输入图片识别处理

grr = cv2.imread("./Images/plate3.png")

opencv2的imread函数导入图片, 返回的是Mat类型。

model = pr.LPR("model/cascade.xml","model/model12.h5","model/ocr_plate_all_gru.h5")

HyperLPRLiite.py中的LPR类构造函数导入model, 参数就是训练好的三个模型文件,名字分别是:

  • model/cascade.xml cascade模型
  • model/model12.h5 左右边界回归模型
  • model/ocr_plate_all_gru.h5 字符识别模型
  • HyperLPRLite.py LPR类中的int()函数

 def __init__(self,model_detection,model_finemapping,model_seq_rec):
     self.watch_cascade = cv2.CascadeClassifier(model_detection)
     self.modelFineMapping = self.model_finemapping()
     self.modelFineMapping.load_weights(model_finemapping)
     self.modelSeqRec = self.model_seq_rec(model_seq_rec)

参数 model_detection 就是文件 model/cascade.xml 用到了opencv2的CascadeClassifier()函数

- cv2.CascadeClassifier()函数

参数输入.xml或者.yaml文件,表示加载模型,是一种基于Haar特征的级联分类器用于物体检测的模型

- model_finemapping()函数:车牌精定位
def model_finemapping(self):
    input = Input(shape=[16, 66, 3])  # change this shape to [None,None,3] to enable arbitraty shape input
    x = 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 model

利用了keras网络模型:对车牌的左右边界进行回归,通过modelFineMapping.loadweights()函数加载模型文件并由modelFineMapping.predict输出网络结果

  • 输入:16663 tensor
  • 输出:长度为2的tensor

预测出车牌的左右边框,进一步裁剪,进行精定位
在这里插入图片描述

- model_seq_rec()函数:ocr识别
def model_seq_rec(self,model_path):
    width, height, n_len, n_class = 164, 48, 7, len(chars)+ 1
    rnn_size = 256
    input_tensor = Input((164, 48, 3))
    x = input_tensor
    base_conv = 32
    for i in range(3):
        x = Conv2D(base_conv * (2 ** (i)), (3, 3))(x)
        x = BatchNormalization()(x)
        x = Activation('relu')(x)
        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)
    x = Dense(n_class, kernel_initializer='he_normal', activation='softmax')(x)
    base_model = Model(inputs=input_tensor, outputs=x)
    base_model.load_weights(model_path)
    return base_model

基于GRU的序列模型从OCR模型中修改的网络模型
model_sec_rec函数
model_path为模型weights文件路径 ocr部分的网络模型(keras模型) 输入层:164483的tensor
输出层:长度为7 的tensor,类别有len(chars)+1种

chars:

chars = [u"京", u"沪", u"津", u"渝", u"冀", u"晋", u"蒙", u"辽", u"吉", u"黑", u"苏", u"浙", u"皖", u"闽", u"赣", u"鲁", u"豫", u"鄂", u"湘", u"粤", u"桂",
             u"琼", u"川", u"贵", u"云", u"藏", u"陕", u"甘", u"青", u"宁", u"新", u"0", u"1", u"2", u"3", u"4", u"5", u"6", u"7", u"8", u"9", u"A",
             u"B", u"C", u"D", u"E", u"F", u"G", u"H", u"J", u"K", u"L", u"M", u"N", u"P", u"Q", u"R", u"S", u"T", u"U", u"V", u"W", u"X",
             u"Y", u"Z",u"港",u"学",u"使",u"警",u"澳",u"挂",u"军",u"北",u"南",u"广",u"沈",u"兰",u"成",u"济",u"海",u"民",u"航",u"空"
             ]

网络结构是三层卷积神经网络(CNN),以及四层内控循环单元(GRU)组成

3、输出参数

for pstr,confidence,rect in model.SimpleRecognizePlateByE2E(grr):
        if confidence>0.7:
            image = drawRectBox(grr, rect, pstr+" "+str(round(confidence,3)))
            print("plate_str:")
            print(pstr)
            print("plate_confidence")
            print(confidence)

输入为一个Mat类型的图片 输出为识别的车牌字符串,以及confidence可信度,

  • HyperLPRLite.py LPR类中的SImpleRecognizePlateByE2E()函数

 def SimpleRecognizePlateByE2E(self,image):
        images = self.detectPlateRough(image,image.shape[0],top_bottom_padding_rate=0.1)
        res_set = []
        for j,plate in enumerate(images):
            plate, rect  =plate
            image_rgb,rect_refine = self.finemappingVertical(plate,rect)
            res,confidence = self.recognizeOne(image_rgb)
            res_set.append([res,confidence,rect_refine])
        return res_set

首先用到detectPlateRough()函数

  • detectPlateRough函数是返回图像中所有车牌的边框在图片中的bbox,返回的是一个表示车牌区域坐标边框的list

对于每个车牌区域的for循环中,经过

  • filemappingVertical()函数

处理后输入

  • recognizeOne()函数

进行ocr识别

下面是SImpleRecognizePlateByE2E()函数中所用到的函数解析

- detectPlateRough()函数:车牌粗定位
def 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.14
         w += w * 0.28
         y -= h * 0.15
         h += h * 0.3
         cropped = 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

利用多尺度检测detectMultiScale,得到可能的车牌,及其在原图中的rect位置
在这里插入图片描述
输入参数:

  • image_gray: 一个rgb图像,Mat类型
  • resize_h: 重新设定的图像大小
  • top_bottom_padding_rate: 表示要裁剪掉图片的上下部占比

这个函数实现的处理:

  • resize图像大小,cv2.resize函数,按照原来图像比例裁剪图片,根据输入的top_bottom_padding_rate如果是0.1,那么上面裁剪掉0.1height,下面也裁剪掉0.1height
  • 将图像从rgb转化为灰度 cv2.cvtColor函数,cv2.COLOR_RGB2GRAY
  • 根据前面的cv2.CascadeClassifier()物体检测模型,输入image_gray灰度图像,边框可识别的最小size,最大size,输出得到车牌在图像中的offset,也就是边框左上角坐标(x, y )以及边框高度( h )和宽度( w )
  • 对得到的车牌边框的bbox进行扩大,也就是宽度左右各扩大0.14倍,高度上下各扩大0.15倍。
  • 返回图片中所有识别出来的车牌边框bbox,这个list作为返回结果。
- filemappingVertical()函数
def finemappingVertical(self,image,rect):
    resized = cv2.resize(image,(66,16))
    resized = resized.astype(np.float)/255
    res_raw= (np.array([resized]))[0]
    res  =res_raw*image.shape[1]
    res = res.astype(np.int)
    H,T = res
    H-=3
    if H<0:
        H=0
    T+=2;
    if T>= image.shape[1]-1:
        T= image.shape[1]-1
    rect[2] -=  rect[2]*(1-res_raw[1] + res_raw[0])
    rect[0]+=res[0]
    image = image[:,H:T+2]
    image = cv2.resize(image, (int(136), int(36)))
    return image,rect

输入参数: 裁剪的车牌区域图像(Mat类型),rect也是裁剪的车牌部分的图像(Mat类型)

实现处理:
1.将原来车牌图像resize大小:66163
2.将原来灰度图颜色通道[0, 255]转化为float类型[0,1]
3.将输入66*16(float),输入进模型进行测试self.modelFineMapping.predict

- recognizeOne函数
   def recognizeOne(self,src):
        x_tempx = src
        x_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)

1.将前面的(136, 36)图像resize成(164, 48)
2.将图像转置,输入

hyperlpr.pipline.py 中的drawRectBox()函数

#打上boundingbox和标签
def drawRectBox(image,rect,addText):
    cv2.rectangle(image, (int(rect[0]), int(rect[1])), (int(rect[0] + rect[2]), int(rect[1] + rect[3])), (0,0, 255), 2,cv2.LINE_AA)
    cv2.rectangle(image, (int(rect[0]-1), int(rect[1])-16), (int(rect[0] + 115), int(rect[1])), (0, 0, 255), -1,
                  cv2.LINE_AA)

    img = Image.fromarray(image)
    draw = ImageDraw.Draw(img)
    draw.text((int(rect[0]+1), int(rect[1]-16)), addText.decode("utf-8"), (255, 255, 255), font=fontC)
    imagex = np.array(img)

    return imagex

参考教程
HyperLPR车牌识别代码解读
hyperlpr学习笔记——demo学习

你可能感兴趣的:(#,项目开发2—,—车牌识别和动态称重系统)