车牌检测识别

---------------
7月更新训练项目:
新的车牌检测+关键点检测+车牌矫正:
https://aistudio.baidu.com/aistudio/projectdetail/6545272
LPRNet矫正后的车牌识别:
https://aistudio.baidu.com/aistudio/projectdetail/5628649
---------------
3月14日更新内容:
网络训练:添加了yolov5和LPRNet的训练过程
https://aistudio.baidu.com/aistudio/projectdetail/5557065
https://aistudio.baidu.com/aistudio/projectdetail/5628649
模型转换:总结了onnx转tflite的更多细节
https://blog.csdn.net/kalahali/article/details/129538442
部署:新增aistudio项目模型部署差异
----------------

AidLux智慧社区AI实战总结之车牌检测与识别,基于yolov5实现车牌检测,加上LPRNet对检测到的车牌进行识别来实现整个识别流程,最后通过AidLux实现在手机端的部署

项目资料/代码:

https://pan.baidu.com/s/147gi2MLqC6KiBGHyzSsbkw 提取码:aid3
手机端需要下载aidlux软件

算法方案

算法方案需要考虑应用场景,不同场景,车牌识别的算法方案也不一样,比如:

  1. 在加油站的车牌识别场景中,车占整个图片的比例较小时,则建议在车牌检测前,增加一个车的检测模块。在检测到车后,再对车进行车牌的检测和识别。(避免漏检)

image.png

  1. 在智慧社区或停车场的场景中,相机一般安装在正对车牌的位置,车牌占据图片的比例比较大,这样建议直接对车牌做检测和识别

5cf1c8b6e49310483587134325ea639.jpg
本项目正是基于智慧社区的应用场景,所以选择算法方案为车牌检测yolov5和车牌识别LPRNet
注意,在实际应用场景中,如果车牌在图片中的倾角较大,一般车牌的水平度和垂直度超过15°,则需要考虑加入矫正,算法方案应为:车牌检测+车牌矫正+车牌识别
这里的智慧社区的车与相机位置可以相对平行固定,故不添加矫正
image.png

数据准备

数据集简介

考虑到智慧社区中,在社区内很少出现工程车辆,所以只需要覆盖大部分的蓝牌和绿牌的场景即可。最普遍的开源车牌数据集是中科大的CCPD数据集,官网链接是:https://github.com/detectRecog/CCPD
中科大车牌数据集有CCPD2019和CCPD2020,其中CCPD2019主要为蓝牌,CCPD2020为绿牌。其中蓝牌是燃油车,绿牌是电动车。
这里我们主要用CCPD2019的蓝牌来作为我们的任务,官网有百度网盘链接:
image.png

Images in CCPD-Base is split to train/val set. Sub-datasets (CCPD-DB, CCPD-Blur, CCPD-FN, CCPD-Rotate, CCPD-Tilt, CCPD-Challenge) in CCPD are exploited for test.

用于训练的数据在CCPD-Base中,从这里划分训练集和验证集
用于测试的数据则有:
CCPD-DB:车牌区域交亮,较暗或则不均匀
CCPD-Blur:由于相机抖动而导致的模糊车牌
CCPD-FN: 车牌距离摄像头较近或则较远
CCPD-Rotate: 旋转/倾角
CCPD-Challenge: 有挑战性
CCPD-Tilt:

数据集格式

每张图片的标签为文件名:
图片命名:“0019-1_1-340&500_404&526-404&524_340&526_340&502_404&500-0_0_11_26_25_28_17-66-3.jpg”
image.png

# 详细解释:
"""
0019:
	车牌区域占整个画面的比例;
1_1: 		
	车牌水平和垂直角度, 水平1°, 竖直1°
340&500_404&526:	
	标注框左上、右下坐标,左上(154, 383), 右下(386, 473)
404&524_340&526_340&502_404&500:	
	标注框四个角点坐标,顺序为右下、左下、左上、右上
0_0_11_26_25_2_8:
	车牌号码, 映射关系如下: 
 		第一个0为省份 对应省份字典provinces中的’皖’,;
        第二个0是该车所在地的地市一级代码,对应地市一级代码字典alphabets的’A’;
        后5位为字母和文字, 查看车牌号ads字典,如11为M,26为2,25为1,2为C,8为J 最终车牌号码为皖AM21CJ
映射列表:
    省份:[“皖”, “沪”, “津”, “渝”, “冀”, “晋”, “蒙”, “辽”, “吉”, “黑”, “苏”, “浙”, “京”, “闽”, “赣”, “鲁”, “豫”, “鄂”, “湘”, “粤”, “桂”, “琼”, “川”, “贵”, “云”, “藏”, “陕”, “甘”, “青”, “宁”, “新”]
    地市:[‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’, ‘G’, ‘H’, ‘J’, ‘K’, ‘L’, ‘M’, ‘N’, ‘P’, ‘Q’, ‘R’, ‘S’, ‘T’, ‘U’, ‘V’, ‘W’,‘X’, ‘Y’, ‘Z’]
    车牌字典:[‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’, ‘G’, ‘H’, ‘J’, ‘K’, ‘L’, ‘M’, ‘N’, ‘P’, ‘Q’, ‘R’, ‘S’, ‘T’, ‘U’, ‘V’, ‘W’, ‘X’,‘Y’, ‘Z’, ‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’]
"""

