ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

用PPOCRLabel做OCR数据标注:从半自动标注到训练集生成

用PPOCRLabel做OCR数据标注:从半自动标注到训练集生成

做OCR项目,真正劝退人的往往不是模型选型,而是标注。之前做个票据识别的小项目,整批图片里有表格线、印章、倾斜文字和低对比度字迹,一开始用通用标注工具拖矩形框、手敲文本,一个小时只能出二十来张图,眼睛全是黢黑。后来换成PPOCRLabel,先用模型自动把检测框和识别结果跑一遍,人工再修正和确认,同一批数据直接缩到三分之一的工作量。这篇文章我就把这套流程完整写下来:怎么装上、怎么启动、怎么把一张图从打开到导出标注,以及格式和训练集怎么对接,最后还有我自己踩过的几个坑,给正在做OCR数据准备的人一份可以直接照着操作的参考。

1. 标注,是OCR项目里最容易被低估的环节

1.1 公开数据集和真实业务数据的错位

很多人一开始做OCR,习惯直接拿公开数据集训练,比如各种打印体、标准排版、街景文字之类的评测集。这类数据确实干净,文字区域工整、背景简单、方向一致。但放到实际项目里,情况马上不一样:票据上有红章压着汉字,快递面单上字体小而且排列紧密,屏幕截图里还有深色底纹,铭牌照片拍出来是斜的,再加上反光、污渍、贴纸。你在公开模型上看到“精度很高”,换到真实场景就崩掉,就是因为训练数据和你业务数据分布不一样。

这时候唯一的出路就是拿一批真实图片做域内标注,让模型知道你的场景长什么样。标注这件事在整个OCR流程里其实占了相当大比重,不只是“画个框”那么简单,检测框怎么画、文本怎么转写、难例怎么处理,都会直接影响后面的训练效果。而我为什么选PPOCRLabel,而不是那些更通用的标注工具,下一节说清楚。

1.2 PPOCRLabel在同类工具里的生态位

市面上能用的标注工具不少,常见的是LabelImg和Labelme,还有商业平台。我拿自己实际用过的感受对比一下:

工具支持框类型自动检测识别联动导出是否贴合OCR训练
LabelImg水平矩形无无需要自己转换
Labelme多边形/矩形无无通用JSON,格式要二次处理
PPOCRLabel四边形四点框有有直接导出PaddleOCR训练常用格式

LabelImg拖框很快,但只有轴对齐矩形,遇到倾斜文字就没法标;Labelme能画多边形,但画完还得自己写脚本转成OCR训练格式,而且没有模型辅助,效率低。PPOCRLabel是PaddleOCR生态里的标注工具,最大的区别是它直接把检测模型和识别模型接进了标注流程:图片打开,它可以先框出一组候选文本区域,再对每个框给出预测文本,人工只需要在候选结果上做删减、修改和确认。这不止是省了画框的力气,连键盘输入也省掉一大截。

我用的版本是PaddleOCR仓库里自带的那套,它可以导出后面训练直接能用的数据集结构。商业标注平台我也试过几个,流程确实完善,但自定义字段和格式导出有时候还被平台限制,而且涉及数据安全,很多项目的图片根本不方便传到云端。PPOCRLabel本地运行,离线工作,这点对所有有保密要求的业务都非常友好。

1.3 半自动标注的现实收益

半自动标注到底省多少,我觉得不能只看“省了多少分钟”,得看整条流水线。假设一张图里有六个需要标注的文本区域,全手动标注要做的事情是:画六个框、输入六段文本、检查六个框是否漏掉、保存。每一步都有人工操作,一张图两分钟很正常。

用PPOCRLabel以后,正常清晰图片上模型自动检测基本能覆盖九成以上区域,人工做的事变成:把漏掉的框补上,把歪的角点拖正,把自动识别出来但错的文本改掉,再把多余的误检框删掉。熟练以后,一张图三十到五十秒能完成,遇到质量更差的图片会慢一些。而且当模型在标注过程中反复接收人工修正结果,我往往会每隔一段时间重新生成一次检测模型,效果会继续变好,标注速度会进一步上升。这个正循环才是它真正值得用的地方。

2. 环境准备阶段,我最想提前告诉你的三件事

2.1 安装步骤和版本选择

安装PPOCRLabel之前,先确认机器上有没有Python环境。个人经验是尽量用conda建一个干净的环境,避免和系统自带的Python互相污染。我一般这么建:

conda create -n paddle python=3.9 -y conda activate paddle pip install paddlepaddle -i https://pypi.tuna.tsinghua.edu.cn/simple git clone https://github.com/PaddlePaddle/PaddleOCR.git cd PaddleOCR pip install -r requirements.txt cd PPOCRLabel python PPOCRLabel.py --lang ch

