Realizar OCR en un PDF escaneado en Python usando borb

En esta guía, veremos cómo aplicar OCR a documentos PDF escaneados (imágenes) y superponer capas para contener texto analizable en Python usando borb.

El Formato de documento portátil (PDF) no es un formato WYSIWYG (Lo que ves es lo que obtienes). Fue desarrollado para ser independiente de la plataforma, independiente del sistema operativo subyacente y los motores de renderizado.

Para lograr esto, PDF se construyó para poder interactuar a través de algo más parecido a un lenguaje de programación, y se basa en una serie de instrucciones y operaciones para lograr un resultado. De hecho, PDF está basado en un lenguaje de secuencias de comandos: Posdata, que fue el primer lenguaje de descripción de página independiente del dispositivo.

En esta guía, utilizaremos borracho, una biblioteca de Python dedicada a leer, manipular y generar documentos PDF. Ofrece un modelo de bajo nivel (que le permite acceder a las coordenadas y el diseño exactos si elige usarlos) y un modelo de alto nivel (donde puede delegar los cálculos precisos de márgenes, posiciones, etc. a un administrador de diseño) .

En esta guía, veremos cómo aplicar Reconocimiento óptico de caracteres (OCR) en un documento PDF escaneado.

Instalación de borb {#instalación de borb}

borb puede descargarse desde la fuente en GitHub, o instalarse a través de pip:

1
$ pip install borb

“¡Mi documento PDF no tiene texto!”

Esta es, con mucho, una de las preguntas más clásicas en cualquier foro de programación o servicio de asistencia:

"Parece que mi documento no tiene texto. ¿Ayuda?"

O:

"Su ejemplo de código de extracción de texto no funciona para mi documento. ¿Por qué?"

La respuesta suele ser tan sencilla como "tu escáner te odia".

La mayoría de los documentos para los que esto no funciona son documentos PDF que son esencialmente imágenes glorificadas. Contienen todos los metadatos necesarios para constituir un PDF, pero sus páginas son solo imágenes grandes (a menudo de baja calidad), creadas escaneando documentos físicos.

Como consecuencia, no hay instrucciones de representación de texto en estos documentos. Y la mayoría de las bibliotecas de PDF no podrán manejarlos. borb, sin embargo, le encanta ayudar y se puede aplicar en estos casos, con soporte incorporado para OCR.

En esta sección usaremos una implementación especial de EventListener llamada OCRAsOptionalContentGroup. Esta clase usa tesseract (o más bien pytesseract) para realizar OCR (reconocimiento óptico de caracteres) en el Documento.

Si desea leer más sobre OCR en Python, lea nuestra Guía para el reconocimiento óptico simple de caracteres con PyTesseract!

Una vez terminado, el texto reconocido se vuelve a insertar en cada página como una "capa" especial (en PDF esto se denomina "grupo de contenido opcional").

Con el contenido ahora restaurado, los trucos habituales (SimpleTextExtraction) dan los resultados esperados.

Comenzará creando un método que genere una imagen PIL con algo de texto. Esta imagen se insertará en un PDF.

Creación de una imagen

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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

Ahora construyamos un PDF con esta imagen, para representar nuestro documento escaneado, que no se puede analizar, ya que no contiene metadatos:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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)

El documento resultante debería verse así:

documento pdf con imagen

Cuando seleccione el texto en este documento, verá inmediatamente que solo la línea superior es en realidad texto. El resto es una Imagen con texto (la Imagen que creaste):

image is not selectable pdf

Ahora, apliquemos OCR a este documento y superpongamos texto real para que se pueda analizar:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 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)

Puede ver que esto creó una capa adicional en el PDF. Esta capa se llama "OCR by borb", y contiene las instrucciones de representación borb reinsertadas en el Documento.

Puede alternar la visibilidad de esta capa (esto puede ser útil durante la depuración):

capa oculta activada borb ocr pdf

capa oculta desactivada borb ocr pdf

Puede ver que borb reinsertó el comando de representación postscript para asegurarse de que "Hello World!" esté en el `Documento. Vamos a ocultar esta capa de nuevo.

Tenga en cuenta que OCR es una heurística. La ubicación y el texto coincidente pueden no ser siempre 100 % correctos. Así es como funciona. Por lo general, mantendrá la capa oculta (pero seleccionable) para que la imagen original esté en su lugar y pueda seleccionar/copiar una aproximación de ella.

Ahora (incluso con la capa oculta), puede seleccionar el texto:

El texto OCR es seleccionable, incluso cuando el borb pdf es invisible

Y si aplica SimpleTextExtraction ahora, debería poder recuperar todo el texto en el Documento.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# 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()

Esto imprime:

1
2
Lorem Ipsum
Hello World!

¡Impresionante!

Conclusión

En esta guía, aprendió cómo aplicar OCR a documentos PDF, asegurándose de que sus documentos escaneados se puedan buscar y estén listos para su procesamiento futuro.