基于飞桨实现项目1 车牌识别

简介: 基于飞桨实现项目1 车牌识别

1 项目介绍

车牌识别系统是指能够检测到受监控路面的车辆并自动提取车辆牌照信息(含汉字字符、英文字母、阿拉伯数字及号牌颜色)进行处理的技术。

本次是学习该博主https://blog.csdn.net/qq_36816848/category_12113641.html的项目智慧交通预测系统的一个总结,使用的是飞桨。

整个项目用DBNet进行文本检测(寻找文本位置),然后用RARE进行文本识别(识别图像中的文字)。

做的事情有整理数据集,然后部署模型,训练模型,导出模型,最终进行测试。下面也从这几个方面进行总结。

原地址:https://aistudio.baidu.com/aistudio/projectdetail/4542547

2 环境配置

因为用的是Paddle,因此需要先配置paddle环境。

首先,拉取PaddleOCR

#下载PaddleOCR
%cd ~/
!git clone -b release/2.1 https://github.com/PaddlePaddle/PaddleOCR.git

这里我执行时直接提示说已经存在,那就不用管了,可以安装使用PaddleOCR的环境

%cd PaddleOCR
!pip install -r requirments.txt
!pip install --upgrade scipy
# !pip install -r requirements.txt -i https://mirror.baidu.com/pypi/simple
%cd ..

如果没有什么问题,到这就算配置完成基本环境了。

3 数据集

3.1 CCPD02020介绍

本次使用的是CCPD02020,其训练集,验证集和测试集已经分组完成:

7357041954674c69a684f196f4928fbe.png

打开任一文件夹后发现文件名称特别长,形式如下,以-为分隔符:0139453125-91_266-231&462_435&531-429&529_231&531_236&462_435&468-0_0_3_26_24_30_32_32-116-38.jpg

其实这是该图像的标注

0139453125是区域

91_266对应两个角度, 水平91°, 竖直266°

231&462_435&531对应边界框左上(231, 462)和右下(435, 531)坐标

429&529_231&531_236&462_435&468对应四个角点坐标

0_0_3_26_24_30_32_32-116-38为映射为数字的车牌号码。第一个0为省份,根据映射关系provinces为皖。后面是数字和字母,根据对照表ads,比如第二个0为A,24是0。

映射关系:

车牌省份: 
provinces = [“皖”, “沪”, “津”, “渝”, “冀”, “晋”, “蒙”, “辽”, “吉”, “黑”, “苏”, “浙”, “京”, “闽”, “赣”, “鲁”, “豫”, “鄂”, “湘”, “粤”, “桂”, “琼”, “川”, “贵”, “云”, “藏”, “陕”, “甘”, “青”, “宁”, “新”, “警”, “学”, “O”]
字母和数字:
ads = [‘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’, ‘O’]

3.2 解压数据集

dbb253430fdd4f9890d93d790fcb4f71.png

项目中,数据是以zip格式给的,需要解压:

!unzip -q data/data168819/CCPD2020.zip -d work/CCPD2020

注意,这里必须在根路径(/home/aistudio)下,可以用!pwd查看

3.3 提取文本检测数据集

我们了解到数据图像的名称是标签,其实是文本检测和文本识别结合到一起的标签。这里需要进行提取。

#转换检测数据,打开注释执行三次生成训练所需txt文件,分别为train、val、test。
%cd ~ # 返回根目录的作用
import os, cv2
words_list = [
    "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" ]
con_list = [
    "皖", "沪", "津", "渝", "冀",
    "晋", "蒙", "辽", "吉", "黑",
    "苏", "浙", "京", "闽", "赣",
    "鲁", "豫", "鄂", "湘", "粤",
    "桂", "琼", "川", "贵", "云",
    "西", "陕", "甘", "青", "宁",
    "新"]
count = 0
data = open('work/train_data_det.txt', 'w', encoding='UTF-8')
# data = open('work/val_data_det.txt', 'w', encoding='UTF-8')
# data = open('work/test_data_det.txt', 'w', encoding='UTF-8')
for item in os.listdir('work/CCPD2020/CCPD2020/ccpd_green/train'):
# for item in os.listdir('work/CCPD2020/CCPD2020/ccpd_green/val'):
# for item in os.listdir('work/CCPD2020/CCPD2020/ccpd_green/test'):
    path = 'work/CCPD2020/CCPD2020/ccpd_green/train/'+item
    # path = 'work/CCPD2020/CCPD2020/ccpd_green/val/'+item
    # path = 'work/CCPD2020/CCPD2020/ccpd_green/test/'+item
    _, _, bbox, points, label, _, _ = item.split('-')
    points = points.split('_')
    points = [_.split('&') for _ in points]
    tmp = points[-2:]+points[:2]
    points = []
    for point in tmp:
        points.append([int(_) for _ in point])
    label = label.split('_')
    con = con_list[int(label[0])]
    words = [words_list[int(_)] for _ in label[1:]]
    label = con+''.join(words)
    line = path+'\t'+'[{"transcription": "%s", "points": %s}]' % (label, str(points))
    line = line[:]+'\n'
    data.write(line)
