Benutzer-Werkzeuge

Webseiten-Werkzeuge


ss17:erkennung_gedruckter_geschrift_einen_kassenbon_lesen

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen gezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen Revision Vorhergehende Überarbeitung
Nächste Überarbeitung
Vorhergehende Überarbeitung
ss17:erkennung_gedruckter_geschrift_einen_kassenbon_lesen [2017/09/20 17:19]
Jan_Philipps [Symbol-Finder]
ss17:erkennung_gedruckter_geschrift_einen_kassenbon_lesen [2017/10/18 09:13] (aktuell)
Jan_Philipps [Symbol-Finder]
Zeile 41: Zeile 41:
  
 =="​Main"​-Methode== =="​Main"​-Methode==
-__Name im Code: SymbolScanner.__ Diese Methode wird vom Primärteil des Programms (neuronales Netz usw.) aufgerufen ​und nutzt verschiedene Hilfsmethoden um möglichst alle Symbole auf dem Kassenbon zu finden und als kleine Bilder zu speichern.+__Name im Code: SymbolScanner.__ Diese Methode wird vom Primärteil des Programms (neuronales Netz usw.) aufgerufen. Sie nutzt verschiedene Hilfsmethoden um das Bild Zeile für Zeile zu durchsuchen und möglichst alle Symbole auf dem Kassenbon zu finden und als kleine Bilder zu speichern.
  
 Die gefundenen Symbole werden in Format //​Zeile//​_//​xKoordinate//​_//​yKoordinate//​.png gespeichert. Die gefundenen Symbole werden in Format //​Zeile//​_//​xKoordinate//​_//​yKoordinate//​.png gespeichert.
Zeile 53: Zeile 53:
  img = cv2.cvtColor(img,​cv2.COLOR_BGR2GRAY) #​Konvertiert das Bild zu schwarz-weiss/​Graustufen  img = cv2.cvtColor(img,​cv2.COLOR_BGR2GRAY) #​Konvertiert das Bild zu schwarz-weiss/​Graustufen
   
- h,​b=img.shape ​                          ​#h ist die Hoehe des Bildes, b ist die Breite des Bildes+ h,​b=img.shape ​                 #h ist die Hoehe des Bildes, b ist die Breite des Bildes
   
  threshold=Plotton(img,​h,​b) #Ruft Plotton auf, um Grenzwert zu erhalten  threshold=Plotton(img,​h,​b) #Ruft Plotton auf, um Grenzwert zu erhalten
Zeile 65: Zeile 65:
  yMax=0 #​Obere Grenze fuer das Programm, damit nichts doppelt gescannt wird.  yMax=0 #​Obere Grenze fuer das Programm, damit nichts doppelt gescannt wird.
   
- Zeit1=time() #​Startet Zeitmessung+ #Zeit1=time() #​Startet Zeitmessung. Auskommentiert weil meist sinnlos, aber manchmal doch ganz nett zu wissen.
   
  while EndPage==False:​ #Diese Schleife wird ausgefuehrt,​ solange die Seite nicht bis zum Ende ueberprueft wurde  while EndPage==False:​ #Diese Schleife wird ausgefuehrt,​ solange die Seite nicht bis zum Ende ueberprueft wurde
Zeile 72: Zeile 72:
   
  zO,​zU=ZeilenFindo(h,​b,​yMax,​threshold,​img) #Ruft ZeilenFindo auf, um Zeilenober- und Untergrenze zu bestimmen  zO,​zU=ZeilenFindo(h,​b,​yMax,​threshold,​img) #Ruft ZeilenFindo auf, um Zeilenober- und Untergrenze zu bestimmen
- yMax=zU ​ #Setzt Obergrenze fuer naechsten Aufruf von ZeilenFindo,​ damit nur der Bereich unterhalb der vorherigen Zeile durchsucht wird + yMax=zU #Setzt Obergrenze fuer naechsten Aufruf von ZeilenFindo,​ damit nur der Bereich unterhalb der vorherigen Zeile durchsucht wird 
- CurrentLine=CurrentLine+1 ​ #Merkt sich die aktuelle Zeile+ CurrentLine=CurrentLine+1 ​ #Merkt sich die aktuelle Zeile
   
  xS, yS=Skannomaton(zU,​b,​xMax,​zO,​threshold,​img) #Ruft Skannomatin auf, um x- und y-Koordinate des ersten Symbols zu finden  xS, yS=Skannomaton(zU,​b,​xMax,​zO,​threshold,​img) #Ruft Skannomatin auf, um x- und y-Koordinate des ersten Symbols zu finden
