lunes, 20 de mayo de 2013

Proyecto Final

Reconocimiento de caracteres

Hola a todos mis compañeros y gente que visita mi blog, esta entrada corresponde a mi proyecto final de la materia de visión computacional, consiste en el reporte del mismo y contiene los siguientes temas (establecidos por la maestra, Dra. Elisa Schaeffer):

REPORTE
 
 Propósito / justificación



El propósito de mi proyecto es principalmente para digitalizar documentos que tengamos en papel, esto ahorra tiempo de escritura en el caso de que se vayan a transcribir documentos largos y además evita el trabajo de hacerlo.
Hay que reconocer que el sistema no es perfecto y podrían existir errores en el reconocimiento de algunos caracteres, pero aún así facilita la transcripción de documentos muy largos como la transcripción de libros completos.
De hecho el motor de reconocimiento de caracteres que usé es desarrollado actualmente por google para su proyecto de digitalización de libros (Google books)


 Diseño del software

El software lo dividí en 4 partes que consisten en la entrada de los datos,  preprocesamiento, el reconocimiento y la salida del sistema, a continuación detallo cada una de las tres partes, además de un diagrama que explica el funcionamiento en conjunto:
  • Entrada: La entrada de los datos (las imágenes) las obtengo por medio de la cámara web del ordenador, primero abro la captura de la cámara con la facilidad que provee OpenCv, después capturo una imagen presionando "backspace".
  • Preprocesamiento: Luego de obtener la imagen con el texto a reconocer (fotografía sosteniendo la hoja o el libro a transcribir), abro la imagen dentro de una ventana de OpenCv, después en la misma ventana marco con el mouse en un rectángulo la zona de interés, en este caso el texto a reconocer.
  • Reconocimiento: Después de obtener la nueva imagen con la zona de interes, le paso esta nueva imagen al motor de reconocimiento "tesseract - OCR", en específico uso el método "image_to_string" y este método llama al ejecutable "tesseract" . El método me regresa el texto y simplemente lo almaceno en una variable.
  • Salida del sistema: El resultado entregado por el motor de reconocimiento lo almaceno en un archivo de texto y luego llamo a un shell que realicé para que abriera dicho archivo en un gedit (de esta manera le facilito la tarea al usuario).



 Librerías utilizadas


Las librerías que yo utilicé fueron pytesser, os, PIL, OpenCV, pyscreenshot, entre otras, a continuación describiré para qué utilicé cada una de ellas.
from pytesser import * 
import os
import Image
import cv2.cv as cv
import pyscreenshot as ImageGrab
from PIL import *



  • PyTesser: Utilicé el motor "tesseract - OCR" de pytesser para realizar el reconocimiento de los caracteres presentes en una imagen (la imagen la obtuve de la cámara web). Pytesser cuenta con diversos módulos de reconocimiento, yo utilicé el método Image_to_string para obtener la cadena reconocida. El motor de reconocimiento de caracteres "tesseract" es actualmente desarrollado por Google, y usado por ellos mismos en su proyecto "Google Books"; inicialmente tesseract fue desarrollado por la compañía HP.
  • Os: El módulo os lo utilicé para poder emplear funciones del sistema operativo, por medio del módulo os abrí un shell que a su vez abre gedit con mi archivo de texto de salida dentro.
  • PIL: El módulo de Python Image Library lo usé para cargar las imágenes con las que trabajé, además de tener un fácil acceso a cada uno de los píxeles y dimensiones de las mismas. Al tener acceso a los pixeles de las imágenes pienso realizar futuros filtros para quitar ruidos y manchas que impiden el correcto reconocimiento.
  • OpenCV: El módulo de OpenCV lo usé para cargar la funcionalidad de cámara web y también para aplicar los filtros de escala de grises y binarización a las imágenes obtenidas, con el fin de presentar imágenes más sencillas y "fáciles" de reconocer por el motor "tesseract - OCR".
  • Pyscreenshot: Este módulo lo usé para poder cortar sobre la imagen obtenida de la cámara web el aŕea de interés del usuario. Tomando en cuenta que no todo lo que está presente en el docuemento es relevante (por ejemplo, dibujos, gráficas, etc).
 Descripción textual y diagramas de los algoritmos desarrollados