# 下面的代码没有什么作用
# total = []
# with open('work/train_data_det.txt', 'r', encoding='UTF-8') as f:
#     for line in f:
#         total.append(line)
# with open('work/val_data_det.txt', 'r', encoding='UTF-8') as f:
#     for line in f:
#         total.append(line)
# with open('work/test_data_det.txt', 'r', encoding='UTF-8') as f:
#     for line in f:
#         total.append(line)
# with open('work/train_det.txt', 'w', encoding='UTF-8') as f:
#     for line in total[:-500]:
#         f.write(line)
# with open('work/dev_det.txt', 'w', encoding='UTF-8') as f:
#     for line in total[-500:]:
#         f.write(line)

982029ae3de649a6a9afbb1c9f812cd4.png

这三块,对应改变注释执行即可(总共进行三次)。下面一块被注释的代码没起到什么作用。

最终得到三个文件

  1. train_data_det
  2. val_data_det
  3. test_data_det

三个文件中,每一行的内容都和下面很类似work/CCPD2020/CCPD2020/ccpd_green/train/303154296875-91_93-224&467_530&571-530&571_224&564_224&470_527&467-0_0_3_24_32_25_33_25-78-103.jpg [{"transcription": "皖AD08191", "points": [[224, 470], [527, 467], [530, 571], [224, 564]]}]

其中空格作为分割,前面是图像的路径,后面是标签(图中文本,图中被框出的文本的四个角的坐标)

3.4 提取文本识别数据集

#识别数据:转换成PaddleOCR使用的格式(图片名+内容),打开注释执行三次生成训练所需txt文件,分别为train、val、test。
%cd ~  # 切换到根路径
import os, cv2
words_list = [
    "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" ]
con_list = [
    "皖", "沪", "津", "渝", "冀",
    "晋", "蒙", "辽", "吉", "黑",
    "苏", "浙", "京", "闽", "赣",
    "鲁", "豫", "鄂", "湘", "粤",
    "桂", "琼", "川", "贵", "云",
    "西", "陕", "甘", "青", "宁",
    "新"]
# if not os.path.exists('work/img'):   #所有数据集都放入一个文件夹,这里其实我没有用到
#     os.mkdir('work/img')
#训练、验证、测试集分开三个文件夹对应解开注释依次执行三次
# if not os.path.exists('work/train_rec_img'):
#     os.mkdir('work/train_rec_img')
# if not os.path.exists('work/val_rec_img'):
#     os.mkdir('work/val_rec_img')
if not os.path.exists('work/test_rec_img'):
    os.mkdir('work/test_rec_img')
count = 0
# data = open('work/train_data_rec.txt', 'w', encoding='UTF-8')
# data = open('work/val_data_rec.txt', 'w', encoding='UTF-8')
data = open('work/test_data_rec.txt', 'w', encoding='UTF-8')
# for item in os.listdir('work/CCPD2020/CCPD2020/ccpd_green/train'):
# for item in os.listdir('work/CCPD2020/CCPD2020/ccpd_green/val'):
for item in os.listdir('work/CCPD2020/CCPD2020/ccpd_green/test'):
    # path = 'work/CCPD2020/CCPD2020/ccpd_green/train/'+item
    # path = 'work/CCPD2020/CCPD2020/ccpd_green/val/'+item
    path = 'work/CCPD2020/CCPD2020/ccpd_green/test/'+item
    #原来的 path = 'work/CCPD2020/ccpd_base/'+item
    _, _, bbox, _, label, _, _ = item.split('-')
    bbox = bbox.split('_')
    x1, y1 = bbox[0].split('&')
    x2, y2 = bbox[1].split('&')
    label = label.split('_')
    con = con_list[int(label[0])]
    words = [words_list[int(_)] for _ in label[1:]]
    label = con+''.join(words)
    bbox = [int(_) for _ in [x1, y1, x2, y2]]
    img = cv2.imread(path)
    crop = img[bbox[1]:bbox[3], bbox[0]:bbox[2], :]
    # cv2.imwrite('work/train_rec_img/%06d.jpg' % count, crop)
    # data.write('work/train_rec_img/%06d.jpg\t%s\n' % (count, label))
    # cv2.imwrite('work/val_rec_img/%06d.jpg' % count, crop)
    # data.write('work/val_rec_img/%06d.jpg\t%s\n' % (count, label))
    cv2.imwrite('work/test_rec_img/%06d.jpg' % count, crop)
    data.write('work/test_rec_img/%06d.jpg\t%s\n' % (count, label))
    count += 1
