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).
Für diejenigen, die an C ++ - Code interessiert sind, kann der folgende Code verwendet werden. Danke Abid Rahman für die nette Erklärung.
Das Verfahren ist das gleiche wie oben, jedoch verwendet die Konturfindung nur die Kontur der ersten Hierarchieebene, sodass der Algorithmus nur die äußere Kontur für jede Ziffer verwendet.
Code zum Erstellen von Muster- und Etikettendaten
Code für Training und Test
Ergebnis
Im Ergebnis wird der Punkt in der ersten Zeile als 8 erkannt und wir haben nicht für den Punkt trainiert. Außerdem betrachte ich jede Kontur in der ersten Hierarchieebene als Beispieleingabe. Der Benutzer kann dies vermeiden, indem er den Bereich berechnet.
quelle
*** stack smashing detected ***:
und daher erhalte ich kein endgültiges Bild, wie Sie oben sehen (Ziffern in grüner Farbe)char name[4];
Ihren Code inchar name[7];
und habe den stapelbezogenen Fehler nicht erhalten, aber ich erhalte immer noch nicht die richtigen Ergebnisse. Ich bekomme ein Bild wie hier < i.imgur.com/qRkV2B4.jpg >Wenn Sie sich für den Stand der Technik im maschinellen Lernen interessieren, sollten Sie sich mit Deep Learning befassen. Sie sollten eine CUDA haben, die die GPU unterstützt, oder alternativ die GPU in Amazon Web Services verwenden.
Google Udacity hat ein nettes Tutorial dazu mit Tensor Flow . In diesem Tutorial lernen Sie, wie Sie Ihren eigenen Klassifikator mit handgeschriebenen Ziffern trainieren. Ich habe eine Genauigkeit von über 97% für den Testsatz mit Convolutional Networks erhalten.
quelle