Entrada del sistema: 

La entrada del sistema la realizo por medio de la cámara web. Al iniciar el programa abro la ventana de la cámara web para que el usuario ponga a la vista de la cámara el documento en papel, luego por medio de la tecla "backspace" el usuario captura la imagen para pasar al módulo de cortado de la imagen. A continuación una imagen del proceso.


Cortar contenido relevante: 

Para obtener solo el contenido relevante de la imagen o del documento en papel del usuario realicé un módulo que te permite cortar la imagen por medio de mouse, eliminando de la imagen fotografías, imágenes o gráficas que no nos interesan (o que no puede procesar el algoritmo de reconocimiento de caracteres). A continuación una imagen del proceso.



Escala de grises y binarización:

Para presentarle al motor de reconocimiento de caracteres "tesseract - OCR" imágenes más sencillas de reconocer, primero paso por escala de grises y binarización la imagen, ya que obtenía mucho mejores resultados después de correr estos fitros (para esto hice uso de OpenCV). En un futuro tengo pensado implementar más filtros, como un limpiador para quitar ruido de la imagen. A continuación una imagen del proceso:




Salida del sistema:

Para finalizar uso el módulo PyTesser para el reconocimiento de los caracteres, el módulo PyTesser cuenta con distintos métodos para distintas funcionalidades, en mi caso usé el método Image_to_String, que, después de pasarle como parámetro mi imagen previamente filtrada, el método llama al ejecutable tesseract.exe que es el que se encarga del reconocimiento. Luego me entrega la cadena de caracteres que guardo en un archivo de texto. Después de esto, por medio de la librería "os" mando llamar a un shell que se encarga de abrir gedit con mi archivo de texto titulado "salida.txt" (para mayor facilidad y comodiad al usuario). A continuación una imagen del proceso.



Si comparamos con la imagen de entrada, podemos notar que solamente hubo un error, pero conforme disminuía el tamaño de las letras del documento naturalmente se presentaban más errores, esto lo compruebo más adelante en la evaluación del desempeño.



(Demostración de funcionamiento, recomendable ponerlo en fullscreen)

Pruebas con distintas imágenes(distinto contenido y tamaño de fuente)

Bien, para realizar las pruebas del algoritmo de reconocimiento de caracteres (tesseract - OCR) utilicé 4 distintas imágenes; distintas referente al contenido y al tamaño de la fuente. Lo que se podía apreciar a simple vista es que el algoritmo de reconocimiento batallaba más conforme iba reduciendo el tamaño de la fuente de los documentos.
Para poder reconocer cada una de las palabras en los textos de fuentes pequeñas necesité acercar demasiado el documento a la cámara web, pero aún así los caracteres resultaban demasiado pequeños para el algoritmo, y el reconocimiento era bastante defectuoso, o de plano no se realizaba el reconocimiento.


A continuación les muestro las imágenes de entrada, los pasos de procesamiento y las salidas de cada uno de los 4 documentos. Comencé realizando las pruebas con documentos de tamaño de fuente grande (tamaño 25), luego fui decrementando el tamaño de 5 en 5 hasta llegar a 10 (todos los documentos escritos en letra Arial).


Prueba 1 - Tamaño de fuente 25.


(Imagen capturada mediante la webcam) 


(Imagen recortada y binarizada)




(Salida del algoritmo de reconocimiento de caracteres)



Prueba 2 - Tamaño de fuente 20.

(Imagen capturada mediante la webcam) 


(Imagen recortada y binarizada)


(Salida del algoritmo de reconocimiento de caracteres)


Prueba 3 - Tamaño de fuente 15.

(Imagen capturada mediante la webcam) 



(Imagen recortada y binarizada)



(Salida del algoritmo de reconocimiento de caracteres)


Prueba 4 - Tamaño de fuente 10.
 