Zeile 100: Zeile 100:
   
  try:  try:
- os.mkdir(pPath) #​Erstellt ​Speicherverzeichniss ​falls noch nicht vorhanden+ os.mkdir(pPath) #​Erstellt ​Speicherverzeichnis ​falls noch nicht vorhanden
   
  except: pass  except: pass
Zeile 115: Zeile 115:
  EndLine=True  EndLine=True
   
- Zeit2=time() #​Beendet Zeitmessung.+ #Zeit2=time() #​Beendet Zeitmessung. Auskommentiert weil meist sinnlos, aber manchmal doch ganz nett zu wissen.
   
- print "​SymbolScanner-Time=",​ (Zeit2-Zeit1) #Gibt an, wie lange die ganze Suche gedauert hat+ #print "​SymbolScanner-Time=",​ (Zeit2-Zeit1) #Gibt an, wie lange die ganze Suche gedauert hat. Auskommentiert weil meist sinnlos, aber manchmal doch ganz nett zu wissen.
  
  
Zeile 130: Zeile 130:
  
 def Plotton(img,​h,​b):​ def Plotton(img,​h,​b):​
 + #​img=BildMatrix;​ h=Bildhoehe in Pixeln; b=BildBreite in Pixeln;
   
  pixelCounter=[0]*256 #Erstellt Liste mit 256 Slots, fuer jeden Farbwert einen  pixelCounter=[0]*256 #Erstellt Liste mit 256 Slots, fuer jeden Farbwert einen
Zeile 139: Zeile 140:
  pixelCounter[img[y,​x]]=pixelCounter[img[y,​x]]+1  pixelCounter[img[y,​x]]=pixelCounter[img[y,​x]]+1
   
- for n in range(len(pixelCounter)):​ #​Diese ​Schelife ​Teilt jedes Listenelement durch die Anzahl der Pixel, (Absolute zur relativen Haeufigkeit)+ for n in range(len(pixelCounter)):​ #​Diese ​Schleife ​Teilt jedes Listenelement durch die Anzahl der Pixel, (Absolute zur relativen Haeufigkeit)
  pixelCounter[n]=100.*(pixelCounter[n]/​nOFpixels)  pixelCounter[n]=100.*(pixelCounter[n]/​nOFpixels)
   
Zeile 165: Zeile 166:
  
 def ZeilenFindo(h,​b,​yMax,​threshold,​img):​ def ZeilenFindo(h,​b,​yMax,​threshold,​img):​
 + #​h=Bildhoehe in Pixeln; b=BildBreite in Pixeln; yMax=OberGrenze fuer Suche; threshold=Grenzwert;​ img=BildMatrix;​
   
  jmp=2 ​ #Legt die Anzahl der Reihen fest, die uebersprungen werden (mehr=schneller=ungenauer)  jmp=2 ​ #Legt die Anzahl der Reihen fest, die uebersprungen werden (mehr=schneller=ungenauer)
Zeile 215: Zeile 217:
  
 def Skannomaton(h,​b,​xMax,​yMax,​threshold,​img):​ def Skannomaton(h,​b,​xMax,​yMax,​threshold,​img):​
- + #​h=Untergrenze fuer Suche; b=BildBreite in Pixeln (rechte Grenze fuer Suche); 
 + #​xMax=LinkeGrenze fuer Suche; yMax=OberGrenze fuer Suche; threshold=Grenzwert;​ img=BildMatrix;​
   
  y=yMax #Obergrenze  y=yMax #Obergrenze
Zeile 226: Zeile 229:
   
  while (cy>​yMax) and (cx<b): #Schleife laeuft bis der Algorithmus an der oberen oder rechten Kante vom Bereich ankommt.  while (cy>​yMax) and (cx<b): #Schleife laeuft bis der Algorithmus an der oberen oder rechten Kante vom Bereich ankommt.
