wake-up-neo.com

Einfache Ziffernerkennung OCR in OpenCV-Python

Ich versuche eine "Digit Recognition OCR" in OpenCV-Python (cv2) zu implementieren. Es ist nur zu Lernzwecken. Ich möchte sowohl die KNearest- als auch die SVM-Funktionen in OpenCV kennenlernen.

Ich habe 100 Proben (d. H. Bilder) von jeder Ziffer. Ich würde gerne mit ihnen trainieren.

Es gibt ein Beispiel letter_recog.py, das mit dem OpenCV-Beispiel geliefert wird. Aber ich konnte immer noch nicht herausfinden, wie man es benutzt. Ich verstehe nicht, was die Beispiele, Antworten usw. sind. Außerdem wird zuerst eine txt-Datei geladen, die ich zuerst nicht verstanden habe.

Bei der späteren Suche konnte ich in cpp-Beispielen eine letter_recognition.data finden. Ich habe es benutzt und einen Code für cv2.KNearest im Modell letter_recog.py erstellt (nur zum Testen):

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()

Es gab mir eine Reihe von Größe 20000, ich verstehe nicht, was es ist.

Fragen:

1) Was ist die Datei letter_recognition.data? Wie erstelle ich diese Datei aus meinem eigenen Datensatz?

2) Was bedeutet results.reval()?

3) Wie können wir ein einfaches Ziffernerkennungswerkzeug mit der Datei letter_recognition.data (entweder KNearest oder SVM) schreiben?

365
Abid Rahman K

Nun, ich habe mich dazu entschlossen, mich auf meine Frage zu konzentrieren, um das obige Problem zu lösen. Ich wollte eine einfache Texterkennung mit KNearest- oder SVM-Funktionen in OpenCV implementieren. Und unten ist was ich getan habe und wie. (Es dient nur zum Erlernen der Verwendung von KNearest für einfache OCR-Zwecke.).

1) Meine erste Frage betraf die Datei letter_recognition.data, die mit OpenCV-Beispielen geliefert wurde. Ich wollte wissen, was sich in dieser Datei befindet.

Es enthält einen Buchstaben sowie 16 Merkmale dieses Buchstabens.

Und this SOF hat mir geholfen, es zu finden. Diese 16 Funktionen werden im Artikel Letter Recognition Using Holland-Style Adaptive Classifiers erläutert. (Obwohl ich einige der Funktionen am Ende nicht verstanden habe)

2) Da ich wusste, dass es schwierig ist, diese Methode durchzuführen, ohne all diese Funktionen zu verstehen. Ich habe einige andere Papiere ausprobiert, aber für Anfänger waren alle etwas schwierig.

_So I just decided to take all the pixel values as my features._ (Ich habe mir keine Sorgen um Genauigkeit oder Leistung gemacht, ich wollte nur, dass es funktioniert, zumindest mit der geringsten Genauigkeit.)

Ich habe folgendes Bild für meine Trainingsdaten aufgenommen:

enter image description here

(Ich weiß, dass die Menge der Trainingsdaten geringer ist. Da jedoch alle Buchstaben die gleiche Schriftart und -größe haben, habe ich mich entschlossen, dies anzuprobieren.).

Um die Daten für das Training vorzubereiten, habe ich in OpenCV einen kleinen Code erstellt. Es macht folgende Dinge:

  1. Es lädt das Bild.
  2. Wählt die Ziffern aus (offensichtlich durch Finden der Kontur und Anwenden von Einschränkungen für den Bereich und die Höhe der Buchstaben, um falsche Erkennungen zu vermeiden).
  3. Zeichnet das Begrenzungsrechteck um einen Buchstaben und wartet auf _key press manually_. Diesmal drücken wir selbst die Zifferntaste , die dem Buchstaben in der Box entspricht.
  4. Sobald die entsprechende Zifferntaste gedrückt wird, ändert sich die Größe dieses Felds auf 10 x 10 und es werden 100 Pixelwerte in einem Array (hier Beispiele) und die entsprechende manuell eingegebene Ziffer in einem anderen Array (hier Antworten) gespeichert.
  5. Speichern Sie dann beide Arrays in separaten TXT-Dateien.

Am Ende der manuellen Klassifizierung der Ziffern werden alle Ziffern in den Zugdaten (train.png) manuell von uns beschriftet. Das Bild sieht wie folgt aus:

enter image description here

Unten ist der Code, den ich für den obigen Zweck verwendet habe (natürlich nicht so sauber):

_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)
_

Jetzt betreten wir den Trainings- und Testteil.

Zum Testen des Teils habe ich das folgende Bild verwendet, das die gleichen Buchstaben hat, die ich zum Trainieren verwendet habe.

enter image description here

Für das Training gehen wir wie folgt vor :

  1. Laden Sie die bereits zuvor gespeicherten txt-Dateien
  2. erstelle eine Instanz des von uns verwendeten Klassifikators (hier ist es KNearest)
  3. Dann verwenden wir die KNearest.train-Funktion, um die Daten zu trainieren

Zu Testzwecken gehen wir wie folgt vor:

  1. Wir laden das zum Testen verwendete Image
  2. verarbeiten Sie das Bild wie zuvor und extrahieren Sie jede Ziffer mit Konturmethoden
  3. Zeichnen Sie einen Begrenzungsrahmen dafür, ändern Sie dann die Größe auf 10 x 10 und speichern Sie die Pixelwerte in einem Array, wie zuvor beschrieben.
  4. Dann verwenden wir die Funktion KNearest.find_nearest (), um den nächstgelegenen Artikel zu finden. (Wenn Sie Glück haben, erkennt es die richtige Ziffer.)

Ich habe die letzten beiden Schritte (Training und Testen) in einem Code zusammengefasst:

_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)
_

Und es hat funktioniert, unten ist das Ergebnis, das ich bekam:

enter image description here


Hier hat es mit 100% Genauigkeit geklappt. Ich nehme an, das liegt daran, dass alle Ziffern von gleicher Art und Größe sind.

Aber auf jeden Fall ist dies ein guter Anfang für Anfänger (ich hoffe es).

502
Abid Rahman K

Für diejenigen, die sich für C++ - Code interessieren, kann der folgende Code verwendet werden. Danke Abid Rahman für die nette Erklärung.


Die Vorgehensweise ist dieselbe wie oben, jedoch wird bei der Konturfindung nur die Kontur der ersten Hierarchieebene verwendet, sodass der Algorithmus nur die Außenkontur für jede Ziffer verwendet.

Code zum Erstellen von Muster- und Etikettendaten

//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 für Schulung und Prüfung

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();

Ergebnis

Im Ergebnis wird der Punkt in der ersten Zeile als 8 erkannt und wir haben nicht auf den Punkt trainiert. Außerdem betrachte ich jede Kontur in der ersten Hierarchieebene als Beispieleingabe. Der Benutzer kann dies vermeiden, indem er die Fläche berechnet.

Results

48
Haris

Wenn Sie sich für den neuesten Stand des maschinellen Lernens interessieren, sollten Sie sich mit Deep Learning befassen. Sie sollten über eine CUDA-unterstützende GPU verfügen oder alternativ die GPU in Amazon Web Services verwenden.

Google Udacity hat ein Nizza Tutorial zu diesem Thema mit Tensor Flow . In diesem Tutorial lernen Sie, wie Sie Ihren eigenen Klassifikator mit handgeschriebenen Ziffern trainieren. Ich habe mit Convolutional Networks eine Genauigkeit von über 97% auf dem Testset erhalten.

11
Yonatan Simson