需要先对图片的标签解析,解析完后才能对其进行训练。
因为中科大在安徽采集数据较多,其中“皖”字占整个数据集汉字的90%,所以如果是对智慧社区定制化开发,需考虑社区所在的地理位置,针对性采集数据,或者模拟数据,优化模型。

数据集整理

因要做两个任务,车牌检测、车牌识别。所以将车牌数据集整理分成两个部分:一是整理成检测任务需要的数据集,二是整理成车牌识别任务需要的数据集。
由于数据量较大,为防止出错,这里分为两步:

  1. 简单验证:

    先将几张图片标签转成yolo文件,并通过yolo转成voc xml格式,用labelimg打开校验。

  2. 批量转换:

    确认转换的方式没有问题后,将所有标签直接转换成yolo格式保存。
    转yolo格式以及yolo转voc格式的脚本在代码资源中位置:tools/ParaseData.py
    转换voc之后用labelimg打开图片文件夹,并查看xml_labels结果,标注的label转换没有问题:
    image.png
    检查没问题后将脚本中的validation设置成False,修改图片路径和保存txt路径:image.png
    考虑训练时长等因素,可以使用脚本tools/cp10000.py,将数据随机抽取1万张来训练,若后期效果不好,可以用8w多张数据重新训练。

车牌识别数据集处理

对于车牌识别步骤,需要将车牌裁切出来作为训练数据,脚本为ools/ccpd2lpr.py,生成以车牌号为文件名的数据集。
image.png

网络训练

训练部分就是yolov5和lprnet的训练。
本人没有GPU电脑,这里选择的是云服务器,除了AutoDL平台,这次白嫖百度飞浆AIStudio平台的算力来训练,项目地址如下,可以直接fork后开启训练:

yolov5

直接使用PaddleYOLO项目快速开启训练
https://aistudio.baidu.com/aistudio/projectdetail/5557065

LPRNet

以下是转写paddle后的训练项目:
https://aistudio.baidu.com/aistudio/projectdetail/5628649

在云服务器上训练并生成onnx文件后,将onnx下载到本地准备下一步部署
若使用torch,可以参考项目资料里的参考代码train_yolov5.py和train_lprnet.py
训练完后保存好pt模型。

测试

yolov5测试代码参考detect_yolov5.py(输出结果在demo/detect_results)
image.png
测试车牌识别的脚本test_lprnet.py
需要注意的是车牌识别作为车牌检测的下游任务,其输入图片需要将检测到的裁剪车牌
image.png
整个流程串联起来的测试脚本detect_torch_pipeline.py
image.png

Android端部署

AidLux

AidLux是安装在ARM架构下的,跨生态(Android/鸿蒙+Linux)一站式开发和部署平台APP。必须要在安卓手机或者安卓的平板上或ARM板卡才能下载运行,设备需要满足ARM64位,安卓6.0以上。可以在手机应用商城搜索下载。
当然如果后面在手机上操作编程有点麻烦,可以通过IP的方式,在局域网内直接映射到电脑上操作。可以点击页面最上方的Cloud_ip,浏览器在手机访问。
image.pngimage.png
浏览器进入桌面的默认密码是:aidlux
可以浏览器桌面方便的传输文件。
image.png
这里贴上aidlux图像处理相关的文档地址:https://docs.aidlux.com/#/api/?id=%e5%9b%be%e5%83%8f%e5%a4%84%e7%90%86

vscode远程连接

使用vscode的remote功能,可以远程连接手机,方便编写代码,ssh配置如下:
hostname需要为cloud_ip里的地址,连接的密码都是aidlux
image.png

模型转换

pt模型移植到Android还需要转换成Android端适配的模型,一般适配的轻量化模型如ncnn, tflite, paddlelite等,这里选择这里我们选择的tflite模型,不过pytorch直接转tflite的工具不齐全,一般都会转成序列化成onnx,再轻量化模型, 以pytorch->onnx->tflite 方式。

转换onnx

yolov5代码中自带导出代码,配置export_yolov5.py中的配置代码,修改weights路径导出onnx
可以在netron查看网络结构https://netron.app/
lprnet的导出代码在export_lprnet.py
注意lprnet网络中的maxpool3d没有被官方支持,这里将maxpool3d用两个maxpool2d去实现(代码里已经修改了)
基于onnx的前向推理可参考detect_onnx_inference.py
image.png

onnx转tflile

模型转换的注意事项还可参考总结:
https://blog.csdn.net/kalahali/article/details/129538442

参考代码export_tflite.py

from onnx_tf.backend import prepare
import onnx
 
