Proste rozpoznawanie cyfr OCR w OpenCV-Python

Próbuję zaimplementować "digit Recognition OCR" w OpenCV-Python (CV2). To tylko w celach edukacyjnych. Chciałbym nauczyć się zarówno funkcji KNearest, jak i SVM w OpenCV.

Mam 100 próbek (tj. obrazów) każdej cyfry. Chciałbym z nimi trenować.

Istnieje próbka letter_recog.py, która jest dostarczana wraz z próbką OpenCV. Ale nadal nie mogłem rozgryźć, jak go użyć. Nie rozumiem jakie są próbki, odpowiedzi itp. Na początku ładuje też plik txt, którego nie najpierw zrozum.

Później szukając trochę, mogłem znaleźć letter_recognition.dane w próbkach cpp. Użyłem go i zrobiłem kod do cv2.Knajpa w modelu letter_recog.py (tylko do testów):

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

Dał mi tablicę wielkości 20000, nie rozumiem, co to jest.

Pytania:

1) Co to jest letter_recognition.plik danych? Jak zbudować ten plik z własnego zestawu danych?

2) co oznacza results.reval()?

3) Jak możemy napisać proste narzędzie do rozpoznawania cyfr za pomocą letter_recognition.plik danych (albo KNearest albo SVM)?

Author: Ultraviolet, 2012-02-23

3 answers

[6]}cóż, postanowiłem poćwiczyć się na moim pytaniu, aby rozwiązać powyższy problem. Chciałem zaimplementować simpl OCR przy użyciu funkcji KNearest lub SVM w OpenCV. A poniżej jest to, co zrobiłem i jak. ( służy tylko do nauki używania KNearest do prostych celów OCR).

1) moje pierwsze pytanie dotyczyło letter_recognition.plik danych dostarczany z samplami OpenCV. Chciałem wiedzieć, co jest w tych aktach.

Zawiera literę, wraz z 16 cechami tego list.

Oraz this SOF pomógł mi go znaleźć. Te 16 cech wyjaśniono w artykuleLetter Recognition Using Holland-Style Adaptive Classifiers. ( Chociaż nie rozumiem niektórych funkcji na końcu)

2) ponieważ wiedziałem, bez zrozumienia wszystkich tych funkcji, trudno jest zrobić tę metodę. Próbowałem innych prac, ale wszystkie były trochę trudne dla początkujących.

So I just decided to take all the pixel values as my features. (nie martwiłem się dokładnością ani wydajnością, chciałem tylko, aby działało, przynajmniej z najmniejsza dokładność)

Zrobiłem poniższy obrazek dla moich danych treningowych:

Tutaj wpisz opis obrazka

( wiem, że ilość danych treningowych jest mniejsza. Ale ponieważ wszystkie litery są tej samej czcionki i rozmiaru, postanowiłem spróbować na tym).

Aby przygotować dane do treningu, zrobiłem mały kod w OpenCV. Robi następujące rzeczy:

A) ładuje obraz.

B) wybiera cyfry (oczywiście poprzez znajdowanie konturu i stosowanie ograniczeń obszaru i wysokości liter, aby uniknąć fałszywe wykrywanie).

C) rysuje obwiedniowy prostokąt wokół jednej litery i czeka na key press manually. Tym razem sami wciskamy klawisz cyfry odpowiadający literze w pudełku.

D) Po naciśnięciu odpowiedniego klawisza cyfry, zmienia rozmiar tego pola do 10x10 i zapisuje wartości 100 pikseli w tablicy(tutaj, próbki) i odpowiadające ręcznie wprowadzone cyfry w innej tablicy (tutaj, odpowiedzi).

E) następnie zapisz obie tablice w oddzielnych plikach txt.

Na końcu ręczna klasyfikacja cyfr, wszystkie cyfry w danych pociągu (pociąg.png) są oznaczane ręcznie przez nas, obraz będzie wyglądał jak poniżej:

Tutaj wpisz opis obrazka

Poniżej znajduje się kod, którego użyłem do powyższego celu (oczywiście nie tak czysty):

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)

Teraz wchodzimy do części szkoleniowej i testowej.

Do części testowej użyłem poniższego obrazka, który ma ten sam typ liter, których używałem do treningu.

Tutaj wpisz opis obrazka

Na szkolenia wykonujemy jako follows :

A) załaduj pliki txt zapisane wcześniej

B) Utwórz instancję klasyfikatora, której używamy (tutaj jest to KNearest)

C) następnie używamy KNearest.funkcja train do trenowania danych

Do celów testowych wykonujemy następujące czynności:

A) wczytujemy obraz używany do testowania

B) przetwarza obraz jak wcześniej i wyodrębnia każdą cyfrę za pomocą metod konturu

C) narysuj dla niego obwiednię, a następnie zmień jej rozmiar na 10x10 i zachowaj jej wartości pikseli w tablicy, jak zrobiono wcześniej.

D) następnie używamy KNearest.funkcja find_nearest (), aby znaleźć najbliższy element do tego, który podaliśmy. (Jeśli ma szczęście, rozpoznaje poprawną cyfrę.)

Dodałem dwa ostatnie kroki ( trening i testy) w jednym kodzie poniżej:

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)

I zadziałało, poniżej wynik jaki otrzymałem:

Tutaj wpisz opis obrazka


Tutaj działało ze 100% dokładnością. Zakładam, że to dlatego, że wszystkie cyfry są tego samego rodzaju i tej samej wielkości.

Ale każdy sposób, to jest dobry początek, aby przejść dla początkujących (mam nadzieję, że tak).

 455
Author: Abid Rahman K,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:11

Dla tych, którzy interesują się kodem C++ mogą zapoznać się z poniższym kodem. Dzięki Abid Rahman za miłe Wyjaśnienie.


Procedura jest taka sama jak powyżej, ale Wyszukiwanie konturu wykorzystuje tylko kontur pierwszego poziomu hierarchii, tak że algorytm używa tylko konturu zewnętrznego dla każdej cyfry.

Kod do tworzenia danych próbki i etykiety

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

Kod do szkoleń i testów

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

Wynik

W wyniku kropka w pierwszym wierszu jest wykrywana jako 8 i nie trenowaliśmy dot. Rozważam również każdy kontur na pierwszym poziomie hierarchii jako przykładowe wejście, użytkownik może go uniknąć, obliczając obszar.

Wyniki

 45
Author: Haris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-28 16:16:33

Jeśli interesuje Cię Stan techniki w uczeniu maszynowym, powinieneś zajrzeć do uczenia głębokiego. Powinieneś mieć CUDA obsługujące GPU lub alternatywnie korzystać z GPU w usługach Amazon Web Services.

Google Udacity ma ładny samouczek na ten temat za pomocą Tensor Flow. Ten samouczek nauczy Cię, jak trenować własnego klasyfikatora na ręcznie pisanych cyfrach. Mam dokładność ponad 97% na zestawie testowym przy użyciu sieci Konwolucyjnych.

 10
Author: Yonatan Simson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-13 11:03:56