共计 10756 个字符,预计需要花费 27 分钟才能阅读完成。
基于 PaddleOCR 的多视角集装箱箱号检测辨认
一、我的项目介绍
集装箱号是指装运进口货物集装箱的箱号,填写托运单时必填此项。标准箱号形成基本概念:采纳 ISO6346(1995)规范
规范集装箱箱号由 11 位编码组成,如:CBHU 123456 7,包含三个局部:
- 第一局部由 4 位英文字母组成。前三位代码次要阐明箱主、经营人,第四位代码阐明集装箱的类型。列如 CBHU 结尾的规范集装箱是表明箱主和经营人为中远集运
- 第二局部由 6 位数字组成。是箱体注册码,用于一个集装箱箱体持有的惟一标识
- 第三局部为校验码由前 4 位字母和 6 位数字通过校验规定运算失去,用于辨认在校验时是否产生谬误。即第 11 位编号
本教程基于 PaddleOCR 进行集装箱箱号检测辨认工作,应用大量数据别离训练检测、辨认模型,最初将他们串联在一起实现集装箱箱号检测辨认的工作
成果展现:
二、环境筹备
首先点击左侧套件抉择 PaddleOCR 进行下载。
三、数据集介绍
本教程所应用的集装箱箱号数据集,该数据蕴含 3003 张分辨率为 1920×1080 的集装箱图像
1、PaddleOCR 检测模型训练标注规定如下,两头用 ”\t” 分隔:
"图像文件名 json.dumps 编码的图像标注信息" | |
ch4_test_images/img_61.jpg [{"transcription": "MASA", "points": [[310, 104], [416, 141], [418, 216], [312, 179]]}, {...}] |
其中 json.dumps 编码前的图像标注信息是蕴含多个字典的 list,字典中的 points 示意文本框的四个点的坐标(x, y),从左上角的点开始顺时针排列。transcription 示意以后文本框的文字,当其内容为“###”时,示意该文本框有效,在训练时会跳过。
2、PaddleOCR 辨认模型训练标注规定如下,两头用 ”\t” 分隔:
"图像文件名 图像标注信息" | |
train_data/rec/train/word_001.jpg 简略可依赖 | |
train_data/rec/train/word_002.jpg 用科技让简单的世界更简略 |
四、数据整顿
4.1 检测模型所需数据筹备
将数据集 3000 张图片按 2:1 划分成训练集和验证集,运行以下代码
from tqdm import tqdm | |
finename = "all_label.txt" | |
f = open(finename) | |
lines = f.readlines() | |
t = open('det_train_label.txt','w') | |
v = open('det_eval_label.txt','w') | |
count = 0 | |
for line in tqdm(lines): | |
if count < 2000: | |
t.writelines(line) | |
count += 1 | |
else: | |
v.writelines(line) | |
f.close() | |
t.close() | |
v.close() |
4.2 辨认模型所需数据筹备
咱们依据检测局部的正文,裁剪数据集尽可能只蕴含文字局部图片作为辨认的数据,运行以下代码
from PIL import Image | |
import json | |
from tqdm import tqdm | |
import os | |
import numpy as np | |
import cv2 | |
import math | |
from PIL import Image, ImageDraw | |
class Rotate(object): | |
def __init__(self, image: Image.Image, coordinate): | |
self.image = image.convert('RGB') | |
self.coordinate = coordinate | |
self.xy = [tuple(self.coordinate[k]) for k in ['left_top', 'right_top', 'right_bottom', 'left_bottom']] | |
self._mask = None | |
self.image.putalpha(self.mask) | |
@property | |
def mask(self): | |
if not self._mask: | |
mask = Image.new('L', self.image.size, 0) | |
draw = ImageDraw.Draw(mask, 'L') | |
draw.polygon(self.xy, fill=255) | |
self._mask = mask | |
return self._mask | |
def run(self): | |
image = self.rotation_angle() | |
box = image.getbbox() | |
return image.crop(box) | |
def rotation_angle(self): | |
x1, y1 = self.xy[0] | |
x2, y2 = self.xy[1] | |
angle = self.angle([x1, y1, x2, y2], [0, 0, 10, 0]) * -1 | |
return self.image.rotate(angle, expand=True) | |
def angle(self, v1, v2): | |
dx1 = v1[2] - v1[0] | |
dy1 = v1[3] - v1[1] | |
dx2 = v2[2] - v2[0] | |
dy2 = v2[3] - v2[1] | |
angle1 = math.atan2(dy1, dx1) | |
angle1 = int(angle1 * 180 / math.pi) | |
angle2 = math.atan2(dy2, dx2) | |
angle2 = int(angle2 * 180 / math.pi) | |
if angle1 * angle2 >= 0: | |
included_angle = abs(angle1 - angle2) | |
else: | |
included_angle = abs(angle1) + abs(angle2) | |
if included_angle > 180: | |
included_angle = 360 - included_angle | |
return included_angle | |
def image_cut_save(path, bbox, save_path): | |
""" | |
:param path: 图片门路 | |
:param left: 区块左上角地位的像素点离图片左边界的间隔 | |
:param upper:区块左上角地位的像素点离图片上边界的间隔 | |
:param right:区块右下角地位的像素点离图片左边界的间隔 | |
:param lower:区块右下角地位的像素点离图片上边界的间隔 | |
""" | |
img_width = 1920 | |
img_height = 1080 | |
img = Image.open(path) | |
coordinate = {'left_top': bbox[0], 'right_top': bbox[1], 'right_bottom': bbox[2], 'left_bottom': bbox[3]} | |
rotate = Rotate(img, coordinate) | |
left, upper = bbox[0] | |
right, lower = bbox[2] | |
if lower-upper > right-left: | |
rotate.run().convert('RGB').transpose(Image.ROTATE_90).save(save_path) | |
else: | |
rotate.run().convert('RGB').save(save_path) | |
return True | |
#读取检测标注制作辨认数据集 | |
files = ["det_train_label.txt","det_eval_label.txt"] | |
filetypes =["train","eval"] | |
for index,filename in enumerate(files): | |
f = open(filename) | |
l = open('rec_'+filetypes[index]+'_label.txt','w') | |
if index == 0: | |
data_dir = "RecTrainData" | |
else: | |
data_dir = "RecEvalData" | |
if not os.path.exists(data_dir): | |
os.mkdir(data_dir) | |
lines = f.readlines() | |
for line in tqdm(lines): | |
image_name = line.split("\t")[0].split("/")[-1] | |
annos = json.loads(line.split("\t")[-1]) | |
img_path = os.path.join("/home/aistudio/input0/images",image_name) | |
for i,anno in enumerate(annos): | |
data_path = os.path.join(data_dir,str(i)+"_"+image_name) | |
if image_cut_save(img_path,anno["points"],data_path): | |
l.writelines(str(i)+"_"+image_name+"\t"+anno["transcription"]+"\n") | |
l.close() | |
f.close() |
五、试验
因为数据集比拟少,为了模型更好和更快的收敛,这里选用 PaddleOCR 中的 PP-OCRv3 模型进行检测和辨认。PP-OCRv3 在 PP-OCRv2 的根底上,中文场景端到端 Hmean 指标相比于 PP-OCRv2 晋升 5%, 英文数字模型端到端成果晋升 11%。具体优化细节请参考 PP-OCRv3 技术报告。
5.1 检测模型
5.1.1 检测模型配置
PaddleOCR 提供了许多检测模型,在门路 PaddleOCR-2.6.0/configs/det
下可找到模型及其配置文件。如咱们选用模型ch_PP-OCRv3_det_student.yml
,其配置文件门路在:PaddleOCR-2.6.0/configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_student.yml
。应用前需对其进行必要的设置,如训练参数、数据集门路等。将局部要害配置展现如下:
# 要害训练参数 | |
use_gpu: true #是否应用显卡 | |
epoch_num: 1200 #训练 epoch 个数 | |
save_model_dir: ./output/ch_PP-OCR_V3_det/ #模型保留门路 | |
save_epoch_step: 200 #每训练 200epoch,保留一次模型 | |
eval_batch_step: [0, 100] #训练每迭代 100 次,进行一次验证 | |
pretrained_model: ./PaddleOCR-release | |
2.5/pretrain_models/ch_PP-OCR_V3_det/best_accuracy.pdparams #预训练模型门路 | |
#训练集门路设置 | |
Train: | |
dataset: | |
name: SimpleDataSet | |
data_dir: /input0/images #图片文件夹门路 | |
label_file_list: | |
- ./det_train_label.txt #标签门路 |
文件间接放在更目录里,自行替换即可 /home/aistudio/ch_PP-OCRv3_det_student.yml
5.1.2 模型微调
在 notebook 中运行如下命令对模型进行微调,其中 -c 传入的为配置好的模型文件门路
%run /home/aistudio/PaddleOCR-2.6.0/tools/train.py \ | |
-c /home/aistudio/PaddleOCR-2.6.0/configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_student.yml |
应用默认超参数,模型 ch_PP-OCRv3_det_student
在训练集上训练 100 个 epoch 后,模型在验证集上的 hmean 达到:90.96%,尔后再无显著增长
[2023/03/21 15:57:09] ppocr INFO: best metric, hmean: 0.909551282051282, precision: 0.8977836411609498, | |
recall: 0.921611681990265, fps: 20.347745459258228, best_epoch: 100 |
5.2 辨认模型
5.2.1 辨认模型配置
PaddleOCR 也提供了许多辨认模型,在门路 PaddleOCR-2.6.0/configs/rec
下可找到模型及其配置文件。如咱们选用模型 ch_PP-OCRv3_rec_distillation,其配置文件门路在:PaddleOCR-2.6.0/configs/rec/PP-OCRv3/ch_PP-OCRv3_rec_distillation.yml
。应用前需对其进行必要的设置,如训练参数、数据集门路等。将局部要害配置展现如下:
# 要害训练参数 | |
use_gpu: true #是否应用显卡 | |
epoch_num: 1200 #训练 epoch 个数 | |
save_model_dir: ./output/rec_ppocr_v3_distillation #模型保留门路 | |
save_epoch_step: 200 #每训练 200epoch,保留一次模型 | |
eval_batch_step: [0, 100] #训练每迭代 100 次,进行一次验证 | |
pretrained_model: ./PaddleOCR-release-2.5/pretrain_models/PPOCRv3/best_accuracy.pdparams #预训练模型门路 | |
#训练集门路设置 | |
Train: | |
dataset: | |
name: SimpleDataSet | |
data_dir: ./RecTrainData/ #图片文件夹门路 | |
label_file_list: | |
- ./rec_train_label.txt #标签门路 | |
Eval: | |
dataset: | |
name: SimpleDataSet | |
data_dir: ./RecEvalData/ | |
label_file_list: | |
- ./rec_eval_label.txt |
文件间接放在更目录里,自行替换即可 /home/aistudio/ch_PP-OCRv3_rec_distillation.yml
5.2.2 模型微调
在 notebook 中运行如下命令对模型进行微调,其中 -c 传入的为配置好的模型文件门路
%run /home/aistudio/PaddleOCR-2.6.0/tools/train.py \ | |
-c /home/aistudio/PaddleOCR-2.6.0/configs/rec/PP-OCRv3/ch_PP-OCRv3_rec_distillation.yml |
应用默认超参数,模型 ch_PP-OCRv3_rec_distillation
在训练集上训练 50 个 epoch 后,模型在验证集上的精度达到:91.11%,尔后再无显著增长
[2023/03/21 20:04:28] ppocr INFO: best metric, acc: 0.9110600272522444, norm_edit_dis: 0.9427426548965615, | |
Teacher_acc: 0.9040291998159589, Teacher_norm_edit_dis: 0.9405629345025616, fps: 246.029195787707, best_epoch: 50 |
六、后果展现
6.1 检测模型推理
在 notebook 中运行如下命令应用微调过的模型检测测试图片中的文字,其中:
Global.infer_img
为图片门路或图片文件夹门路,Global.pretrained_model
为微调过的模型,Global.save_res_path
为推理后果保留门路
%run /home/aistudio/PaddleOCR-2.6.0/tools/infer_det.py \ | |
-c /home/aistudio/PaddleOCR-2.6.0/configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_student.yml \ | |
-o Global.infer_img="/home/aistudio/input0/images" Global.pretrained_model="./output/ch_PP-OCR_V3_det/best_accuracy" Global.save_res_path="./output/det_infer_res/predicts.txt" |
6.2 辨认模型推理
在 notebook 中运行如下命令应用微调过的模型检测测试图片中的文字,其中:
Global.infer_img
为图片门路或图片文件夹门路,Global.pretrained_model
为微调过的模型,Global.save_res_path
为推理后果保留门路
%run /home/aistudio/PaddleOCR-2.6.0/tools/infer_rec.py \ | |
-c /home/aistudio/PaddleOCR-2.6.0/configs/rec/PP-OCRv3/ch_PP-OCRv3_rec_distillation.yml \ | |
-o Global.infer_img="./RecEvalData/" Global.pretrained_model="./output/rec_ppocr_v3_distillation/best_accuracy" Global.save_res_path="./output/rec_infer_res/predicts.txt" |
局部后果展现:
./RecEvalData/0_1-122720001-OCR-AS-B01.jpg {"Student": {"label": "EITU1786393", "score": 0.9737951755523682}, "Teacher": {"label": "EITU1786393", "score": 0.9882291555404663}} | |
./RecEvalData/0_1-122720001-OCR-LB-C02.jpg {"Student": {"label": "EITU1786393", "score": 0.9709678888320923}, "Teacher": {"label": "EITU1786393", "score": 0.9925146698951721}} | |
./RecEvalData/0_1-122720001-OCR-RF-D01.jpg {"Student": {"label": "EITU1786393", "score": 0.9985160231590271}, "Teacher": {"label": "EITU1786393", "score": 0.9967824816703796}} | |
./RecEvalData/0_1-122728001-OCR-RF-D01.jpg {"Student": {"label": "DFSU4119250", "score": 0.9663339257240295}, "Teacher": {"label": "DFSU4119250", "score": 0.9600133299827576}} | |
./RecEvalData/0_1-122740001-OCR-AH-A01.jpg {"Student": {"label": "MRKU4306585", "score": 0.9916775226593018}, "Teacher": {"label": "MRKU4306585", "score": 0.9929805994033813}} | |
./RecEvalData/0_1-122749001-OCR-AH-A01.jpg {"Student": {"label": "FCGU4996010", "score": 0.9195910096168518}, "Teacher": {"label": "FCGU4996010", "score": 0.9424482583999634}} | |
./RecEvalData/0_1-122830001-OCR-AS-B01.jpg {"Student": {"label": "MEDU4024195", "score": 0.9861812591552734}, "Teacher": {"label": "MEDU4024195", "score": 0.9718942642211914}} | |
./RecEvalData/0_1-122843001-OCR-RF-D01.jpg {"Student": {"label": "TGU864295", "score": 0.9045045375823975}, "Teacher": {"label": "TGU864395", "score": 0.8963061571121216}} |
6.3 检测辨认模型串联推理
6.3.1 模型转换
在串联推理前首先须要将训练保留的模型转换成推理模型,别离执行如下检测命令即可。其中,-c
传入要转换模型的配置文件门路,-o Global.pretrained_model
为要被转换的模型文件,Global.save_inference_dir
为转换失去推理模型的贮存门路
# 检测模型转换 | |
%run /home/aistudio/PaddleOCR-2.6.0/tools/export_model.py \ | |
-c /home/aistudio/PaddleOCR-2.6.0/configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_student.yml \ | |
-o Global.pretrained_model="./output/ch_PP-OCR_V3_det/best_accuracy" Global.save_inference_dir="./output/det_inference/" | |
# 辨认模型转换 | |
%run /home/aistudio/PaddleOCR-2.6.0/tools/export_model.py \ | |
-c /home/aistudio/PaddleOCR-2.6.0/configs/rec/PP-OCRv3/ch_PP-OCRv3_rec_distillation.yml \ | |
-o Global.pretrained_model="./output/rec_ppocr_v3_distillation/best_accuracy" Global.save_inference_dir="./output/rec_inference/" |
6.3.2 模型串联推理
转换结束后,PaddleOCR 提供了检测和辨认模型的串联工具,能够将训练好的任一检测模型和任一辨认模型串联成两阶段的文本识别系统。输出图像通过文本检测、检测框改正、文本辨认、得分过滤四个次要阶段输入文本地位和辨认后果。执行代码如下, 其中 image_dir
为单张图像或者图像汇合的门路,det_model_dir
为检测 inference 模型的门路,rec_model_dir
为辨认 inference 模型的门路。可视化辨认后果默认保留到 ./inference_results 文件夹外面。
%run /home/aistudio/PaddleOCR-2.6.0/tools/infer/predict_system.py \ | |
--image_dir="OCRTest" \ | |
--det_model_dir="./output/det_inference/" \ | |
--rec_model_dir="./output/rec_inference/Student/" |
后果展现:
1-122700001-OCR-LF-C01.jpg [{"transcription": "TTEMU3108252", "points": [[1226, 133], [1322, 133], [1322, 883], [1226, 883]]}, {"transcription": "22G1", "points": [[1417, 214], [1479, 216], [1471, 463], [1409, 461]]}] | |
1-122720001-OCR-AH-A01.jpg [{"transcription": "ITU1786393", "points": [[225, 206], [918, 215], [917, 318], [224, 309]]}] | |
1-122720001-OCR-AS-B01.jpg [{"transcription": "EITU1786393", "points": [[919, 283], [1389, 296], [1387, 372], [917, 359]]}, {"transcription": "45G1", "points": [[1104, 399], [1288, 399], [1288, 486], [1104, 486]]}] | |
1-122720001-OCR-LB-C02.jpg [{"transcription": "TU1", "points": [[226, 10], [515, 6], [516, 97], [227, 102]]}, {"transcription": "45G1", "points": [[489, 114], [784, 104], [787, 204], [492, 213]]}] | |
1-122720001-OCR-RF-D01.jpg [{"transcription": "EITU1786393", "points": [[216, 38], [941, 27], [942, 125], [217, 135]]}, {"transcription": "45G1", "points": [[452, 137], [719, 133], [720, 218], [453, 223]]}] |
以 1 -122720001-OCR-AS-B01.jpg 测试样例进行展现:
多视角辨认后果为:EITU1786393
七. 总结
本我的项目做了基于 PaddleOCR 的多视角集装箱箱号检测辨认,应用大量数据别离训练检测、辨认模型,最初将他们串联在一起实现集装箱箱号检测辨认的工作。其中集装箱号是指装运进口货物集装箱的箱号,填写托运单时必填此项。标准箱号形成基本概念:采纳 ISO6346(1995)规范。
从后果上看,基于 PaddleOCR 的多视角集装箱箱号检测辨认获得了不错的成果,但也存在一些改良中央。
- 数据集样本量和丰盛度不够
- 训练不充沛,因为算力和工夫限度,自己就简略训练了 100 epochs 左右进行了。
- 模型参数调优(目前默认参数)
等等
源我的项目链接:https://aistudio.baidu.com/aistudio/projectdetail/5766320?contributionType=1
欢送关注 fork 三连