Nun, ich habe mich entschlossen, meine Frage zu bearbeiten, um das obige Problem zu lösen. Ich wollte eine einfache OCR 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 wird. Ich wollte wissen, was sich in dieser Datei befindet.
Es enthält einen Brief sowie 16 Merkmale dieses Briefes.
Und this SOF
hat mir geholfen, es zu finden. Diese 16 Funktionen werden in diesem Artikel erläutert Letter Recognition Using Holland-Style Adaptive Classifiers
. (Obwohl ich einige der Funktionen am Ende nicht verstanden habe)
2) Da ich wusste, ohne all diese Funktionen zu verstehen, ist es schwierig, diese Methode anzuwenden. 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 war nicht besorgt über Genauigkeit oder Leistung, ich wollte nur, dass es funktioniert, zumindest mit der geringsten Genauigkeit)
Ich habe das folgende Bild für meine Trainingsdaten aufgenommen:
(Ich weiß, dass die Menge der Trainingsdaten geringer ist. Da jedoch alle Buchstaben dieselbe Schriftart und Größe haben, habe ich beschlossen, dies auszuprobieren.)
Um die Daten für das Training vorzubereiten, habe ich in OpenCV einen kleinen Code erstellt. Es macht folgende Dinge:
- Es lädt das Bild.
- Wählt die Ziffern aus (offensichtlich durch Kontursuche und Anwenden von Einschränkungen für Fläche und Höhe von Buchstaben, um falsche Erkennungen zu vermeiden).
- Zeichnet das Begrenzungsrechteck um einen Buchstaben und wartet
key press manually
. Diesmal drücken wir die Zifferntaste selbst , die dem Buchstaben im Feld entspricht.
- 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.
- Speichern Sie dann beide Arrays in separaten TXT-Dateien.
Am Ende der manuellen Klassifizierung der Ziffern werden alle Ziffern in den Zugdaten (train.png) von uns manuell beschriftet. Das Bild sieht wie folgt aus:
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 treten wir in den Schulungs- und Testteil ein.
Zum Testen des Teils habe ich das folgende Bild verwendet, das die gleichen Buchstaben enthält, die ich zum Trainieren verwendet habe.
Für das Training machen wir wie folgt :
- Laden Sie die bereits zuvor gespeicherten txt-Dateien
- Erstellen Sie eine Instanz des Klassifikators, den wir verwenden (hier ist es KNearest).
- Dann verwenden wir die Funktion KNearest.train, um die Daten zu trainieren
Zu Testzwecken gehen wir wie folgt vor:
- Wir laden das zum Testen verwendete Bild
- Verarbeiten Sie das Bild wie zuvor und extrahieren Sie jede Ziffer mit Konturmethoden
- Zeichnen Sie einen Begrenzungsrahmen dafür, ändern Sie die Größe auf 10 x 10 und speichern Sie die Pixelwerte wie zuvor in einem Array.
- Dann verwenden wir die Funktion KNearest.find_nearest (), um das Element zu finden, das dem von uns angegebenen am nächsten kommt. (Wenn Sie Glück haben, erkennt es die richtige Ziffer.)
Ich habe die letzten beiden Schritte (Training und Testen) in den folgenden Code aufgenommen:
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 bekommen habe:
Hier hat es mit 100% Genauigkeit funktioniert. Ich gehe davon aus, 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).