1. 项目由来:当“批量重命名”遇上乱糟糟的扫描件
先说说我为什么要折腾这东西。手头经常要处理一堆PDF发票、合同扫描件、课程截图,还有从微信群里保存下来的各种图片。这些文件的默认命名简直是灾难现场:“微信图片_20250423153012.jpg”“扫描件_001.pdf”“image_20250423_153012.png”——正常人根本看不出里面装的是什么内容。真要急用的时候,只能一张张打开、看一下、再手动改成有意义的文件名。十几张还好,几百张的时候纯属磨人性命。
市面上不是没有OCR工具,Adobe Acrobat能识别,但那是付费的;在线工具得传文件,隐私先把人劝退。自己用Python写过批量识别脚本,用Tesseract效果差强人意,中文识别率一言难尽,遇到倾斜、模糊的扫描件基本就废了。
直到我换了PaddleOCR,识别率算是真正能打的了,但批量处理起来还是不够顺手——没有图形界面,参数调起来全靠改代码,家里长辈或者办公室同事想用,根本教不会。
后来我看到了Umi-OCR这个项目,发现它背后的识别引擎是PaddleOCR-json,一个把PaddleOCR封装成命令行工具的方案,不需要装Python环境,直接发送图片路径就能返回识别结果,输出还是JSON格式,解析特别方便。顺着这个思路,我用Python + PySide6做了个桌面工具,把OCR识别和文件重命名这两个环节串起来,这就是“OCR-RenameStudio”的由来。简单说,项目干的事情就三件:扫描图片或PDF → 识别其中的文字 → 根据规则提取关键信息,自动改成可读的文件名。
这个工具适合谁用?如果你手头有成堆的发票、合同、考试试卷、名片、聊天截图、网课课件,需要按内容批量命名,那这个项目基本就是给你准备的。对技术基础的要求不高,能装Python、能运行脚本就行,核心逻辑我都已经写好,自己按需改规则即可。
2. 整体设计思路:为什么选PaddleOCR-json而不是直接上PaddleOCR
我在项目立项的时候,核心纠结只有一件事:识别引擎到底怎么接。
PaddleOCR本身是个完整的深度学习OCR框架,提供了Python接口,也支持训练自己的模型。但对这个项目来说,它有致命的问题——部署太重。桌面工具要发给别人用,总不能让每个人先去装Python、再装PaddleOCR的依赖、再下载模型参数吧?光是装GPU版还是CPU版就能劝退一堆人,更别提PaddleOCR每次初始化的时候要加载模型,动不动吃掉几百MB内存。
2.1 PaddleOCR-json的关键优势
PaddleOCR-json本质上是把PaddleOCR的推理过程做成了一个独立的可执行程序,直接通过命令行交互。你给它一个图片路径,它把识别结果以JSON格式打印出来,然后退出。没有额外的Python依赖,不用碰模型文件,拿过来就能跑。Umi-OCR之所以能用,靠的就是这层封装。
我用实际例子对比一下两套接入方式的差异:
直接调PaddleOCR的Python接口,需要处理的事情包括:装paddlepaddle、装paddleocr库、初始化OCR对象、管理模型下载路径、处理不同的图像格式。这些步骤每一步都可能出错,尤其在国内网络环境下,模型下载还可能失败,很折腾。
接PaddleOCR-json就清爽多了:下载对应的可执行文件,启动子进程,把图片路径作为参数传过去,从标准输出里读取JSON结果。Python代码里一个subprocess.Popen就能搞定,测试一两次就能稳定跑起来。
2.2 桌面端选型:用PySide6的私心
识别引擎定了之后,UI层我选了PySide6。原因很朴素:一个是Python写起来快,另一个是PySide6的QTableView拖文件进来显示预览列表特别方便,开发效率比原生C++高好几倍。
界面设计上,我没走花哨路线,就是左边一个文件列表,右边一个预览区,最底下是重命名规则配置面板。核心交互逻辑就三步:拖入文件 → 点“开始识别” → 看结果列表,勾选确认后执行重命名。这个流程简单直接,即使完全不懂技术的人也能上手。
3. 核心功能拆解:规则引擎才是灵魂所在
OCR识别只是把图片里的文字捞出来,真正让这个工具好用的,是它背后的规则引擎。我做了几天后发现,识别文本出来只是第一步,怎么从一大段文字中提取出“发票号码”“客户名称”“日期”这些关键字段,才是重命名智能化的关键。
3.1 文本解析规则怎么设计
规则引擎我分了三层来处理:
第一层是正则表达式匹配。比如发票号码、合同编号、日期这类格式相对固定的内容,直接用正则在识别结果全文里找。日期常见格式\d{4}[-年]\d{1,2}[-月]\d{1,2}日?,发票号常见格式[A-Z]{2}\d{8,10},都能命中。这一层的优点是匹配准确、速度快,缺点是只对格式规范的内容有效。
第二层是关键词定位,针对第一层匹配不到的场景。比如“客户名称”这种没有固定格式的字段,我先在全文里搜索“客户”“购方”“付款单位”这类关键词,找到它出现的位置,然后截取后面N个字符作为候选值。这个思路听起来简单,实操的时候要注意:截取的长度必须能覆盖可能出现的名称长度,但又不能太长以至于把无关内容也纳进来。我试过取值20个字符,但有些公司全称太长,最后调整到50个字符,再结合常见后缀词(有限公司、股份有限公司)来截断,效果好了很多。
第三层是兜底策略。前面两层都匹配不到的时候,就取识别文本的前N个字符作为文件名,至少保证可读性。这一层虽然简单,却是整个流程能跑通的关键。
3.2 多文件批处理的任务队列设计
一开始我是串行逐个识别,一次只处理一个文件。等文件数量上来之后发现问题很大——识别慢的时候,单个文件要两三秒,几十个文件就是几分钟,期间界面完全卡死。
后来改成生产者-消费者模式:主线程负责读取文件列表、往任务队列里塞任务;OCR识别放在子线程里跑;识别结果用信号发回UI线程更新列表。这样界面始终能响应,而且后续扩展多进程并行识别也方便。
任务队列还要考虑异常情况:某个图片损坏了、某些PDF加密了、某些文件路径包含中文导致识别引擎崩溃。每个任务都必须有独立的异常捕获,失败的要标记出来,不能因为一个文件出错整个队列就停下来。
3.3 PDF文件的预处理链路
PDF是我们日常遇到最多的扫描件类型,但PaddleOCR-json只接受图片输入,所以PDF得先转成图片。我开始用的PyMuPDF(fitz),直接按300dpi渲染,一张A4纸大概能渲染出2000x2800的图,清晰度足够识别。
后来发现一个效率瓶颈:如果PDF本身就是扫描版的,每页都适合识别;但如果PDF是从Word导出的文字版,OCR反而多此一举。所以我加了一步判断:先用PyMuPDF尝试提取文本,如果提取出来的非空字符数超过一定阈值,就判定为文字型PDF,直接用内置文本;否则才进入OCR流程。这一步把一部分文档的处理从几秒降到了几毫秒,体验提升明显。
4. 实操全记录:从环境配置到跑通第一个重命名任务
下面按我的实际操作记录来梳理,每一步都写清楚了命令和参数。环境是Windows 11,Python 3.10,其他系统大同小异,遇到坑我会专门标注。
4.1 环境准备与PaddleOCR-json接入
先装基础依赖:
pip install PySide6 PyMuPDF Pillow opencv-pythonPaddleOCR-json本身是独立可执行文件,从Umi-OCR项目的Releases页面下载对应平台的exe版本,解压到一个固定目录。不用装任何Python库就能调用。
调用和验证我写了段最小测试代码:
import subprocess, json # 启动PaddleOCR-json子进程,持续通信模式 proc = subprocess.Popen( r"D:\tools\PaddleOCR-json.exe -port=127.0.0.1:2024", stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) # 发送图片识别指令 proc.stdin.write('{"image_path": "D:/test.png"}\n') proc.stdin.flush() result = json.loads(proc.stdout.readline()) print(result)两种接入方式值得说明一下:一种是命令行模式,每次传一张图片路径,识别完程序退出,重新启动一次开销大;另一种是持续通信模式,程序启动后保持运行,通过标准输入持续发送指令,识别速度快很多,适合批量场景。我最终采用的是持续通信模式,批量识别时几乎无额外启动开销。
提示:Windows下路径里的反斜杠容易出问题,建议统一用正斜杠转换,避免JSON解析报错。
试跑一张截图,识别率超出预期,中文和数字都准确,大约0.5秒出结果。CPU占用率在识别时飙升到80%,但识别完马上降回个位数,不影响正常办公。
4.2 识别精度调优那点事:图像预处理很重要
PaddleOCR-json本身识别能力已经很强,但真实的扫描件质量参差不齐,直接扔给它识别经常翻车。我做了一些图像预处理,实测效果提升很明显。
第一步是转灰度。彩色图片里,文字和背景的颜色差异通常是灰度差异,转成灰度图后,PaddleOCR处理起来更稳。第二步是增加对比度,使用OpenCV的cv2.equalizeHist()做直方图均衡化。这一步对偏灰、发暗的扫描件特别管用。第三步是纠偏,用图像投影法估算倾斜角,然后做仿射变换。如果图片倾斜超过5度,这一步基本不能省。
要注意的是,预处理也不能过度。我一开始给所有图片都做了降噪和锐化,结果部分本来就清晰的截图反而识别变差了——锐化加重了噪点干扰,降噪又让文字边缘发虚。后来改成:清晰图片直接走原图,模糊图片才预处理。判断依据就是Laplacian算子的方差,低于阈值就走预处理。
4.3 规则引擎的核心代码骨架
还是以发票场景为例,核心逻辑大概长这样:
import re def extract_invoice_info(text): """从识别文本中提取发票关键信息""" info = {} # 1. 发票号码 m = re.search(r'发票号码[::\s]*([A-Z0-9]{8,12})', text) if not m: m = re.search(r'[A-Z]{2}\d{8,12}', text) info["invoice_no"] = m.group(1) if m else None # 2. 开票日期 m = re.search(r'开票日期[::\s]*(\d{4}年\d{2}月\d{2}日)', text) if not m: m = re.search(r'(\d{4}[-年]\d{1,2}[-月]\d{1,2}日?)', text) info["invoice_date"] = m.group(1) if m else None # 3. 模板化文件名的组合 if info["invoice_no"] and info["invoice_date"]: return f"{info['invoice_date']}_发票_{info['invoice_no']}.pdf" return None这只是最基础的版本。实际项目里,每个规则类都还可以配置优先级、是否必须匹配到、匹配不到时用什么兜底字段。我建议把规则写成配置化,而不是硬编码在代码里,后面加场景就不用改代码了。
4.4 从“能跑”到“好用的关键一步:重命名预览与跨平台注意事项
没有预览直接重命名,一次失败就足够让人崩溃,所以我把操作拆成了两步。第一步是识别完成后,所有文件旁都会显示新的名字,用户能编辑、删除、重新触发规则,确认无误后再执行重命名。这个设计的核心思路是:OCR识别结果本身就有不确定性,必须留出人工确认的余地,全自动批量重命名很可能在识别错误时全部改错,那样就麻烦了。第二步才是执行重命名,同时写入日志防止出错。文件名字符要排除Windows非法字符<>:"/\|?*,文件名长度要控制在一定范围内。
换到macOS/Linux系统,PaddleOCR-json可执行文件要换对应平台版本,PyMuPDF渲染PDF的代码不用改,界面逻辑也无需改动。Windows下用了中文字体,在macOS下界面字体显示也没问题,PySide6的跨平台做得还是比较到位的。
5. 踩坑经验:实操中遇到的几个典型问题和解决思路
5.1 中文文件名乱码问题
刚开始批量重命名时,出现一批文件名乱码——识别结果是正常的,一到写文件名就出错。查了一下,是Windows命令行默认编码不是UTF-8导致的。解决方式是在代码里显式指定打开文件时使用UTF-8编码,同时把控制台编码也设置一下,保证所有文本处理流程都走一致编码。
5.2 PaddleOCR-json进程不退出、CPU始终占用
持续通信模式有个问题:如果子进程异常退出,主进程会卡在读取输出上,界面直接假死。还有一个更隐蔽的问题:识别完几百张图之后,PaddleOCR-json进程的内存占用会缓慢上升,长时间运行后可能飙到1GB以上。
解决方式是:给子进程输出加超时控制,超过N秒没有响应就重启进程;内存占用超过阈值就自动杀掉重启。这条兜底逻辑在长任务场景下非常关键。
5.3 旋转图片识别率骤降
有些手机拍的文档,EXIF信息里带着方向标记,显示上看是正的,实际像素数据是旋转过的。直接喂给OCR,识别率惨不忍睹。解决方式是读取EXIF方向信息,用PIL转换一下再送到OCR。这个坑很隐蔽,但遇到一次就知道有多疼。
5.4 批量PDF处理太慢
一次处理几十份合同PDF,每份几十页,全部转成图片识别,跑下来用时确实不短。后来发现很多PDF其实是电子签章,文字根本不需要OCR。加了前面提到的文本PDF检测后,整体速度提升非常明显。
6. 使用实测与效果评估
真实场景下我拿一批凌乱命名的发票测试,效果如下:识别一百张发票图片,重命名成功94张,失败6张,原因分别是:两张折痕太深、一张拍摄角度过斜、三张文件名相同但内容不同(比例7%)。这个成功率对日常使用来说是够的,毕竟失败的文件会在预览列表里标出来,人工看一眼改一下就行。
识别耗时是另一个关键指标:单张图片平均0.6秒,PDF每页约0.8秒,一百张图大约一分钟跑完。相比人工逐个打开看再重命名,时间至少节省了十倍。即便把人工复核新文件名的几分钟算进去,整体效率提升依然非常可观。
这个项目后续还可以继续扩展的方向,我自己在规划的是:加入模板管理(发票、合同、名片、试卷分别一套规则),接入批量复制而不是重命名(保留原文件),以及支持用户自定义正则规则。
最后分享一个小经验:做这类工具,识别准确率固然重要,但流程设计更重要。把识别结果摆给用户看、让用户确认再执行重命名,听起来比“全自动一把梭”麻烦,实际用起来反而更省心——因为一旦自动改错文件名,找回来比手动改还要痛苦得多。先跑通一个最小的功能闭环,再逐步加规则,是这类工具落地最稳的路径。