ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

PaddleOCR 2.0 本地部署实战:CPU批量识别与坐标输出全攻略

PaddleOCR 2.0 本地部署实战:CPU批量识别与坐标输出全攻略 简介PaddleOCR 2.0 是一款基于 PaddlePaddle 的文字识别工具面向需要离线批量处理图片文字的办公人员、开发者与内容整理者。支持本地单机运行无需云端服务具备中文与英语识别能力可对图片进行旋转、镜像后识别也能延时截图识别并支持批量打开图片文件列表配合 CPU 检测与识别区域坐标查看适合日常办公、资料数字化、截图提取等场景。资源以 zip 压缩包形式提供整体约 98.84MB解压后即可使用无需额外复杂配置。已有 204 人学习下载包内整合了核心识别功能与配套程序可帮助用户快速搭建本地 OCR 环境减少重复手工录入提升图文处理效率。1. 为什么我留了一套 PaddleOCR 2.0 本地包批量识别不是跑通 demo 就完事我最早把 OCR 批量识别想简单了。拿一台 2015 年的老笔记本处理三百张课堂板书照片官方 PaddleOCR demo 跑单张顺得很真把图片批量导进程序才发现延时截图要自己做、旋转和镜像要先处理、识别坐标没人给你看、旧 CPU 能不能撑住也是黑匣子折腾到第二天才勉强跑完一个完整流程。PaddleOCR 2.0 这套资源就是把上述环节打包好的本地单机方案——模型文件、CPU 检测、批量识别、坐标导出一体不依赖联网下载模型也不吃 GPU。适合经常处理截图、扫描件、报表的测试、运营和文档管理员尤其是手边只有办公电脑的人。新版模型这几年迭代很快但如果你要的是“稳定的离线批量识别工具”2.0 这套仍然值得先部署起来。2. 本地部署 PaddleOCR 2.0三段式模型、CPU 检测与第一张图的识别2.1 模型目录拆开看检测、方向分类、识别三个环节先理解 PaddleOCR 2.0 的模型结构部署才不会踩错。它把文字识别拆成三段检测模型在整张图里找“哪里是文字”方向分类模型判断每个文本区域是否需要旋转校正识别模型再把校正后的区域转成字符串。2.0 时代默认是把这三个模型分开存放而不是一个大模型包搞定一切。PaddleOCR-2.0-local/ ├── models/ │ ├── ch_PP-OCRv2_det_infer/ # 文本检测找文字区域 │ ├── ch_PP-OCRv2_rec_infer/ # 文本识别区域转字符 │ └── ch_ppocr_mobile_v2.0_cls_infer/ # 方向分类把旋转文本转正 ├── imgs/ ├── predict.py ├── batch_predict.py ├── gui.py └── requirements.txtdet 输出的是文字框坐标cls 判断每个框是否需要转正rec 做实际字符识别。mobile 系列的模型体积小适合 CPU 单机推理如果机器内存够大也可以换 server 系列模型精度更高但推理时间翻倍。离线包的价值就在这些 infer 目录已经躺好在 models 里启动时直接加载不会出现官方版本那样运行到一半自动下载模型、卡在进度条的情况。依赖安装也简单2.0 时代的核心依赖就三个pip install -r requirements.txt # 或者手工装 pip install paddlepaddle2.0.2 pip install paddleocr2.0.6 pip install shapely pyclipperPython 版本建议 3.63.8Python 3.9 以上装 pyclipper 容易遇到编译报错常见做法是直接下载对应平台的 whl 本地安装。paddleocr 会自动拉取飞桨但老机器装 paddlepaddle 前最好先确认 CPU 是否支持 AVX不支持就要换 noavx 版这个在 2.3 节单独说。2.2 跑通第一张图最小 Python 调用与返回结构模型目录就位后跑通第一张图是最有成就感的一步。核心代码最短可以是这样from paddleocr import PaddleOCR ocr PaddleOCR( det_model_dirmodels/ch_PP-OCRv2_det_infer, rec_model_dirmodels/ch_PP-OCRv2_rec_infer, cls_model_dirmodels/ch_ppocr_mobile_v2.0_cls_infer, use_angle_clsTrue, # 是否启用方向分类 langch, # 中英文混排识别 use_gpuFalse, # 本地 CPU 推理 show_logFalse, # 关闭调试日志 ) result ocr.ocr(imgs/class01.png, clsTrue) for idx, line in enumerate(result): box, (text, conf) line print(idx, [round(p, 1) for p in box], text, round(conf, 4))返回结构是 list每个元素对应一个检测框第一个值是四角坐标顺序是左上、右上、右下、左下第二个值是二元组包含识别文本和置信度。打印出来的结果大概是0 [[145.0, 98.0], [385.0, 96.0], [386.0, 168.0], [146.0, 170.0]] 第一章 0.9992 1 [[102.0, 198.0], [360.0, 196.0], [361.0, 236.0]] 文本检测与识别 0.9987use_angle_clsTrue 会额外做一次方向分类CPU 上每张图多花几十毫秒但对手机拍的倒置照片、扫描件反放非常关键。langch 决定加载中文字典如果图片是纯英文改成 langen 识别速度略快中英混排则必须保持 ch。drop_score 参数可以放在 PaddleOCR 初始化里低于该阈值的识别结果直接丢弃默认 0.5处理模糊截图时可以适当降到 0.3但别低于 0.2否则一堆噪声文本会混进来。2.3 CPU 检测在查什么AVX 指令集与线程数的取舍PaddlePaddle 的 CPU 版本分两种带 AVX 指令集优化的普通版和 noavx 版。AVX 是 CPU 的向量运算扩展指令2008 年后的主流处理器基本都支持但虚拟机、低功耗工控机、老服务器上经常缺失。装了普通版飞桨又跑 AVX 指令轻则报 Illegal instruction 崩溃重则整个 Python 进程直接被杀。资源包里的 CPU 检测脚本就是先确认指令集再决定加载方式和性能参数from cpuinfo import get_cpu_info info get_cpu_info() flags set(info[flags]) # AVX 需要 CPU 支持 AVX 且操作系统已启用 YMM 寄存器即 osxsave has_avx avx in flags and osxsave in flags num_threads 4 if has_avx else 2 rec_batch_num 6 if has_avx else 2 if not has_avx: print(当前 CPU 不支持 AVX建议换 noavx 版飞桨) ocr PaddleOCR( use_gpuFalse, langch, num_threadsnum_threads, rec_batch_numrec_batch_num, )AVX 对应的 CPUID 标志位在 ECX 寄存器的 bit28而 OSXSAVE 在 bit27两个标志同时存在才可能真正启用 AVX。py-cpuinfo 把这一层封装好了直接用就行。检测的意义不只是“能不能跑”还影响性能和稳定性没有 AVX 时线程数从 4 降到 2rec_batch_num 从 6 降到 2识别速度不会惊喜但至少不会崩。真正的老机器再降一档识别大图前先把图像缩放到 2000px 宽以内能省下大量时间。3. 截图、旋转与坐标把 PaddleOCR 2.0 当日常 OCR 工具用3.1 延时截图参数与黑屏兜底延时截图解决的是“先切到目标窗口再开始抓取”的场景——比如打开聊天记录、下拉菜单、展开报表后再截屏。资源包 GUI 里这项独立成功能命令行下的最小实现是这样import time import mss delay 5 # 留给手动切换窗口的秒数 for i in range(delay, 0, -1): print(f{i}s 后截图...) time.sleep(1) with mss.mss() as sct: sct.shot(outputshot_01.png) print(截图完成shot_01.png)delay 的合适范围是 38 秒太短来不及切窗口太长等得心焦。mss 截图比 PIL 的 ImageGrab 快不少因为它直接走系统底层抓屏接口不需要经过 GDI 的像素转换多显示器环境用 monitor 参数指定屏幕编号。参数含义建议delay触发到截图之间的等待秒数38output截图保存路径用相对路径识别后及时改名备份monitormss 屏幕编号单屏默认 1多屏先确认编号再填bbox区域截屏格式为 (left, top, width, height)全屏识别可以省略黑屏是延时截图最常见的翻车点。现象是图片文件存在但打开全黑或只有桌面壁纸。原因通常是某些全屏独占程序在 GDI 抓屏下返回黑帧mss 在多数情况能绕过但如果目标程序用了独占全屏渲染mss 也拿不到画面。兜底做法是先截一张预览确认画面正常再执行 OCR如果确实截不到只能让程序先退出全屏或改窗口模式。提示延时截图和“截图后延时”是两种场景。前者等窗口切换后者等渲染完成比如切到视频画面后等 2 秒让播放器画出第一帧。自己做热键脚本时建议把两个延时拆开别共用一个参数。3.2 旋转和镜像什么时候该手动预处理PaddleOCR 2.0 的方向分类模型只处理接近 0/180 度的倒置90 度翻转、镜像要手动预处理。很多新手把图丢进去就能出结果但横排文本转成竖排时会识别成一列乱码这属于“不如先转一下”的典型场景。图像状态处理方式接口/参数倒置 180 度开启方向分类自动转正use_angle_clsTrue向左/向右旋转 90 度PIL rotate 手动旋转img.rotate(90 或 -90, expandTrue)水平镜像文字反序PIL transposeimg.transpose(Image.FLIP_LEFT_RIGHT)垂直镜像上下颠倒PIL transposeimg.transpose(Image.FLIP_TOP_BOTTOM)预处理代码示例from PIL import Image for name in [scan_a.jpg, scan_b.png]: img Image.open(name) # 横排文本被旋成竖排时先转回 90 度 img img.rotate(-90, expandTrue, fillcolor(255, 255, 255)) # 扫描件反放时用水平镜像 img img.transpose(Image.FLIP_LEFT_RIGHT) img.save(fixed_ name)rotate 的两个细节值得记住expandTrue 防止旋转后图像被裁边fillcolor(255, 255, 255) 把旋转产生的空白区域填成白色否则黑底会被检测模型误判成大片文字区域识别结果里出现一整圈黑色边框框。镜像操作则不需要 expand因为尺寸不变。遇到不确定方向的图先开 cls 跑一遍用输出坐标的文本可读性判断要不要手动旋转这种“先看结果再决定”的思路比盲猜效率高。3.3 识别区域坐标查看从文本到结构化落盘坐标输出的价值不只是画框。PaddleOCR 2.0 的结果里每个检测框都带四角坐标把坐标和文本一起落盘后续才能做模板匹配、区域筛选、自动核对。GUI 里“查看识别区域”的功能本质上就是把 box 坐标叠回原图再可视化。import json from paddleocr import PaddleOCR ocr PaddleOCR(langch, use_gpuFalse) res ocr.ocr(scan_01.png, clsTrue) blocks [] for box, (text, score) in res: if score 0.5: continue # 置信度低于 0.5 的当噪声丢弃 blocks.append({ points: [[round(x, 2), round(y, 2)] for x, y in box], text: text, score: round(float(score), 4), }) with open(scan_01_result.json, w, encodingutf-8) as f: json.dump(blocks, f, ensure_asciiFalse, indent2)坐标保留 float 而不是转成 int后续如果对图片做缩放可以用比例还原准确位置。points 的四点顺序固定是左上、右上、右下、左下画线时按顺序依次连接最后回到起点闭合才能画出正常的四边形顺序乱画出来就是交叉线。这里还有一个容易忽略的点如果不是对原图识别而是先旋转再识别保存下来的坐标是旋转后图像上的坐标。要映射回原图得把旋转矩阵反向算一遍否则画回去位置对不上。这个问题在第六章会再提到。4. 避坑五个真实翻车场景与排查顺序4.1 识别结果成碎字det 阈值调过头了现象一句完整的文本被切成十几个单字检测框大量嵌套背景纹理也被框进去。原因det_db_thresh 默认 0.3 已经是够用的值。有人为了“多抓点文字”把它调到 0.1 或 0.2检测模型把纸张纹路、阴影、水印都当成文本区域结果每个字一个框上下文的语义全被打散。解决阈值调回 0.30.5 区间det_db_box_thresh 保持 0.6 左右表格密集或者背景复杂的图才需要往 0.5 方向调。ocr PaddleOCR( langch, use_gpuFalse, det_db_thresh0.3, det_db_box_thresh0.6, drop_score0.5, )调完阈值后务必拿一张带复杂背景的截图重新验证不要拿纯白底的文档测试那测不出问题。4.2 启动就报 AVX 非法指令CPU 检测不是摆设现象资源包在正常办公电脑上跑得好好的换到一台虚拟机或老 CPU 机器上Python 进程直接崩溃或者报 Illegal instruction。原因飞桨默认安装包按 AVX 指令集编译老 CPU 不支持 AVX代码执行到 AVX 指令时 CPU 无法识别直接触发非法指令异常。解决先用 2.3 节的 cpuinfo 检测确认没有 AVX 后换官方 noavx 版飞桨。安装方式不是 pip 默认源而是下载对应平台的 noavx wheel 后本地安装装完重新跑一遍 CPU 检测脚本确认提示变为支持状态。这台机器的 rec_batch_num 和线程数都按保守档设置别试图用大 batch 提升速度收益微薄还容易崩。4.3 中文图片识别成英文串lang 与模型目录不匹配现象图像是中文输出是英文字母或拼音串且置信度显示得还挺高。原因lang 参数决定字典和字符映射表。langch 时默认加载中文字典但如果手动指定的 det_model_dir、rec_model_dir 混搭了英文模型目录字典索引错位中文文字被映射到英文字符上。解决三个模型目录统一使用同一套 ch 模型包。资源包内模型目录名带 ch 前缀就是为了避免混搭只更换其中一个模型目录时保持 lang 不变换完先跑一张已知中文内容的图片验证。这条是模型混用的经典坑新版模型之间差异更大更要注意。4.4 批量跑到一半内存爆掉实例和 batch 的锅现象单张识别完全正常批量处理 30 张左右开始变慢40 张之后内存占用飙升甚至进程被杀。原因两个常见元凶。一是 PaddleOCR 实例写在了循环体内部每张图都重建一次模型加载二是识别结果的列表无限累积中间变量一直不释放。这两个叠加内存不爆才怪。解决PaddleOCR 实例只创建一次放到循环外每张图处理完把中间结果置空结果只保留需要的字段rec_batch_num 从 6 降到 4。如果一次要处理几十张超过 3000px 宽的大图先批量缩放到 2000px 宽再进 OCR速度和内存都会好很多。4.5 坐标对不上鼠标Windows DPI 缩放在作怪现象识别框画回原图上位置偏移左上侧偏移尤其明显偏移量差不多是 1.5 倍。原因Windows 显示缩放设为 150% 时PIL ImageGrab 和很多界面库使用逻辑像素而 PaddleOCR 处理的是物理像素。两种坐标系混用画框位置自然对不上。解决截图、显示、落盘统一用物理像素。Windows 下常见做法是先用 ctypes 调用 SetProcessDpiAwareness 设置进程 DPI 感知再执行截图不做全局设置就手动按缩放系数换算坐标。资源包 GUI 的“查看识别区域”功能默认做了换算自己写脚本时记得处理这个环节。5. 把 OCR 接进工作流批量导出、剪贴板与快捷键5.1 批量结果导出 CSVutf-8-sig 与坐标 JSON 双保险批量识别跑出来的结果最常用的落盘格式是 CSV。但有两个细节决定文件“能不能直接用”Excel 打开 CSV 默认按系统编码读中文字符串用 utf-8 存会乱码必须用 utf-8-sig坐标列存成 JSON 字符串后续要画框或做模板匹配时直接 json.loads 还原不用再手拆字符串。import csv import json import os from paddleocr import PaddleOCR ocr PaddleOCR(langch, use_gpuFalse) files [ os.path.join(imgs, f) for f in os.listdir(imgs) if f.lower().endswith((.png, .jpg, .jpeg)) ] with open(ocr_out.csv, w, newline, encodingutf-8-sig) as f: writer csv.writer(f) writer.writerow([图片, 文本, 置信度, 区域坐标]) for path in files: for box, (text, score) in ocr.ocr(path): writer.writerow([ os.path.basename(path), text, round(float(score), 4), json.dumps(box), ])识别实例在循环外只创建一次这是批量性能的关键。CSV 路径里的 newline 也要保留否则 Windows 下每行之间会多一个空行。这个脚本适合每天跑一批固定目录的截图配合 Windows 任务计划程序定时执行OCR 就变成了一个无人值守的日常任务。5.2 延时截屏 剪贴板输出做一个全局 OCR 热键把延时截图和 OCR 串成一个函数再绑一个快捷键就是日常最好用的 OCR 工具——看到屏幕上有文字想复制但选不中按一下热键文本已经进剪贴板。import time import mss import pyperclip from paddleocr import PaddleOCR ocr PaddleOCR(langch, use_gpuFalse, drop_score0.5) def ocr_screen_to_clipboard(delay3): # 第一步等待窗口切换 for i in range(delay, 0, -1): print(f{i} 秒后截屏...) time.sleep(1) # 第二步mss 全屏截图 with mss.mss() as sct: sct.shot(output_tmp_ocr.png) # 第三步识别并过滤低置信度结果 texts [t for _, (t, s) in ocr.ocr(_tmp_ocr.png) if s 0.5] pyperclip.copy(\n.join(texts)) # 第四步删掉临时截图 import os os.remove(_tmp_ocr.png) ocr_screen_to_clipboard(delay3)配合 keyboard 库监听快捷键一键触发这个函数整套流程就齐了。注意临时截图文件路径固定每次调用前自动覆盖识别完立刻删除避免磁盘上堆积大量截图。4K 屏用户截全屏后建议先用 PIL 把图像宽缩到 2000px 再交给 OCR速度差距非常明显精度损失在普通文字场景下几乎不可感知。5.3 模型边界什么时候该换 v6 tiny 再说速度PaddleOCR 2.0 资源包的优势是模型和脚本一处打包、离线跑通不足是推理速度已经被后来的 v6 tiny 等新模型甩开。v6 tiny 适合追求单图毫秒级响应、机器配置又不错的场景但它的输出字段、模型目录结构和 2.0 的 PaddleOCR 调用方式不完全一致直接替换会遇到 4.3 节说的字典混搭问题。我的建议是先让 2.0 的批量流程稳定跑起来建立坐标落盘和模板验证的习惯真要追求速度单独开一条测试线去适配新模型两条线并行不要在生产脚本里直接改模型目录。6. 坐标回填验证把 OCR 输出当断言用识别完成别直接抽文本先把坐标回填到原图再用模板区域做断言这是我从表格识别和票据核对场景里沉淀出来的习惯。比如一张发票模板里预先定义好“姓名”“金额”“日期”三个字段的期望区域识别完成后用每个检测框的中心点判断落入了哪个区域再输出该区域的文本和置信度。这样 OCR 的输出就不再是一堆无结构文本而是可以自动核对的结构化结果。import json from paddleocr import PaddleOCR from PIL import Image, ImageDraw ocr PaddleOCR(langch, use_gpuFalse) res ocr.ocr(invoice_01.png, clsTrue) img Image.open(invoice_01.png).convert(RGB) draw ImageDraw.Draw(img) # template.json 结构{姓名: [x1, y1, x2, y2], 金额: [x1, y1, x2, y2]} with open(template.json, encodingutf-8) as f: zones json.load(f) hits {} for box, (text, score) in res: cx sum(p[0] for p in box) / 4 cy sum(p[1] for p in box) / 4 for field, (x1, y1, x2, y2) in zones.items(): if x1 cx x2 and y1 cy y2: hits[field] (text, score) # 命中的框叠加到原图方便肉眼复核 draw.line(box [box[0]], fillgreen, width2) img.save(annotated.png) print(hits)逻辑很简单每行识别结果算出中心点循环模板字段判断中心点落在哪个区域有命中的区域就在图上画绿框。这里面有个必须注意的前提——模板坐标必须和模型输入的像素坐标一致否则区域判断永远偏移第六章开头说的 DPI 问题在这里会再次爆发。实际使用时可以先跑一张已知排版正确的样本生成模板再拿新图片去核对。这个验证方法教会我一件很重要的事OCR 的坐标框不是给人看的辅助信息而是判断识别是否可信的关键依据。文本可以识别错但文本位置的偏移往往能暴露出预处理、缩放、坐标系里更深层的问题。我最初做票据识别时完全不看坐标输出只抽文本往 Excel 里填结果有一次模板改版文字全部识别成功但位置整体右移填进去的字段全部错位返工花了一整天。从那以后我每次跑完 OCR 都强制先做一遍坐标回填验证确认框的位置和文本对得上才敢把结果交给下游流程。这套 PaddleOCR 2.0 本地包也是这样部署、批量、坐标、验证一路跑通后面换什么新模型都不慌。希望帮到你。本文还有配套的精品资源点击获取
返回列表