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,其训练集,验证集和测试集已经分组完成:
打开任一文件夹后发现文件名称特别长,形式如下,以-
为分隔符: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 解压数据集
项目中,数据是以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)
这三块,对应改变注释执行即可(总共进行三次)。下面一块被注释的代码没起到什么作用。
最终得到三个文件:
- train_data_det
- val_data_det
- 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-34
,38-40
,42-44
,46-48
,64-71
五块,对应改变注释执行即可(总共进行三次)。下面一块被注释的代码没起到什么作用。
而data.close()
后面的with模块,生成的是标签映射关系,如A对应1,43对应吉。
最终,生成文件及目录:
- word_dicrt.txt
- train_data_rec.txt
- val_data_rec.txt
- test_data_rec.txt
- train_rec_img
- val_rec_img
- test_rec_img
其中234是训练文件,每一行内容类似work/train_rec_img/000000.jpg 皖AD08191。空格隔开,前面是图像的路径,后面是理想下识别得到的文字。
567是文本识别的训练集,里面是训练,验证,测试图像。
到这里数据集就准备完毕了,其实还是有很多注释的部分,暂时没遇到需要这些的东西。
4 模型
PaddleOCR提供的检测与识别模型:
模型库: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,可以看到里面的内容如下:
其中包含了epoch总数等各种信息。
接下来,打开里面的数据集文件路径./configs/det/det_db_icdar15_reader.yml
修改数据集路径。该文件内容如下:
这里图像数据路径写根路径/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,可以选择更大的。
保存的模型参数等文件如下图:
4.3 文本识别检测
和文本检测很像,也是先修改数据集路径,然后训练。
这次,先找到PaddleOCR目录下的configs/rec/rec_mv3_tps_bilstm_attn.yml
,内容如下:
其中的东西也与文本检测类似,重点关注配置数据集路径文件./configs/rec/rec_chinese_reader.yml
。
打开后如下:
同样,图像数据设置为根路径即可,后面路径训练脚本自动拼接,要求提取文本识别数据集中的路径代码不能随意更改。
然后执行训练脚本:
# %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
的参数修改为指定配置文件。
比如
我要使用第一个,要求的配置文件是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两个文件。导出后仍是这两个名称的文件,在没有报错的情况下,是正常的(覆盖了)。
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
结果类似即正确:
原文中结果的置信度基本接近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