- if img[cy,​cx]<​threshold:​ #Wenn der aktuell ​Pixel dunkel ist, werden die Koordinaten zurueckgegeben+ if img[cy,​cx]<​threshold:​ #Wenn der aktuelle ​Pixel dunkel ist, werden die Koordinaten zurueckgegeben
  return cx,cy  return cx,cy
  cx=cx+1 #Geht eins nach rechts  cx=cx+1 #Geht eins nach rechts
Zeile 238: Zeile 241:
   
  while (cy>​yMax) and (cx<​b):​ #​Schleife laeuft bis der Algorithmus an der oberen oder rechten Kante vom Bereich ankommt.  while (cy>​yMax) and (cx<​b):​ #​Schleife laeuft bis der Algorithmus an der oberen oder rechten Kante vom Bereich ankommt.
- if img[cy,​cx]<​threshold:​ #Wenn der aktuell ​Pixel dunkel ist, werden die Koordinaten zurueckgegeben+ if img[cy,​cx]<​threshold:​ #Wenn der aktuelle ​Pixel dunkel ist, werden die Koordinaten zurueckgegeben
  return cx,cy  return cx,cy
  cx=cx+1 #Geht eins nach rechts  cx=cx+1 #Geht eins nach rechts
Zeile 249: Zeile 252:
  return 0,0 #Das steht nur hier, weil das Programm sonst nicht mal starten will. Diese Zeile wird nie aufgerufen.  return 0,0 #Das steht nur hier, weil das Programm sonst nicht mal starten will. Diese Zeile wird nie aufgerufen.
   
 +
 +</​code>​
 +
 +== Koordinaten-Anpasser Oberkante==
 +__Name im Code: Korrektoid.__ Diese Methode wurde zu dem Zweck geschrieben,​ den "Halbe Zeichen"​-Fehler zu beheben. Sie nimmt den zuvor von //​Skannomaton//​ gefundenen Punkt und bringt ihn auf die Höhe des höchsten Punktes des Buchstaben. Die Funktionsweise ähnelt der von //​Findomat_UR//​ und //​Findomat_OL//​. Der Helligkeitswert des Pixels über dem Startpunkt wird getestet. Ist der Pixel dunkel, "​bewegt"​ sich Algorithmus nach oben, ist der Pixel hell, wird der Pixel auf der rechten Seit überprüft. Falls dieser Pixel dunkel ist, wird er zu neuen Startpunkt. Dies wird so lange wiederholt, bis beide Pixel hell sind, der Algorithmus also am lokalen Maximum des Zeichens angelangt ist. Der y-Wert des von //​Scannomaton//​ übergeben Punktes wird nun durch den des lokalen Maximums ersetzt.
 +
 +<code python>
 +
 +def Korrektoid(xS,​yS,​threshold,​img):​
 + #​xS=x-Koordinate Startpunkt; yS=y-Koordinate Startpunkt; treshold=Grenzwert;​ img=BildMatrix
 +
 + cx=xS
 + cy=yS
 + localMax=False
 + #Legt Startkoordinaten fest und schaltet Abbruchbedingung aus
 +
 + while localMax==False and (cy>​(yS-20)):​
 + #Solange das Maximum nicht gefunden wurde und das Programm sich nicht mehr  ​
 + #als 19 von der Starthoehe entfernt hat, wird nach dem Maximum gesucht.
 +
 + if img[(cy-1),​cx]<​threshold:​
 + cy=cy-1
 + #Wenn der Pixel ueber dem aktuellen dunkel ist, wird dieser der neue Ausgagngspixel
 +
 + else:
 + if img[cy,​(cx+1)]<​threshold:​
 + cx=cx+1
 + #Wenn der Pixel rechts von dem aktuellen dunkel ist, wird dieser der neue Ausgagngspixel
 +
 + else:
 + localMax=True
 + #Gibt es keine dunklen Pixel direkt ueber oder direkt rechts vom aktuelle, ist das Maximum gefunden
 +
 + return cy #Hoehe des Maximums wird returnt
 +
  
 </​code>​ </​code>​
