Cóż, postanowiłem sam poćwiczyć na moim pytaniu, aby rozwiązać powyższy problem. Chciałem zaimplementować prosty 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 korzystania z KNearest do prostych celów OCR).
1) Moje pierwsze pytanie dotyczyło pliku letter_recognition.data, który jest dostarczany z próbkami OpenCV. Chciałem wiedzieć, co jest w tym pliku.
Zawiera list wraz z 16 cechami tego listu.
I this SOF
pomógł mi to znaleźć. Te 16 funkcji wyjaśniono w artykule Letter Recognition Using Holland-Style Adaptive Classifiers
. (Chociaż na końcu nie rozumiałem niektórych funkcji)
2) Ponieważ wiedziałem, nie rozumiejąc wszystkich tych cech, trudno jest zastosować tę metodę. Próbowałem innych artykułów, ale dla początkujących wszystkie były trochę trudne.
So I just decided to take all the pixel values as my features.
(Nie martwiłem się o dokładność ani wydajność, chciałem tylko, żeby działała, przynajmniej z najmniejszą dokładnością)
Zrobiłem poniższe zdjęcie dla moich danych treningowych:
(Wiem, że ilość danych treningowych jest mniejsza. Ale ponieważ wszystkie litery mają tę samą czcionkę i rozmiar, postanowiłem spróbować).
Aby przygotować dane do szkolenia, stworzyłem mały kod w OpenCV. Robi następujące rzeczy:
- Ładuje obraz.
- Wybiera cyfry (oczywiście poprzez wyszukiwanie konturu i stosowanie ograniczeń dotyczących obszaru i wysokości liter, aby uniknąć fałszywych detekcji).
- Rysuje prostokąt ograniczający wokół jednej litery i czeka na
key press manually
. Tym razem sami naciskamy klawisz cyfry odpowiadający literze w polu.
- Po naciśnięciu odpowiedniego przycisku cyfry zmienia rozmiar tego pola na 10x10 i zapisuje wartości 100 pikseli w tablicy (tutaj próbki) i odpowiadającą ręcznie wprowadzoną cyfrę w innej tablicy (tutaj odpowiedzi).
- Następnie zapisz obie tablice w osobnych plikach txt.
Pod koniec ręcznej klasyfikacji cyfr wszystkie cyfry w danych pociągu (train.png) są przez nas samodzielnie oznaczone ręcznie, obraz będzie wyglądał jak poniżej:
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 przystępujemy do części szkoleniowej i testowej.
Do testowania użyłem poniższego obrazu, który ma ten sam typ liter, którego użyłem do treningu.
W przypadku szkoleń wykonujemy następujące czynności :
- Załaduj wcześniej pliki txt
- utwórz instancję klasyfikatora, którego używamy (tutaj KNearest)
- Następnie używamy funkcji KNearest.train do trenowania danych
Do celów testowych wykonujemy następujące czynności:
- Ładujemy obraz używany do testowania
- przetworzyć obraz jak wcześniej i wyodrębnić każdą cyfrę za pomocą metod konturu
- Narysuj obwiednię, a następnie zmień rozmiar na 10x10 i zapisz wartości pikseli w tablicy, jak wcześniej.
- Następnie używamy funkcji KNearest.find_nearest (), aby znaleźć element najbliższy podanemu. (Przy odrobinie szczęścia rozpoznaje prawidłową cyfrę.)
Ostatnie dwa kroki (szkolenie i testy) umieściłem 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 mam wynik:
Tutaj działało ze 100% dokładnością. Zakładam, że dzieje się tak, ponieważ wszystkie cyfry są tego samego rodzaju i mają taki sam rozmiar.
Ale w każdym razie jest to dobry początek dla początkujących (mam nadzieję).