这里有个容易被忽略的地方:paddlepaddle是CPU版本,如果你的机器有NVIDIA显卡,想标注大图更流畅,应该装GPU版,比如paddlepaddle-gpu。PPOCRLabel的自动检测和识别都要跑模型,CPU也能跑,但大图多的时候明显吃力,GPU能让体验提升一个档次。

版本选择上,我的建议是不要盲目追最新,先看你目标训练生态的PaddleOCR版本。仓库不同分支可能对应不同版本,启动脚本的参数也会有细微变化。之前看到有人拿新版的预训练模型放到旧版工具里用,结果检测框出不来,其实是模型结构不匹配,不是代码问题。如果你只想标注数据不研究新特性,选择和你训练计划一致的分支最省心。

2.2 模型文件从哪里来

PPOCRLabel第一次启动时,会用到一个检测模型和一个识别模型,默认会根据--lang ch参数决定下载中文模型。模型文件会放在用户目录的.paddleocr文件夹下面,具体路径根据版本不同略有差异。

如果你的网络环境不太稳定,模型下载可能失败,程序会一直卡在“下载模型”的界面。这时候别干等,直接手动把对应模型文件下载好,放到它期望的目录下再启动。我常用的做法是先让它启动一次,然后从日志里看它具体去哪个路径找模型,再把模型压缩包解压到那个路径下面,重新启动程序。需要注意模型文件名和目录层级都不能随意改动,否则程序识别不出来。

2.3 启动失败最常见的几种情况

我帮朋友看过的启动问题,基本集中在下面几个:

症状常见原因解决办法
启动后界面一闪而过缺少系统图形库安装libGL相关依赖后再试
报No module named 'skimage'requirements没装全在PaddleOCR目录下重新装依赖
窗口打开但加载图片就报错路径里有中文或特殊空格把工程目录改成纯英文路径
自动检测按钮没有反应模型没有下载成功检查.paddleocr目录下的模型是否完整
检测结果全是空白框检测模型和工具版本不匹配换成对应版本的模型文件

这些看起来都是小问题,但每一个都能让人折腾半天。最稳妥的做法是严格按照仓库文档的依赖安装,不要跳过任何一条。尤其是只用pip install paddleocr而不clone仓库的同学,后面启动PPOCRLabel经常缺这缺那,我实际用下来还是clone源码最完整。

3. 第一次完整标注流程,从打开图片到导出标注

3.1 图片目录的准备

启动PPOCRLabel后,左侧是图片列表,右侧是两个面板,一个放检测框候选列表,另一个放识别文本输入。第一次使用,建议先把图片整理到一个干净的文件夹里,图片命名最好规则化,比如001.jpg、002.jpg,不要用特别长的中文名,也不要混合存放不同来源的图片。

图片格式方面,JPG和PNG是最稳的,只要读取没问题基本都可以。要注意的是图片尺寸,如果单张图超过四五千万像素,加载和模型推理都会很慢,建议先对超大图做切片处理,标注完成后再把切片的标注坐标换算回原图。这一步很多人会忽略,结果标注到一半程序卡死,前功尽弃。

3.2 自动检测和人工框选怎么配合

图片打开以后,最省事的操作是先点自动检测。工具会用检测模型把当前图片里看起来像是文字的区域标出来,然后把结果列在右侧列表里。正常情况下,票据、单据这类版式相对固定的图片,检测框命中率很高。

自动检测不可能完全可靠,我的习惯是把它当作“初稿”,然后人工逐条检查。如果发现漏检的区域,用手动框选补上;如果发现误检的区域,比如把印章、条形码、背景纹理框进去了,直接删掉。框选的时候尽量贴着文字边缘,不要留大片空白,也不要切到文字中间,这在后面训练质量里是硬指标。

手动绘制框的时候,PPOCRLabel用的是四点四边形,不是简单的水平矩形。也就是说你可以沿着倾斜文字的方向点出四个角点,框跟着文字走,而不是等图片旋转之后再画。这一点非常重要,因为实际场景里大量文本是倾斜的,用轴对齐矩形会圈进很多干扰像素。

3.3 识别文本:用模型还是手输

框确定以后,右侧要填这段框对应的文本内容。PPOCRLabel可以和识别模型联动,自动把框里的文字识别出来,填到对应的文本框里。此时人工要做的是检查识别结果有没有错字、漏字和多余字符。

我见过很多人偷懒,自动识别出来就不看了,结果训练文本里全是错别字和多余空格,模型精度一路狂跌。正确做法是哪怕自动识别看起来很好,也要对一遍。遇到模糊、低对比度、生僻字、特殊编号,就直接手动输入。手动输入时注意不要带额外的左右空格,标点符号按图片实际内容保留,看不清的字符宁可标注成难例,也不要硬编造一个字符进去。

3.4 快捷键的正确打开方式