data.close()
with open('work/word_dict.txt', 'w', encoding='UTF-8') as f:
    for line in words_list+con_list:
        f.write(line+'\n')
# 下面的代码没有什么作用
# total = []
# with open('work/train_data_rec.txt', 'r', encoding='UTF-8') as f:
#     for line in f:
#         total.append(line)
# with open('work/val_data_rec.txt', 'r', encoding='UTF-8') as f:
#     for line in f:
#         total.append(line)
# with open('work/test_data_rec.txt', 'r', encoding='UTF-8') as f:
#     for line in f:
#         total.append(line)

同样25-3438-4042-4446-4864-71五块,对应改变注释执行即可(总共进行三次)。下面一块被注释的代码没起到什么作用。

data.close()后面的with模块,生成的是标签映射关系,如A对应1,43对应吉。

最终,生成文件及目录

  1. word_dicrt.txt
  2. train_data_rec.txt
  3. val_data_rec.txt
  4. test_data_rec.txt
  5. train_rec_img
  6. val_rec_img
  7. test_rec_img

其中234是训练文件,每一行内容类似work/train_rec_img/000000.jpg 皖AD08191。空格隔开,前面是图像的路径,后面是理想下识别得到的文字。

567是文本识别的训练集,里面是训练,验证,测试图像。

到这里数据集就准备完毕了,其实还是有很多注释的部分,暂时没遇到需要这些的东西。

4 模型

PaddleOCR提供的检测与识别模型


e3cbeee017d9493589ab9628b9e802e8.png

模型库:https://github.com/PaddlePaddle/PaddleOCR/blob/release/2.6/doc/doc_ch/models_list.md#1.1

该博主用以MobileNetV3为backbone的DBNet和RARE进行示例。

其中DBNet原理总结:https://blog.csdn.net/weixin_51691064/article/details/130272531

4.1 下载模型

首先下载预训练模型

!wget -P ./pretrain_models/ https://paddleocr.bj.bcebos.com/ch_models/ch_det_mv3_db.tar
!wget -P ./pretrain_models/ https://paddleocr.bj.bcebos.com/rec_mv3_tps_bilstm_attn.tar

注意,这里是在PaddleOCR文件夹下完成的。其中第一个是文本检测模型DBNet,第二个是文本识别模型RARE。

解压模型参数

%cd pretrain_models 
!tar -xf ch_det_mv3_db.tar && rm -rf ch_det_mv3_db.tar
!tar -xf rec_mv3_tps_bilstm_attn.tar && rm -rf rec_mv3_tps_bilstm_attn.tar

%cd pretrain_models 是进入PaddleOCR的pretrain_models文件夹,即预训练模型下载的位置。解压后两个压缩包被删除,pretrain_models目录下多了两个文件夹,即det_mv3_db和rec_mv3_tps_bilstm_attn,分别是文本检测模型和文本识别模型。

进去后,emmm…看不懂

4.2 文本检测训练

为了训练模型,我们需要修改配置文件下数据集的路径。首先找到PaddleOCR目录下的configs/det/det_mv3_db.yml,可以看到里面的内容如下:

2dc7f3f7b1214e3b9c48711318f42c4d.png

其中包含了epoch总数等各种信息。

接下来,打开里面的数据集文件路径./configs/det/det_db_icdar15_reader.yml修改数据集路径。该文件内容如下:

885cdd4fc26042daaeef775e5c40020e.png

这里图像数据路径写根路径/home/aistudio/即可,猜测是执行train.py,自动数据集将后面的路径进行拼接了。这就要求我们解压数据集时不要修改解压路径,否则可能出错。

修改完数据路径后,进行训练:

%cd ~/PaddleOCR
# 设置PYTHONPATH路径
%env PYTHONPATH=$PYTHONPATH:.
# GPU单卡训练
%env CUDA_VISIBLE_DEVICES=0
!python3 tools/train.py -c configs/det/det_mv3_db.yml

%cd ~/PaddleOCR作用是返回PaddleOCR目录,如果手动返回了,不要这行也可以。

