PaddleOCR入门——以电表读数识别为例

前言:

本篇针对OCR初学者,帮助新手快速上路。

内容为使用百度飞桨(Paddlepaddle) 进行图片内容识别(以水电表读数识别为具体案例)。内容包括:

  1. OCR简介
  2. 百度飞桨(PaddleOCR)简介和使用入门
  3. 完成图片内容识别——以水电表读数识别为例

一 OCR简介

OCR (Optical Character Recognition,光学字符识别)是指电子设备(例如扫描仪或数码相机)检查纸上打印的字符,通过检测暗、亮的模式确定其形状,然后用字符识别方法将形状翻译成计算机文字的过程。

计算机视觉(Computer Vision,简称CV)是指通过计算机算法和数学模型实现对图像或视频中的信息进行识别、解析和理解的能力。它可以帮助计算机“看懂”图像或视频中的信息,如物体、场景、人脸等。CV的技术主要包括图像处理、计算机视觉算法、机器学习等。

二 PaddleOCR简介

基于飞桨的OCR工具库,包含总模型仅8.6M的超轻量级中文OCR,单模型支持中英文数字组合识别、竖排文本识别、长文本识别。同时支持多种文本检测、文本识别的训练算法。

PaddleOCR: 基于飞桨的OCR工具库,包含总模型仅8.6M的超轻量级中文OCR,单模型支持中英文数字组合识别、竖排文本识别、长文本识别。同时支持多种文本检测、文本识别的训练算法。 - Gitee.com

开源项目 - 飞桨AI Studio星河社区-人工智能学习与实训社区

效果展示:

本篇使用PaddleOCR完成水电表读数识别的任务,原因是该平台上有许多现成且成熟的预训练模型可以使用,读者可在平台搜索,寻找最适合自己的项目进行学习和使用。

三 图片识别

该部分的目的是完成图片内容识别,以作者在水电表读数识别任务的整个流程为例,希望能给新人启发。

1 接到任务

工作中接到了识别水电表读数的任务。如图所示:

目标是取出目标区域的读数(00001)。调研后发现百度飞桨可以实现该目标,故开始使用。

2 寻找相似项目

有前人造好的轮子可以复用自然最好,瞅瞅大佬们的作品看看能不能复用。在百度飞桨平台搜索后,发现“深渊上的坑”大佬做过相关项目,效果不错。PPOCR:多类别电表读数识别 - 飞桨AI Studio星河社区

故开始阅读和复现。

3 理解和复现

3.1 进入项目

飞桨提供了AI Studio可以在云上直接使用大佬的项目(包括了数据和代码),所以可以先在上面进行尝试,理解后也可以在本地复现(注:本地运行的话需要装各种包,新人需要耐心查找安装)

以下先从云上开始。先找到项目,点击运行一下:

弹出运行环境选择界面,注意,选择GPU环境,不然容易出错。

进入codeLab界面,用起来和jupyter差不多。左侧是文件,右侧有大佬的文字说明和代码。

这里开始,可以按照大佬的代码一步一步运行尝试。再次附上链接https://aistudio.baidu.com/projectdetail/3429765

3.2 开始运行

环境要求

  • PaddlePaddle >= 2.1.0
  • 3.5 <= Python < 3.9
  • PaddleOCR >= 2.1

如果是在百度的平台,可以无视环境要求。本地的话需要按照以上要求调好。

正式开始,首先克隆项目(将PaddleOCR拖到工作地址下),并安装ppocr。云上运行会很顺利,本地报错的话,按照报错的提示注意排查即可。

# 克隆项目
!git clone https://gitee.com/paddlepaddle/PaddleOCR.git

# 安装ppocr
!pip install fasttext==0.8.3
!pip install paddleocr --no-deps -r requirements.txt

克隆结束,安装ppocr后切换到PaddleOCR文件夹下。

%cd PaddleOCR/

实际在运行的时候,发现缺少了包,安装如下。

