? Escáner de documentos + ? reconocimiento de texto (OCR) ?| OpenCV con Python

Por Administrador

En el anterior post y video habíamos estado tratando el tema de la transformación de perspectiva para alinear o enderezar una porción de imagen dados los 4 puntos correspondientes a los cuatro vértices de la región de interés y luego habíamos especificado 4 puntos o coordenadas para la imagen de destino.

Con todo ese conocimiento previo, vamos a proceder con un proyecto que sé que te va a gustar mucho, vamos a realizar un scanner de documentos y luego extraeremos el texto que este contiene, así que si quieres saber más sobre la realización de este proyecto, ¡continúa leyendo!.

CONTENIDO

  • ? Escaner documentos con OpenCV
  • ? Reconocimiento de texto (OCR) ?
  • Resultados

? Escaner documentos con OpenCV

Antes de continuar, recordemos un poco el post anterior. Para conseguir los 4 puntos correspondientes a los vértices la región de interés, habíamos aplicado prueba y error, y luego los eventos del mouse. Pero este procedimento puede no ser el más óptimo, después de todo sería mejor si tomamos la imagen del documento y que este automáticamente se alinee, y eso es precisamente lo que intentaremos realizar hoy.

Figura 1: Ejemplo del post anterior.

El orden de cada uno de los puntos correspondientes a los 4 vértices es super importante, así que mantendremos cierto orden. Por lo que estaremos usando el mismo orden del anterior post es decir: vértice superior izquierdo en la posición 0, vértice superior derecho en la posición 1, vértice inferior izquierdo en la posición 2, vértice inferior derecho posición 3 (esto cuando vayamos a aplicar la transformación de perspectiva).

En esta ocasión usaremos las siguientes imágenes como entrada:

Figura 2: Imagen de entrada 1.

Figura 3: Imagen de entrada 2.

Ahora que gracias al anterior tutorial, entendemos como funciona la transformación de perspectiva, vamos a pasar con la programación. Creamos un script llamado scanner_y_ocr.py.

import cv2
import numpy as np
import pytesseract

pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'

def ordenar_puntos(puntos):
    n_puntos = np.concatenate([puntos[0], puntos[1], puntos[2], puntos[3]]).tolist()

    y_order = sorted(n_puntos, key=lambda n_puntos: n_puntos[1])

    x1_order = y_order[:2]
    x1_order = sorted(x1_order, key=lambda x1_order: x1_order[0])

    x2_order = y_order[2:4]
    x2_order = sorted(x2_order, key=lambda x2_order: x2_order[0])
    
    return [x1_order[0], x1_order[1], x2_order[0], x2_order[1]]

Línea 1 a 3: Importamos los paquetes necesarios: OpenCV, numpy y pytesseract que usaremos más tarde en este post para reconocer el texto.

Línea 5: Añadimos esta línea para indicar donde está ubicado tesseract.exe, en mi caso está en el disco C. Este proceso ya lo habíamos realizado en el tutorial de su instalación, así que te lo dejo a continuación por si aún no lo has instalado: Como instalar ? Tesseract – ORC ? y Pytesseract en Windows.

Línea 7 a 18: Crearemos una función llamada ordenar puntos. Esta nos servirá para ordenar los 4 vértices correspondiente al documento, que obtengamos luego del procesamiento. Con ello podremos ordenar los puntos de la siguiente manera: vértice superior izquierdo, vértice superior derecho, vértice inferior izquierdo, vértice inferior derecho (recuerda que este orden lo estoy estableciendo arbitrariamente, tú podrías elegir otro orden).

NOTA: Esta función la usaremos más adelante en este tutorial

  • Línea 8: Tomamos los 4 puntos y los convertimos en una lista.
  • Línea 10: Ordenamos los puntos de menor a mayor en el eje y. Esto nos servirá para poder ordenar las 4 coordenadas (tomando en cuenta las imágenes de entrada). De este modo los dos primeros puntos ordenados pertenecerán a los vértices superiores:

Figura 4: Vértices superiores.

          Y los dos últimos corresponderán a los vértices inferiores:

Figura 5: Vértices inferiores.

  • Línea 12: Asumimos que los dos primeros elementos de y_order corresponden a los vértices superiores.
  • Línea 13: Tomamos los puntos y los ordenamos de menor a mayor en x, entonces aquí ya tendríamos ordenados los dos vértices superiores de izquierda a derecha (es decir vértice superior izquierdo y luego el derecho).
  • Línea 15: Asumimos que los dos últimos elementos de y_order corresponden a los vértices inferiores.
  • Línea 16: Tomamos los puntos y los ordenamos de menor a mayor en x, entonces aquí ya tendríamos ordenados los dos vértices inferiores de izquierda a derecha (es decir vértice inferior izquierdo y luego el derecho).
  • Línea 18: Retornamos un array con las coordenadas ordenadas.
