在 Python 应用中调用 ocrmypdf.ocr:OcrOptions、日志配置与 stdout 约束
2026/9/12 17:49:17 网站建设 项目流程

在 Python 应用中调用 ocrmypdf.ocr:OcrOptions、日志配置与 stdout 约束

【免费下载链接】OCRmyPDFOCRmyPDF adds an OCR text layer to scanned PDF files, allowing them to be searched项目地址: https://gitcode.com/GitHub_Trending/oc/OCRmyPDF

如果你的 Python 应用需要给扫描版 PDF 加上可搜索的 OCR 文本层,又不想走 subprocess 拼命令行,OCRmyPDF 提供了高层函数ocrmypdf.ocr:传入输入输出文件和一组选项,函数运行完整 OCR 流程并返回退出码。本文覆盖调用这个函数时的三件关键事:用OcrOptions组织参数、配置日志、以及遵守它对 stdout 的严格约束。

适用前提:你的 Python 环境可以import ocrmypdf(该包以命令行程序起家,但文档明确说明其部分能力可以被其他 Python 应用导入使用)。文档同时提示,有些应用更宜通过 subprocess 调用命令行以隔离其行为,本文是"进程内调用"这条路径。

用 OcrOptions 发起一次 OCR

OcrOptions自 17.0 起从顶层ocrmypdf模块导出。它是一个 Pydantic 模型,提供完整类型提示、IDE 自动补全,并在构造时校验选项值。推荐的调用方式是构造一个OcrOptions对象作为唯一参数:

import ocrmypdf from ocrmypdf import OcrOptions if __name__ == '__main__': # To ensure correct behavior on Windows and macOS options = OcrOptions( input_file='input.pdf', output_file='output.pdf', deskew=True, languages=['eng'], ) ocrmypdf.ocr(options)

其中input.pdf/output.pdf替换为你的实际输入输出路径。OcrOptions中的字段与命令行参数一一对应,例如languages是识别语言列表、deskew控制纠偏。

旧的位置参数风格(面向 OCRmyPDF < 17 的兼容)仍然受支持:

import ocrmypdf if __name__ == '__main__': # To ensure correct behavior on Windows and macOS ocrmypdf.ocr('input.pdf', 'output.pdf', deskew=True)

这一风格下所有命令行参数都可以作为等价的关键字参数传入。两个已知差异:

  • verbosequiet不可用。API 中与命令行--quiet/--verbose没有对应物,输出必须通过配置 logging 管理(见后文)。旧式调用里传verbose=只会被忽略并产生警告,提示改用ocrmypdf.configure_logging()
  • 传入OcrOptions时不要同时传其他 OCR 参数,否则会抛ValueError;需要额外设置请写进OcrOptions本身。plugins=plugin_manager=例外,可以与OcrOptions同时传入,但二者互斥。

让调用在子进程中进行

ocrmypdf.ocr的运行方式接近命令行执行:它会创建 worker 进程或线程、管理 worker 的信号标志、执行其他子进程(fork 并运行其他程序)。因此调用它的 Python 进程必须有足够权限完成这些动作。文档给出的建议是创建子进程来调用ocr(),这样即使 OCRmyPDF 因任何原因失败,你的应用也能存活并保持交互:

from multiprocessing import Process import ocrmypdf from ocrmypdf import OcrOptions def ocrmypdf_process(): options = OcrOptions(input_file='input.pdf', output_file='output.pdf') ocrmypdf.ocr(options) def call_ocrmypdf_from_my_app(): p = Process(target=ocrmypdf_process) p.start() p.join()

同一文档列出的几条与父进程相关的约束:

  • ocr()会持有线程锁,防止同一解释器进程内多个实例同时运行;由于插件系统与 Python 导入机制的原因,它不是线程安全的。需要并行时请用多进程。jobs=参数限制 worker 进程数量,目前没有其他调度手段。
  • 除 Windows 外,调用ocr()的程序应安装 SIGBUS 信号处理器,以便内存映射文件访问失败时抛出异常——OCRmyPDF 可能使用内存映射。
  • 在 Windows 和 macOS 上,调用脚本必须带if __name__ == '__main__'保护,否则进程语义会导致 OCRmyPDF 无法正确工作。上面的示例都保留了这个守卫。

配置日志:configure_logging 或自行管理

OCRmyPDF 在名为ocrmypdf的 logger 下记录日志,此外它导入的pdfminerPIL也分别在这两个命名空间下打日志。你有两条路:

  1. 调用ocrmypdf.configure_logging,让日志输出与 ocrmypdf 命令行界面一致。第一个参数是Verbosity枚举(取值:quiet = -1default = 0debug = 1debug_all = 2):
