仅限 OCR 图像的 PDF 文件。





5.00/5 (2投票s)
一个类库和命令行实用程序,用于向仅图像 PDF 文件添加 OCR 信息。
引言
早在 2009 年,我写了一篇关于如何索引仅限图像的 PDF 文件的文章。当时的任务只是从仅限图像的 PDF 文件中提取文本,放入 iFilter 中,这样文件就可以在 WIndows 环境中被索引。这一次,任务略有不同——为 PDF 文件添加文本信息。 这种方法的好处是文件可以被标准的 Adobe/Microsoft iFilter 索引,也可以使用可视工具(Adobe Reader、Chome、Edge,...)选择文本
背景
我需要为数千个 PDF 文件(实际上存储在 SQL 服务器中)添加 OCR 信息。我想创建一个脚本/实用程序,该脚本/实用程序可以每天执行以索引任何尚未包含可搜索文本的新 PDF 文件。 经过一番搜索,我找到了一个配方
- 使用 ghostscript 从 PDF 中提取单个页面到图像 (JPG) 文件
- 使用 Tesseract 从图像中提取 OCR
- 将提取的文本存储回 PDF
方案 1
显然,自从我上次在 2009 年接触 Tesseract 以来,他们添加了一个新功能:图像和 OCR 文本将导出为 PDF 文件。 我认为您需要 Tesseract 4+ 版本。 因此,解决方案似乎很简单,并且在接下来的 20 分钟内出现了以下批处理文件(参见附加项目中的 ocr.bat)
set gs="C:\Program Files\gs\gs9.52\bin\gswin64c.exe"
set tesseract="C:\Program Files\Tesseract-OCR\tesseract.exe"
if '%1'=='' goto :badParams
if '%2'=='' goto :badParams
mkdir %temp%\ocr\
set nm=%~n1
SETLOCAL ENABLEDELAYEDEXPANSION
rem split pdf into multiple jpeg
%gs% -dSAFER -dBATCH -dNOPAUSE -sDEVICE=jpeg -r300 -dTextAlphaBits=4 -o "%temp%\ocr\ocr_%nm%_%%04d.jpg" -f "%1"
rem ocr each jpeg
for %%i in (%temp%\ocr\ocr_%nm%_*.jpg) do %tesseract% -l eng "%%i" %%~pni pdf
del %temp%\ocr\ocr_%nm%_*.jpg
rem combine pdfs
set ff=#
for %%i in (%temp%\ocr\ocr_%nm%_*.pdf) do set ff=!ff! %%i
set ff=%ff:#=%
%gs% -dNOPAUSE -dQUIET -dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite -o "%2" %ff%
del %temp%\ocr\ocr_%nm%_*.pdf
goto :eof
:badParams
echo usage %0 pdf-In pdf-Out
正如您所看到的,该脚本执行以下操作
- 使用 GhostScript 将各个页面提取到 %temp%\ocr\####.jpg 文件中
- 对于每个 JPG 文件,运行 Tesseract 以创建 %temp%\ocr\####.pdf 文件
- 使用 ghost script 将所有 PDF 文件合并到输出文件中。
方案 1 问题
对于大型 PDF 文件(10+ 页),方案 1 的问题很快就出现了
- 它非常慢。 但我想,由于它只是作为后台进程运行于新文件,也许我可以接受这一点。
- 输出文件比源文件大得多,大约是 4 倍。乘以数千个文件——成为一个阻碍因素。
方案 2 - HOCR2PDF
其他人也有同样的问题。 输入 HOCR2PDF (https://archive.codeplex.com/?p=hocrtopdf)。 显然,除了将 OCR 输出为文本或 PDF 之外,Tesseract 还可以将结果输出为 HOCR 文件——有效地编码的 HTML 文件。 因此,任务略有变化
- 使用 GhostScript 将 PDF 文件拆分成多个 JPG
- 使用 tesseract 将 JPG 转换为 HOCR 文件
- 解析 HOCR 文件
- 使用 PDF 库 (iTextShart) 将文本信息添加到输出 PDF
方案 2 - 问题
好吧,HOCR2PDF 的问题与我的原始脚本类似。 它仍然很慢,文件仍然很大,尽管只有方案 #1 大小的一半。
方案 3 - 最终方案
所以我继续创建了自己的项目。
经过一些故障排除和性能改进,例如启用压缩,对整个页面使用单一字体,我发现大小膨胀归结为单个 iTextShart 函数调用
stamp.GetImportedPage(stamp.Reader, pg)
单独的调用似乎每页增加了大约 30K。 并且需要它的唯一原因是获取页面高度。 替换这个调用后
stamp.Reader.GetPageSizeWithRotation(pg).Height
大小膨胀消失了,令我惊讶的是,输出文件实际上变得比源文件更小(可能是由于启用了压缩并删除了未使用的对象)。
为了解决性能问题,我决定使用 ThreadPool 并发地为每个页面运行 Tesseract。 对于大型(10+ 页)文件,性能提升非常显着。
使用代码
该项目包含一个 PdfOcr 类,其中有一个公共方法 OcrFile。 使用方法如下
string txt = new PdfOcr().OcrFile(fileIn, fileOut);
此代码将 OCR fileIn pdf 文件,创建 fileOut 并返回 OCR 文本。 可以通过更改/分配以下静态变量来定制该类
- GhostScript - GhostScript 可执行文件的位置
- Tesseract - Tesseract 可执行文件的位置。
- wdiTemp - 将生成临时文件的文件夹
- tmpPrfx - 所有临时文件的前缀
该类存储在 Program.cs 文件中,该文件以及主程序接受 2 个参数 - 源文件和目标文件名。
关注点
- https://github.com/UB-Mannheim/tesseract/wiki - TesserAct windows 二进制文件
- https://www.ghostscript.com/download/gsdnld.html - GhostScript 二进制文件下载。
- https://archive.codeplex.com/?p=hocrtopdf - HOCR2PDF .NET 实用程序
历史
- 初始版本
- 2023/8/17 - 使文本透明。 更新的 Zip 文件