Zeile 259: Zeile 297:
  
 def Findomat_UR(x,​y,​yH,​threshold,​img):​ def Findomat_UR(x,​y,​yH,​threshold,​img):​
 + #​x=x-Koordinate Startpunkt; y=y-Koordinate Startpunkt; yH=Von Korrektoid verbesserte y-Koordinate;​
 + #​treshold=Grenzwert;​ img=BildMatrix
   
  cx=x+1  cx=x+1
Zeile 295: Zeile 335:
  
 == Schwarz/​Weiß-Tester Unten Rechts== == Schwarz/​Weiß-Tester Unten Rechts==
-__Name im Code: swTester_UR.__ Die Methode bekommt einen Startpunkt, eine Zielkoordinate und eine Variable, die klar macht ob die Zielkoordinate ein x-Wert oder y-Wert ist. Dann versucht sie eine Line vom Start- zum Zielpunkt zu ziehen ohne einen dunklen Pixel zu treffen. Die "​Linien"​ können nur von unten nach oben und von rechts nach links gezogen weren. Ist die Aktion erfolgreich, ​wurd "​True"​ zurückgegeben,​ schlägt der versuch fehl, ist der Rückgabewert "​False"​.+__Name im Code: swTester_UR.__ Die Methode bekommt einen Startpunkt, eine Zielkoordinate und eine Variable, die klar macht ob die Zielkoordinate ein x-Wert oder y-Wert ist. Dann versucht sie eine Line vom Start- zum Zielpunkt zu ziehen ohne einen dunklen Pixel zu treffen. Die "​Linien"​ können nur von unten nach oben und von rechts nach links gezogen weren. Ist die Aktion erfolgreich, ​wurde "​True"​ zurückgegeben,​ schlägt der versuch fehl, ist der Rückgabewert "​False"​. 
 + 
 +<code python>​ 
 + 
 +def swTester_UR(x,​y,​w,​zk,​threshold,​img):​ 
 + #​x=Startbreite;​ y=Starthoehe;​ w=horizontal oder vertikal; zk=Zielkoordinate;​ 
 + 
 + h,​b=img.shape #h ist die Hoehe des Bildes, b ist die Breite des Bildes 
 + 
 + cy=y 
 + cx=x 
 + #Setzt Startparameter 
 +  
 + if w==1:  
 + #Diese Schleife wird ausgefuehrt,​ wenn in x-Richtung getestet werden soll 
 + while (img[cy,​cx]>​=threshold):​ 
 + #Diese Schleife bewegt sich so lange nach links, bis entweder ein  
 + #dunkler Pixel gefunden wird (False) oder die Zielkoordinate erreicht wurde (True) 
 + if cx==zk: 
 + return True  
 + cx=cx-1 
 + return False 
 +  
 + else: 
 + #Diese Schleife wird ausgefuehrt,​ wenn in y-Richtung getestet werden soll 
 + while (img[cy,​cx]>​=threshold):​ 
 + #Diese Schleife bewegt sich so lange nach oben, bis entweder ein  
 + #dunkler Pixel gefunden wird (False) oder die Zielkoordinate erreicht wurde (True) 
 + if cy==zk: 
 + return True 
 + cy=cy-1 
 + return False 
 + 
 +</​code>​
  
 == Koordinaten-Anpasser Unterkante== == Koordinaten-Anpasser Unterkante==
-__Name im Code: Korrektomat.__ Um korrekt zu funktionieren,​ benötigt //​Findomat_OL//​ einen dunkler ​Pixel, welcher teil des Symbols ist. Diese Methode wurde geschrieben,​ um diesen Pixel zu finden. Die Startkoordinaten für die Suche sind die zuvor von //​Findomat_UR//​ gefundenen Koordinaten der unteren rechten Ecke. Von dort aus bewegt sich die Methode nach links oben, bis sie auf einen dunklen Pixel trifft.+__Name im Code: Korrektomat.__ Um korrekt zu funktionieren,​ benötigt //​Findomat_OL//​ einen dunklen ​Pixel, welcher teil des Symbols ist. Diese Methode wurde geschrieben,​ um diesen Pixel zu finden. Die Startkoordinaten für die Suche sind die zuvor von //​Findomat_UR//​ gefundenen Koordinaten der unteren rechten Ecke. Von dort aus bewegt sich die Methode nach links oben, bis sie auf einen dunklen Pixel trifft.
  
 <code python> <code python>
  
 def Korrektomat(x,​y,​threshold,​img):​ def Korrektomat(x,​y,​threshold,​img):​
 + #​x=x-Koordinate Startpunkt; y=y-Koordinate Startpunkt; treshold=Grenzwert;​ img=BildMatrix
   
  cx=x-1  cx=x-1