import ocrmypdf ocrmypdf.configure_logging(ocrmypdf.Verbosity.default)
  1. 自行配置:如果不调用configure_logging,ocrmypdf 不会替你配置日志,由调用方用标准库logging按需处理ocrmypdf命名空间。源码文档提示pdfminerlogging.INFO级别下非常啰嗦,可以一并调高它的级别。

几个必须知道的边界:

  • configure_logging的细节是内部微调、随时可能变化;它是为"想要和 ocrmypdf 命令行几乎一致的包装脚本"设计的。如果你的应用自己管理日志,文档明确说"你可能并不想要这个函数"。
  • 该函数不会创建命令行在特定 verbose 级别下会生成的debug.log日志文件;应用要自己配置 debug 日志。
  • 进度条基于rich包实现。configure_logging会把日志输出配置到sys.stderr,其方式与进度条显示兼容;不想显示进度条时用ocrmypdf.ocr(..., progress_bar=False)

stdout 约束:进程内调用时最容易踩的坑

OCRmyPDF 严格地不向标准输出写任何东西,目的是让用户可以安全地把它用在管道中并得到合法输出文件。对进程内调用者这意味着两点:

  • 如果你的应用希望兼容这一行为、支持把结果管道到文件,你自己的代码也不要向 stdout 写东西。上面的子进程方案还有一个附带好处:ocrmypdf 的杂散输出不会干扰父进程的 stdout。
  • output_file='-'时,最终 PDF 直接写到sys.stdout,此时 stdout 上的字节必须恰好是 PDF 且别无其他。可选地,在调用ocr()之前调用ocrmypdf.configure_stdout_protection()可以强化这一保证:它把文件描述符 1 重定向到 stderr,同时保存真实 stdout 的私有副本,这样任何误写 stdout 的内容会无害地落到 stderr,而 OCRmyPDF 仍把最终 PDF 输出到保留的描述符。这要求尽早、只调用一次(在加载任何插件或启动任何 worker 之前),让后续代码继承重定向后的描述符。反过来,管理自己 stdout 的应用(例如长驻服务在进程内反复调用ocr不应该调用它,因为它会改动进程全局的文件描述符。

如何判断这次调用是否成功

ocrmypdf.ocr的返回值是ocrmypdf.ExitCode整数退出码,条件性成功时以返回码表达,而不是抛异常:

import ocrmypdf from ocrmypdf import OcrOptions if __name__ == '__main__': options = OcrOptions(input_file='input.pdf', output_file='output.pdf') code = ocrmypdf.ocr(options) print(f'exit code: {code}', file=sys.stderr) # 注意不要写 stdout

ExitCode定义在 exceptions.py,ok = 0表示正常;其他取值如bad_args = 1input_file = 2missing_dependency = 3already_done_ocr = 6encrypted_pdf = 8other_error = 15ctrl_c = 130

失败则体现为异常。api.py 的函数文档列出了可能抛出的异常,父进程应当提供异常处理器,常见包括:

异常触发条件
MissingDependencyError依赖的命令行程序缺失或不在 PATH 上
UnsupportedImageFormatError输入图像无法读取,或输入不是 PDF
DpiError输入是图像但分辨率不可信(继续会产生糟糕的 OCR)
EncryptedPdfError输入 PDF 加密受保护;OCRmyPDF 不解除密码
PriorOcrFoundError输入 PDF 疑似已有 OCR 或数字文本,而设置未指示继续
OutputFileAccessError写入目标输出文件失败
InputFileError/SubprocessOutputError/TesseractConfigError其他输入文件问题 / 子进程执行错误 / Tesseract 报告配置无效

此外还可能出现标准 Python 异常、部分与 multiprocessing 相关的异常以及KeyboardInterrupt。发生异常时,OCRmyPDF 会自动清理其临时文件和 worker 进程,你的处理逻辑不需要替它善后。

限制与下一步

  • 同一个 Python 进程同时只能运行一个 OCRmyPDF 任务(线程锁);水平扩展请用多个 Python 进程。
  • 传入OcrOptions与其他 OCR 关键字参数并存会直接ValueError,不是"覆盖"关系。
  • 插件开发相关:自 16.13 起插件钩子接收OcrOptions对象而非argparse.Namespace,但二者鸭子类型兼容,多数现有插件无需修改。

更多参数含义以 docs/api.md 和与命令行参数一一对应的选项文档为准;公开 API 的完整参考见 docs/apiref.md。

【免费下载链接】OCRmyPDFOCRmyPDF adds an OCR text layer to scanned PDF files, allowing them to be searched项目地址: https://gitcode.com/GitHub_Trending/oc/OCRmyPDF

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询