? Escáner de documentos + ? reconocimiento de texto (OCR) ?| OpenCV con Python
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.
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:
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:
Y los dos últimos corresponderán a los 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.
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
.
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
:
? 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
.
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:
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.
Muy bueno muchas gracias por compartir
Hola Gaby, muy buena tu implementación y explicación.
Te pido por favor ayuda con lo siguiente.
Al ejecutar texto = pytesseract.image_to_string( xxxxx, lang=’spa’)
Windows lo deniega así: PermissionError: [WinError 5] Acceso denegado
Es como si pytesseract necesitara permisos superiores, en ese caso, se los puedo otorgar en el código? pytesseract.exe tiene para todos los usuarios Control Total. No obstante, la función pytesseract no debería estar intentando cambiar o borrar nada para que se deniegue el permiso.
Desde ya muchas gracias por todo
la verdad tesseract me ha dado muchos errores con números, porque depende mucho del diccionario y del lenguaje, pero los numeros no tienen un diccionario que los contenga. deberia suponerse que si reconoces ascii, podrias reconocer tambien todo lenguaje, sin embargo falla mucho si el idioma no es el correcto.