Reconnaissance OCR à simple chiffre en OpenCV-Python

j'essaie d'implémenter un" OCR de reconnaissance numérique " dans OpenCV-Python (cv2). C'est juste à des fins d'apprentissage. J'aimerais apprendre à la fois KNearest et SVM dans OpenCV.

j'ai 100 échantillons (images) de chaque chiffre. Je voudrais m'entraîner avec eux.

il y a un échantillon letter_recog.py qui vient avec L'échantillon OpenCV. Mais je ne pouvais toujours pas à comprendre comment l'utiliser. Je ne comprends pas quels sont les échantillons, les réponses etc. Également, il charge un fichier txt au début, je ne comprenais pas le premier.

plus tard, en cherchant un peu, j'ai pu trouver une reconnaissance de lettre.les données du rpc échantillons. Je l'ai utilisé et fait un code pour cv2.S'agenouille dans le modèle de letter_recog.py (juste pour tester):

import numpy as np
import cv2

fn = 'letter-recognition.data'
a = np.loadtxt(fn, np.float32, delimiter=',', converters={ 0 : lambda ch : ord(ch)-ord('A') })
samples, responses = a[:,1:], a[:,0]

model = cv2.KNearest()
retval = model.train(samples,responses)
retval, results, neigh_resp, dists = model.find_nearest(samples, k = 10)
print results.ravel()

Il m'a donné un tableau de taille 20000, je ne comprends pas ce que c'est.

Questions:

1) Qu'est-ce que letter_recognition.fichier de données? Comment construire ce fichier à partir de mon propre jeu de données?

2) Que signifie results.reval() ?

3) Comment écrire un outil de reconnaissance numérique simple en utilisant letter_recognition.fichier de données (KNearest ou SVM)?

330
demandé sur Ultraviolet 2012-02-23 16:37:22

3 réponses

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'est mettre en place un 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 pour des buts OCR simples).

1) ma première question portait sur la reconnaissance des lettres.fichier de données fourni avec les échantillons OpenCV. Je voulais savoir ce qu'il y avait dans ce dossier.

It contient une lettre, avec 16 caractéristiques de cette lettre.

et this SOF m'ont aidé à le trouver. Ces 16 caractéristiques sont expliquées dans le document Letter Recognition Using Holland-Style Adaptive Classifiers . ( Bien que je n'ai pas compris certaines des fonctionnalités à la fin)

2) puisque je savais, sans comprendre toutes ces caractéristiques, il est difficile de faire cette méthode. J'ai essayé quelques autres papiers, mais tous ont été un peu difficile pour un débutant.

So I just decided to take all the pixel values as my features. (Je ne m'inquiétais pas de l'exactitude ou de la performance, je voulais juste que cela fonctionne, au moins avec la moindre précision)

j'ai pris ci-dessous l'image pour mes données de formation:

enter image description here

( je sais la quantité de données d'apprentissage est moins. Mais comme toutes les lettres ont la même police et la même taille, j'ai décidé d'essayer ceci).

pour préparer les données pour la formation, j'ai fait un petit code dans OpenCV. Il fait les choses suivantes:

A) il charge l'image.

b) choisit les chiffres ( évidemment en trouvant le contour et en appliquant des contraintes sur la surface et la hauteur des lettres pour éviter les fausses détections).

C) dessine le rectangle limitatif autour d'une lettre et attend key press manually . Cette fois nous appuyez sur la touche de chiffre nous correspondant à la lettre dans la boîte.

D) une fois que la touche de chiffre correspondante est pressée, il redimensionne cette case à 10x10 et sauve 100 valeurs de pixel dans un tableau (ici, samples) et le chiffre correspondant entré manuellement dans un autre tableau(ici, réponses).

E) puis enregistrer les deux tableaux dans des fichiers TXT séparés.

à la fin de la classification manuelle des chiffres, tous les chiffres des données relatives au train( train.png) sont étiqueté manuellement par nous-mêmes, l'image ressemblera à ci-dessous:

enter image description here

ci-dessous est le code que j'ai utilisé à la fin 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 de formation et de test.

pour tester la partie I utilisée ci-dessous image, qui a le même type de lettres que j'ai utilisé pour former.

enter image description here

Pour la formation, nous faisons comme suit :

a) charger les fichiers txt que nous avons déjà enregistrés plus tôt

B) créer une instance de classifier que nous utilisons ( ici, C'est KNearest)

C) puis nous utilisons KNearest.fonction train pour former les données

à des fins d'essai, nous faisons comme suit:

A) Nous charger l'image utilisée pour l'essai

B) traiter l'image comme une image antérieure et extraire chaque chiffre à l'aide des méthodes de contour

C) Tirage au sort de la boîte englobante, puis redimensionner à 10x10, et de stocker les valeurs des pixels dans un tableau comme fait précédemment.

D) nous utilisons alors KNearest.find_nearest () fonction pour trouver l'article le plus proche de celui que nous avons donné. ( Si la chance, il reconnaît le bon chiffre.)

j'ai inclus les deux dernières étapes ( formation et tests) en 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 il a fonctionné, ci - dessous est le résultat que j'ai obtenu:

enter image description here


" ici, il a fonctionné avec une précision de 100%. Je suppose que c'est parce que tous les chiffres sont de même type et de même taille.