PPOCRLabel的快捷键在不同版本之间会有变化,所以我不会给你写一串死键位。你要做的是打开软件后看一眼“帮助”菜单或者界面上悬停提示,先搞清楚当前版本里“创建框、删除框、切换下一张、确认标签”这些动作分别绑在哪些按键上。

我自己的使用习惯是,把最高频的几个动作固定成肌肉记忆:新建框、删除误检框、保存当前标注、切到下一张。这四个动作一天要重复几百次,如果每次都点鼠标菜单,效率会低很多。同样重要的是小心误触,我曾经连续按错键删掉了已经标注好的框,后来养成了每标注完几张就立刻保存一次的习惯。

4. 决定训练效果的标注质量规范

4.1 文本框怎么画才算规范

标注规范这件事,比工具本身更容易被忽略,但它的影响非常直接。检测框画得是否规范,决定了模型在检测阶段能不能把文字区域和背景分开;文本转录是否准确,决定了识别模型能不能学到正确的字符序列。

我订过一套自己的标注规矩,核心原则只有三条:框住文字主体,不要框大背景;一个语义完整的文本区域不要拆成多个框;一行文字有轻微弯曲时,用四个角点尽量贴合文字走向。

举个例子,发票上“金额”两个字,如果你只框“金额”这两个汉字,那没问题,因为这是独立语义;但如果一行文字是“发票号码:12345678”,最合理的做法是整个框住这一行,而不是把“发票号码”和“12345678”拆成两个框。拆框会让模型把局部字符组合当成固定样本,训练出来的识别结果反而不稳定。

4.2 方向、特殊结构到底怎么处理

倾斜是OCR标注里最常遇到的情况。PPOCRLabel支持画四点斜框,所以不用提前旋转图片,但你要保证框内的文本在训练数据里是“可读方向”的。什么意思?就是框里那一串字的阅读顺序,应该和人眼看到的阅读顺序一致,不能左一个右一个乱排。

多行文本的问题要特别小心。如果一个文本框里包含两行文字,我的建议是拆成两个检测框,每行一个,每个框单独转录。因为现在主流OCR识别链路里,检测和识别模型基本按“行”来理解文本,你把多行塞进一个框,识别模型很容易把它当成一整段连续字符来学,效果一般不好。

遇到印章压字、文字被污渍覆盖、或者文本只有一半的情况,我建议不要强行输入完整文本,而是标记成难例。PPOCRLabel的导出格式里保留了difficult字段,把这类样本标出来,训练时可以对这些难例做特殊处理,提升模型在复杂场景下的鲁棒性。

4.3 空转录和错转录是最大的隐性雷区

标注数据里最隐蔽的问题不是框画歪了,而是有些框的转录文本是空的,或者文本里混入了不可见字符。空转录常见于自动检测后漏掉了识别环节,你在检查框的时候没注意到右侧文本框是空的就保存了;不可见字符则常见于文本在中间环节被复制粘贴过,混入了制表符、换行符和全角空格。

我写了一个最简单的校验脚本,在导出后扫描一遍数据,把所有“有框但没有转录文本”的样本找出来,这个动作已经救过我很多次。脚本核心思路就是逐行读取标注文件,解析出每个框和转录文本,如果发现转录为空,就打印出对应的图片路径,提醒复查。

import json from pathlib import Path label_file = Path("Label.txt") bad_items = [] for line in label_file.read_text(encoding="utf-8").splitlines(): parts = line.split("\t", 1) if len(parts) < 2: bad_items.append((line, "缺少标注内容")) continue image_path, content = parts[0], parts[1] try: boxes = json.loads(content) except Exception: bad_items.append((image_path, "JSON解析失败")) continue for box in boxes: text = box.get("transcription", "") if not text.strip(): bad_items.append((image_path, "空转录")) print(f"发现 {len(bad_items)} 个可疑样本") for item in bad_items[:30]: print(item)

每次导出前跑一遍这个脚本,能省掉大量返工时间。文本里如果出现肉眼都看不出来的不可见字符,后续训练时报错或字表对不上,排查成本非常高。

5. 导出文件的底层格式与训练管线对接

5.1 一次导出会得到什么

标注工作完成以后,点击导出,工具会在你图片目录下生成一套训练用的数据文件。不同版本导出细节略有差异,但我用到的版本会生成这样几个东西:Label.txt、一个crop_img目录,以及Rec_gt.txt。

Label.txt是检测任务的标注文件,里面维护了图片路径和对应的检测框信息;crop_img目录保存的是每个检测框对应裁剪出来的小图;Rec_gt.txt则是识别任务需要的标注文件,每条包含一张裁剪图片和它对应的文本内容。这套结构基本就是PaddleOCR训练前需要的输入雏形。

5.2 检测训练格式怎么对上