image = cv2.imread('img_00.jpeg')

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
canny = cv2.Canny(gray, 10, 150)
canny = cv2.dilate(canny, None, iterations=1)

cnts = cv2.findContours(canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[:1]

Línea 20: Leemos la imagen de entrada (correspondiente a la figura 2 o 3).

Línea 22: Transformamos la imagen de entrada a escala de grises.

Línea 23 y 24: Usamos cv2.Canny para aplicar detección de bordes, y luego dilatamos los bordes encontrados.

Figura 6: Imagen binaria, luego de aplicar detección de bordes y dilatación a la imagen de la figura 1.

Línea 26 y 27: Usamos cv2.findContours, para encontrar los contornos de la imagen y luego tomamos el contorno más grande.

for c in cnts:
    epsilon = 0.01*cv2.arcLength(c,True)
    approx = cv2.approxPolyDP(c,epsilon,True)
    
    if len(approx)==4:
        cv2.drawContours(image, [approx], 0, (0,255,255),2)
        
        puntos = ordenar_puntos(approx)

        cv2.circle(image, tuple(puntos[0]), 7, (255,0,0), 2)
        cv2.circle(image, tuple(puntos[1]), 7, (0,255,0), 2)
        cv2.circle(image, tuple(puntos[2]), 7, (0,0,255), 2)
        cv2.circle(image, tuple(puntos[3]), 7, (255,255,0), 2)
        
        pts1 = np.float32(puntos)
        pts2 = np.float32([[0,0],[270,0],[0,310],[270,310]])
        M = cv2.getPerspectiveTransform(pts1,pts2)
        dst = cv2.warpPerspective(gray,M,(270,310))
        cv2.imshow('dst', dst)

Líneas 30 y 31: Estas funciones ya las hemos usado en los tutoriales anteriores correspondientes a la detección de figuras geométricas. Entonces el objetivo aquí será detectar el rectángulo correspondiente al documento dentro de la imagen. Esta información nos la dará approx que almacena el número de vértices del contorno.

Línea 33: Si la longitud de approx es 4, querrá decir que se ha detectado un rectángulo en la imagen, por lo que ingresará en la condición.

Línea 34: Dibujamos el contorno correspondiente al documento con ayuda de las coordenadas almacenadas en approx.

Línea 36: Dado que los puntos encontrados por approx están desordenados, procedemos a llamar a nuestra función ordenar_puntos y como argumento le entregamos la información de approx, de tal modo que en puntos se encontrarán los vértices ordenados.

Línea 38 a 41: Dibujamos una circunferencia cada una de las coordenadas correspondientes a los vértices del documento que están almacenadas en puntos.

Figura 7: Dibujando el contorno del documento y los 4 vértices correspondientes a la imagen de la figura 1.

Línea 43 a 47: Aplicamos la transformación de perspectiva vista en el tutorial anterior (recuerda ir a ese tutorial para más información sobre la transformación de perspectiva). Entonces obtendremos una imagen destino de 270 pixeles de ancho y 310 de alto, estos valores lo he establecido yo luego de pruebas, ya que obtuve buenos resultados al aplicar OCR (Reconocimiento Óptico de Caracteres).

Entonces visualizamos la imagen resultante dst:

Figura 8: Imagen resultante del escáner de documentos con OpenCV.

? Reconocimiento de texto (OCR) ?

Finalmente, una vez que tenemos la imagen alineada procederemos con un pequeño ejemplo de extracción de texto, usado tesseract y pytesseract. Ya que las condiciones con las que fueron tomadas las imágenes son ideales, no tendremos problema al extraer su texto, veamos:

        texto = pytesseract.image_to_string(dst, lang='spa')
        print('texto: ', texto)

cv2.imshow('Image', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

Línea 49: Para aplicar OCR vamos a usar pytesseract.image_to_string, en ella vamos a especificar la imagen destino dst e idioma 'spa' de español. El texto resultante se almacenará en la variable texto.

Línea 50: Imprimimos la información contenida en texto.

Figura 9: Texto extraído a través tesseract.

Línea 52 a 54: Por último visualizamos la imagen de entrada a la que se han dibujado los vértices y rodeado el documento detectado. La visualización estará presente hasta que una tecla sea presionada y se cierren las ventanas.

Resultados

Tal vez te estés preguntando por qué he elegido como entrada las imágenes correspondientes a las figuras 2 y 3 si tienen el mismo texto. Y pues la razón es sencilla, esto nos permitirá evaluar si se están ordenando correctamente los vértices con nuestra función ordenar_puntos, así que veamos los resultados de ambas imagenes:

Figura 10: Resultados del escáner y el reconocimiento del texto con OpenCV y tesseract en la imagen de la figura 1.

 

Figura 11: Resultados del escáner y el reconocimiento del texto con OpenCV y tesseract en la imagen de la figura 2.

Y esto ha sido todo por este tutorial, espero que lo hayas encontrado útil y espero  que te hayas divertido al igual que yo. Nos vemos en el siguiente tutorial o video.