Nun, ich habe beschlossen, mich mit meiner Frage zu beschäftigen, um das obige Problem zu lösen. Ich wollte eine einfache OCR mit KNearest oder SVM Features in OpenCV implementieren. Und unten ist, was ich tat und wie. (es ist nur zum Lernen, wie man KNearest für einfache OCR-Zwecke verwendet).
1) Meine erste Frage bezog sich auf letter_recognition.data
Datei, die mit OpenCV-Beispielen geliefert wird. Ich wollte wissen, was in dieser Datei steht.
Er enthält einen Buchstaben und 16 Merkmale dieses Buchstabens.
Und this SOF
hat mir geholfen, sie zu finden. Diese 16 Merkmale werden in dem Papier erläutert Letter Recognition Using Holland-Style Adaptive Classifiers
. (Obwohl ich einige der Funktionen am Ende nicht verstanden habe)
2) Da ich wusste, dass es schwierig ist, diese Methode anzuwenden, ohne all diese Funktionen zu verstehen. Ich habe einige andere Papiere ausprobiert, aber alle waren ein wenig schwierig für einen Anfänger.
Also habe ich beschlossen, alle Pixelwerte als Merkmale zu verwenden. (Ich war nicht besorgt über die Genauigkeit oder Leistung, ich wollte nur, dass es funktioniert, zumindest mit der geringsten Genauigkeit)
Ich habe das unten stehende Bild für meine Trainingsdaten verwendet:
(Ich weiß, dass die Menge der Trainingsdaten geringer ist. Aber da alle Buchstaben die gleiche Schriftart und -größe haben, habe ich beschlossen, es damit zu versuchen).
Um die Daten für das Training vorzubereiten, habe ich einen kleinen Code in OpenCV erstellt. Er macht die folgenden Dinge:
- Es lädt das Bild.
- Wählt die Ziffern aus (natürlich durch Kontursuche und Anwendung von Beschränkungen auf Fläche und Höhe der Buchstaben, um falsche Erkennungen zu vermeiden).
- Zeichnet das begrenzende Rechteck um einen Buchstaben und wartet auf
key press manually
. Dieses Mal haben wir drücken Sie selbst die Zifferntaste entsprechend dem Buchstaben im Kästchen.
- Sobald die entsprechende Zifferntaste gedrückt wird, wird die Größe dieses Feldes auf 10x10 verkleinert und alle 100 Pixelwerte in einem Array (hier: Muster) und die entsprechende manuell eingegebene Ziffer in einem anderen Array (hier: Antworten) gespeichert.
- Dann speichern Sie die beiden Arrays in separaten
.txt
Dateien.
Am Ende der manuellen Klassifizierung von Ziffern werden alle Ziffern in den Trainingsdaten ( train.png
) von uns manuell beschriftet werden, sieht das Bild wie folgt aus:
Nachfolgend finden Sie den Code, den ich für den oben genannten 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 kommen wir zum Teil der Ausbildung und Prüfung.
Für den Testteil habe ich das unten stehende Bild verwendet, das dieselbe Art von Buchstaben enthält, die ich in der Trainingsphase verwendet habe.
Für die Ausbildung gehen wir folgendermaßen vor :
- Laden Sie die
.txt
Dateien, die wir bereits zuvor gespeichert haben
- eine Instanz des von uns verwendeten Klassifikators erstellen (in diesem Fall KNearest)
- Dann verwenden wir die Funktion KNearest.train, um die Daten zu trainieren
Zu Testzwecken gehen wir folgendermaßen vor:
- Wir laden das für den Test verwendete Bild
- Verarbeitung des Bildes wie zuvor und Extraktion der einzelnen Ziffern mit Hilfe von Konturverfahren
- Zeichnen Sie einen Begrenzungsrahmen, ändern Sie die Größe auf 10x10 und speichern Sie die Pixelwerte in einem Array, wie zuvor geschehen.
- Dann verwenden wir die Funktion KNearest.find_nearest(), um das Element zu finden, das dem von uns angegebenen am nächsten liegt. (Wenn wir Glück haben, erkennt sie die richtige Ziffer.)
Ich habe die letzten beiden Schritte (Training und Test) in einem einzigen Code unten 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 geklappt, unten ist das Ergebnis zu sehen:
Hier funktionierte es mit 100%iger Genauigkeit. Ich nehme an, dass dies daran liegt, dass alle Ziffern von der gleichen Art und Größe sind.
Aber wie auch immer, dies ist ein guter Anfang für Anfänger (hoffe ich).