!pip install --upgrade scipy
!pip install --upgrade scipy
!pip install Polygon3   # pip install Polygon3 -i https://pypi.tuna.tsinghua.edu.cn/simple
!pip install lanms
!pip install rapidfuzz
!pip install scikit-image
!pip install imgaug
!pip install pyclipper
!pip install lmdb

完成了准备工作,经过阅读,发现项目的作者提供了一个已经训练好的模型,我们来试试。

# 提供的预训练模型和配置文件
!tar -xvf ../my_exps.tar -C ./

注意,这里的my_exps 是线上才有的,本地运行没有这个文件。如果要本地运行,需要去线上把这个下载到本地。

把大佬的模型解压好后,就可以使用了。作者提供的语句如下:

!python tools/infer_det.py -c configs/det/ch_PP-OCRv2/ch_PP-OCRv2_det_student.yml -o Global.infer_img="./M2021/test.jpg" -o Global.checkpoints="my_exps/det_dianbiao_size1600_copypaste/best_accuracy"

直接运行会报错,原因是云上的图片是压缩包格式,不存在需要识别的图片: ./M2021/test.jpg。所以去data/data117381下解压好。(这里的图片是作者提供的,可以使用自己的图片。云上的话可以将自己的图片上传,本地的话给正确的图片路径就行)

再来运行一下(注意改好需要识别的图片名称)。

!python tools/infer_det.py -c configs/det/ch_PP-OCRv2/ch_PP-OCRv2_det_student.yml -o Global.infer_img="./M2021/IMG_20210712_095825.jpg" -o Global.checkpoints="my_exps/det_dianbiao_size1600_copypaste/best_accuracy"

结果如上,运行成功,但是不够直观,我们再加工一下。

将模型导出,这里是用作者给的模型best_accuracy直接替换whl下预测用的检测模型。

# 模型导出
!python tools/export_model.py -c configs/det/ch_PP-OCRv2/ch_PP-OCRv2_det_student.yml -o Global.pretrained_model=./my_exps/det_dianbiao_size1600_copypaste/best_accuracy Global.save_inference_dir=./inference/det_db

然后运行预测用的检测模型,给出结果并显示

from paddleocr import PaddleOCR, draw_ocr
# 模型路径下必须含有model和params文件
ocr = PaddleOCR(det_model_dir='./inference/det_db', 
                use_angle_cls=True)
img_path = '/home/aistudio/data/data117381/M2021/IMG_20210712_130915.jpg'
result = ocr.ocr(img_path, cls=True)
for line in result:
    print(line)

# 显示结果
from PIL import Image
image = Image.open(img_path).convert('RGB')
result0 = result[0]
result0.sort(key=lambda x: x[1][1], reverse=True)
boxes = [line[0] for line in result0]
txts = [line[1][0] for line in result0]
scores = [line[1][1] for line in result0]
im_show = draw_ocr(image, boxes, txts, scores)
im_show = Image.fromarray(im_show)
im_show
#im_show.save('result.jpg')

结果如上,项目作者的目的是同时读取电表的读数和电表编号,故模型效果会输出两个值(读数和编号)

至此,大致的复现已经完成。

4 微调

每个项目都有特殊性,本任务是尽可能准的识别读数且,读取读数时,无需电表最末尾红色区的小数。并且不需要电表编号。故需要对模型进行微调。

作者“深渊上的坑”也在项目中给出了重新训练的思路:

  • 首先基于自己的数据集,进行数据标注。
  • 其次,重头训练或者基于预训练模型进行微调
  • 最后,导出训练好的模型进行使用。
4.1 数据标注

要让模型识别我们特定的区域,需要有相应的数据。我们使用PPOCRLabel 来完成数据标注,如下:

PaddleOCR: 基于飞桨的OCR工具库,包含总模型仅8.6M的超轻量级中文OCR,单模型支持中英文数字组合识别、竖排文本识别、长文本识别。同时支持多种文本检测、文本识别的训练算法。 - Gitee.com