TF_PATH = "weights/LPRNet_Simplified.pb" # where the representation of tensorflow model will be stored
ONNX_PATH = "weights/LPRNet_Simplified.onnx" # path to my existing ONNX model
onnx_model = onnx.load(ONNX_PATH)  # load onnx model
tf_rep = prepare(onnx_model)  # creating TensorflowRep object
tf_rep.export_graph(TF_PATH)
 
import tensorflow as tf
 
TF_PATH = "weights/LPRNet_Simplified.pb"
TFLITE_PATH = "weights/LPRNet_Simplified.tflite"
converter = tf.lite.TFLiteConverter.from_saved_model(TF_PATH)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
tf_lite_model = converter.convert()
with open(TFLITE_PATH, 'wb') as f:
    f.write(tf_lite_model)

pc端的tflite推理参考detect_tflite_pipeline.py
需要注意,在netron中看到yolov5.tflite模型的输出output顺序是40,20,80,在tflite模型输出后处理的时候需要注意anchor的对应。
image.png
image.png

部署

参考代码detect_aidlux_inference.py
需要注意模型的in_shape 和out_shape 要与netron中查看的输入输出保持一致,并且还要*4,表示float32有4个字节
跟多关于aidlite.ANNModel的详细信息可以在前面提到文档中查看:https://docs.aidlux.com/#/api/?id=annmodel
修改自己的两个模型路径后,运行
image.png
另外关于cv2不支持中文可以参考下面测试案例,利用PIL实现和cv2.putText的相同效果:

import numpy as np
from PIL import Image, ImageDraw, ImageFont
import cv2


def cv2AddChinese(img, text, position, fontScale=1., textColor=(0,255,0)):
    if (isinstance(img, np.ndarray)):
        img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    baseFontSize = 34  # 以此字体大小进行缩放,字体高度
    height = baseFontSize*0.8  # 字体实际高度,去空白
    x, y = position
    position_new = (x, y-fontScale*height)
    fontsize = int(fontScale * baseFontSize)
    draw = ImageDraw.Draw(img)
    # SimHei.ttf为字体文件,可在网上下载,若读取失败,改为完整路径
    fontStyle = ImageFont.truetype("SimHei.ttf", fontsize, encoding="utf-8")  
    # width, height = fontStyle.getsize(text)  # 获取字体宽高
    draw.text(position_new, text, textColor, font=fontStyle)
    return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)

text = "Chines中文"
img_dir = 'test.JPG'  # 自己的测试图片
position = (0, 40)
fontScale = 1.5
img_data = cv2.imread(img_dir)
img_data = cv2AddChinese(img_data, text, position, fontScale, (0,255,0))

# t_size = cv2.getTextSize(text, 0, fontScale, thickness=2)  # cv2获取字体宽高
cv2.putText(img_data, text, position, 0, fontScale, (255,0,0), 2)
cv2.imshow('demo', img_data)
cv2.waitKey(1000)
aistudio项目模型部署差异

这里新增一小节,对上文中提到的aistudio训练项目模型与本文资料包代码的区别做个说明

  1. yolov5的训练模型与资料包不同,aistudio训练的yolov5_n,输出shape为(1,25200,5),是检测框未缩放未缩放回原图尺寸的检测框坐标+分类置信度。所以在aidlux中需要注意:
# 模型的导入
det_model_path = '模型的存放位置'
in_shape0 = [1 * 3* 640 * 640 * 4]  # 4是4个字节,float32
out_shape0 = [1 * 25200 * 5 * 4]
aidlite.ANNModel(det_model_path, in_shape0, out_shape0, 4, -1)
  1. 相应的,检测模型的后处理也需要修改:
def det_poseprocess(outputs, imgsz, scale, left, top ,conf_thresh, iou_thresh):
    # nms
    true_conf = outputs[..., 4]
    mask = true_conf > conf_thresh
    # 调整为原图下的坐标
    pred = outputs[mask][:, :4]
    boxes = np.zeros_like(pred)
    boxes[:, 0] = pred[:, 0] - left
    boxes[:, 2] = pred[:, 2] - left
    boxes[:, 1] = pred[:, 1] - top
    boxes[:, 3] = pred[:, 3] - top
    boxes /= scale
    confs = true_conf[mask]
    keep = non_max_suppression(boxes, confs, iou_thresh=0.5)
    boxes = boxes[keep]
    confs = confs[keep].reshape((-1, 1))
    classes = np.zeros_like(confs).reshape((-1, 1))
    return boxes, confs, classes
  1. 对于LPRNet的车牌识别模型,训练的时候使用的图片是RGB格式,所以如果是cv2读取的图片数据需要在图片前处理中将图片从BGR转为RGB
# reg_preprocess
img_crop = cv2.cvtColor(img_crop, cv2.COLOR_BGR2RGB)

手机端运行效果:

使用aidlux在手机端部署车牌检测识别


对于车牌识别的流程,数据中“皖”字开头的牌照占整个数据集汉字的90%,想要更好的效果,还需针对性采集数据,或者模拟数据,来优化模型。

猜你喜欢

转载自blog.csdn.net/kalahali/article/details/129360510