(Imagen capturada mediante la webcam) 


(Imagen recortada y binarizada, son pocas palabras porque tuve que acercar demasiado el documento a la cámatra web, de otra manera, el algoritmo no reconocía nada)



(Salida del algoritmo de reconocimiento de caracteres)



Evaluación de desempeño

Para realizar la evaluación del desempeño realicé una gráfica con los errores y aciertos cometidos durante la detección de los caracteres, para hacer las pruebas tomé 4 documentos con contenido distinto y también con tamaño de fuente distinto. 
Usé los tamaños de fuente 10, 15, 20 y 25, todos a fuente Arial.




El algoritmo de reconocimiento de caracteres "tesseract" tuvo un mayor número de aciertos con el documento de fuente tamaño 20, pero esto no quiere decir que el tamaño de fuente sea el ideal o el que mayores aciertos tiene sobre todos, sino que debemos recordar que cada documento tenía distinto número de palabras y distinto contenido. En todo caso el documento con el que se ovtuvo mayor acierto fue el de tamaño de fuente 25, naturalmente. También esto explica la cantidad de errores con el tamaño de fuente 10, ya que el número de palabras a reconocer solo fue de 17, porque eran tan pequeñas las letras que tuve que acercar demasiado el documento a la cámara.

A partir del número de fuente 15, los errores eran tantos que el texto parecía escrito en alemán :). 

Trabajo a futuro (mejoras e ideas)

El trabajo a realizar a futuro es desarrollar nuevos filtros para presentar una mejor entrada al motor de reconocimiento de caracteres "tesseract - OCR". Entre los filtros que pienso desarrollar son un limpiador de imágenes, que quite todo el ruido que impide el corrector reconocimiento. 




Además de una filtro que corrija el ángulo de documento, ya que por medio de la cámara web, es bastante propensa la mala colocación del documento, impidiento el correcto reconocimiento.


A continuación les dejo con la presentación que expuse en clase:




CÓDIGO
 

- Control de versiones (liga al repositorio): https://github.com/eddypre/TrianaVisionProject

- Comentarios: Las funciones más relevantes de mi código están comentarizadas para su fácil entendimiento.
 
- README & INSTALL: Agregué en el repositorio 2 archivos de texto un README y un INSTALL que muestran los pasos para correr el programa y también para instalar las librerías necesarias, respectivamente.

- Ejemplos (datos): En el repositorio se encuentran los archivos de texto con las salidas de cada imagen tomada por la cámara web, se trata de 4 imágenes con distinto contenido y con distinto tamaño de letra (tamaño 5, 10, 15 y 20. Todos con fuente Arial).


BIBLIOGRAFÍA

- Creación, carga y lecturas de archivo de texto, [Web en línea]. <http://pythonya.appspot.com/detalleconcepto?deta=Creaci%C3%B3n,%20carga%20y%20lectura%20de%20archivos%20de%20texto>. [Consulta: 25-03-13]
- Converting image with Python + OpenCV, [Web en línea]. <http://extr3metech.wordpress.com/2012/09/23/convert-photo-to-grayscale-with-python-opencv/>. [Consulta: 25-03-13]

- Converting an OpenCV Image to black and white, [Web en línea]. <http://stackoverflow.com/questions/7624765/converting-an-opencv-image-to-black-and-white>. [Consulta: 25-03-13]

- Ubuntu 12.04, the installation PyTesser in OCR recognition, [Web en línea]. <http://www.rapidsnail.com/Tutorial/t/2012/1031/11/21498/ubuntu-1204-the-installation-pytesser-in-ocr-recognition.aspx>. [Consulta: 25-03-13]


Eso es todo por mi parte.

Cualquier duda o aclaración pueden dejarla en la caja de comentarios. 

Saludos a todos! 

jueves, 9 de mayo de 2013

Laboratorio 9

Laboratorio 9 - Detección de esquinas

Hola a toda la gente que visita este blog, esta entrada corresponde a la actividad número 9 del laboratorio de Visión Computacional. La actividad consiste en la detección de esquinas de polígonos, para posteriormente pintar cada uno de ellos.