PPOCRLabel是一款适用于OCR领域的半自动化图形标注工具,内置PP-OCR模型对数据自动标注和重新识别。使用Python3和PyQT5编写,支持矩形框标注和四点标注模式,导出格式可直接用于PaddleOCR检测和识别模型的训练。

我们按照官网的教程即可,流程如下:

注,因为我们自己的数据集一般在本地,所以接下来的数据标准操作都在本地完成(使用jupyter)。

如果对操作有疑问,可以参照大佬的流程:

电表检测部署应用:手把手教你把PPOCRLabel改成想要的模样 - 飞桨AI Studio星河社区

流程如下:

# 克隆项目 text_renderer在项目中已经准备好
!git clone https://gitee.com/paddlepaddle/PaddleOCR.git

# 安装ppocr
!pip install fasttext==0.8.3
!pip install paddleocr --no-deps -r requirements.txt

# 安装ppocrLabel  
!pip install PPOCRLabel  

运行PPOCRLabel

import os
os.environ['KMP_DUPLICATE_LIB_OK']='True'     
#如果不小心重复安装了paddle,运行的时候加上这个可以绕过一些错误

启动PPOCRLabel

!PPOCRLabel --lang ch

启动后界面如下:

把图片数据加载进来:点击 文件 → 打开目录 → 选择文件夹

开始标注!对图片框出自己需要的区域。

标注完成后,导出标注结果。

标注文件会存在图片所在的文件夹下,txt格式。

4.2 模型微调

有了自己的标注后的数据,我们可以开始进行模型微调。

由于本地机器性能有限,我们将文件夹放到云端(含图片和label.txt)。

图片上传完成后(这里以M2021文件夹存在了PaddleOCR下),接上我们在第三部分的代码后面继续(这样不用重新拉取PaddleOCR.git)

本项目使用 configs/det/ch_PP-OCRv2/ch_PP-OCRv2_det_student.yml 配置文件。在找到路径下找到ch_PP-OCRv2_det_student.yml,更改点如下所示:

Global:
  use_gpu: true
  epoch_num: 20      #减少了轮数,微调无需太多,节约时间
  log_smooth_window: 20
  print_batch_step: 10
  save_model_dir: ./output/det_dianbiao_v3    #将模型保存到我们要的位置
  save_epoch_step: 1200
  # evaluation is run every 5000 iterations after the 4000th iteration
  eval_batch_step: [0, 400]
  cal_metric_during_train: False
  pretrained_model: ./my_exps/student.pdparams    #这里使用了项目作者的预训练模型
  checkpoints:
  save_inference_dir:
  use_visualdl: False
  infer_img: ./M2021/IMG_20210712_085852.jpg    #修改图片位置
  save_res_path: ./output/det_db/predicts_db.txt

Architecture:
  model_type: det
  algorithm: DB
  Transform:
  Backbone:
    name: MobileNetV3
    scale: 0.5
    model_name: large
    disable_se: True
  Neck:
    name: DBFPN
    out_channels: 96
  Head:
    name: DBHead
    k: 50

Loss:
  name: DBLoss
  balance_loss: true
  main_loss_type: DiceLoss
  alpha: 5
  beta: 10
  ohem_ratio: 3

Optimizer:
  name: Adam
  beta1: 0.9
  beta2: 0.999
  lr:
    name: Cosine
    learning_rate: 0.001
    warmup_epoch: 2
  regularizer:
    name: 'L2'
    factor: 0

PostProcess:
  name: DBPostProcess
  thresh: 0.3
  box_thresh: 0.6
  max_candidates: 1000
  unclip_ratio: 1.5

Metric:
  name: DetMetric
  main_indicator: hmean