Mais de toute façon, c'est un bon début pour aller pour les débutants ( je l'espère).

455
répondu Abid Rahman K 2017-05-23 11:55:11

pour ceux qui s'intéressent au code C++, référez-vous au code ci-dessous. Merci Abid Rahman pour la belle explication.


la procédure est la même que ci-dessus, mais la détermination du contour utilise seulement le contour de premier niveau de hiérarchie, de sorte que l'algorithme utilise seulement le contour externe pour chaque chiffre.

Code pour la création de données d'échantillon et D'étiquette

//Process image to extract contour
Mat thr,gray,con;
Mat src=imread("digit.png",1);
cvtColor(src,gray,CV_BGR2GRAY);
threshold(gray,thr,200,255,THRESH_BINARY_INV); //Threshold to find contour
thr.copyTo(con);

// Create sample and label data
vector< vector <Point> > contours; // Vector for storing contour
vector< Vec4i > hierarchy;
Mat sample;
Mat response_array;  
findContours( con, contours, hierarchy,CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE ); //Find contour

for( int i = 0; i< contours.size(); i=hierarchy[i][0] ) // iterate through first hierarchy level contours
{
    Rect r= boundingRect(contours[i]); //Find bounding rect for each contour
    rectangle(src,Point(r.x,r.y), Point(r.x+r.width,r.y+r.height), Scalar(0,0,255),2,8,0);
    Mat ROI = thr(r); //Crop the image
    Mat tmp1, tmp2;
    resize(ROI,tmp1, Size(10,10), 0,0,INTER_LINEAR ); //resize to 10X10
    tmp1.convertTo(tmp2,CV_32FC1); //convert to float
    sample.push_back(tmp2.reshape(1,1)); // Store  sample data
    imshow("src",src);
    int c=waitKey(0); // Read corresponding label for contour from keyoard
    c-=0x30;     // Convert ascii to intiger value
    response_array.push_back(c); // Store label to a mat
    rectangle(src,Point(r.x,r.y), Point(r.x+r.width,r.y+r.height), Scalar(0,255,0),2,8,0);    
}

// Store the data to file
Mat response,tmp;
tmp=response_array.reshape(1,1); //make continuous
tmp.convertTo(response,CV_32FC1); // Convert  to float

FileStorage Data("TrainingData.yml",FileStorage::WRITE); // Store the sample data in a file
Data << "data" << sample;
Data.release();

FileStorage Label("LabelData.yml",FileStorage::WRITE); // Store the label data in a file
Label << "label" << response;
Label.release();
cout<<"Training and Label data created successfully....!! "<<endl;

imshow("src",src);
waitKey();

Code pour la formation et les tests

Mat thr,gray,con;
Mat src=imread("dig.png",1);
cvtColor(src,gray,CV_BGR2GRAY);
threshold(gray,thr,200,255,THRESH_BINARY_INV); // Threshold to create input
thr.copyTo(con);


// Read stored sample and label for training
Mat sample;
Mat response,tmp;
FileStorage Data("TrainingData.yml",FileStorage::READ); // Read traing data to a Mat
Data["data"] >> sample;
Data.release();

FileStorage Label("LabelData.yml",FileStorage::READ); // Read label data to a Mat
Label["label"] >> response;
Label.release();


KNearest knn;
knn.train(sample,response); // Train with sample and responses
cout<<"Training compleated.....!!"<<endl;

vector< vector <Point> > contours; // Vector for storing contour
vector< Vec4i > hierarchy;

//Create input sample by contour finding and cropping
findContours( con, contours, hierarchy,CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
Mat dst(src.rows,src.cols,CV_8UC3,Scalar::all(0));

for( int i = 0; i< contours.size(); i=hierarchy[i][0] ) // iterate through each contour for first hierarchy level .
{
    Rect r= boundingRect(contours[i]);
    Mat ROI = thr(r);
    Mat tmp1, tmp2;
    resize(ROI,tmp1, Size(10,10), 0,0,INTER_LINEAR );
    tmp1.convertTo(tmp2,CV_32FC1);
    float p=knn.find_nearest(tmp2.reshape(1,1), 1);
    char name[4];
    sprintf(name,"%d",(int)p);
    putText( dst,name,Point(r.x,r.y+r.height) ,0,1, Scalar(0, 255, 0), 2, 8 );
}

imshow("src",src);
imshow("dst",dst);
imwrite("dest.jpg",dst);
waitKey();

résultat

dans le résultat le point dans la première ligne est détecté comme 8 et nous n'avons pas formé pour le point. Aussi je considère chaque contour dans le premier niveau de hiérarchie que l'entrée d'échantillon, l'utilisateur peut l'éviter en calculant la zone.

Results

45
répondu Haris 2014-09-28 16:16:33

si vous êtes intéressé par L'état de L'art dans L'apprentissage Machine, vous devriez regarder dans L'apprentissage en profondeur. Vous devriez avoir un GPU compatible avec CUDA ou utiliser le GPU sur les services web D'Amazon.

Google Udacity a un beau tutoriel sur ce en utilisant Tensor Flow . Ce tutoriel vous apprendra comment former votre propre Classificateur sur des chiffres écrits à la main. J'ai obtenu une précision de plus de 97% sur le test en utilisant des réseaux convolutionnels.

10
répondu Yonatan Simson 2016-06-13 11:03:56