Zeile 323: Zeile 397:
  
 def Findomat_OL(x,​y,​threshold,​img):​ def Findomat_OL(x,​y,​threshold,​img):​
 + #​x=x-Koordinate Startpunkt; y=y-Koordinate Startpunkt; treshold=Grenzwert;​ img=BildMatrix
  
  cx=x-1  cx=x-1
Zeile 360: Zeile 435:
 __Name im Code: swTester_OL.__ Die Funktionsweise dieser Methode ist fast identisch mit der von //​swTester_UR//​. Der einzige Unterschied besteht darin, dass die der Algorithmus sich von oben nach unten Bzw. von links nach rechts über das Bild  bewegt. __Name im Code: swTester_OL.__ Die Funktionsweise dieser Methode ist fast identisch mit der von //​swTester_UR//​. Der einzige Unterschied besteht darin, dass die der Algorithmus sich von oben nach unten Bzw. von links nach rechts über das Bild  bewegt.
  
-== Koordinaten-Anpasser Oberkante== +<code python>​ 
-__Name im Code: Korrektoid.__ Diese Methode wurde zu dem Zweck geschriebenden "Halbe Zeichen"​-Fehler zu behebenSie nimmt den zuvor von //​Scannomaton//​ gefundenen Punkt und bringt ihn auf die Höhe des höchsten Punktes ​des Buchstaben. Die Funktionsweise ähnelt der von //​Findomat_UR//​ und //​Findomat_OL//​. Der Helligkeitswert des Pixels über dem Startpunkt ​wird getestet. Ist der Pixel dunkel"bewegt" ​sich Algorithmus ​nach obenist der Pixel hell, wird der Pixel auf der rechten Seit überprüft. Falls dieser Pixel dunkel ist, wird er zu neuen Startpunkt. Dies wird so lange wiederholt, bis beide Pixel hell sind, der Algorithmus also am lokalen Maximum des Zeichens angelangt ist. Der y-Wert des von //​Scannomaton//​ übergeben Punktes ​wird nun durch den des lokalen Maximums ersetzt.+ 
 +def swTester_OL(x,​y,​w,​zk,​threshold,​img):​ 
 + #x=Startbreite;​ y=Starthoehe; w=horizontal oder vertikal; zk=Zielkoordinate;​ 
 + 
 + h,b=img.shape #h ist die Hoehe des Bildes, b ist die Breite ​des Bildes 
 +  
 + cy=y 
 + cx=x 
 + #Setzt Startparameter 
 +  
 +  
 + if w==1: 
 + #Diese Schleife ​wird ausgefuehrt,​ wenn in x-Richtung ​getestet ​werden soll 
 + while (img[cy,cx]>​=threshold) and (cx<​b):​ 
 + #Diese Schleife ​bewegt sich so lange nach rechtsbis entweder ein  
 + #​dunkler ​Pixel gefunden ​wird (False) oder die Zielkoordinate erreicht wurde (True) 
 + if cx==zk: 
 + return True  
 + cx=cx+1 
 + return False 
 +  
 + else: 
 + #Diese Schleife ​wird ausgefuehrt,​ wenn in y-Richtung getestet werden soll 
 + while (img[cy,​cx]>​=threshold) and (cy<​h):​ 
 + #Diese Schleife bewegt sich so lange nach unten, bis entweder ein  
 + #​dunkler ​Pixel gefunden ​wird (False) oder die Zielkoordinate erreicht wurde (True) 
 + if cy==zk: 
 + return True  
 + cy=cy+1 
 + return False 
 + 
 +</​code>​
  
  
Zeile 386: Zeile 492:
 == Artefakte == == Artefakte ==
  