Bien los pasos que seguí para realizar esta actividad son los siguientes:
  • Escala de grises (ya lo habíamos realizado)
  • Obtener el filtro medio (desde la escala de grises)
  • Realizar una diferencia para obtener los puntos esquina (diferencia entre escala de grises y filtro medio)
  • Realizar filtros para limpiar la imagen (simples umbrales)
  • Unir los puntos esquina obtenidos 

                                                                           (Imagen original)
Como ya mencioné antes el proceso para escala de grises ya lo teníamos, por lo tanto no entraré a detalle en eso.

Para obtener el filtro medio, simplemente guardé en una lista los valores de los pixeles de los vecinos, luego los ordené de menor a mayor y saqué el de enmedio, cada pixel del filtro medio lo asigné a una nueva imagen.
El código que realiza esto es el siguiente:



 

 (Filtro medio)
Después para obtener la diferencia simplemente resté los valores de cada pixel de la escala de grises a la de filtro medio, el código que realiza este proceso es el siguiente (Aquí también agregué el código para trazar las lineas de punto a punto, para destacar el polígono ):
 

 (Diferencia + filtro con umbrales)


(Trazado de líneas para unir puntos)


Una de las impresiones que muestro en pantalla con las coordenadas de las esquinas encontradas:


Eso es todo por mi parte.
Liga a mi git: https://github.com/eddypre/VisionTriana

Cualquier duda o aclaración pueden dejarla en la caja de comentarios. 

Saludos a todos!

jueves, 2 de mayo de 2013

Laboratorio 8

Laboratorio 8 - Detección de polígonos

Hola compañeros y gente que regularmente visita mi blog, esta entrada corresponde a la actividad número 8 de laboratorio de Visión Computacional. La actividad consiste en la detección de poligonos. Los pasos para llevar acabo la actividad son los siguientes:

Lo primero que realicé fue pasar la imagen a escala de grises. Realizado ese proceso, apliqué convolución para obtener los bordes de las figuras, después corrí bfs para obtener los pixeles de los diferentes polígonos de la imagen.
Teniendo las coordenandas de los pixeles de los posibles polígonos, me posicioné sobre un pixel para ver si los pixeles vecinos contaban con los mismos valores de pendiente; con esto nos damos cuenta si la colección de pixeles verificados correspondian a un mismo segmento, por ejemplo en un tríangulo contariamos con 3 segmentos, cada segmento (colección de pixeles) lo fui guardando.

                                                                                    (Original)
Código que realiza los procesos anteriores:




Luego de obtenidos los segmentos, obtuve el punto medio de cada uno de ellos, además de los puntos iniciales y finales


El código para realizar esto es el siguiente:



Luego obtuve el punto medio y con el punto medio de los segmentos trasladé cada uno de ellos hacia el centro, ahí se intersectan cada uno de ellos. Además obtuve el porcentaje del polígono.
El código que realiza este proceso es el siguiente:


El resultado es el siguiente:



Resultado con un tríangulo:
 




Es todo por esta actividad.
Liga a mi repositorio: https://github.com/eddypre/VisionTriana
Cualquier duda o aclaración pueden dejarla en comentarios. 

Saludos a todos!

jueves, 25 de abril de 2013

Laboratorio 7

Laboratorio 7 - Detección de agujeros (preprocesamiento)

Hola, esta entrada corresponde a la actividad número 7 del laboratorio de Visión Computacional, consiste en la detección de agujeros por medio de histogramas.


Bien, para la detección de agujeros usé la técnica de histograma lateral, lo primero que realicé fue obtener los histogramas lateral vertical y horizontal, esto lo realicé sumando cada uno de los pixeles por fila y al final se obtenía un total en cada fila; también realicé lo mismo con las columnas, esto se hace con el propósito de obtener las intensidades de pixeles y así por medio de picos (superior o inferior) de intensidades de pixeles se obtienen los posibles agujeros.

A continuación muestro el proceso detallado para realizar lo antes mencionado.