PaddleOCR的检测训练通常需要每张图片对应一个或多个检测框,框用四个点的坐标表示,再加上转录文本。PPOCRLabel导出的Label.txt内容大概长这样:

images/001.jpg [{"transcription": "金额", "points": [[56, 88], [210, 88], [210, 118], [56, 118]], "difficult": false}]

在PaddleOCR训练配置里,路径一般指到类似train_data/det_label.txt这样的文件,对应关系就是:前面是图片相对路径,后面是检测框数组的JSON。只要你把导出结果按训练配置的路径管理好,检测阶段数据就能直接用。

这里有一点要提醒:points里的四个坐标顺序要保持一致,建议按左上、右上、右下、左下的顺序。坐标顺序乱掉以后,训练时数据增强里的随机旋转会出问题,模型会学到错误的框走向。

5.3 识别训练格式怎么对上

识别训练需要的是另一种形态的数据:裁剪出来的小图和这段小图上的文字。PPOCRLabel导出时会把每个检测框对应的小图裁剪到crop_img目录,裁剪图和文本之间的对应关系写在Rec_gt.txt里,类似这样:

crop_img/001_0.jpg 金额 crop_img/001_1.jpg 发票号码:12345678

在PaddleOCR识别训练里,你通常还需要准备一个字符集合文件,也就是字典。把标注数据里出现的所有字符收集起来去重,一行一个字符,顺序和训练配置里指定的一致。如果字符集不完整,训练时对应字符就会变成“未定义”,损失直接波动。

5.4 训练集和验证集划分的一条硬规矩

数据集划分最忌讳的是按裁剪图划分。不能把同一张原图裁剪出的多个小图,一部分放进训练集、一部分放进验证集,这会造成严重的数据泄漏,模型验证分数虚高。正确做法是先把原图按比例划分成训练集和验证集,再让每个集合各自对应自己的检测标注和识别标注。

我常用的比例是九比一,如果数据量大,八比二也可以,但至少保证验证集里有足够多的不同原图类型。划分完之后,把Label.txt和Rec_gt.txt对应拆成train和val两份,再交给训练脚本。这个步骤虽然简单,却是最容易让人忽略的坑。

6. 我踩过的坑,希望你不再踩

6.1 自动检测把印章误判成文字

票据类图片里,红色印章和文字叠在一起是最常见的,也是自动检测最容易出问题的。印章的文字会和正文交叉重叠,模型会把印章边缘、发票专用章那几个字一起框出来,或者把正文和印章文字混在同一个框里。

这种场景下,我的经验是不要完全依赖自动检测的框,手动重新框一遍更靠谱。框选时只框完整可读的正文,印章里模糊、重叠、残缺的文字,不要硬转录;如果一段文字确实被印章严重遮挡,鉴于是难例,保留difficult标记。最终模型反而能在检测阶段学会忽略这些干扰框。

6.2 用Excel改标注文本导致JSON解析失败

有一次我图省事,导出Label.txt后用Excel批量替换了一批错字,保存后再回去跑训练,结果程序一直报错。原因是Excel把文本里的英文引号、逗号、花括号都自动做了替换,还把false变成了False,纯文本JSON格式直接被破坏掉了。从那以后我再也不用办公软件碰标注文件,所有文本替换都改成专门的小脚本,用Python处理,编码强制UTF-8,格式不会变。

6.3 长图标注到一半进度全丢

我踩过最痛的一次坑,是标注一批高分辨率长图,一张图里密密麻麻排了二三十个小框。标到一半,软件直接卡死,重开以后发现标注结果没有保存全。后来我调整了策略:高分辨率大图先切成若干小块再标注,切块后的图片最大边长控制在像素范围内,既保证模型推理不卡,也避免单张图片标注文件过大导致保存失败。切完的标注坐标要记得按切片偏移量换算回原图坐标,换算脚本很简单,但换算出的坐标才是训练真正需要的值。

6.4 多人协作时文件管理混乱

如果是团队标注,最忌讳的是多人同时在同一个目录下操作,互相覆盖标注结果。我的做法是把原图按人名或批次分目录,每个人认领独立目录,标注完成后统一合并。合并之前跑一遍校验脚本,检查有没有重复图片、空转录、JSON解析失败这三类问题。合并完的最终文件再按上一节的方式切分训练集和验证集,这样即使某一个目录出问题,也能快速定位回对应批次的标注文件。

在我实际标注数据和模型迭代的过程中,PPOCRLabel真正解决的问题,是把“人工逐字录入”和“模型自动预测”这两个环节自然地接在一起。越是图片质量差、业务场景杂的项目,你对标注格式和标注规范的控制越要细。建议第一次用的时候,先拿二十张图完整走一遍从打开图片到导出数据的流程,确认每个字段的含义之后再批量铺开。这个前期投资,在后续训练排障的时候一定会加倍赚回来。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进