-Flecken, Schatten, Druckfehler, ​Stempeln ​oder ähnliche Unreinheiten auf dem Papier sind teilweise dunkel genug um vom Programm als Symbol erkannt zu werden. Das kann dazu führen, dass Zeilen nicht richtig erkannt werden, in manchen Fällen hält das Programm dann zwei Zeilen für eine einzige. In solchen Fällen werden oft Buchstaben aus den beiden Zeilen nicht gefunden. Dieser Fall tritt aber nur bei relativ großen Artefakten ein, welche ziemlich selten sind, wenn man sich mit dem Foto etwas Mühe gibt.+Flecken, Schatten, Druckfehler, ​Stempel ​oder ähnliche Unreinheiten auf dem Papier sind teilweise dunkel genug um vom Programm als Symbol erkannt zu werden. Das kann dazu führen, dass Zeilen nicht richtig erkannt werden, in manchen Fällen hält das Programm dann zwei Zeilen für eine einzige. In solchen Fällen werden oft Buchstaben aus den beiden Zeilen nicht gefunden. Dieser Fall tritt aber nur bei relativ großen Artefakten ein, welche ziemlich selten sind, wenn man sich mit dem Foto etwas Mühe gibt.
 Kleinere Artefakte werden einfach als Symbole erkannt und an das neuronale Netzwerk übergeben, welches diese dann aussortieren soll. In diesem Teil des Programmes eine Lösung zu finden wäre zu aufwendig, vor allem da man mit einem neuronales Netzwerk ziemlich einfach erkennen kann, ob es sich um eine richtiges Symbol handelt oder nicht. Kleinere Artefakte werden einfach als Symbole erkannt und an das neuronale Netzwerk übergeben, welches diese dann aussortieren soll. In diesem Teil des Programmes eine Lösung zu finden wäre zu aufwendig, vor allem da man mit einem neuronales Netzwerk ziemlich einfach erkennen kann, ob es sich um eine richtiges Symbol handelt oder nicht.
 ==== Machine Learning ==== ==== Machine Learning ====
Zeile 726: Zeile 832:
  
 == Predictions und Verarbeitung == == Predictions und Verarbeitung ==
-Ein weiteres Problem was wir hatten war die Modelle zu Bewerten. Dazu haben wir das Modell einfach über unsere Testbilder laufen lassen und geguckt wie viele es richtig bewertet. +Ein weiteres Problem was wir hatten war die Modelle zu Bewerten. Dazu haben wir das Modell einfach über unsere Testbilder laufen lassen und geguckt wie viele es richtig bewertet. ​Dabei ging es aber erst mal darum einen Eindruck über die ungefähre Genauigkeit zu bekommen. Den richtigen Test haben wir dann mit Bildern von ausgeschnittenen Text gemacht welche das Programm dann als String ausgeben sollte. Dabei sind jedoch die meisten Modelle gescheitert und man konnte oft nicht wiedererkennen was der eigentliche Text sein sollte. Ein weiteres Problem was wir hatten war die Leerzeichen richtig zu setzen. Dies haben wir dann dadurch gelöst, dass wir den Anfangs und Endpixel in Y- Richtung uns merken und dann bei einem zu großen Unterschied ein Leerzeichen in den Text einfügen. Dieses Verfahren ist aber sehr anfällig, weil man einen festen Wert braucht der ungefähr die Größe eines Leerzeichens darstellt.
- +
- +
  
  
  
  
-Unsere Gruppe besteht aus Jan, Tessa, Lucas, Lea und Benni 
  
-Unser Ziel ist es einen Kassenbon einlesen zu können und diese Daten dann darzustellen. 
  
-Dazu müssen wir zwei Funktionen implementieren und zwar brauchen wir eine Funktion, die ein Bild in kleine Bilder aufteilt so, dass in jedem kleinem Bild nur ein Buchstabe ist.  
  
-Die zweite Funktion muss in dem kleinen Bild einen Buchstaben erkennen.+Unsere Gruppe bestand aus Jan, Tessa, Lucas, Lea und Benni
  
 [[ss17:​protokolle_kassenzettel |Protokolle ​ ]] [[ss17:​protokolle_kassenzettel |Protokolle ​ ]]
  
ss17/erkennung_gedruckter_geschrift_einen_kassenbon_lesen.1505920772.txt.gz · Zuletzt geändert: 2017/09/20 17:19 von Jan_Philipps