Unverified Commit 7c09c97d authored by zhoujun's avatar zhoujun Committed by GitHub
Browse files

Merge pull request #4 from PaddlePaddle/develop

merge paddleocr
parents c1d19ce2 5ee40948
......@@ -4,12 +4,11 @@ English | [简体中文](README_cn.md)
PaddleOCR aims to create rich, leading, and practical OCR tools that help users train better models and apply them into practice.
**Recent updates**
- 2020.8.16, Release text detection algorithm [SAST](https://arxiv.org/abs/1908.05498) and text recognition algorithm [SRN](https://arxiv.org/abs/2003.12294)
- 2020.7.23, Release the playback and PPT of live class on BiliBili station, PaddleOCR Introduction, [address](https://aistudio.baidu.com/aistudio/course/introduce/1519)
- 2020.7.15, Add mobile App demo , support both iOS and Android ( based on easyedge and Paddle Lite)
- 2020.7.15, Improve the deployment ability, add the C + + inference , serving deployment. In addtion, the benchmarks of the ultra-lightweight OCR model are provided.
- 2020.7.15, Add several related datasets, data annotation and synthesis tools.
- 2020.7.9 Add a new model to support recognize the character "space".
- 2020.7.9 Add the data augument and learning rate decay strategies during training.
- [more](./doc/doc_en/update_en.md)
## Features
......@@ -91,7 +90,7 @@ Mobile DEMO experience (based on EasyEdge and Paddle-Lite, supports iOS and Andr
PaddleOCR open source text detection algorithms list:
- [x] EAST([paper](https://arxiv.org/abs/1704.03155))
- [x] DB([paper](https://arxiv.org/abs/1911.08947))
- [ ] SAST([paper](https://arxiv.org/abs/1908.05498))(Baidu Self-Research, comming soon)
- [x] SAST([paper](https://arxiv.org/abs/1908.05498))(Baidu Self-Research)
On the ICDAR2015 dataset, the text detection result is as follows:
......@@ -101,6 +100,13 @@ On the ICDAR2015 dataset, the text detection result is as follows:
|EAST|MobileNetV3|81.67%|79.83%|80.74%|[Download link](https://paddleocr.bj.bcebos.com/det_mv3_east.tar)|
|DB|ResNet50_vd|83.79%|80.65%|82.19%|[Download link](https://paddleocr.bj.bcebos.com/det_r50_vd_db.tar)|
|DB|MobileNetV3|75.92%|73.18%|74.53%|[Download link](https://paddleocr.bj.bcebos.com/det_mv3_db.tar)|
|SAST|ResNet50_vd|92.18%|82.96%|87.33%|[Download link](https://paddleocr.bj.bcebos.com/SAST/sast_r50_vd_icdar2015.tar)|
On Total-Text dataset, the text detection result is as follows:
|Model|Backbone|precision|recall|Hmean|Download link|
|-|-|-|-|-|-|
|SAST|ResNet50_vd|88.74%|79.80%|84.03%|[Download link](https://paddleocr.bj.bcebos.com/SAST/sast_r50_vd_total_text.tar)|
For use of [LSVT](https://github.com/PaddlePaddle/PaddleOCR/blob/develop/doc/doc_en/datasets_en.md#1-icdar2019-lsvt) street view dataset with a total of 3w training data,the related configuration and pre-trained models for text detection task are as follows:
|Model|Backbone|Configuration file|Pre-trained model|
......@@ -120,7 +126,7 @@ PaddleOCR open-source text recognition algorithms list:
- [x] Rosetta([paper](https://arxiv.org/abs/1910.05085))
- [x] STAR-Net([paper](http://www.bmva.org/bmvc/2016/papers/paper043/index.html))
- [x] RARE([paper](https://arxiv.org/abs/1603.03915v1))
- [ ] SRN([paper](https://arxiv.org/abs/2003.12294))(Baidu Self-Research, comming soon)
- [x] SRN([paper](https://arxiv.org/abs/2003.12294))(Baidu Self-Research)
Refer to [DTRB](https://arxiv.org/abs/1904.01906), the training and evaluation result of these above text recognition (using MJSynth and SynthText for training, evaluate on IIIT, SVT, IC03, IC13, IC15, SVTP, CUTE) is as follow:
......@@ -134,8 +140,14 @@ Refer to [DTRB](https://arxiv.org/abs/1904.01906), the training and evaluation r
|STAR-Net|MobileNetV3|81.56%|rec_mv3_tps_bilstm_ctc|[Download link](https://paddleocr.bj.bcebos.com/rec_mv3_tps_bilstm_ctc.tar)|
|RARE|Resnet34_vd|84.90%|rec_r34_vd_tps_bilstm_attn|[Download link](https://paddleocr.bj.bcebos.com/rec_r34_vd_tps_bilstm_attn.tar)|
|RARE|MobileNetV3|83.32%|rec_mv3_tps_bilstm_attn|[Download link](https://paddleocr.bj.bcebos.com/rec_mv3_tps_bilstm_attn.tar)|
|SRN|Resnet50_vd_fpn|88.33%|rec_r50fpn_vd_none_srn|[Download link](https://paddleocr.bj.bcebos.com/SRN/rec_r50fpn_vd_none_srn.tar)|
**Note:** SRN model uses data expansion method to expand the two training sets mentioned above, and the expanded data can be downloaded from [Baidu Drive](todo).
The average accuracy of the two-stage training in the original paper is 89.74%, and that of one stage training in paddleocr is 88.33%. Both pre-trained weights can be downloaded [here](https://paddleocr.bj.bcebos.com/SRN/rec_r50fpn_vd_none_srn.tar).
We use [LSVT](https://github.com/PaddlePaddle/PaddleOCR/blob/develop/doc/doc_en/datasets_en.md#1-icdar2019-lsvt) dataset and cropout 30w traning data from original photos by using position groundtruth and make some calibration needed. In addition, based on the LSVT corpus, 500w synthetic data is generated to train the model. The related configuration and pre-trained models are as follows:
|Model|Backbone|Configuration file|Pre-trained model|
|-|-|-|-|
|ultra-lightweight OCR model|MobileNetV3|rec_chinese_lite_train.yml|[Download link](https://paddleocr.bj.bcebos.com/ch_models/ch_rec_mv3_crnn.tar)|[inference model](https://paddleocr.bj.bcebos.com/ch_models/ch_rec_mv3_crnn_enhance_infer.tar) & [pre-trained model](https://paddleocr.bj.bcebos.com/ch_models/ch_rec_mv3_crnn_enhance.tar)|
......
......@@ -4,12 +4,11 @@
PaddleOCR旨在打造一套丰富、领先、且实用的OCR工具库,助力使用者训练出更好的模型,并应用落地。
**近期更新**
- 2020.8.16 开源文本检测算法[SAST](https://arxiv.org/abs/1908.05498)和文本识别算法[SRN](https://arxiv.org/abs/2003.12294)
- 2020.7.23 发布7月21日B站直播课回放和PPT,PaddleOCR开源大礼包全面解读,[获取地址](https://aistudio.baidu.com/aistudio/course/introduce/1519)
- 2020.7.15 添加基于EasyEdge和Paddle-Lite的移动端DEMO,支持iOS和Android系统
- 2020.7.15 完善预测部署,添加基于C++预测引擎推理、服务化部署和端侧部署方案,以及超轻量级中文OCR模型预测耗时Benchmark
- 2020.7.15 整理OCR相关数据集、常用数据标注以及合成工具
- 2020.7.9 添加支持空格的识别模型,识别效果,预测及训练方式请参考快速开始和文本识别训练相关文档
- 2020.7.9 添加数据增强、学习率衰减策略,具体参考[配置文件](./doc/doc_ch/config.md)
- [more](./doc/doc_ch/update.md)
......@@ -93,7 +92,7 @@ PaddleOCR旨在打造一套丰富、领先、且实用的OCR工具库,助力
PaddleOCR开源的文本检测算法列表:
- [x] EAST([paper](https://arxiv.org/abs/1704.03155))
- [x] DB([paper](https://arxiv.org/abs/1911.08947))
- [ ] SAST([paper](https://arxiv.org/abs/1908.05498))(百度自研, coming soon)
- [x] SAST([paper](https://arxiv.org/abs/1908.05498))(百度自研)
在ICDAR2015文本检测公开数据集上,算法效果如下:
......@@ -103,8 +102,16 @@ PaddleOCR开源的文本检测算法列表:
|EAST|MobileNetV3|81.67%|79.83%|80.74%|[下载链接](https://paddleocr.bj.bcebos.com/det_mv3_east.tar)|
|DB|ResNet50_vd|83.79%|80.65%|82.19%|[下载链接](https://paddleocr.bj.bcebos.com/det_r50_vd_db.tar)|
|DB|MobileNetV3|75.92%|73.18%|74.53%|[下载链接](https://paddleocr.bj.bcebos.com/det_mv3_db.tar)|
|SAST|ResNet50_vd|92.18%|82.96%|87.33%|[下载链接](https://paddleocr.bj.bcebos.com/SAST/sast_r50_vd_icdar2015.tar)|
在Total-text文本检测公开数据集上,算法效果如下:
|模型|骨干网络|precision|recall|Hmean|下载链接|
|-|-|-|-|-|-|
|SAST|ResNet50_vd|88.74%|79.80%|84.03%|[下载链接](https://paddleocr.bj.bcebos.com/SAST/sast_r50_vd_total_text.tar)|
使用[LSVT](https://github.com/PaddlePaddle/PaddleOCR/blob/develop/doc/doc_ch/datasets.md#1icdar2019-lsvt)街景数据集共3w张数据,训练中文检测模型的相关配置和预训练文件如下:
|模型|骨干网络|配置文件|预训练模型|
|-|-|-|-|
|超轻量中文模型|MobileNetV3|det_mv3_db.yml|[下载链接](https://paddleocr.bj.bcebos.com/ch_models/ch_det_mv3_db.tar)|
......@@ -122,7 +129,7 @@ PaddleOCR开源的文本识别算法列表:
- [x] Rosetta([paper](https://arxiv.org/abs/1910.05085))
- [x] STAR-Net([paper](http://www.bmva.org/bmvc/2016/papers/paper043/index.html))
- [x] RARE([paper](https://arxiv.org/abs/1603.03915v1))
- [ ] SRN([paper](https://arxiv.org/abs/2003.12294))(百度自研, coming soon)
- [x] SRN([paper](https://arxiv.org/abs/2003.12294))(百度自研)
参考[DTRB](https://arxiv.org/abs/1904.01906)文字识别训练和评估流程,使用MJSynth和SynthText两个文字识别数据集训练,在IIIT, SVT, IC03, IC13, IC15, SVTP, CUTE数据集上进行评估,算法效果如下:
......@@ -136,6 +143,10 @@ PaddleOCR开源的文本识别算法列表:
|STAR-Net|MobileNetV3|81.56%|rec_mv3_tps_bilstm_ctc|[下载链接](https://paddleocr.bj.bcebos.com/rec_mv3_tps_bilstm_ctc.tar)|
|RARE|Resnet34_vd|84.90%|rec_r34_vd_tps_bilstm_attn|[下载链接](https://paddleocr.bj.bcebos.com/rec_r34_vd_tps_bilstm_attn.tar)|
|RARE|MobileNetV3|83.32%|rec_mv3_tps_bilstm_attn|[下载链接](https://paddleocr.bj.bcebos.com/rec_mv3_tps_bilstm_attn.tar)|
|SRN|Resnet50_vd_fpn|88.33%|rec_r50fpn_vd_none_srn|[下载链接](https://paddleocr.bj.bcebos.com/SRN/rec_r50fpn_vd_none_srn.tar)|
**说明:** SRN模型使用了数据扰动方法对上述提到对两个训练集进行增广,增广后的数据可以在[百度网盘](todo)上下载。
原始论文使用两阶段训练平均精度为89.74%,PaddleOCR中使用one-stage训练,平均精度为88.33%。两种预训练权重均在[下载链接](https://paddleocr.bj.bcebos.com/SRN/rec_r50fpn_vd_none_srn.tar)中。
使用[LSVT](https://github.com/PaddlePaddle/PaddleOCR/blob/develop/doc/doc_ch/datasets.md#1icdar2019-lsvt)街景数据集根据真值将图crop出来30w数据,进行位置校准。此外基于LSVT语料生成500w合成数据训练中文模型,相关配置和预训练文件如下:
......
Global:
algorithm: SAST
use_gpu: true
epoch_num: 2000
log_smooth_window: 20
print_batch_step: 2
save_model_dir: ./output/det_sast/
save_epoch_step: 20
eval_batch_step: 5000
train_batch_size_per_card: 8
test_batch_size_per_card: 8
image_shape: [3, 512, 512]
reader_yml: ./configs/det/det_sast_icdar15_reader.yml
pretrain_weights: ./pretrain_models/ResNet50_vd_ssld_pretrained/
save_res_path: ./output/det_sast/predicts_sast.txt
checkpoints:
save_inference_dir:
Architecture:
function: ppocr.modeling.architectures.det_model,DetModel
Backbone:
function: ppocr.modeling.backbones.det_resnet_vd_sast,ResNet
layers: 50
Head:
function: ppocr.modeling.heads.det_sast_head,SASTHead
model_name: large
only_fpn_up: False
# with_cab: False
with_cab: True
Loss:
function: ppocr.modeling.losses.det_sast_loss,SASTLoss
Optimizer:
function: ppocr.optimizer,RMSProp
base_lr: 0.001
decay:
function: piecewise_decay
boundaries: [30000, 50000, 80000, 100000, 150000]
decay_rate: 0.3
PostProcess:
function: ppocr.postprocess.sast_postprocess,SASTPostProcess
score_thresh: 0.5
sample_pts_num: 2
nms_thresh: 0.2
expand_scale: 1.0
shrink_ratio_of_width: 0.3
\ No newline at end of file
Global:
algorithm: SAST
use_gpu: true
epoch_num: 2000
log_smooth_window: 20
print_batch_step: 2
save_model_dir: ./output/det_sast/
save_epoch_step: 20
eval_batch_step: 5000
train_batch_size_per_card: 8
test_batch_size_per_card: 1
image_shape: [3, 512, 512]
reader_yml: ./configs/det/det_sast_totaltext_reader.yml
pretrain_weights: ./pretrain_models/ResNet50_vd_ssld_pretrained/
save_res_path: ./output/det_sast/predicts_sast.txt
checkpoints:
save_inference_dir:
Architecture:
function: ppocr.modeling.architectures.det_model,DetModel
Backbone:
function: ppocr.modeling.backbones.det_resnet_vd_sast,ResNet
layers: 50
Head:
function: ppocr.modeling.heads.det_sast_head,SASTHead
model_name: large
only_fpn_up: False
# with_cab: False
with_cab: True
Loss:
function: ppocr.modeling.losses.det_sast_loss,SASTLoss
Optimizer:
function: ppocr.optimizer,RMSProp
base_lr: 0.001
decay:
function: piecewise_decay
boundaries: [30000, 50000, 80000, 100000, 150000]
decay_rate: 0.3
PostProcess:
function: ppocr.postprocess.sast_postprocess,SASTPostProcess
score_thresh: 0.5
sample_pts_num: 6
nms_thresh: 0.2
expand_scale: 1.2
shrink_ratio_of_width: 0.2
\ No newline at end of file
TrainReader:
reader_function: ppocr.data.det.dataset_traversal,TrainReader
process_function: ppocr.data.det.sast_process,SASTProcessTrain
num_workers: 8
img_set_dir: ./train_data/
label_file_path: [./train_data/icdar13/train_label_json.txt, ./train_data/icdar15/train_label_json.txt, ./train_data/icdar17_mlt_latin/train_label_json.txt, ./train_data/coco_text_icdar_4pts/train_label_json.txt]
data_ratio_list: [0.1, 0.45, 0.3, 0.15]
min_crop_side_ratio: 0.3
min_crop_size: 24
min_text_size: 4
max_text_size: 512
EvalReader:
reader_function: ppocr.data.det.dataset_traversal,EvalTestReader
process_function: ppocr.data.det.sast_process,SASTProcessTest
img_set_dir: ./train_data/icdar2015/text_localization/
label_file_path: ./train_data/icdar2015/text_localization/test_icdar2015_label.txt
max_side_len: 1536
TestReader:
reader_function: ppocr.data.det.dataset_traversal,EvalTestReader
process_function: ppocr.data.det.sast_process,SASTProcessTest
infer_img:
img_set_dir: ./train_data/icdar2015/text_localization/
label_file_path: ./train_data/icdar2015/text_localization/test_icdar2015_label.txt
do_eval: True
TrainReader:
reader_function: ppocr.data.det.dataset_traversal,TrainReader
process_function: ppocr.data.det.sast_process,SASTProcessTrain
num_workers: 8
img_set_dir: ./train_data/
label_file_path: [./train_data/art_latin_icdar_14pt/train_no_tt_test/train_label_json.txt, ./train_data/total_text_icdar_14pt/train/train_label_json.txt]
data_ratio_list: [0.5, 0.5]
min_crop_side_ratio: 0.3
min_crop_size: 24
min_text_size: 4
max_text_size: 512
EvalReader:
reader_function: ppocr.data.det.dataset_traversal,EvalTestReader
process_function: ppocr.data.det.sast_process,SASTProcessTest
img_set_dir: ./train_data/afs/
label_file_path: ./train_data/afs/total_text/test_label_json.txt
max_side_len: 768
TestReader:
reader_function: ppocr.data.det.dataset_traversal,EvalTestReader
process_function: ppocr.data.det.sast_process,SASTProcessTest
infer_img:
max_side_len: 768
Global:
algorithm: SRN
use_gpu: true
epoch_num: 72
log_smooth_window: 20
print_batch_step: 10
save_model_dir: output/rec_pvam_withrotate
save_epoch_step: 1
eval_batch_step: 8000
train_batch_size_per_card: 64
test_batch_size_per_card: 1
image_shape: [1, 64, 256]
max_text_length: 25
character_type: en
loss_type: srn
num_heads: 8
average_window: 0.15
max_average_window: 15625
min_average_window: 10000
reader_yml: ./configs/rec/rec_benchmark_reader.yml
pretrain_weights:
checkpoints:
save_inference_dir:
infer_img:
Architecture:
function: ppocr.modeling.architectures.rec_model,RecModel
Backbone:
function: ppocr.modeling.backbones.rec_resnet50_fpn,ResNet
layers: 50
Head:
function: ppocr.modeling.heads.rec_srn_all_head,SRNPredict
encoder_type: rnn
num_encoder_TUs: 2
num_decoder_TUs: 4
hidden_dims: 512
SeqRNN:
hidden_size: 256
Loss:
function: ppocr.modeling.losses.rec_srn_loss,SRNLoss
Optimizer:
function: ppocr.optimizer,AdamDecay
base_lr: 0.0001
beta1: 0.9
beta2: 0.999
#Thu Aug 22 15:05:37 CST 2019
#Wed Jul 22 23:48:44 CST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
......
......@@ -32,6 +32,9 @@
| loss_type | 设置 loss 类型 | ctc | 支持两种loss: ctc / attention |
| distort | 设置是否使用数据增强 | false | 设置为true时,将在训练时随机进行扰动,支持的扰动操作可阅读[img_tools.py](https://github.com/PaddlePaddle/PaddleOCR/blob/develop/ppocr/data/rec/img_tools.py) |
| use_space_char | 设置是否识别空格 | false | 仅在 character_type=ch 时支持空格 |
| average_window | ModelAverage优化器中的窗口长度计算比例 | 0.15 | 目前仅应用与SRN |
| max_average_window | 平均值计算窗口长度的最大值 | 15625 | 推荐设置为一轮训练中mini-batchs的数目|
| min_average_window | 平均值计算窗口长度的最小值 | 10000 | \ |
| reader_yml | 设置reader配置文件 | ./configs/rec/rec_icdar15_reader.yml | \ |
| pretrain_weights | 加载预训练模型路径 | ./pretrain_models/CRNN/best_accuracy | \ |
| checkpoints | 加载模型参数路径 | None | 用于中断后加载参数继续训练 |
......
# 更新
- 2020.8.16 开源文本检测算法[SAST](https://arxiv.org/abs/1908.05498)和文本识别算法[SRN](https://arxiv.org/abs/2003.12294)
- 2020.7.23 发布7月21日B站直播课回放和PPT,PaddleOCR开源大礼包全面解读,[获取地址](https://aistudio.baidu.com/aistudio/course/introduce/1519)
- 2020.7.15 添加基于EasyEdge和Paddle-Lite的移动端DEMO,支持iOS和Android系统
- 2020.7.15 完善预测部署,添加基于C++预测引擎推理、服务化部署和端侧部署方案,以及超轻量级中文OCR模型预测耗时Benchmark
......
# RECENT UPDATES
- 2020.8.16 Release text detection algorithm [SAST](https://arxiv.org/abs/1908.05498) and text recognition algorithm [SRN](https://arxiv.org/abs/2003.12294)
- 2020.7.23, Release the playback and PPT of live class on BiliBili station, PaddleOCR Introduction, [address](https://aistudio.baidu.com/aistudio/course/introduce/1519)
- 2020.7.15, Add mobile App demo , support both iOS and Android ( based on easyedge and Paddle Lite)
- 2020.7.15, Improve the deployment ability, add the C + + inference , serving deployment. In addtion, the benchmarks of the ultra-lightweight Chinese OCR model are provided.
......
# Version: 1.0.0
FROM hub.baidubce.com/paddlepaddle/paddle:latest-gpu-cuda9.0-cudnn7-dev
# PaddleOCR base on Python3.7
RUN pip3.7 install --upgrade pip -i https://pypi.tuna.tsinghua.edu.cn/simple
RUN python3.7 -m pip install paddlepaddle==1.7.2 -i https://pypi.tuna.tsinghua.edu.cn/simple
RUN pip3.7 install paddlehub --upgrade -i https://pypi.tuna.tsinghua.edu.cn/simple
RUN git clone https://gitee.com/PaddlePaddle/PaddleOCR
WORKDIR /PaddleOCR
RUN pip3.7 install -r requirments.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
RUN mkdir -p /PaddleOCR/inference
# Download orc detect model(light version). if you want to change normal version, you can change ch_det_mv3_db_infer to ch_det_r50_vd_db_infer, also remember change det_model_dir in deploy/hubserving/ocr_system/params.py)
ADD https://paddleocr.bj.bcebos.com/ch_models/ch_det_mv3_db_infer.tar /PaddleOCR/inference
RUN tar xf /PaddleOCR/inference/ch_det_mv3_db_infer.tar -C /PaddleOCR/inference
# Download orc recognition model(light version). If you want to change normal version, you can change ch_rec_mv3_crnn_infer to ch_rec_r34_vd_crnn_enhance_infer, also remember change rec_model_dir in deploy/hubserving/ocr_system/params.py)
ADD https://paddleocr.bj.bcebos.com/ch_models/ch_rec_mv3_crnn_infer.tar /PaddleOCR/inference
RUN tar xf /PaddleOCR/inference/ch_rec_mv3_crnn_infer.tar -C /PaddleOCR/inference
EXPOSE 8866
CMD ["/bin/bash","-c","export PYTHONPATH=. && hub install deploy/hubserving/ocr_system/ && hub serving start -m ocr_system"]
\ No newline at end of file
# Version: 1.0.0
FROM hub.baidubce.com/paddlepaddle/paddle:latest-gpu-cuda10.0-cudnn7-dev
# PaddleOCR base on Python3.7
RUN pip3.7 install --upgrade pip -i https://pypi.tuna.tsinghua.edu.cn/simple
RUN python3.7 -m pip install paddlepaddle-gpu==1.7.2.post107 -i https://pypi.tuna.tsinghua.edu.cn/simple
RUN pip3.7 install paddlehub --upgrade -i https://pypi.tuna.tsinghua.edu.cn/simple
RUN git clone https://gitee.com/PaddlePaddle/PaddleOCR
WORKDIR /home/PaddleOCR
RUN pip3.7 install -r requirments.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
RUN mkdir -p /PaddleOCR/inference
# Download orc detect model(light version). if you want to change normal version, you can change ch_det_mv3_db_infer to ch_det_r50_vd_db_infer, also remember change det_model_dir in deploy/hubserving/ocr_system/params.py)
ADD https://paddleocr.bj.bcebos.com/ch_models/ch_det_mv3_db_infer.tar /PaddleOCR/inference
RUN tar xf /PaddleOCR/inference/ch_det_mv3_db_infer.tar -C /PaddleOCR/inference
# Download orc recognition model(light version). If you want to change normal version, you can change ch_rec_mv3_crnn_infer to ch_rec_r34_vd_crnn_enhance_infer, also remember change rec_model_dir in deploy/hubserving/ocr_system/params.py)
ADD https://paddleocr.bj.bcebos.com/ch_models/ch_rec_mv3_crnn_infer.tar /PaddleOCR/inference
RUN tar xf /PaddleOCR/inference/ch_rec_mv3_crnn_infer.tar -C /PaddleOCR/inference
EXPOSE 8866
CMD ["/bin/bash","-c","export PYTHONPATH=. && hub install deploy/hubserving/ocr_system/ && hub serving start -m ocr_system"]
\ No newline at end of file
# Docker化部署服务
在日常项目应用中,相信大家一般都会希望能通过Docker技术,把PaddleOCR服务打包成一个镜像,以便在Docker或k8s环境里,快速发布上线使用。
本文将提供一些标准化的代码来实现这样的目标。大家通过如下步骤可以把PaddleOCR项目快速发布成可调用的Restful API服务。(目前暂时先实现了基于HubServing模式的部署,后续作者计划增加PaddleServing模式的部署)
## 1.实施前提准备
需要先完成如下基本组件的安装:
a. Docker环境
b. 显卡驱动和CUDA 10.0+(GPU)
c. NVIDIA Container Toolkit(GPU,Docker 19.03以上版本可以跳过此步)
d. cuDNN 7.6+(GPU)
## 2.制作镜像
a.下载PaddleOCR项目代码
```
git clone https://github.com/PaddlePaddle/PaddleOCR.git
```
b.切换至Dockerfile目录(注:需要区分cpu或gpu版本,下文以cpu为例,gpu版本需要替换一下关键字即可)
```
cd docker/cpu
```
c.生成镜像
```
docker build -t paddleocr:cpu .
```
## 3.启动Docker容器
a. CPU 版本
```
sudo docker run -dp 8866:8866 --name paddle_ocr paddleocr:cpu
```
b. GPU 版本 (通过NVIDIA Container Toolkit)
```
sudo nvidia-docker run -dp 8866:8866 --name paddle_ocr paddleocr:gpu
```
c. GPU 版本 (Docker 19.03以上版本,可以直接用如下命令)
```
sudo docker run -dp 8866:8866 --gpus all --name paddle_ocr paddleocr:gpu
```
d. 检查服务运行情况(出现:Successfully installed ocr_system和Running on http://0.0.0.0:8866/等信息,表示运行成功)
```
docker logs -f paddle_ocr
```
## 4.测试服务
a. 计算待识别图片的Base64编码(如果只是测试一下效果,可以通过免费的在线工具实现,如:http://tool.chinaz.com/tools/imgtobase/)
b. 发送服务请求(可参见sample_request.txt中的值)
```
curl -H "Content-Type:application/json" -X POST --data "{\"images\": [\"填入图片Base64编码(需要删除'data:image/jpg;base64,')\"]}" http://localhost:8866/predict/ocr_system
```
c. 返回结果(如果调用成功,会返回如下结果)
```
{"msg":"","results":[[{"confidence":0.8403433561325073,"text":"约定","text_region":[[345,377],[641,390],[634,540],[339,528]]},{"confidence":0.8131805658340454,"text":"最终相遇","text_region":[[356,532],[624,530],[624,596],[356,598]]}]],"status":"0"}
```
curl -H "Content-Type:application/json" -X POST --data "{\"images\": [\"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\"]}" http://localhost:8866/predict/ocr_system
\ No newline at end of file
......@@ -31,22 +31,27 @@ class TrainReader(object):
def __init__(self, params):
self.num_workers = params['num_workers']
self.label_file_path = params['label_file_path']
print(self.label_file_path)
self.use_mul_data = False
if isinstance(self.label_file_path, list):
self.use_mul_data = True
self.data_ratio_list = params['data_ratio_list']
self.batch_size = params['train_batch_size_per_card']
assert 'process_function' in params,\
"absence process_function in Reader"
self.process = create_module(params['process_function'])(params)
def __call__(self, process_id):
with open(self.label_file_path, "rb") as fin:
label_infor_list = fin.readlines()
img_num = len(label_infor_list)
img_id_list = list(range(img_num))
if sys.platform == "win32" and self.num_workers != 1:
print("multiprocess is not fully compatible with Windows."
"num_workers will be 1.")
self.num_workers = 1
def sample_iter_reader():
with open(self.label_file_path, "rb") as fin:
label_infor_list = fin.readlines()
img_num = len(label_infor_list)
img_id_list = list(range(img_num))
random.shuffle(img_id_list)
if sys.platform == "win32" and self.num_workers != 1:
print("multiprocess is not fully compatible with Windows."
"num_workers will be 1.")
self.num_workers = 1
for img_id in range(process_id, img_num, self.num_workers):
label_infor = label_infor_list[img_id_list[img_id]]
outs = self.process(label_infor)
......@@ -54,13 +59,64 @@ class TrainReader(object):
continue
yield outs
def sample_iter_reader_mul():
batch_size = 1000
data_source_list = self.label_file_path
batch_size_list = list(map(int, [max(1.0, batch_size * x) for x in self.data_ratio_list]))
print(self.data_ratio_list, batch_size_list)
data_filename_list, data_size_list, fetch_record_list = [], [], []
for data_source in data_source_list:
image_files = open(data_source, "rb").readlines()
random.shuffle(image_files)
data_filename_list.append(image_files)
data_size_list.append(len(image_files))
fetch_record_list.append(0)
image_batch = []
# get a batch of img_fns and poly_fns
for i in range(0, len(batch_size_list)):
bs = batch_size_list[i]
ds = data_size_list[i]
image_names = data_filename_list[i]
fetch_record = fetch_record_list[i]
data_path = data_source_list[i]
for j in range(fetch_record, fetch_record + bs):
index = j % ds
image_batch.append(image_names[index])
if (fetch_record + bs) > ds:
fetch_record_list[i] = 0
random.shuffle(data_filename_list[i])
else:
fetch_record_list[i] = fetch_record + bs
if sys.platform == "win32":
print("multiprocess is not fully compatible with Windows."
"num_workers will be 1.")
self.num_workers = 1
for label_infor in image_batch:
outs = self.process(label_infor)
if outs is None:
continue
yield outs
def batch_iter_reader():
batch_outs = []
for outs in sample_iter_reader():
batch_outs.append(outs)
if len(batch_outs) == self.batch_size:
yield batch_outs
batch_outs = []
if self.use_mul_data:
print("Sample date from multiple datasets!")
for outs in sample_iter_reader_mul():
batch_outs.append(outs)
if len(batch_outs) == self.batch_size:
yield batch_outs
batch_outs = []
else:
for outs in sample_iter_reader():
batch_outs.append(outs)
if len(batch_outs) == self.batch_size:
yield batch_outs
batch_outs = []
return batch_iter_reader
......
#copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve.
#
#Licensed under the Apache License, Version 2.0 (the "License");
#you may not use this file except in compliance with the License.
#You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
#Unless required by applicable law or agreed to in writing, software
#distributed under the License is distributed on an "AS IS" BASIS,
#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#See the License for the specific language governing permissions and
#limitations under the License.
import math
import cv2
import numpy as np
import json
class SASTProcessTrain(object):
"""
SAST process function for training
"""
def __init__(self, params):
self.img_set_dir = params['img_set_dir']
self.min_crop_side_ratio = params['min_crop_side_ratio']
self.min_crop_size = params['min_crop_size']
image_shape = params['image_shape']
self.input_size = image_shape[1]
self.min_text_size = params['min_text_size']
self.max_text_size = params['max_text_size']
def convert_label_infor(self, label_infor):
label_infor = label_infor.decode()
label_infor = label_infor.encode('utf-8').decode('utf-8-sig')
substr = label_infor.strip("\n").split("\t")
img_path = self.img_set_dir + substr[0]
label = json.loads(substr[1])
nBox = len(label)
wordBBs, txts, txt_tags = [], [], []
for bno in range(0, nBox):
wordBB = label[bno]['points']
txt = label[bno]['transcription']
wordBBs.append(wordBB)
txts.append(txt)
if txt == '###':
txt_tags.append(True)
else:
txt_tags.append(False)
wordBBs = np.array(wordBBs, dtype=np.float32)
txt_tags = np.array(txt_tags, dtype=np.bool)
return img_path, wordBBs, txt_tags, txts
def quad_area(self, poly):
"""
compute area of a polygon
:param poly:
:return:
"""
edge = [
(poly[1][0] - poly[0][0]) * (poly[1][1] + poly[0][1]),
(poly[2][0] - poly[1][0]) * (poly[2][1] + poly[1][1]),
(poly[3][0] - poly[2][0]) * (poly[3][1] + poly[2][1]),
(poly[0][0] - poly[3][0]) * (poly[0][1] + poly[3][1])
]
return np.sum(edge) / 2.
def gen_quad_from_poly(self, poly):
"""
Generate min area quad from poly.
"""
point_num = poly.shape[0]
min_area_quad = np.zeros((4, 2), dtype=np.float32)
if True:
rect = cv2.minAreaRect(poly.astype(np.int32)) # (center (x,y), (width, height), angle of rotation)
center_point = rect[0]
box = np.array(cv2.boxPoints(rect))
first_point_idx = 0
min_dist = 1e4
for i in range(4):
dist = np.linalg.norm(box[(i + 0) % 4] - poly[0]) + \
np.linalg.norm(box[(i + 1) % 4] - poly[point_num // 2 - 1]) + \
np.linalg.norm(box[(i + 2) % 4] - poly[point_num // 2]) + \
np.linalg.norm(box[(i + 3) % 4] - poly[-1])
if dist < min_dist:
min_dist = dist
first_point_idx = i
for i in range(4):
min_area_quad[i] = box[(first_point_idx + i) % 4]
return min_area_quad
def check_and_validate_polys(self, polys, tags, xxx_todo_changeme):
"""
check so that the text poly is in the same direction,
and also filter some invalid polygons
:param polys:
:param tags:
:return:
"""
(h, w) = xxx_todo_changeme
if polys.shape[0] == 0:
return polys, np.array([]), np.array([])
polys[:, :, 0] = np.clip(polys[:, :, 0], 0, w - 1)
polys[:, :, 1] = np.clip(polys[:, :, 1], 0, h - 1)
validated_polys = []
validated_tags = []
hv_tags = []
for poly, tag in zip(polys, tags):
quad = self.gen_quad_from_poly(poly)
p_area = self.quad_area(quad)
if abs(p_area) < 1:
print('invalid poly')
continue
if p_area > 0:
if tag == False:
print('poly in wrong direction')
tag = True # reversed cases should be ignore
poly = poly[(0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1), :]
quad = quad[(0, 3, 2, 1), :]
len_w = np.linalg.norm(quad[0] - quad[1]) + np.linalg.norm(quad[3] - quad[2])
len_h = np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[1] - quad[2])
hv_tag = 1
if len_w * 2.0 < len_h:
hv_tag = 0
validated_polys.append(poly)
validated_tags.append(tag)
hv_tags.append(hv_tag)
return np.array(validated_polys), np.array(validated_tags), np.array(hv_tags)
def crop_area(self, im, polys, tags, hv_tags, txts, crop_background=False, max_tries=25):
"""
make random crop from the input image
:param im:
:param polys:
:param tags:
:param crop_background:
:param max_tries: 50 -> 25
:return:
"""
h, w, _ = im.shape
pad_h = h // 10
pad_w = w // 10
h_array = np.zeros((h + pad_h * 2), dtype=np.int32)
w_array = np.zeros((w + pad_w * 2), dtype=np.int32)
for poly in polys:
poly = np.round(poly, decimals=0).astype(np.int32)
minx = np.min(poly[:, 0])
maxx = np.max(poly[:, 0])
w_array[minx + pad_w: maxx + pad_w] = 1
miny = np.min(poly[:, 1])
maxy = np.max(poly[:, 1])
h_array[miny + pad_h: maxy + pad_h] = 1
# ensure the cropped area not across a text
h_axis = np.where(h_array == 0)[0]
w_axis = np.where(w_array == 0)[0]
if len(h_axis) == 0 or len(w_axis) == 0:
return im, polys, tags, hv_tags, txts
for i in range(max_tries):
xx = np.random.choice(w_axis, size=2)
xmin = np.min(xx) - pad_w
xmax = np.max(xx) - pad_w
xmin = np.clip(xmin, 0, w - 1)
xmax = np.clip(xmax, 0, w - 1)
yy = np.random.choice(h_axis, size=2)
ymin = np.min(yy) - pad_h
ymax = np.max(yy) - pad_h
ymin = np.clip(ymin, 0, h - 1)
ymax = np.clip(ymax, 0, h - 1)
# if xmax - xmin < ARGS.min_crop_side_ratio * w or \
# ymax - ymin < ARGS.min_crop_side_ratio * h:
if xmax - xmin < self.min_crop_size or \
ymax - ymin < self.min_crop_size:
# area too small
continue
if polys.shape[0] != 0:
poly_axis_in_area = (polys[:, :, 0] >= xmin) & (polys[:, :, 0] <= xmax) \
& (polys[:, :, 1] >= ymin) & (polys[:, :, 1] <= ymax)
selected_polys = np.where(np.sum(poly_axis_in_area, axis=1) == 4)[0]
else:
selected_polys = []
if len(selected_polys) == 0:
# no text in this area
if crop_background:
txts_tmp = []
for selected_poly in selected_polys:
txts_tmp.append(txts[selected_poly])
txts = txts_tmp
return im[ymin : ymax + 1, xmin : xmax + 1, :], \
polys[selected_polys], tags[selected_polys], hv_tags[selected_polys], txts
else:
continue
im = im[ymin: ymax + 1, xmin: xmax + 1, :]
polys = polys[selected_polys]
tags = tags[selected_polys]
hv_tags = hv_tags[selected_polys]
txts_tmp = []
for selected_poly in selected_polys:
txts_tmp.append(txts[selected_poly])
txts = txts_tmp
polys[:, :, 0] -= xmin
polys[:, :, 1] -= ymin
return im, polys, tags, hv_tags, txts
return im, polys, tags, hv_tags, txts
def generate_direction_map(self, poly_quads, direction_map):
"""
"""
width_list = []
height_list = []
for quad in poly_quads:
quad_w = (np.linalg.norm(quad[0] - quad[1]) + np.linalg.norm(quad[2] - quad[3])) / 2.0
quad_h = (np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[2] - quad[1])) / 2.0
width_list.append(quad_w)
height_list.append(quad_h)
norm_width = max(sum(width_list) / (len(width_list) + 1e-6), 1.0)
average_height = max(sum(height_list) / (len(height_list) + 1e-6), 1.0)
for quad in poly_quads:
direct_vector_full = ((quad[1] + quad[2]) - (quad[0] + quad[3])) / 2.0
direct_vector = direct_vector_full / (np.linalg.norm(direct_vector_full) + 1e-6) * norm_width
direction_label = tuple(map(float, [direct_vector[0], direct_vector[1], 1.0 / (average_height + 1e-6)]))
cv2.fillPoly(direction_map, quad.round().astype(np.int32)[np.newaxis, :, :], direction_label)
return direction_map
def calculate_average_height(self, poly_quads):
"""
"""
height_list = []
for quad in poly_quads:
quad_h = (np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[2] - quad[1])) / 2.0
height_list.append(quad_h)
average_height = max(sum(height_list) / len(height_list), 1.0)
return average_height
def generate_tcl_label(self, hw, polys, tags, ds_ratio,
tcl_ratio=0.3, shrink_ratio_of_width=0.15):
"""
Generate polygon.
"""
h, w = hw
h, w = int(h * ds_ratio), int(w * ds_ratio)
polys = polys * ds_ratio
score_map = np.zeros((h, w,), dtype=np.float32)
tbo_map = np.zeros((h, w, 5), dtype=np.float32)
training_mask = np.ones((h, w,), dtype=np.float32)
direction_map = np.ones((h, w, 3)) * np.array([0, 0, 1]).reshape([1, 1, 3]).astype(np.float32)
for poly_idx, poly_tag in enumerate(zip(polys, tags)):
poly = poly_tag[0]
tag = poly_tag[1]
# generate min_area_quad
min_area_quad, center_point = self.gen_min_area_quad_from_poly(poly)
min_area_quad_h = 0.5 * (np.linalg.norm(min_area_quad[0] - min_area_quad[3]) +
np.linalg.norm(min_area_quad[1] - min_area_quad[2]))
min_area_quad_w = 0.5 * (np.linalg.norm(min_area_quad[0] - min_area_quad[1]) +
np.linalg.norm(min_area_quad[2] - min_area_quad[3]))
if min(min_area_quad_h, min_area_quad_w) < self.min_text_size * ds_ratio \
or min(min_area_quad_h, min_area_quad_w) > self.max_text_size * ds_ratio:
continue
if tag:
# continue
cv2.fillPoly(training_mask, poly.astype(np.int32)[np.newaxis, :, :], 0.15)
else:
tcl_poly = self.poly2tcl(poly, tcl_ratio)
tcl_quads = self.poly2quads(tcl_poly)
poly_quads = self.poly2quads(poly)
# stcl map
stcl_quads, quad_index = self.shrink_poly_along_width(tcl_quads, shrink_ratio_of_width=shrink_ratio_of_width,
expand_height_ratio=1.0 / tcl_ratio)
# generate tcl map
cv2.fillPoly(score_map, np.round(stcl_quads).astype(np.int32), 1.0)
# generate tbo map
for idx, quad in enumerate(stcl_quads):
quad_mask = np.zeros((h, w), dtype=np.float32)
quad_mask = cv2.fillPoly(quad_mask, np.round(quad[np.newaxis, :, :]).astype(np.int32), 1.0)
tbo_map = self.gen_quad_tbo(poly_quads[quad_index[idx]], quad_mask, tbo_map)
return score_map, tbo_map, training_mask
def generate_tvo_and_tco(self, hw, polys, tags, tcl_ratio=0.3, ds_ratio=0.25):
"""
Generate tcl map, tvo map and tbo map.
"""
h, w = hw
h, w = int(h * ds_ratio), int(w * ds_ratio)
polys = polys * ds_ratio
poly_mask = np.zeros((h, w), dtype=np.float32)
tvo_map = np.ones((9, h, w), dtype=np.float32)
tvo_map[0:-1:2] = np.tile(np.arange(0, w), (h, 1))
tvo_map[1:-1:2] = np.tile(np.arange(0, w), (h, 1)).T
poly_tv_xy_map = np.zeros((8, h, w), dtype=np.float32)
# tco map
tco_map = np.ones((3, h, w), dtype=np.float32)
tco_map[0] = np.tile(np.arange(0, w), (h, 1))
tco_map[1] = np.tile(np.arange(0, w), (h, 1)).T
poly_tc_xy_map = np.zeros((2, h, w), dtype=np.float32)
poly_short_edge_map = np.ones((h, w), dtype=np.float32)
for poly, poly_tag in zip(polys, tags):
if poly_tag == True:
continue
# adjust point order for vertical poly
poly = self.adjust_point(poly)
# generate min_area_quad
min_area_quad, center_point = self.gen_min_area_quad_from_poly(poly)
min_area_quad_h = 0.5 * (np.linalg.norm(min_area_quad[0] - min_area_quad[3]) +
np.linalg.norm(min_area_quad[1] - min_area_quad[2]))
min_area_quad_w = 0.5 * (np.linalg.norm(min_area_quad[0] - min_area_quad[1]) +
np.linalg.norm(min_area_quad[2] - min_area_quad[3]))
# generate tcl map and text, 128 * 128
tcl_poly = self.poly2tcl(poly, tcl_ratio)
# generate poly_tv_xy_map
for idx in range(4):
cv2.fillPoly(poly_tv_xy_map[2 * idx],
np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32),
float(min(max(min_area_quad[idx, 0], 0), w)))
cv2.fillPoly(poly_tv_xy_map[2 * idx + 1],
np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32),
float(min(max(min_area_quad[idx, 1], 0), h)))
# generate poly_tc_xy_map
for idx in range(2):
cv2.fillPoly(poly_tc_xy_map[idx],
np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), float(center_point[idx]))
# generate poly_short_edge_map
cv2.fillPoly(poly_short_edge_map,
np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32),
float(max(min(min_area_quad_h, min_area_quad_w), 1.0)))
# generate poly_mask and training_mask
cv2.fillPoly(poly_mask, np.round(tcl_poly[np.newaxis, :, :]).astype(np.int32), 1)
tvo_map *= poly_mask
tvo_map[:8] -= poly_tv_xy_map
tvo_map[-1] /= poly_short_edge_map
tvo_map = tvo_map.transpose((1, 2, 0))
tco_map *= poly_mask
tco_map[:2] -= poly_tc_xy_map
tco_map[-1] /= poly_short_edge_map
tco_map = tco_map.transpose((1, 2, 0))
return tvo_map, tco_map
def adjust_point(self, poly):
"""
adjust point order.
"""
point_num = poly.shape[0]
if point_num == 4:
len_1 = np.linalg.norm(poly[0] - poly[1])
len_2 = np.linalg.norm(poly[1] - poly[2])
len_3 = np.linalg.norm(poly[2] - poly[3])
len_4 = np.linalg.norm(poly[3] - poly[0])
if (len_1 + len_3) * 1.5 < (len_2 + len_4):
poly = poly[[1, 2, 3, 0], :]
elif point_num > 4:
vector_1 = poly[0] - poly[1]
vector_2 = poly[1] - poly[2]
cos_theta = np.dot(vector_1, vector_2) / (np.linalg.norm(vector_1) * np.linalg.norm(vector_2) + 1e-6)
theta = np.arccos(np.round(cos_theta, decimals=4))
if abs(theta) > (70 / 180 * math.pi):
index = list(range(1, point_num)) + [0]
poly = poly[np.array(index), :]
return poly
def gen_min_area_quad_from_poly(self, poly):
"""
Generate min area quad from poly.
"""
point_num = poly.shape[0]
min_area_quad = np.zeros((4, 2), dtype=np.float32)
if point_num == 4:
min_area_quad = poly
center_point = np.sum(poly, axis=0) / 4
else:
rect = cv2.minAreaRect(poly.astype(np.int32)) # (center (x,y), (width, height), angle of rotation)
center_point = rect[0]
box = np.array(cv2.boxPoints(rect))
first_point_idx = 0
min_dist = 1e4
for i in range(4):
dist = np.linalg.norm(box[(i + 0) % 4] - poly[0]) + \
np.linalg.norm(box[(i + 1) % 4] - poly[point_num // 2 - 1]) + \
np.linalg.norm(box[(i + 2) % 4] - poly[point_num // 2]) + \
np.linalg.norm(box[(i + 3) % 4] - poly[-1])
if dist < min_dist:
min_dist = dist
first_point_idx = i
for i in range(4):
min_area_quad[i] = box[(first_point_idx + i) % 4]
return min_area_quad, center_point
def shrink_quad_along_width(self, quad, begin_width_ratio=0., end_width_ratio=1.):
"""
Generate shrink_quad_along_width.
"""
ratio_pair = np.array([[begin_width_ratio], [end_width_ratio]], dtype=np.float32)
p0_1 = quad[0] + (quad[1] - quad[0]) * ratio_pair
p3_2 = quad[3] + (quad[2] - quad[3]) * ratio_pair
return np.array([p0_1[0], p0_1[1], p3_2[1], p3_2[0]])
def shrink_poly_along_width(self, quads, shrink_ratio_of_width, expand_height_ratio=1.0):
"""
shrink poly with given length.
"""
upper_edge_list = []
def get_cut_info(edge_len_list, cut_len):
for idx, edge_len in enumerate(edge_len_list):
cut_len -= edge_len
if cut_len <= 0.000001:
ratio = (cut_len + edge_len_list[idx]) / edge_len_list[idx]
return idx, ratio
for quad in quads:
upper_edge_len = np.linalg.norm(quad[0] - quad[1])
upper_edge_list.append(upper_edge_len)
# length of left edge and right edge.
left_length = np.linalg.norm(quads[0][0] - quads[0][3]) * expand_height_ratio
right_length = np.linalg.norm(quads[-1][1] - quads[-1][2]) * expand_height_ratio
shrink_length = min(left_length, right_length, sum(upper_edge_list)) * shrink_ratio_of_width
# shrinking length
upper_len_left = shrink_length
upper_len_right = sum(upper_edge_list) - shrink_length
left_idx, left_ratio = get_cut_info(upper_edge_list, upper_len_left)
left_quad = self.shrink_quad_along_width(quads[left_idx], begin_width_ratio=left_ratio, end_width_ratio=1)
right_idx, right_ratio = get_cut_info(upper_edge_list, upper_len_right)
right_quad = self.shrink_quad_along_width(quads[right_idx], begin_width_ratio=0, end_width_ratio=right_ratio)
out_quad_list = []
if left_idx == right_idx:
out_quad_list.append([left_quad[0], right_quad[1], right_quad[2], left_quad[3]])
else:
out_quad_list.append(left_quad)
for idx in range(left_idx + 1, right_idx):
out_quad_list.append(quads[idx])
out_quad_list.append(right_quad)
return np.array(out_quad_list), list(range(left_idx, right_idx + 1))
def vector_angle(self, A, B):
"""
Calculate the angle between vector AB and x-axis positive direction.
"""
AB = np.array([B[1] - A[1], B[0] - A[0]])
return np.arctan2(*AB)
def theta_line_cross_point(self, theta, point):
"""
Calculate the line through given point and angle in ax + by + c =0 form.
"""
x, y = point
cos = np.cos(theta)
sin = np.sin(theta)
return [sin, -cos, cos * y - sin * x]
def line_cross_two_point(self, A, B):
"""
Calculate the line through given point A and B in ax + by + c =0 form.
"""
angle = self.vector_angle(A, B)
return self.theta_line_cross_point(angle, A)
def average_angle(self, poly):
"""
Calculate the average angle between left and right edge in given poly.
"""
p0, p1, p2, p3 = poly
angle30 = self.vector_angle(p3, p0)
angle21 = self.vector_angle(p2, p1)
return (angle30 + angle21) / 2
def line_cross_point(self, line1, line2):
"""
line1 and line2 in 0=ax+by+c form, compute the cross point of line1 and line2
"""
a1, b1, c1 = line1
a2, b2, c2 = line2
d = a1 * b2 - a2 * b1
if d == 0:
#print("line1", line1)
#print("line2", line2)
print('Cross point does not exist')
return np.array([0, 0], dtype=np.float32)
else:
x = (b1 * c2 - b2 * c1) / d
y = (a2 * c1 - a1 * c2) / d
return np.array([x, y], dtype=np.float32)
def quad2tcl(self, poly, ratio):
"""
Generate center line by poly clock-wise point. (4, 2)
"""
ratio_pair = np.array([[0.5 - ratio / 2], [0.5 + ratio / 2]], dtype=np.float32)
p0_3 = poly[0] + (poly[3] - poly[0]) * ratio_pair
p1_2 = poly[1] + (poly[2] - poly[1]) * ratio_pair
return np.array([p0_3[0], p1_2[0], p1_2[1], p0_3[1]])
def poly2tcl(self, poly, ratio):
"""
Generate center line by poly clock-wise point.
"""
ratio_pair = np.array([[0.5 - ratio / 2], [0.5 + ratio / 2]], dtype=np.float32)
tcl_poly = np.zeros_like(poly)
point_num = poly.shape[0]
for idx in range(point_num // 2):
point_pair = poly[idx] + (poly[point_num - 1 - idx] - poly[idx]) * ratio_pair
tcl_poly[idx] = point_pair[0]
tcl_poly[point_num - 1 - idx] = point_pair[1]
return tcl_poly
def gen_quad_tbo(self, quad, tcl_mask, tbo_map):
"""
Generate tbo_map for give quad.
"""
# upper and lower line function: ax + by + c = 0;
up_line = self.line_cross_two_point(quad[0], quad[1])
lower_line = self.line_cross_two_point(quad[3], quad[2])
quad_h = 0.5 * (np.linalg.norm(quad[0] - quad[3]) + np.linalg.norm(quad[1] - quad[2]))
quad_w = 0.5 * (np.linalg.norm(quad[0] - quad[1]) + np.linalg.norm(quad[2] - quad[3]))
# average angle of left and right line.
angle = self.average_angle(quad)
xy_in_poly = np.argwhere(tcl_mask == 1)
for y, x in xy_in_poly:
point = (x, y)
line = self.theta_line_cross_point(angle, point)
cross_point_upper = self.line_cross_point(up_line, line)
cross_point_lower = self.line_cross_point(lower_line, line)
##FIX, offset reverse
upper_offset_x, upper_offset_y = cross_point_upper - point
lower_offset_x, lower_offset_y = cross_point_lower - point
tbo_map[y, x, 0] = upper_offset_y
tbo_map[y, x, 1] = upper_offset_x
tbo_map[y, x, 2] = lower_offset_y
tbo_map[y, x, 3] = lower_offset_x
tbo_map[y, x, 4] = 1.0 / max(min(quad_h, quad_w), 1.0) * 2
return tbo_map
def poly2quads(self, poly):
"""
Split poly into quads.
"""
quad_list = []
point_num = poly.shape[0]
# point pair
point_pair_list = []
for idx in range(point_num // 2):
point_pair = [poly[idx], poly[point_num - 1 - idx]]
point_pair_list.append(point_pair)
quad_num = point_num // 2 - 1
for idx in range(quad_num):
# reshape and adjust to clock-wise
quad_list.append((np.array(point_pair_list)[[idx, idx + 1]]).reshape(4, 2)[[0, 2, 3, 1]])
return np.array(quad_list)
def extract_polys(self, poly_txt_path):
"""
Read text_polys, txt_tags, txts from give txt file.
"""
text_polys, txt_tags, txts = [], [], []
with open(poly_txt_path) as f:
for line in f.readlines():
poly_str, txt = line.strip().split('\t')
poly = map(float, poly_str.split(','))
text_polys.append(np.array(poly, dtype=np.float32).reshape(-1, 2))
txts.append(txt)
if txt == '###':
txt_tags.append(True)
else:
txt_tags.append(False)
return np.array(map(np.array, text_polys)), \
np.array(txt_tags, dtype=np.bool), txts
def __call__(self, label_infor):
infor = self.convert_label_infor(label_infor)
im_path, text_polys, text_tags, text_strs = infor
im = cv2.imread(im_path)
if im is None:
return None
if text_polys.shape[0] == 0:
return None
h, w, _ = im.shape
text_polys, text_tags, hv_tags = self.check_and_validate_polys(text_polys, text_tags, (h, w))
if text_polys.shape[0] == 0:
return None
#set aspect ratio and keep area fix
asp_scales = np.arange(1.0, 1.55, 0.1)
asp_scale = np.random.choice(asp_scales)
if np.random.rand() < 0.5:
asp_scale = 1.0 / asp_scale
asp_scale = math.sqrt(asp_scale)
asp_wx = asp_scale
asp_hy = 1.0 / asp_scale
im = cv2.resize(im, dsize=None, fx=asp_wx, fy=asp_hy)
text_polys[:, :, 0] *= asp_wx
text_polys[:, :, 1] *= asp_hy
h, w, _ = im.shape
if max(h, w) > 2048:
rd_scale = 2048.0 / max(h, w)
im = cv2.resize(im, dsize=None, fx=rd_scale, fy=rd_scale)
text_polys *= rd_scale
h, w, _ = im.shape
if min(h, w) < 16:
return None
#no background
im, text_polys, text_tags, hv_tags, text_strs = self.crop_area(im, \
text_polys, text_tags, hv_tags, text_strs, crop_background=False)
if text_polys.shape[0] == 0:
return None
#continue for all ignore case
if np.sum((text_tags * 1.0)) >= text_tags.size:
return None
new_h, new_w, _ = im.shape
if (new_h is None) or (new_w is None):
return None
#resize image
std_ratio = float(self.input_size) / max(new_w, new_h)
rand_scales = np.array([0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0, 1.0, 1.0, 1.0, 1.0])
rz_scale = std_ratio * np.random.choice(rand_scales)
im = cv2.resize(im, dsize=None, fx=rz_scale, fy=rz_scale)
text_polys[:, :, 0] *= rz_scale
text_polys[:, :, 1] *= rz_scale
#add gaussian blur
if np.random.rand() < 0.1 * 0.5:
ks = np.random.permutation(5)[0] + 1
ks = int(ks/2)*2 + 1
im = cv2.GaussianBlur(im, ksize=(ks, ks), sigmaX=0, sigmaY=0)
#add brighter
if np.random.rand() < 0.1 * 0.5:
im = im * (1.0 + np.random.rand() * 0.5)
im = np.clip(im, 0.0, 255.0)
#add darker
if np.random.rand() < 0.1 * 0.5:
im = im * (1.0 - np.random.rand() * 0.5)
im = np.clip(im, 0.0, 255.0)
# Padding the im to [input_size, input_size]
new_h, new_w, _ = im.shape
if min(new_w, new_h) < self.input_size * 0.5:
return None
im_padded = np.ones((self.input_size, self.input_size, 3), dtype=np.float32)
im_padded[:, :, 2] = 0.485 * 255
im_padded[:, :, 1] = 0.456 * 255
im_padded[:, :, 0] = 0.406 * 255
# Random the start position
del_h = self.input_size - new_h
del_w = self.input_size - new_w
sh, sw = 0, 0
if del_h > 1:
sh = int(np.random.rand() * del_h)
if del_w > 1:
sw = int(np.random.rand() * del_w)
# Padding
im_padded[sh: sh + new_h, sw: sw + new_w, :] = im.copy()
text_polys[:, :, 0] += sw
text_polys[:, :, 1] += sh
score_map, border_map, training_mask = self.generate_tcl_label((self.input_size, self.input_size),
text_polys, text_tags, 0.25)
# SAST head
tvo_map, tco_map = self.generate_tvo_and_tco((self.input_size, self.input_size), text_polys, text_tags, tcl_ratio=0.3, ds_ratio=0.25)
# print("test--------tvo_map shape:", tvo_map.shape)
im_padded[:, :, 2] -= 0.485 * 255
im_padded[:, :, 1] -= 0.456 * 255
im_padded[:, :, 0] -= 0.406 * 255
im_padded[:, :, 2] /= (255.0 * 0.229)
im_padded[:, :, 1] /= (255.0 * 0.224)
im_padded[:, :, 0] /= (255.0 * 0.225)
im_padded = im_padded.transpose((2, 0, 1))
return im_padded[::-1, :, :], score_map[np.newaxis, :, :], border_map.transpose((2, 0, 1)), training_mask[np.newaxis, :, :], tvo_map.transpose((2, 0, 1)), tco_map.transpose((2, 0, 1))
class SASTProcessTest(object):
"""
SAST process function for test
"""
def __init__(self, params):
super(SASTProcessTest, self).__init__()
if 'max_side_len' in params:
self.max_side_len = params['max_side_len']
else:
self.max_side_len = 2400
def resize_image(self, im):
"""
resize image to a size multiple of max_stride which is required by the network
:param im: the resized image
:param max_side_len: limit of max image size to avoid out of memory in gpu
:return: the resized image and the resize ratio
"""
h, w, _ = im.shape
resize_w = w
resize_h = h
# Fix the longer side
if resize_h > resize_w:
ratio = float(self.max_side_len) / resize_h
else:
ratio = float(self.max_side_len) / resize_w
resize_h = int(resize_h * ratio)
resize_w = int(resize_w * ratio)
max_stride = 128
resize_h = (resize_h + max_stride - 1) // max_stride * max_stride
resize_w = (resize_w + max_stride - 1) // max_stride * max_stride
im = cv2.resize(im, (int(resize_w), int(resize_h)))
ratio_h = resize_h / float(h)
ratio_w = resize_w / float(w)
return im, (ratio_h, ratio_w)
def __call__(self, im):
src_h, src_w, _ = im.shape
im, (ratio_h, ratio_w) = self.resize_image(im)
img_mean = [0.485, 0.456, 0.406]
img_std = [0.229, 0.224, 0.225]
im = im[:, :, ::-1].astype(np.float32)
im = im / 255
im -= img_mean
im /= img_std
im = im.transpose((2, 0, 1))
im = im[np.newaxis, :]
return [im, (ratio_h, ratio_w, src_h, src_w)]
......@@ -26,7 +26,7 @@ from ppocr.utils.utility import initial_logger
from ppocr.utils.utility import get_image_file_list
logger = initial_logger()
from .img_tools import process_image, get_img_data
from .img_tools import process_image, process_image_srn, get_img_data
class LMDBReader(object):
......@@ -43,6 +43,9 @@ class LMDBReader(object):
self.mode = params['mode']
self.drop_last = False
self.use_tps = False
self.num_heads = None
if "num_heads" in params:
self.num_heads = params['num_heads']
if "tps" in params:
self.ues_tps = True
self.use_distort = False
......@@ -119,12 +122,19 @@ class LMDBReader(object):
img = cv2.imread(single_img)
if img.shape[-1] == 1 or len(list(img.shape)) == 2:
img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
norm_img = process_image(
img=img,
image_shape=self.image_shape,
char_ops=self.char_ops,
tps=self.use_tps,
infer_mode=True)
if self.loss_type == 'srn':
norm_img = process_image_srn(
img=img,
image_shape=self.image_shape,
num_heads=self.num_heads,
max_text_length=self.max_text_length)
else:
norm_img = process_image(
img=img,
image_shape=self.image_shape,
char_ops=self.char_ops,
tps=self.use_tps,
infer_mode=True)
yield norm_img
else:
lmdb_sets = self.load_hierarchical_lmdb_dataset()
......@@ -144,14 +154,25 @@ class LMDBReader(object):
if sample_info is None:
continue
img, label = sample_info
outs = process_image(
img=img,
image_shape=self.image_shape,
label=label,
char_ops=self.char_ops,
loss_type=self.loss_type,
max_text_length=self.max_text_length,
distort=self.use_distort)
outs = []
if self.loss_type == "srn":
outs = process_image_srn(
img=img,
image_shape=self.image_shape,
num_heads=self.num_heads,
max_text_length=self.max_text_length,
label=label,
char_ops=self.char_ops,
loss_type=self.loss_type)
else:
outs = process_image(
img=img,
image_shape=self.image_shape,
label=label,
char_ops=self.char_ops,
loss_type=self.loss_type,
max_text_length=self.max_text_length)
if outs is None:
continue
yield outs
......
......@@ -381,3 +381,84 @@ def process_image(img,
assert False, "Unsupport loss_type %s in process_image"\
% loss_type
return (norm_img)
def resize_norm_img_srn(img, image_shape):
imgC, imgH, imgW = image_shape
img_black = np.zeros((imgH, imgW))
im_hei = img.shape[0]
im_wid = img.shape[1]
if im_wid <= im_hei * 1:
img_new = cv2.resize(img, (imgH * 1, imgH))
elif im_wid <= im_hei * 2:
img_new = cv2.resize(img, (imgH * 2, imgH))
elif im_wid <= im_hei * 3:
img_new = cv2.resize(img, (imgH * 3, imgH))
else:
img_new = cv2.resize(img, (imgW, imgH))
img_np = np.asarray(img_new)
img_np = cv2.cvtColor(img_np, cv2.COLOR_BGR2GRAY)
img_black[:, 0:img_np.shape[1]] = img_np
img_black = img_black[:, :, np.newaxis]
row, col, c = img_black.shape
c = 1
return np.reshape(img_black, (c, row, col)).astype(np.float32)
def srn_other_inputs(image_shape,
num_heads,
max_text_length):
imgC, imgH, imgW = image_shape
feature_dim = int((imgH / 8) * (imgW / 8))
encoder_word_pos = np.array(range(0, feature_dim)).reshape((feature_dim, 1)).astype('int64')
gsrm_word_pos = np.array(range(0, max_text_length)).reshape((max_text_length, 1)).astype('int64')
lbl_weight = np.array([37] * max_text_length).reshape((-1,1)).astype('int64')
gsrm_attn_bias_data = np.ones((1, max_text_length, max_text_length))
gsrm_slf_attn_bias1 = np.triu(gsrm_attn_bias_data, 1).reshape([-1, 1, max_text_length, max_text_length])
gsrm_slf_attn_bias1 = np.tile(gsrm_slf_attn_bias1, [1, num_heads, 1, 1]) * [-1e9]
gsrm_slf_attn_bias2 = np.tril(gsrm_attn_bias_data, -1).reshape([-1, 1, max_text_length, max_text_length])
gsrm_slf_attn_bias2 = np.tile(gsrm_slf_attn_bias2, [1, num_heads, 1, 1]) * [-1e9]
encoder_word_pos = encoder_word_pos[np.newaxis, :]
gsrm_word_pos = gsrm_word_pos[np.newaxis, :]
return [lbl_weight, encoder_word_pos, gsrm_word_pos, gsrm_slf_attn_bias1, gsrm_slf_attn_bias2]
def process_image_srn(img,
image_shape,
num_heads,
max_text_length,
label=None,
char_ops=None,
loss_type=None):
norm_img = resize_norm_img_srn(img, image_shape)
norm_img = norm_img[np.newaxis, :]
[lbl_weight, encoder_word_pos, gsrm_word_pos, gsrm_slf_attn_bias1, gsrm_slf_attn_bias2] = \
srn_other_inputs(image_shape, num_heads, max_text_length)
if label is not None:
char_num = char_ops.get_char_num()
text = char_ops.encode(label)
if len(text) == 0 or len(text) > max_text_length:
return None
else:
if loss_type == "srn":
text_padded = [37] * max_text_length
for i in range(len(text)):
text_padded[i] = text[i]
lbl_weight[i] = [1.0]
text_padded = np.array(text_padded)
text = text_padded.reshape(-1, 1)
return (norm_img, text,encoder_word_pos, gsrm_word_pos, gsrm_slf_attn_bias1, gsrm_slf_attn_bias2,lbl_weight)
else:
assert False, "Unsupport loss_type %s in process_image"\
% loss_type
return (norm_img, encoder_word_pos, gsrm_word_pos, gsrm_slf_attn_bias1, gsrm_slf_attn_bias2)
......@@ -97,6 +97,23 @@ class DetModel(object):
'shrink_mask':shrink_mask,\
'threshold_map':threshold_map,\
'threshold_mask':threshold_mask}
elif self.algorithm == "SAST":
input_score = fluid.layers.data(
name='score', shape=[1, 128, 128], dtype='float32')
input_border = fluid.layers.data(
name='border', shape=[5, 128, 128], dtype='float32')
input_mask = fluid.layers.data(
name='mask', shape=[1, 128, 128], dtype='float32')
input_tvo = fluid.layers.data(
name='tvo', shape=[9, 128, 128], dtype='float32')
input_tco = fluid.layers.data(
name='tco', shape=[3, 128, 128], dtype='float32')
feed_list = [image, input_score, input_border, input_mask, input_tvo, input_tco]
labels = {'input_score': input_score,\
'input_border': input_border,\
'input_mask': input_mask,\
'input_tvo': input_tvo,\
'input_tco': input_tco}
loader = fluid.io.DataLoader.from_generator(
feed_list=feed_list,
capacity=64,
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment