如何用borb在Python中对扫描的PDF进行OCR处理

828 阅读4分钟

可移植文档格式(PDF)并不是一种 所见即所得(WYSIWYG)格式。它被开发成与平台无关,独立于底层操作系统和渲染引擎。

为了实现这一点,PDF的构造是通过更像编程语言的东西进行交互,并依靠一系列的指令和操作来实现一个结果。事实上,PDF是基于一种脚本语言--PostScript,它是第一个独立于设备的页面描述语言

在本指南中,我们将使用 borb- 一个专门用于阅读、操作和生成 PDF 文档的 Python 库。它提供了一个低级别的模型(如果你选择使用精确的坐标和布局,允许你访问这些)和一个高级别的模型(你可以将边距、位置等的精确计算委托给一个布局管理器)。

在本指南中,我们将看一下如何在扫描的PDF文档上应用光学字符识别(OCR)

安装borb

borb可以从GitHub上的源代码下载,或通过pip

$ pip install borb

"我的PDF文档没有文本!"

这是迄今为止任何编程论坛或帮助台中最经典的问题之一。

"我的文档中似乎没有文字。帮助?"

或者

"你的文本提取代码样本对我的文档不起作用。怎么会这样?"

答案往往就像*"你的扫描仪讨厌你 "*一样简单明了。

大多数无法工作的文件都是PDF文件,它们本质上是美化的图像。它们包含构成PDF所需的所有元数据,但它们的页面只是大的(通常是低质量的)图像,是通过扫描实物纸张创建的。

因此,这些文件中没有文本渲染的指示。而大多数PDF库将无法处理它们。borb然而,WWW喜欢帮忙,可以在这些情况下应用,内置支持OCR。

在本节中,我们将使用一个特殊的EventListener 实现,称为OCRAsOptionalContentGroup 。这个类使用tesseract (或者说pytesseract )来对Document 进行OCR(光学字符识别)。

一旦完成,被识别的文本将作为一个特殊的 "层 "重新插入每一页(在PDF中这被称为 "可选内容组")。

随着内容的恢复,通常的技巧(SimpleTextExtraction )会产生预期的结果。

你将首先创建一个方法,建立一个带有一些文字的PIL图像。然后这个图像将被插入到一个PDF中。

创建一个图像

import typing
from pathlib import Path

from PIL import Image as PILImage  # Type: ignore [import]
from PIL import ImageDraw, ImageFont

def create_image() -> PILImage:
    # Create new Image
    img = PILImage.new("RGB", (256, 256), color=(255, 255, 255))

    # Create ImageFont
    # CAUTION: you may need to adjust the path to your particular font directory
    font = ImageFont.truetype("/usr/share/fonts/truetype/ubuntu/UbuntuMono-B.ttf", 24)

    # Draw text
    draw = ImageDraw.Draw(img)
    draw.text((10, 10),
              "Hello World!",
              fill=(0, 0, 0),
              font=font)

    # Return
    return img

现在让我们用这个图像建立一个PDF,来表示我们的扫描文件,这个文件是不可解析的,因为它不包含元数据。

import typing
# New imports
from borb.pdf.canvas.layout.image.image import Image
from borb.pdf.canvas.layout.page_layout.multi_column_layout import SingleColumnLayout
from borb.pdf.canvas.layout.page_layout.page_layout import PageLayout
from borb.pdf.canvas.layout.text.paragraph import Paragraph
from borb.pdf.document import Document
from borb.pdf.page.page import Page
from borb.pdf.pdf import PDF

# Main method to create the document
def create_document():

    # Create Document
    d: Document = Document()

    # Create/add Page
    p: Page = Page()
    d.append_page(p)

    # Set PageLayout
    l: PageLayout = SingleColumnLayout(p)

    # Add Paragraph
    l.add(Paragraph("Lorem Ipsum"))

    # Add Image
    l.add(Image(create_image()))

    # Write
    with open("output_001.pdf", "wb") as pdf_file_handle:
        PDF.dumps(pdf_file_handle, d)

最后的文件应该是这样的。

pdf document with image

当你选择这个文件中的文本时,你会立即看到只有最上面的一行是真正的文本。其余的是一个带有文字的图像(你创建的图像)。

image is not selectable pdf

现在,让我们把OCR应用到这个文件上,并叠加实际的文本,使其成为可解析的。

# New imports
from pathlib import Path
from borb.toolkit.ocr.ocr_as_optional_content_group import OCRAsOptionalContentGroup
from borb.toolkit.text.simple_text_extraction import SimpleTextExtraction

def apply_ocr_to_document():

    # Set up everything for OCR
    tesseract_data_dir: Path = Path("/home/joris/Downloads/tessdata-master/")
    assert tesseract_data_dir.exists()
    l: OCRAsOptionalContentGroup = OCRAsOptionalContentGroup(tesseract_data_dir)

    # Read Document
    doc: typing.Optional[Document] = None
    with open("output_001.pdf", "rb") as pdf_file_handle:
        doc = PDF.loads(pdf_file_handle, [l])

    assert doc is not None

    # Store Document
    with open("output_002.pdf", "wb") as pdf_file_handle:
        PDF.dumps(pdf_file_handle, doc)

你可以看到这在PDF中创建了一个额外的层。这个图层被命名为*"OCR by borb"*,它包含了渲染指令borb ,并被重新插入到Document 中。

你可以切换该层的可见性(这在调试时很方便)。

hidden layer turned on borb ocr pdf

hidden layer turned off borb ocr pdf

你可以看到borb重新插入了postscript渲染指令,以确保*"Hello World!"*在`Document中。让我们再次隐藏这个层。

请记住OCR是一种启发式方法。位置和匹配的文本可能并不总是100%正确。这就是它的运作方式。通常情况下,你会把这个层隐藏起来(但可以选择),这样原始图像就在原处,你可以选择/复制它的近似值。

现在(即使隐藏了图层),你可以选择文本。

ocr text is selectable, even when invisible pdf borb

如果你现在应用SimpleTextExtraction ,你应该能够检索到Document 中的所有文本。

# New imports
from borb.toolkit.text.simple_text_extraction import SimpleTextExtraction

def read_modified_document():

    doc: typing.Optional[Document] = None
    l: SimpleTextExtraction = SimpleTextExtraction()
    with open("output_002.pdf", "rb") as pdf_file_handle:
        doc = PDF.loads(pdf_file_handle, [l])

    print(l.get_text_for_page(0))


def main():
    create_document()
    apply_ocr_to_document()
    read_modified_document()

    
if __name__ == "__main__":
    main()

这样就可以打印了

Lorem Ipsum
Hello World!

真棒!

总结

在本指南中,你已经学会了如何将OCR应用于PDF文件,确保你的扫描文件是可搜索的,并为未来的处理做好准备。