<jupyter_start><jupyter_text>常规赛：2021年12月中文场景文字识别-第3名技术方案分享本项目为[常规赛：中文场景文字识别](https://aistudio.baidu.com/aistudio/competition/detail/20) 2021年12月份第3名的技术方案分享项目。最终得分为84.22158。本项目使用PaddleOCR-develop(静态图版本)，PaddleOCR主要由DB文本检测、检测框矫正和CRNN文本识别三部分组成，本次中文场景文本识别只需要使用第三阶段的文本识别器即可。采用CRNN文本识别模型作为baseline，更多关于PaddleOCR的信息详见[PaddleOCR-develop](https://github.com/PaddlePaddle/PaddleOCR/tree/develop)下面将从环境安装、数据处理、模型调优、训练与预测四个方面进行介绍。  一、环境安装 1.1 安装PaddleOCRAI Studio已经提供了paddlepaddle1.8.4及python3.7的环境，因此只需要参考[官方教程](https://github.com/PaddlePaddle/PaddleOCR/blob/develop/doc/doc_ch/installation.md)安装PaddleOCR即可。<jupyter_code>!cd ~/work && git clone -b develop https://gitee.com/paddlepaddle/PaddleOCR.git
!cd ~/work/PaddleOCR && pip install -r requirements.txt && python setup.py install<jupyter_output>Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Collecting shapely (from -r requirements.txt (line 1))
[?25l  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/ae/20/33ce377bd24d122a4d54e22ae2c445b9b1be8240edb50040b40add950cd9/Shapely-1.8.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl (1.1MB)
[K     |████████████████████████████████| 1.1MB 5.2MB/s eta 0:00:01
[?25hCollecting imgaug (from -r requirements.txt (line 2))
[?25l  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/66/b1/af3142c4a85cba6da9f4ebb5ff4e21e2616309552caca5e8acefe9840622/imgaug-0.4.0-py2.py3-none-any.whl (948kB)
[K     |████████████████████████████████| 952kB 7.8MB/s eta 0:00:01
[?25hCollecting pyclipper (from -r requirements.txt (line 3))
[?25l  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/c5/fa/2c294127e4f88967149a68ad5b3e43636e94e3721109572f8f17ab15b772/pyclipper-1.3.0.post2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl (603kB)
[K     |██████████████████████[...]<jupyter_text>二、数据处理 2.1 解压数据集<jupyter_code>!cd ~/data/data62842/ && unzip train_images.zip
!cd ~/data/data62843/ && unzip test_images.zip
!cd ~/data/data62842/ && mv train_images ../ && mv train_label.csv ../
!cd ~/data/data62843/ && mv test_images ../<jupyter_output><empty_output><jupyter_text>2.2 数据增强* 本次比赛中，使用数据增强的目的是用来防止过拟合，并且数据增强适用于dataset较小的时候。* 我选择使用[text_render](https://github.com/Sanster/text_renderer)进行数据增强。使用的操作主要包括明暗变换，文本边界调整，添加噪声，颜色调整，文本字体特效变换等等。* 安装text_render后，需要手动修改text_render/configs/default.yaml配置，如下所示``` Small font_size will make text looks like blured/prydownfont_size:  min: 14  max: 23 choose Text color range color boundary is in R,G,B formatfont_color:  enable: true  blue:    fraction: 0.5    l_boundary: [0,0,150]    h_boundary: [60,60,255]  brown:    fraction: 0.5    l_boundary: [139,70,19]    h_boundary: [160,82,43] By default, text is drawed by Pillow with (https://stackoverflow.com/questions/43828955/measuring-width-of-text-python-pil) If `random_space` is enabled, some text will be drawed char by char with a random spacerandom_space:  enable: false  fraction: 0.3  min: -0.1  -0.1 will make chars very close or even overlapped  max: 0.1 Do remap with sin() Currently this process is very slow!curve:  enable: false  fraction: 0.3  period: 360   degree, sin 函数的周期  min: 1  sin 函数的幅值范围  max: 5 random crop text heightcrop:  enable: false  fraction: 0.5   top and bottom will applied equally  top:    min: 5    max: 10  in pixel, this value should small than img_height  bottom:    min: 5    max: 10  in pixel, this value should small than img_height Use image in bg_dir as background for textimg_bg:  enable: false  fraction: 0.5 Not work when random_space appliedtext_border:  enable: true  fraction: 0.3   lighter than word color  light:    enable: true    fraction: 0.5   darker than word color  dark:    enable: true    fraction: 0.5 https://docs.opencv.org/3.4/df/da0/group__photo__clone.htmlga2bf426e4c93a6b1f21705513dfeca49d https://www.cs.virginia.edu/~connelly/class/2014/comp_photo/proj2/poisson.pdf Use opencv seamlessClone() to draw text on background For some background image, this will make text image looks more realseamless_clone:  enable: true  fraction: 0.5perspective_transform:  max_x: 25  max_y: 25  max_z: 3blur:  enable: true  fraction: 0.03 If an image is applied blur, it will not be applied prydownprydown:  enable: true  fraction: 0.03  max_scale: 1.5  Image will first resize to 1.5x, and than resize to 1xnoise:  enable: true  fraction: 0.3  gauss:    enable: true    fraction: 0.25  uniform:    enable: true    fraction: 0.25  salt_pepper:    enable: true    fraction: 0.25  poisson:    enable: true    fraction: 0.25line:  enable: false  fraction: 0.05  under_line:    enable: false    fraction: 0.2  table_line:    enable: false    fraction: 0.3  middle_line:    enable: false    fraction: 0.5line_color:  enable: false  black:    fraction: 0.5    l_boundary: 0,0,0    h_boundary: 64,64,64  blue:    fraction: 0.5    l_boundary: [0,0,150]    h_boundary: [60,60,255] These operates are applied on the final output image, so actually it can also be applied in training process as an data augmentation method. By default, text is darker than background. If `reverse_color` is enabled, some images will have dark background and light textreverse_color:  enable: true  fraction: 0.3emboss:  enable: true  fraction: 0.3sharp:  enable: true  fraction: 0.3```    * PaddleOCR的[FAQ1.1.8](https://github.com/PaddlePaddle/PaddleOCR/blob/develop/doc/doc_ch/FAQ.mdq118%E8%AF%B7%E9%97%AEpaddleocr%E9%A1%B9%E7%9B%AE%E4%B8%AD%E7%9A%84%E4%B8%AD%E6%96%87%E8%B6%85%E8%BD%BB%E9%87%8F%E5%92%8C%E9%80%9A%E7%94%A8%E6%A8%A1%E5%9E%8B%E7%94%A8%E4%BA%86%E5%93%AA%E4%BA%9B%E6%95%B0%E6%8D%AE%E9%9B%86%E8%AE%AD%E7%BB%83%E5%A4%9A%E5%B0%91%E6%A0%B7%E6%9C%ACgpu%E4%BB%80%E4%B9%88%E9%85%8D%E7%BD%AE%E8%B7%91%E4%BA%86%E5%A4%9A%E5%B0%91%E4%B8%AAepoch%E5%A4%A7%E6%A6%82%E8%B7%91%E4%BA%86%E5%A4%9A%E4%B9%85)中介绍到，PaddleOCR的识别模型采用520W左右的数据集（真实数据26W+合成数据500W）进行训练，可见数据增广的重要性。<jupyter_code>!cd ~/work && git clone https://github.com/Sanster/text_renderer
!cd ~/work/text_renderer && pip install -r requirements.txt<jupyter_output>Cloning into 'text_renderer'...
remote: Enumerating objects: 694, done.[K
remote: Counting objects: 100% (6/6), done.[K
remote: Compressing objects: 100% (6/6), done.[K
remote: Total 694 (delta 1), reused 1 (delta 0), pack-reused 688[K
Receiving objects: 100% (694/694), 12.91 MiB | 26.00 KiB/s, done.
Resolving deltas: 100% (384/384), done.
Checking connectivity... done.
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Requirement already satisfied: Cython in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from -r requirements.txt (line 1)) (0.29)
Requirement already satisfied: opencv-python in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from -r requirements.txt (line 2)) (4.2.0.32)
Requirement already satisfied: pillow in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages (from -r requirements.txt (line 3)) (7.1.2)
Requirement already satisfied: numpy in /opt/conda/envs/python35-paddle120-env/lib/python3.7/site[...]<jupyter_text>* 通过统计训练集的图像尺寸，可以发现训练集的高度固定为48，而宽度与图中的文字个数有关。<jupyter_code>import glob
import os
import cv2


def get_aspect_ratio(img_set_dir):
    m_width = 0
    m_height = 0
    width_dict = {}
    height_dict = {}
    images = glob.glob(img_set_dir+'*.jpg')
    for image in images:
        img = cv2.imread(image)
        width_dict[int(img.shape[1])] = 1 if (int(img.shape[1])) not in width_dict else 1 + width_dict[int(img.shape[1])]
        height_dict[int(img.shape[0])] = 1 if (int(img.shape[0])) not in height_dict else 1 + height_dict[int(img.shape[0])]
        m_width += img.shape[1]
        m_height += img.shape[0]
    m_width = m_width/len(images)
    m_height = m_height/len(images)
    aspect_ratio = m_width/m_height
    width_dict = dict(sorted(width_dict.items(), key=lambda item: item[1], reverse=True))
    height_dict = dict(sorted(height_dict.items(), key=lambda item: item[1], reverse=True))
    return aspect_ratio,m_width,m_height,width_dict,height_dict
aspect_ratio,m_width,m_height,width_dict,height_dict = get_aspect_ratio("/home/aistudio/data/train_images/")
print("aspect ratio is: {}, mean width is: {}, mean height is: {}".format(aspect_ratio,m_width,m_height))
print("Width dict:{}".format(width_dict))
print("Height dict:{}".format(height_dict))
import pandas as pd

def Q2B(s):
    """全角转半角"""
    inside_code=ord(s)
    if inside_code==0x3000:
        inside_code=0x0020
    else:
        inside_code-=0xfee0
    if inside_code<0x0020 or inside_code>0x7e: #转完之后不是半角字符返回原来的字符
        return s
    return chr(inside_code)

def stringQ2B(s):
    """把字符串全角转半角"""
    return "".join([Q2B(c) for c in s])

def is_chinese(s):
    """判断unicode是否是汉字"""
    for c in s:
        if c < u'\u4e00' or c > u'\u9fa5':
            return False
    return True

def is_number(s):
    """判断unicode是否是数字"""
    for c in s:
        if c < u'\u0030' or c > u'\u0039':
            return False
    return True

def is_alphabet(s):
    """判断unicode是否是英文字母"""
    for c in s:
        if c < u'\u0061' or c > u'\u007a':
            return False
    return True

def del_other(s):
    """判断是否非汉字，数字和小写英文"""
    res = str()
    for c in s:
        if not (is_chinese(c) or is_number(c) or is_alphabet(c)):
            c = ""
        res += c
    return res


df = pd.read_csv("/home/aistudio/data/train_label.csv", encoding="gbk")
name, value = list(df.name), list(df.value)
for i, label in enumerate(value):
    # 全角转半角
    label = stringQ2B(label)
    # 大写转小写
    label = "".join([c.lower() for c in label])
    # 删除所有空格符号
    label = del_other(label)
    value[i] = label

# 删除标签为""的行
data = zip(name, value)
data = list(filter(lambda c: c[1]!="", list(data)))
# 保存到work目录
with open("/home/aistudio/data/train_label.txt", "w") as f:
    for line in data:
        f.write(line[0] + "\t" + line[1] + "\n")

# 记录训练集中最长标签
label_max_len = 0
with open("/home/aistudio/data/train_label.txt", "r") as f:
    for line in f:
        name, label = line.strip().split("\t")
        if len(label) > label_max_len:
            label_max_len = len(label)

print("label max len: ", label_max_len)
def create_label_list(train_list):
    classSet = set()
    with open(train_list) as f:
        next(f)
        for line in f:
            img_name, label = line.strip().split("\t")
            for e in label:
                classSet.add(e)
    # 在类的基础上加一个blank
    classList = sorted(list(classSet))
    with open("/home/aistudio/data/label_list.txt", "w") as f:
        for idx, c in enumerate(classList):
            f.write("{}\t{}\n".format(c, idx))
    
    # 为数据增广提供词库
    with open("/home/aistudio/work/text_renderer/data/chars/ch.txt", "w") as f:
        for idx, c in enumerate(classList):
            f.write("{}\n".format(c))
            
    return classSet

classSet = create_label_list("/home/aistudio/data/train_label.txt")
print("classify num: ", len(classSet))<jupyter_output>aspect ratio is: 3.451128333333333, mean width is: 165.65416, mean height is: 48.0
Width dict:{48: 741, 96: 539, 44: 392, 42: 381, 144: 365, 45: 345, 43: 323, 88: 318, 72: 318, 40: 312, 52: 301, 36: 298, 50: 297, 120: 294, 54: 288, 84: 286, 51: 283, 32: 283, 24: 281, 100: 277, 64: 276, 80: 276, 76: 275, 102: 272, 81: 270, 90: 269, 56: 268, 66: 267, 78: 266, 37: 262, 82: 261, 41: 259, 89: 258, 92: 257, 46: 256, 60: 251, 86: 249, 53: 246, 168: 246, 105: 243, 61: 242, 57: 241, 128: 241, 112: 240, 85: 239, 91: 237, 39: 237, 68: 235, 98: 234, 93: 233, 192: 233, 75: 232, 34: 229, 33: 229, 74: 229, 70: 228, 87: 226, 25: 226, 110: 224, 104: 224, 58: 223, 101: 221, 108: 221, 62: 221, 49: 221, 132: 221, 150: 218, 126: 218, 73: 217, 94: 217, 28: 216, 129: 216, 69: 215, 30: 215, 99: 212, 160: 211, 38: 210, 136: 209, 26: 207, 109: 207, 55: 206, 118: 205, 35: 205, 116: 204, 115: 203, 174: 201, 117: 200, 106: 200, 148: 200, 122: 199, 113: 198, 67: 197, 77: 197, 172: 195, 114: 195, 156: 194, 130: 191,[...]<jupyter_text>- 生成字符长度为1，2，3，4，5的数据集各2000张，共10000张。<jupyter_code># 清空已经生成的数据集
!cd ~/work/text_renderer/output/default && rm ./* 
!cd ~/work/text_renderer && python main.py --length 1 --img_width 32 --img_height 48 --chars_file "./data/chars/ch.txt" --corpus_mode 'random' --num_img 2000
!cd ~/work/text_renderer && python main.py --length 2 --img_width 64 --img_height 48 --chars_file "./data/chars/ch.txt" --corpus_mode 'random' --num_img 2000
!cd ~/work/text_renderer && python main.py --length 3 --img_width 96 --img_height 48 --chars_file "./data/chars/ch.txt" --corpus_mode 'random' --num_img 2000
!cd ~/work/text_renderer && python main.py --length 4 --img_width 128 --img_height 48 --chars_file "./data/chars/ch.txt" --corpus_mode 'random' --num_img 2000
!cd ~/work/text_renderer && python main.py --length 5 --img_width 160 --img_height 48 --chars_file "./data/chars/ch.txt" --corpus_mode 'random' --num_img 2000<jupyter_output>Total fonts num: 1
Background num: 1
Generate text images in ./output/default
Retry gen_img: Range cannot be empty (low >= high) unless no samples are taken
Traceback (most recent call last):
  File "main.py", line 75, in gen_img_retry
    return renderer.gen_img(img_index)
  File "/home/aistudio/work/text_renderer/textrenderer/renderer.py", line 55, in gen_img
    word_img, text_box_pnts, word_color = self.draw_text_on_bg(word, font, bg)
  File "/home/aistudio/work/text_renderer/textrenderer/renderer.py", line 283, in draw_text_on_bg
    np_img = self.draw_text_seamless(font, bg, word, word_color, word_height, word_width, offset)
  File "/home/aistudio/work/text_renderer/textrenderer/renderer.py", line 318, in draw_text_seamless
    font, word_color)
  File "/home/aistudio/work/text_renderer/textrenderer/renderer.py", line 394, in draw_text_wrapper
    self.draw_border_text(draw, text, x, y, font, text_color)
  File "/home/aistudio/work/text_renderer/textrenderer/renderer.py", line 41[...]<jupyter_text>- 将生成的数据集与原数据集合并<jupyter_code>!cp ~/work/text_renderer/output/default/*.jpg ~/data/train_images
import os

with open('work/text_renderer/output/default/tmp_labels.txt','r',encoding='utf-8') as src_label:
    with open('data/train_label.txt','a',encoding='utf-8') as dst_label:
        lines = src_label.readlines()
        for line in lines:
            [img,text] = line.split(' ')
            print('{}.jpg\t{}'.format(img,text),file=dst_label,end='')<jupyter_output><empty_output><jupyter_text>三、模型调优* 可以选择PaddleOCR提供的CRNN预训练模型，或[其他模型](https://github.com/PaddlePaddle/PaddleOCR/blob/develop/doc/doc_ch/recognition.md)* 根据前面统计的训练集尺寸，将模型输入尺寸设置为高度48，宽度256* 采用cosine_decay和warmup策略，加快模型收敛 - CRNN模型是在2015年论文"An End-to-End Trainable Neural Network for Image-based Sequence Recognition and Its Application to Scene Text Recognition"[[论文]](https://arxiv.org/abs/1507.05717)[[代码]](https://github.com/bgshih/crnn)提出的，用于不定长序列的文本识别。- 下图是CRNN模型的结构图，其主要由CNN层，RNN层及CTC翻译层三部分构成。其中CNN层从输入图片提取图片特征，原文使用的是VGG网络，而PaddleOCR使用的是ResNet34和MobileNetV3。大模型往往能取得更好的效果，因此本项目采用ResNet34作为baseline来改进。改进方向则为调整CNN的特征提取网络，尝试ResNet50及更深的结构。![](https://ai-studio-static-online.cdn.bcebos.com/354a7443969b4adc999222fc3edcfbb8971570260b3a45248afa59c72e12e660)<jupyter_code>!cd ~/work/PaddleOCR && mkdir pretrain_weights && cd pretrain_weights && wget https://paddleocr.bj.bcebos.com/20-09-22/server/rec/ch_ppocr_server_v1.1_rec_pre.tar
!cd ~/work/PaddleOCR/pretrain_weights && tar -xf ch_ppocr_server_v1.1_rec_pre.tar<jupyter_output><empty_output><jupyter_text>* 在PaddleOCR/configs/rec中，分别添加训练配置文件 my_rec_ch_train.yml和my_rec_ch_reader.yml* 本次比赛结果的调优过程：设定了161轮迭代（从epoch0到epoch160），初始学习率为0.0001，fc_decay为0.00001，l2学习率衰减为0.00001* 为了适当提升学习速度，使用了cosine_decay和warmup。其中step_each_epoch为1000，warmup_minibatch为2000，衰减总轮数为161* 经测试以上参数设定可以达到较好的结果```my_rec_ch_train.ymlGlobal:  algorithm: CRNN  use_gpu: true  epoch_num: 161        训练轮数  log_smooth_window: 20  print_batch_step: 10     save_model_dir: ./output/my_rec_ch  save_epoch_step: 20    保存模型间隔轮数  eval_batch_step: 1000  train_batch_size_per_card: 256  test_batch_size_per_card: 128  image_shape: [3, 48, 256]  max_text_length: 80  character_type: ch  character_dict_path: ./ppocr/utils/ppocr_keys_v1.txt  loss_type: ctc  distort: true  use_space_char: true  reader_yml: ./configs/rec/my_rec_ch_reader.yml  pretrain_weights: ./pretrain_weights/ch_ppocr_server_v1.1_rec_pre/best_accuracy  checkpoints:  save_inference_dir:   infer_img:Architecture:  function: ppocr.modeling.architectures.rec_model,RecModelBackbone:  function: ppocr.modeling.backbones.rec_resnet_vd,ResNet  layers: 34Head:  function: ppocr.modeling.heads.rec_ctc_head,CTCPredict  encoder_type: rnn  fc_decay: 0.00001  SeqRNN:    hidden_size: 256    Loss:  function: ppocr.modeling.losses.rec_ctc_loss,CTCLossOptimizer:  function: ppocr.optimizer,AdamDecay  base_lr: 0.0001      初始学习率  l2_decay: 0.00001    学习率衰减  beta1: 0.9  beta2: 0.999  decay:    function: cosine_decay_warmup    step_each_epoch: 1000    total_epoch: 161    warmup_minibatch: 2000``````my_rec_ch_reader.ymlTrainReader:  reader_function: ppocr.data.rec.dataset_traversal,SimpleReader  num_workers: 1  img_set_dir: /home/aistudio/data/train_images  label_file_path: /home/aistudio/data/train_label.txt  EvalReader:  reader_function: ppocr.data.rec.dataset_traversal,SimpleReader  img_set_dir: /home/aistudio/data/train_images  label_file_path: /home/aistudio/data/train_label.txtTestReader:  reader_function: ppocr.data.rec.dataset_traversal,SimpleReader```  四、训练与预测- 参考[PaddleOCR官方教程](https://github.com/PaddlePaddle/PaddleOCR/blob/develop/doc/doc_ch/recognition.md)进行模型训练  4.1 训练模型- 添加训练配置文件 my_rec_ch_train.yml和my_rec_ch_reader.yml以后，输入以下命令就可以开始训练。<jupyter_code>!cd ~/work/PaddleOCR && python tools/train.py -c configs/rec/my_rec_ch_train.yml<jupyter_output>2021-12-24 19:19:21,079-INFO: Already save model in ./output/my_rec_ch/iter_epoch_160<jupyter_text>4.2 导出模型（根据需求选择）- 训练完成后，模型和参数会被保存到PaddleOCR/output文件下，选择需要导出最终的模型，如下操作导出的是对iter_epoch_160的模型进行导出，同时设置导出的路径为PaddleOCR/inference/CRNN_R34，这些路径都可以自行修改。<jupyter_code>!cd ~/work/PaddleOCR && python tools/export_model.py -c configs/rec/my_rec_ch_train.yml -o Global.checkpoints=./output/my_rec_ch/iter_epoch_160 Global.save_inference_dir=./inference/CRNN_R34<jupyter_output>2021-12-24 19:50:10,162-INFO: {'Global': {'debug': False, 'algorithm': 'CRNN', 'use_gpu': True, 'epoch_num': 161, 'log_smooth_window': 20, 'print_batch_step': 10, 'save_model_dir': './output/my_rec_ch', 'save_epoch_step': 20, 'eval_batch_step': 1000, 'train_batch_size_per_card': 256, 'test_batch_size_per_card': 128, 'image_shape': [3, 48, 256], 'max_text_length': 80, 'character_type': 'ch', 'character_dict_path': './ppocr/utils/ppocr_keys_v1.txt', 'loss_type': 'ctc', 'distort': True, 'use_space_char': True, 'reader_yml': './configs/rec/my_rec_ch_reader.yml', 'pretrain_weights': './pretrain_weights/ch_ppocr_server_v1.1_rec_pre/best_accuracy', 'checkpoints': './output/my_rec_ch/iter_epoch_160', 'save_inference_dir': './inference/CRNN_R34', 'infer_img': None}, 'Architecture': {'function': 'ppocr.modeling.architectures.rec_model,RecModel'}, 'Backbone': {'function': 'ppocr.modeling.backbones.rec_resnet_vd,ResNet', 'layers': 34}, 'Head': {'function': 'ppocr.modeling.heads.rec_ctc_head,CTCPre[...]<jupyter_text>4.3 预测结果在work/PaddleOCR/tools/路径下，新建python文件infer_rec_new.py复制如下代码到infer_rec_new.py中``` Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. 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.from __future__ import absolute_importfrom __future__ import divisionfrom __future__ import print_functionimport numpy as npimport osimport sysimport globimport re__dir__ = os.path.dirname(os.path.abspath(__file__))sys.path.append(__dir__)sys.path.append(os.path.abspath(os.path.join(__dir__, '..')))def set_paddle_flags(**kwargs):    for key, value in kwargs.items():        if os.environ.get(key, None) is None:            os.environ[key] = str(value) NOTE(paddle-dev): All of these flags should be set before `import paddle`. Otherwise, it would not take any effect.set_paddle_flags(    FLAGS_eager_delete_tensor_gb=0,   enable GC to save memory)import tools.program as programfrom paddle import fluidfrom ppocr.utils.utility import initial_loggerlogger = initial_logger()from ppocr.utils.utility import enable_static_modefrom ppocr.data.reader_main import reader_mainfrom ppocr.utils.save_load import init_modelfrom ppocr.utils.character import CharacterOpsfrom ppocr.utils.utility import create_modulefrom ppocr.utils.utility import get_image_file_listdef main():    config = program.load_config(FLAGS.config)    program.merge_config(FLAGS.opt)    logger.info(config)    char_ops = CharacterOps(config['Global'])    config['Global']['char_ops'] = char_ops     check if set use_gpu=True in paddlepaddle cpu version    use_gpu = config['Global']['use_gpu']         check_gpu(use_gpu)    place = fluid.CUDAPlace(0) if use_gpu else fluid.CPUPlace()    exe = fluid.Executor(place)    rec_model = create_module(config['Architecture']['function'])(params=config)    startup_prog = fluid.Program()    eval_prog = fluid.Program()    with fluid.program_guard(eval_prog, startup_prog):        with fluid.unique_name.guard():            _, outputs = rec_model(mode="test")            fetch_name_list = list(outputs.keys())            fetch_varname_list = [outputs[v].name for v in fetch_name_list]            print(fetch_varname_list)    eval_prog = eval_prog.clone(for_test=True)    exe.run(startup_prog)    init_model(config, eval_prog, exe)    blobs = reader_main(config, 'test')()                  print(blobs)    infer_img = config['Global']['infer_img']    infer_list = get_image_file_list(infer_img)    infer_list.sort(key=lambda x: int(re.split('/home/aistudio/data/test_images/|.jpg',x)[1]))        print(infer_list)    images = glob.glob("/home/aistudio/data/test_images/*.jpg")    images.sort(key=lambda x: int(re.split('/home/aistudio/data/test_images/|.jpg',x)[1]))    max_img_num = len(infer_list)    if len(infer_list) == 0:        logger.info("Can not find img in infer_img dir.")    from tqdm import tqdm    f = open('test2.txt',mode='w',encoding='utf8')       f.write('new_name\tvalue\n')                         for i in tqdm( range(max_img_num)):    for image in images:         print("infer_img:",infer_list[i])        img = next(blobs)        predict = exe.run(program=eval_prog,                          feed={"image": img},img                          fetch_list=fetch_varname_list,                          return_numpy=False)        preds = np.array(predict[0])        if preds.shape[1] == 1:            preds = preds.reshape(-1)            preds_lod = predict[0].lod()[0]            preds_text = char_ops.decode(preds)        else:            end_pos = np.where(preds[0, :] == 1)[0]            if len(end_pos) <= 1:                preds_text = preds[0, 1:]            else:                preds_text = preds[0, 1:end_pos[1]]            preds_text = preds_text.reshape(-1)            preds_text = char_ops.decode(preds_text)        f.write('{}\t{}\n'.format(os.path.basename(img_path),preds_text))           f.write('{}\t{}\n'.format(infer_list[i].replace('/home/aistudio/data/test_images/', ''),preds_text))        print(image)         print("\t index:",preds)         print("\t word :",preds_text)    f.close()     save for inference model    target_var = []    for key, values in outputs.items():        target_var.append(values)    fluid.io.save_inference_model(        "./output/",        feeded_var_names=['image'],        target_vars=target_var,        executor=exe,        main_program=eval_prog,        model_filename="model",        params_filename="params")if __name__ == '__main__':    enable_static_mode()    parser = program.ArgsParser()    FLAGS = parser.parse_args()    FLAGS.config = 'configs/rec/my_rec_ch_train.yml'    main()```* 结果（.txt）将会被保存至work/PaddleOCR/的路径下，命名为test2.txt**注意：此处的test2.txt文件中的内容是乱序的，根据比赛要求，需要对其中的预测内容排序后再提交** * 最终比赛提交的结果，checkpoints使用的是/home/aistudio/work/PaddleOCR/output/my_rec_ch/路径下的**best_accuracy*** 通过下面的命令即可对测试集图像进行预测* Global.checkpoints 模型检查点文件* -c 配置文件* Global.infer_img 预测图片路径，可以为图像文件或者图像目录<jupyter_code>%cd ~/work/PaddleOCR
!python tools/infer_rec_new.py \
    -c configs/rec/my_rec_ch_train.yml \
    -o Global.checkpoints=./output/my_rec_ch/best_accuracy \
    Global.infer_img=/home/aistudio/data/test_images<jupyter_output>/home/aistudio/work/PaddleOCR
2021-12-24 20:22:46,421-INFO: {'Global': {'debug': False, 'algorithm': 'CRNN', 'use_gpu': True, 'epoch_num': 161, 'log_smooth_window': 20, 'print_batch_step': 10, 'save_model_dir': './output/my_rec_ch', 'save_epoch_step': 20, 'eval_batch_step': 1000, 'train_batch_size_per_card': 256, 'test_batch_size_per_card': 128, 'image_shape': [3, 48, 256], 'max_text_length': 80, 'character_type': 'ch', 'character_dict_path': './ppocr/utils/ppocr_keys_v1.txt', 'loss_type': 'ctc', 'distort': True, 'use_space_char': True, 'reader_yml': './configs/rec/my_rec_ch_reader.yml', 'pretrain_weights': './pretrain_weights/ch_ppocr_server_v1.1_rec_pre/best_accuracy', 'checkpoints': './output/my_rec_ch/iter_epoch_160', 'save_inference_dir': None, 'infer_img': '/home/aistudio/data/test_images'}, 'Architecture': {'function': 'ppocr.modeling.architectures.rec_model,RecModel'}, 'Backbone': {'function': 'ppocr.modeling.backbones.rec_resnet_vd,ResNet', 'layers': 34}, 'Head': {'function': [...]<jupyter_text>4.4 对txt文件内容排序* 用python写了一个小算法，对txt文件中的内容排序，最终将结果输出到test112.txt文件中，该排序文件命名为ZhuanHuan.py* 在work/PaddleOCR/路径下，新建ZhuanHuan.py文件，复制以下代码到ZhuanHuan.py中。```f = open('test7.txt', 'r', encoding='utf8')something = f.readlines()print(something)new = []for x in something:    first = x.strip('\n')    second = first.split()    new.append(second)print(new)print(new[1][1])for i in new:    print(new)    new[i][0].replace('.jpg','')    int(new[i][0])for i in range(1,10001):    if new[i][1] == []:        new[i][0] = new[i][0].replace('', ' ')for i in range(1,10001):    new[i][0]=new[i][0].replace('.jpg', '')    new[i][0]=int(new[i][0])    new[i][0]=int(new[i][0])    new[i][0].sort()print(new)    for j in range(len(new[0])):f = open('test1112.txt', mode='w', encoding='utf8')  f.write('new_name\tvalue\n')  b = 0for j in range(10000):    for i in range(1,10001):        if new[i][0] == b:            if len(new[i]) == 2:                f.write('{}.jpg\t{}\n'.format(new[i][0], new[i][1]))            else:                f.write('{}.jpg\t{}\n'.format(new[i][0], ''))    b = b+1print(j)f.close()print("finish")```<jupyter_code>!python /home/aistudio/work/PaddleOCR/ZhuanHuan.py<jupyter_output>水饺
[['new_name', 'value'], [2316, '水饺'], [4500, '84426783'], [5402, '60'], [1347, 'caohejing'], [7928, '政兴装饰'], [4192, 'welcome'], [9608, '中华老字号'], [2912, '15135125785'], [631, '传真文具办公用品批发'], [410, '曹记食品店'], [1547, '唐记'], [3463, 'g'], [765, '271号'], [6174, '江苏炒货'], [899, '瑞英制衣'], [8727, '营业时间'], [2626, '急慢性扭挂伤经验丰富'], [4561, 'wesom'], [1764, '欢迎光临'], [1658, '68329118'], [1355, '人防门'], [3262, '通体大理石'], [259, '红焖狗肉'], [6110, '广场'], [5729, '华铭印健'], [1315, '主营婚宴喜宴喜饼电话15'], [2107, '净水机'], [5142, '13'], [4073, '永发'], [3819, '机械'], [4601, '炭知味'], [1088, '书香景苑物业管理服务中心'], [2728, '天翼'], [5139, '助残志愿服务基地'], [2756, '新潮发廊'], [8737, '停车场'], [8049, '正宗飘香鸡'], [3635, '缝纫订做床上用品'], [2506, '专业值得信赖'], [6027, '助人育人与奋斗者同行'], [2082, '形象设计'], [2215, '炒饭'], [7575, '遇见餐厅'], [5473, '沙县小吃'], [3367, '平价超市'], [6500, 'bakeddurian'], [7866, 'a9'], [1511, '13327783161'], [6771, '金川'], [5948, '电话03163318564'], [4769, '周一二四五'], [4036, '康馨家'], [2303, '电话5975078959754421手机13601863296'], [7306, '烧烤时代'], [2000, '丽涵'], [9172, [...]
{'fraction_non_alphanumeric': 0.1504727921498662, 'fraction_numerical': 0.0743978590544157, 'mean_word_length': 4.496371837615219, 'pattern_counts': {'":': 1, '<': 42, '<?xml version=': 0, '>': 43, 'https://': 21, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 1, 'substrings_counts': 1, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '11', 'dir': 'jupyter-structured-clean-dedup', 'id': '91019', 'license': 'Apache-2.0', 'n_tokens_mistral': 13037, 'n_tokens_neox': 11891, 'n_words': 2108, 'path': 'Chinese scene text recognition.ipynb', 'repo_name': '707908377/Paddleocr-2021.12', 'size': '28025'}