我用的环境是V100 16G,挺慢的。因此仅仅训练了10个epoch,可以选择更大的。

保存的模型参数等文件如下图:

0e664a6f186b4a86bd6d1437681b001a.png

4.3 文本识别检测

和文本检测很像,也是先修改数据集路径,然后训练。

这次,先找到PaddleOCR目录下的configs/rec/rec_mv3_tps_bilstm_attn.yml,内容如下:

0a256b9caf034ab6ac4d904e4cd330de.png

其中的东西也与文本检测类似,重点关注配置数据集路径文件./configs/rec/rec_chinese_reader.yml

打开后如下:

1c76e1b2ce0a48b489ffb65155cdb012.png

同样,图像数据设置为根路径即可,后面路径训练脚本自动拼接,要求提取文本识别数据集中的路径代码不能随意更改

然后执行训练脚本

# %cd ~/PaddleOCR
# GPU单卡训练
%env CUDA_VISIBLE_DEVICES=0
!python3 tools/train.py -c configs/rec/rec_mv3_tps_bilstm_attn.yml

上面文本检测和文本识别脚本都是单卡训练,如果要多卡训练,比如四张,%env CUDA_VISIBLE_DEVICES=0改为%env CUDA_VISIBLE_DEVICES=0,1,2,3

到这里模型训练部分就完成了

4.4 修改模型

假如我们不想使用DBNet或者RARE,可以对模型进行修改。

首先选择模型,有两个入口:

拉取的PaddleOCR的README文件

官方模型库https://github.com/PaddlePaddle/PaddleOCR/blob/release/2.6/doc/doc_ch/models_list.md

其次,下载模型后,参照4.1节中解压模型压缩文件,路径最好不变。

最后修改配置文件,比如文本检测:

%cd ~/PaddleOCR
# 设置PYTHONPATH路径
%env PYTHONPATH=$PYTHONPATH:.
# GPU单卡训练
%env CUDA_VISIBLE_DEVICES=0
!python3 tools/train.py -c configs/det/det_mv3_db.yml

从上面可以看到DBNet配置文件configs/det/det_mv3_db.yml,那么就到里面去修改pretrain_weights的值为解压的模型文件路径。

如果下载的模型对配置文件有要求,将上述训练命令中-c的参数修改为指定配置文件。

比如

20a050dc72c44f4999458368f2d82d82.png

我要使用第一个,要求的配置文件是ch_PP-OCRv3_det_cml.yml,那就将-c设置为上述配置文件。修改的模型路径也是上述配置文件的pretrain_weights值。

5 导出模型

%cd ~/PaddleOCR
# 导出检测模型
!python3 tools/export_model.py \
        -c configs/det/det_mv3_db.yml \
        -o Global.checkpoints=./myoutput/det_db/best_accuracy \
        Global.save_inference_dir=./inference/mydet_db
# 导出识别模型
!python3 tools/export_model.py \
        -c configs/rec/rec_mv3_tps_bilstm_attn.yml \
        -o Global.checkpoints=./myoutput/rec_RARE_atten_new/best_accuracy \
        Global.save_inference_dir=./inference/myrec_rare

我第一次执行时有两个包没有安装,pip安装即可。

其次,导出模型前,PaddleOCR/interface/mydet_db和PaddleOCR/interface/myrec_rare路径下已经存在model和params两个文件。导出后仍是这两个名称的文件,在没有报错的情况下,是正常的(覆盖了)。

9a6d2d64cd49490b89926f930e12b1e3.png

6 模型测试

首先安装深度学习数据增强库imgaug:

!pip install imgaug

然后执行训练脚本

%cd ~/PaddleOCR
!python3 tools/infer/predict_system.py \
    --image_dir="../imgtest" \
    --det_model_dir="./inference/mydet_db" \
    --rec_model_dir="./inference/myrec_rare" \
    --rec_image_shape="3, 32, 320" \
    --rec_char_type="ch" \
    --rec_algorithm="RARE" \
    --use_space_char False \
    --max_text_length 8 \
    --rec_char_dict_path="../word_dict.txt" \
    --use_gpu False 

结果类似即正确:

22244ec73f2e4aedb17ac4a9570af7a7.png

原文中结果的置信度基本接近1,可能我这是文本检测训练的不足,置信度略低。

7 模型部署

7.1 ONNX

首先安装环境:

!pip install onnx==1.10.1 onnxruntime-gpu==1.10 paddle2onnx
!paddle2onnx --model_dir ./stac --model_filename model.pdmodel --params_filename model.pdiparams --opset_version 11 --save_file result.onnx

导出检测模型

%cd ~/PaddleOCR
!paddle2onnx \
    --model_dir ./inference/mydet_db \
    --model_filename __model__ \
    --params_filename __params__ \
    --save_file ./out_onnx/det/det.onnx \
    --opset_version 12 \
    --enable_onnx_checker True

其中--model_dir是模型的保存路径,--save_file是onnx文件的保存路径,修改这两个即可。

导出识别模型

%cd ~/PaddleOCR
!paddle2onnx \
    --model_dir ./inference/rec_RARE_attn_new \
    --model_filename __model__ \
    --params_filename __params__ \
    --save_file ./out_onnx/rec/rec.onnx \
    --opset_version 12 \
    --enable_onnx_checker True

8 参考

https://aistudio.baidu.com/aistudio/projectdetail/4542547

https://blog.csdn.net/qq_36816848/category_12113641.html

https://aistudio.baidu.com/aistudio/projectdetail/4359114?channelType=0&channel=0


相关文章
|
机器学习/深度学习 JSON 算法
智能零售柜商品检测软件(Python+YOLOv5深度学习模型+清新界面)
智能零售柜商品检测软件(Python+YOLOv5深度学习模型+清新界面)
822 0
|
2月前
|
机器学习/深度学习 人工智能 PyTorch
AI计算机视觉笔记三十二:LPRNet车牌识别
LPRNet是一种基于Pytorch的高性能、轻量级车牌识别框架,适用于中国及其他国家的车牌识别。该网络无需对字符进行预分割,采用端到端的轻量化设计,结合了squeezenet和inception的思想。其创新点在于去除了RNN,仅使用CNN与CTC Loss,并通过特定的卷积模块提取上下文信息。环境配置包括使用CPU开发板和Autodl训练环境。训练和测试过程需搭建虚拟环境并安装相关依赖,执行训练和测试脚本时可能遇到若干错误,需相应调整代码以确保正确运行。使用官方模型可获得较高的识别准确率,自行训练时建议增加训练轮数以提升效果。
|
3月前
|
机器学习/深度学习 监控 TensorFlow
使用Python实现深度学习模型:智能停车管理系统
【8月更文挑战第22天】 使用Python实现深度学习模型:智能停车管理系统
67 8
|
5月前
|
机器学习/深度学习 计算机视觉 异构计算
基于YOLOv8深度学习的玉米叶片病害智能诊断与防治系统【python源码+Pyqt5界面+数据集+训练代码】深度学习实战、目标分类(2)
基于YOLOv8深度学习的玉米叶片病害智能诊断与防治系统【python源码+Pyqt5界面+数据集+训练代码】深度学习实战、目标分类
基于YOLOv8深度学习的玉米叶片病害智能诊断与防治系统【python源码+Pyqt5界面+数据集+训练代码】深度学习实战、目标分类(2)
|
5月前
|
机器学习/深度学习 监控 搜索推荐
基于YOLOv8深度学习的玉米叶片病害智能诊断与防治系统【python源码+Pyqt5界面+数据集+训练代码】深度学习实战、目标分类(1)
基于YOLOv8深度学习的玉米叶片病害智能诊断与防治系统【python源码+Pyqt5界面+数据集+训练代码】深度学习实战、目标分类
|
人工智能 并行计算 PyTorch
YOLOv5项目搭建(AI识别皮卡丘)
在网上看了很多关于yolov5的博客,很多都是解读参数配置,或者是解析它的底层原理什么的,对新手不是特别友好,我认为学一样东西,先不管其他的,先把这个东西做出来,然后再去学习内部的原理,这样学习起来就不会那么枯燥乏味了,本篇博客就是纯手把手教你yolov5的搭建、训练、识别。
263 1
|
机器学习/深度学习 人工智能 编解码
最近大火的两大AI绘图工具 Midjourney VS StableDiffusion
最近大火的两大AI绘图工具 Midjourney VS StableDiffusion
322 0
|
测试技术 异构计算 Python
ModelScope开源mPLUG模型带你一键体验大火的视觉问答能力(1)
ModelScope开源mPLUG模型带你一键体验大火的视觉问答能力
|
机器学习/深度学习 自然语言处理 机器人
ModelScope开源mPLUG模型带你一键体验大火的视觉问答能力(2)
ModelScope开源mPLUG模型带你一键体验大火的视觉问答能力
|
人工智能 安全 Python
python小白0基础利用飞桨平台实现AI识别加口罩
python小白0基础利用飞桨平台实现AI识别加口罩
177 0
python小白0基础利用飞桨平台实现AI识别加口罩
下一篇
无影云桌面