Train:
  dataset:
    name: SimpleDataSet
    data_dir: ./
    label_file_list:
      - M2021/M2021_label_train.txt   #这里改成我们标准好的label文件
    ratio_list: [1.0]
    transforms:
      - DecodeImage: # load image
          img_mode: BGR
          channel_first: False
      - DetLabelEncode: # Class handling label
      - IaaAugment:
          augmenter_args:
            - { 'type': Fliplr, 'args': { 'p': 0.5 } }
            - { 'type': Affine, 'args': { 'rotate': [-10, 10] } }
            - { 'type': Resize, 'args': { 'size': [0.5, 3] } }
      - EastRandomCropData:
          size: [960, 960]
          max_tries: 50
          keep_ratio: true
      - MakeBorderMap:
          shrink_ratio: 0.4
          thresh_min: 0.3
          thresh_max: 0.7
      - MakeShrinkMap:
          shrink_ratio: 0.4
          min_text_size: 8
      - NormalizeImage:
          scale: 1./255.
          mean: [0.485, 0.456, 0.406]
          std: [0.229, 0.224, 0.225]
          order: 'hwc'
      - ToCHWImage:
      - KeepKeys:
          keep_keys: ['image', 'threshold_map', 'threshold_mask', 'shrink_map', 'shrink_mask'] # the order of the dataloader list
  loader:
    shuffle: True
    drop_last: False
    batch_size_per_card: 8
    num_workers: 4

Eval:
  dataset:
    name: SimpleDataSet
    data_dir: ./
    label_file_list:
      - ./M2021/M2021_label_train.txt    #这里改成我们标准好的label文件
    transforms:
      - DecodeImage: # load image
          img_mode: BGR
          channel_first: False
      - DetLabelEncode: # Class handling label
      - DetResizeForTest:
#           image_shape: [736, 1280]
      - NormalizeImage:
          scale: 1./255.
          mean: [0.485, 0.456, 0.406]
          std: [0.229, 0.224, 0.225]
          order: 'hwc'
      - ToCHWImage:
      - KeepKeys:
          keep_keys: ['image', 'shape', 'polys', 'ignore_tags']
  loader:
    shuffle: False
    drop_last: False
    batch_size_per_card: 1 # must be 1
    num_workers: 2

配置改好后开始训练

%cd PaddleOCR/
# 开始训练
!python tools/train.py -c configs/det/ch_PP-OCRv2/ch_PP-OCRv2_det_student.yml

耐心等待,20轮后模型存到我们在配置文件中设置的地址(save_model_dir: ./output/det_dianbiao_v3 )。

4.3 导出和使用

模型导出和串接,和第三部分相似,先将模型导出,然后把whl下预测用的检测模型用新训练的模型直接替换掉,就可以看到finetune后的检测效果了。

!python tools/export_model.py -c configs/det/ch_PP-OCRv2/ch_PP-OCRv2_det_student.yml -o Global.pretrained_model=./output/det_dianbiao_v3/latest Global.save_inference_dir=./inference/det_db

查看效果

from paddleocr import PaddleOCR, draw_ocr
# 模型路径下必须含有model和params文件
ocr = PaddleOCR(det_model_dir='./inference/det_db', 
                use_angle_cls=True)
img_path = './M2021/IMG_20210712_095313.jpg'
result = ocr.ocr(img_path, cls=True)
for line in result:
    print(line)

# 显示结果
from PIL import Image
image = Image.open(img_path).convert('RGB')
result0 = result[0]
result0.sort(key=lambda x: x[1][1], reverse=True)
boxes = [line[0] for line in result0]
txts = [line[1][0] for line in result0]
scores = [line[1][1] for line in result0]
im_show = draw_ocr(image, boxes, txts, scores)
# im_show = draw_ocr(image, boxes, txts, scores)
im_show = Image.fromarray(im_show)
im_show

圆满完成任务,大功告成。

感谢大佬“深渊上的坑”,放上链接,以供学习。

深渊上的坑-飞桨星河社区

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lzc1009840152

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值