Bueno, decidí entrenarme en mi pregunta para resolver el problema anterior. Lo que quería era implementar un OCR simple usando las funciones KNearest o SVM en OpenCV. Y a continuación es lo que hice y cómo. (es solo para aprender a usar KNearest para propósitos simples de OCR).
1) Mi primera pregunta fue sobre el archivo letter_recognition.data que viene con muestras de OpenCV. Quería saber qué hay dentro de ese archivo.
Contiene una letra, junto con 16 características de esa letra.
Y this SOF
me ayudó a encontrarlo. Estas 16 características se explican en el documento Letter Recognition Using Holland-Style Adaptive Classifiers
. (Aunque no entendí algunas de las características al final)
2) Como sabía, sin comprender todas esas características, es difícil hacer ese método. Intenté algunos otros papeles, pero todos fueron un poco difíciles para un principiante.
So I just decided to take all the pixel values as my features.
(No estaba preocupado por la precisión o el rendimiento, solo quería que funcionara, al menos con la menor precisión)
Tomé la imagen de abajo para mis datos de entrenamiento:
(Sé que la cantidad de datos de entrenamiento es menor. Pero, dado que todas las letras son de la misma fuente y tamaño, decidí probar esto).
Para preparar los datos para el entrenamiento, hice un pequeño código en OpenCV. Hace las siguientes cosas:
- Carga la imagen.
- Selecciona los dígitos (obviamente, buscando el contorno y aplicando restricciones en el área y la altura de las letras para evitar detecciones falsas).
- Dibuja el rectángulo delimitador alrededor de una letra y espera
key press manually
. Esta vez presionamos la tecla del dígito correspondiente a la letra en el cuadro.
- Una vez que se presiona la tecla del dígito correspondiente, cambia el tamaño de este cuadro a 10x10 y guarda valores de 100 píxeles en una matriz (aquí, muestras) y el dígito correspondiente ingresado manualmente en otra matriz (aquí, respuestas).
- Luego guarde ambas matrices en archivos txt separados.
Al final de la clasificación manual de dígitos, todos los dígitos en los datos del tren (train.png) están etiquetados manualmente por nosotros mismos, la imagen se verá a continuación:
A continuación se muestra el código que utilicé para el propósito anterior (por supuesto, no tan limpio):
import sys
import numpy as np
import cv2
im = cv2.imread('pitrain.png')
im3 = im.copy()
gray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray,(5,5),0)
thresh = cv2.adaptiveThreshold(blur,255,1,1,11,2)
################# Now finding Contours ###################
contours,hierarchy = cv2.findContours(thresh,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
samples = np.empty((0,100))
responses = []
keys = [i for i in range(48,58)]
for cnt in contours:
if cv2.contourArea(cnt)>50:
[x,y,w,h] = cv2.boundingRect(cnt)
if h>28:
cv2.rectangle(im,(x,y),(x+w,y+h),(0,0,255),2)
roi = thresh[y:y+h,x:x+w]
roismall = cv2.resize(roi,(10,10))
cv2.imshow('norm',im)
key = cv2.waitKey(0)
if key == 27: # (escape to quit)
sys.exit()
elif key in keys:
responses.append(int(chr(key)))
sample = roismall.reshape((1,100))
samples = np.append(samples,sample,0)
responses = np.array(responses,np.float32)
responses = responses.reshape((responses.size,1))
print "training complete"
np.savetxt('generalsamples.data',samples)
np.savetxt('generalresponses.data',responses)
Ahora entramos en la parte de entrenamiento y prueba.
Para probar la parte que usé debajo de la imagen, que tiene el mismo tipo de letras que solía entrenar.
Para el entrenamiento hacemos lo siguiente :
- Cargue los archivos txt que ya guardamos anteriormente
- crear una instancia de clasificador que estamos usando (aquí, es KNearest)
- Luego usamos la función KNearest.train para entrenar los datos
Para fines de prueba, hacemos lo siguiente:
- Cargamos la imagen utilizada para probar
- procesar la imagen como antes y extraer cada dígito usando métodos de contorno
- Dibuje un cuadro delimitador para él, luego cambie el tamaño a 10x10 y almacene sus valores de píxeles en una matriz como se hizo anteriormente.
- Luego usamos la función KNearest.find_nearest () para encontrar el artículo más cercano al que le dimos. (Si tiene suerte, reconoce el dígito correcto).
Incluí los últimos dos pasos (entrenamiento y prueba) en el código único a continuación:
import cv2
import numpy as np
####### training part ###############
samples = np.loadtxt('generalsamples.data',np.float32)
responses = np.loadtxt('generalresponses.data',np.float32)
responses = responses.reshape((responses.size,1))
model = cv2.KNearest()
model.train(samples,responses)
############################# testing part #########################
im = cv2.imread('pi.png')
out = np.zeros(im.shape,np.uint8)
gray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
thresh = cv2.adaptiveThreshold(gray,255,1,1,11,2)
contours,hierarchy = cv2.findContours(thresh,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
if cv2.contourArea(cnt)>50:
[x,y,w,h] = cv2.boundingRect(cnt)
if h>28:
cv2.rectangle(im,(x,y),(x+w,y+h),(0,255,0),2)
roi = thresh[y:y+h,x:x+w]
roismall = cv2.resize(roi,(10,10))
roismall = roismall.reshape((1,100))
roismall = np.float32(roismall)
retval, results, neigh_resp, dists = model.find_nearest(roismall, k = 1)
string = str(int((results[0][0])))
cv2.putText(out,string,(x,y+h),0,1,(0,255,0))
cv2.imshow('im',im)
cv2.imshow('out',out)
cv2.waitKey(0)
Y funcionó, a continuación se muestra el resultado que obtuve:
Aquí funcionó con 100% de precisión. Supongo que esto se debe a que todos los dígitos son del mismo tipo y del mismo tamaño.
Pero de cualquier manera, este es un buen comienzo para principiantes (espero que sí).