Eh bien, j'ai décidé de m'entraîner sur ma question pour résoudre le problème ci-dessus. Ce que je voulais, c'était implémenter une OCR simple en utilisant les fonctionnalités KNearest ou SVM dans OpenCV. Et voici ce que j'ai fait et comment. (c'est juste pour apprendre à utiliser KNearest à des fins d'OCR simples).
1) Ma première question concernait le fichier letter_recognition.data fourni avec les échantillons OpenCV. Je voulais savoir ce qu'il y a dans ce fichier.
Il contient une lettre, ainsi que 16 caractéristiques de cette lettre.
Et this SOF
m'a aidé à le trouver. Ces 16 caractéristiques sont expliquées dans le document Letter Recognition Using Holland-Style Adaptive Classifiers
. (Bien que je n'aie pas compris certaines fonctionnalités à la fin)
2) Comme je le savais, sans comprendre toutes ces fonctionnalités, il est difficile de faire cette méthode. J'ai essayé d'autres papiers, mais tous étaient un peu difficiles pour un débutant.
So I just decided to take all the pixel values as my features.
(Je ne m'inquiétais pas de la précision ou des performances, je voulais juste que cela fonctionne, au moins avec le moins de précision)
J'ai pris l'image ci-dessous pour mes données d'entraînement:
(Je sais que la quantité de données d'entraînement est inférieure. Mais, comme toutes les lettres ont la même police et la même taille, j'ai décidé d'essayer).
Pour préparer les données pour la formation, j'ai fait un petit code dans OpenCV. Il fait les choses suivantes:
- Il charge l'image.
- Sélectionne les chiffres (évidemment en trouvant les contours et en appliquant des contraintes sur la surface et la hauteur des lettres pour éviter les fausses détections).
- Dessine le rectangle englobant autour d'une lettre et attend
key press manually
. Cette fois, nous appuyons nous-mêmes sur la touche numérique correspondant à la lettre en boîte.
- Une fois que la touche numérique correspondante est enfoncée, elle redimensionne cette case à 10x10 et enregistre les valeurs de 100 pixels dans un tableau (ici, les échantillons) et le chiffre correspondant saisi manuellement dans un autre tableau (ici, les réponses).
- Enregistrez ensuite les deux tableaux dans des fichiers txt séparés.
À la fin du classement manuel des chiffres, tous les chiffres dans les données du train (train.png) sont étiquetés manuellement par nous-mêmes, l'image ressemblera à ci-dessous:
Ci-dessous est le code que j'ai utilisé pour le but ci-dessus (bien sûr, pas si propre):
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)
Maintenant, nous entrons dans la partie formation et test.
Pour tester la partie, j'ai utilisé l'image ci-dessous, qui a le même type de lettres que j'ai utilisé pour former.
Pour la formation, nous procédons comme suit :
- Chargez les fichiers txt que nous avons déjà enregistrés plus tôt
- créer une instance de classificateur que nous utilisons (ici, c'est KNearest)
- Ensuite, nous utilisons la fonction KNearest.train pour former les données
À des fins de test, nous procédons comme suit:
- Nous chargeons l'image utilisée pour les tests
- traiter l'image comme précédemment et extraire chaque chiffre en utilisant des méthodes de contour
- Dessinez un cadre de délimitation pour lui, puis redimensionnez-le à 10 x 10 et stockez ses valeurs de pixels dans un tableau comme précédemment.
- Ensuite, nous utilisons la fonction KNearest.find_nearest () pour trouver l'élément le plus proche de celui que nous avons donné. (Si chanceux, il reconnaît le bon chiffre.)
J'ai inclus les deux dernières étapes (formation et tests) dans un seul code ci-dessous:
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)
Et cela a fonctionné, voici le résultat que j'ai obtenu:
Ici, cela a fonctionné avec une précision de 100%. Je suppose que c'est parce que tous les chiffres sont de même nature et de même taille.
Mais de toute façon, c'est un bon début pour les débutants (je l'espère).