Bien lo primero que tengo es la imagen original (tomada con mi cámara), a esta le pasó por el proceso de escala de grises que habíamos desarrollado en tareas pasadas. 

(Imagen original) 

(Imagen escala de grises)
 
Teniendo la imagen en esacala de grises, sabemos que cada pixel tiene el mismo valor en sus 3 componentes (RGB), de esta manera sumamos cualquiera de ellos a su pixel consecutivo, y así hasta terminar la fila o columna. Si continuamos con el proceso podemos concluir que tendremos 2 listas del tamaño de las dimensiones de la imagen, y cada elemento de la lista será el total de la suma de intensidades de pixeles para una respectiva fila o columna. El código que hace todo este proceso es el siguiente:


Como pueden observar en el código también fui rellenando archivos dat (uno para histograma lateral vertical y otro para horizontal), en los que contiene el resultado de las sumas de cada fila y columna, junto con su correspondiente número de fila/columna, esto con el objetivo de graficar en gnuplot cada uno de los histogramas.

Este es el resultado del archivo "horizontal.dat" usado para graficar el histograma en gnuplot, el valor de la columna 1 son las filas de la imagen (solo puse hasta el 25, pero son mucho más, obviamente depende de las dimensiones de tu imagen); la columna 2 son las sumas de la intensidad de los pixeles.


0 141835
1 141895
2 141890
3 141757
4 141698
5 141803
6 141817
7 141685
8 141859
9 142024
10 141962
11 141760
12 141657
13 141662
14 141812
15 142030
16 141920
17 141918
18 141951
19 142003
20 142015
21 141910
22 141774
23 141748
24 142089
25 142136

Este es el resultado del archivo "vertical.dat" el valor de la columna 1 corresponde a las columnas de la imagen, mientras que el de la columna 2 corresponde  a las sumas de las intensidades de la respectiva columna (obviamente tampoco puse todas, solo las primeras 25).


0 134999
1 134896
2 134912
3 135076
4 135153
5 135076
6 135008
7 135046
8 134931
9 134854
10 135049
11 135276
12 135119
13 134867
14 134904
15 135087
16 134957
17 135071
18 134839
19 134753
20 134903
21 135025
22 135075
23 134918
24 135052

Los resultados ploteados en gnuplot se ven de la siguiente manera:


(Lateral horizontal)


(Lateral vertical)


Después calculé umbrales para poder tomar solo los picos resultantes, los umbrales los obtuve promediando la listas donde contenía todas las sumas de las filas y las columnas, es decir, tengo 2 umbrales; luego filtré los puntos obtenidos para pintar una linea por cada supuesto agujero (pico). El código que realiza este proceso es el siguiente:




El resultado es el siguiente:


Ahora, no siempre tendrán picos inferiores, como los que observan en las imágenes anteriores de gnuplot; esto se dio porque los orificios mostraban una auscencia de luz, por lo tanto era más obscuro que el fondo (imagen blanca). En cambio si en los agujeros hay más luz que en el fondo (osea la hoja) es obvio que los picos de las gráficas de gnuplot serán superiores, es decir, estarán apuntando hacia arriba.


Para comprobar esto yo invertí los colores de la imagen anterior con la ayuda de Kolour paint y realicé la prueba, así se ven los nuevos picos en gnuplot:

(Lateral horizontal)

  
(Lateral vertical)




Prueba 2

Esta vez ya no explico el proceso porque es prácticamente lo mismo.

(Imagen original)

(Lateral horizontal)


(Lateral vertical)


(Trazado de líneas en posibles agujeros)


Una de las tantas impresiones que hice en terminal fueron los puntos donde se intersectan las líneas con los picos o posibles agujeros.


Eso es todo por esta actividad.  
Liga a mi repositorio: https://github.com/eddypre/VisionTriana
Referencias: http://elisa.dyndns-web.com/~elisa/teaching/comp/vision/agujeros.pdf

Cualquier duda o aclaración pueden dejarla en comentarios.

Saludos!