![]() |
OCR für Arme, und ich bin noch ärmer
...an zündenden Ideen.
Aloah! Ich bin darum gebeten worden (=Anweisung von oben ;)) eine kleine Texterkennung für Bildschirmtext zu basteln. Hintergrund ist eine Anwendung bei einem Kunden, deren Ausgabe überwacht werden soll, die jedoch nicht über Messages erreichbare WinControls dargestellt wird. Das Bild der Anwendung zu bekommen ist kein Problem, auch die Positionen und Ausmaße der jeweiligen Stellen sind bekannt, ebenso die Schriftart und -größe. Das heisst ich kann bereits 1a zugeschnittene Bildstreifchen erzeugen die es dann zu analysieren gilt. Es gibt auch keine Probleme mit den Farben, das ganze ist S/W ohne Firlefanz. Das Problem ist bei mir nun nur das prinzipielle Vorgehen, diese Bildchen in einen String zu verwursten (es gibt nicht einmal Zeilenumbrüche). Das Ganze soll jedoch auf einer nicht mehr soooo aktuellen Kiste laufen, und einen möglichst schlanken Fuß machen, da ich doch einige Stellen und relativ schnell auswerten muss, so dass es mir wiederstrebt da nun Pixel für Pixel jede x-Position jeweils mit Bitmaps aller möglichen Buchstaben zu vergleichen, was ja durchaus schnell in mehrere tausende Vergleiche für einen kleinen pupeligen String ausartet - und zudem fehleranfällig ist: Ich erkenne im Moment z.B. ein kleines 'd' als 'dl', weil der Strich vom 'd' von seiner x-Position aus gesehen genau wie das 'l' ausschaut. Was für Anzätze gäbe es das a) effizienter und b) robuster zu erledigen? |
Re: OCR für Arme, und ich bin noch ärmer
Das könnte sehr gut mit NN gehen ;)
NN steht für neuronale Netze - Beispiel ist hier: ![]() |
Re: OCR für Arme, und ich bin noch ärmer
Ich hab ja befürchtet dass NNs auf den Tisch kommen - aber nicht so schnell :)
Problem 1: Ich habe null,keine Ahnung wie NNs funktionieren, und letztlich wie sie arbeiten. Der Lernaufwand wäre gigantisch, was der Terminerfüllung evtl. nicht ganz so gut täte. Problem 2: Auch bei NNs muss ich denke ich das Bild in seine einzelnen Buchstaben zerlegen, wofür ich im Vorfeld aber erstmal erkennen muss wo genau diese sind. Dafür muss ich schon jeden Pixel anfassen, und danach erneut ins NN einführen. Mit schwebte etwas vor, bei dem man nach Möglichkeit jeden Pixel maximal 1 Mal "anfassen" muss. Und zu guter letzt kann ich mir gut vorstellen dass es einen performanteren und naiveren Weg geben muss, da ja die Randbedingungen exakt bekannt sind. Es muss ja im Grunde nichts durch Abstrahierung wirklich erkannt werden, sondern lediglich verglichen (denke ich). So reizvoll das Thema NNs ist, ich fürchte dass ich den Aufand da im Moment nicht hineinstecken kann, und vor allem die Angepasstheit an das Problem bezweifle. (Kanonen -> Spatzen *poof* :stupid:) |
Re: OCR für Arme, und ich bin noch ärmer
Also das nachträglich zu erkennen wird schon kompliziert, wie dir ja wohl schon klar ist...
Vorschlag: Der Text muss so oder so ausgegeben werden. Die entsprechenden Routinen (DrawText usw.) könntest du vielleicht einfach hooken und anhand deren Aufrufe die Textausgaben mitloggen. Das wäre in jedem Fall einfacher. ;-) |
Re: OCR für Arme, und ich bin noch ärmer
Wenn alle Parameter bekannt sind, könntest du das Bild in einzelne Teile, die nur einen Buchstaben enthalten, auftrennen. Am Ende hast du dann einzelne Teile, die noch von weissen Pixeln umgeben sind/sein können, diese Pixel solltest du beim auftrennen nach Möglichkeit auch schon rauswerfen. Dann hashst du alle einzelnen Teile mit einer für die Buchstabenmenge eineindeutigen Funktion, deren Ergebnis du für eine Lookuptabelle verwenden kannst - done. Das Auftrennen sollte auch ganz einfach sein: weisse Spalten beenden den aktuellen Schnipsel, leere Schnipsel und weisse Spalten werden verworfen. Das fasst jeden Pixel nur ein Mal an.
|
Re: OCR für Arme, und ich bin noch ärmer
Hi,
ich würde deinen Bild-String erstmal in einzelne Buchstaben trennen bzw. kleine Bildchen für die Buchstaben. Also für jeden Buchstaben ein Bild. Einfach als Trennsymbol eine Vertikale weiße Linie = Neuer Buchstabe. So bekommst du für jeden Buchstaben/Symbol ein kleines Bildchen. Du hast vermutlich inzwischen von jedem Buchstaben/Symbol ein kleines Bildchen angelegt mit denen du vergleichst. Damit du pro Buchstabe nicht alle Bildchen vergleichen muss, kannst du die Anzahl der möglichen Buchstaben ja nochmal ein schränken. Wenn dein 'Prüfsymbol' z.B. 2 Pixel breit ist, dann reicht es ja mit allen Buchstaben zu vergleichen die nur 2 Pixel Breit sind. so kannst du dir Vergleiche mit breiteren Buchstaben wie dem 'm' gleich mal sparen. Dann kommt es auch nicht zu deinem Problem mit dem 'd' dass als 'dl' erkannt wird, weil du mit dem 'l' gar nicht mehr vergleichst da es nicht breit genug ist und somit von vorneherein aussortiert wird. Zum anderen kannst du überprüfen wie viele Pixel des 'Prüfling' mit dem Potentiellen Buchstaben übereinstimmen. ein 'd' hat dann sicher keine 100%ige Übereinstimmung mit dem 'l'. Oder du machst eine XOR verknüpfung der beiden Bilder. Wenn das Ergebnis dann ein einfarbiges Bild ergibt hast du einen Treffer. Und bei 'd' und 'l' gibt das dann kein einfarbiges Ergebnis bei einer XOR Verknüpfung. Ok, mehrere weiße Vertikale Linien sind dann eben ein Leerzeichen. Da muss man noch aufpassen Wobei der Ansatz einer Überarbeitung bedarf wenn die Zeichen alle gleich breit sind wie z.B. bei 'Courier New' oder anderen Mono Space Schriftarten. kleiner Schönheitsfehler bei der Idee: Das Zeichen " (Doppelte Anführungszeichen) wird als evtl. als '' (Zwei Apostrophe) erkannt, oder gar nicht. Da muss man dann einen Sonderfall einbauen. Btw: mal was anderes... welche Font hast du? Da kann es je nach Font zu Problemen kommen. Ein großes 'I' und ein kleines 'L' sehen in Arial z.B. gleich aus. Ich hoffe die Idee hilft dir weiter. Gargoyl [EDIT]Oh während ich getippt habe, war Dax schon mit der gleichen Idee schneller [/EDIT] |
Re: OCR für Arme, und ich bin noch ärmer
Zitat:
|
Re: OCR für Arme, und ich bin noch ärmer
Zitat:
Zitat:
|
Re: OCR für Arme, und ich bin noch ärmer
Danke für euren Input erstmal!
Cleartype spielt keine Rolle hierbei - das würde es wirklich ganz schön verkomplizieren. Die Schriftart is MS Sans Serif in 11pt, und hier sind tatsächlich 'I' und 'l' identisch. Das kann ich aber umgehen in dem ich bei Wortanfängen einen Großbuchstaben voraussetze (bislang habe ich noch kein mit kleinem l anfangendes Wort entdeckt). Ich hoffe das bleibt so, dann ist das ausreichend. Ein Problem ist hingegen evtl. das Zerschneiden in einzelne Buchstaben. Wenn man in der Schriftart und -größe mal "VWXY" hintereinander schreibt, fällt auf dass zwischen den Buchstaben kein "weisser" Streifen bleibt den ich als Trennung heranziehen könnte. Das fiel mir erst auf als ich mein Alphabet-Bitmap für die Vergleiche gemacht habe, und ich fürchte daher nun dass ich allgemein nicht nach diesem Kriterium schneiden kann. Auch sind Leerzeichen mal 2 und mal 3 Pixel breit :? Ich könnte moglicherweise alle Buchstabenkombinationen mit ohne Trennung als Einheit verarbeiten, aber bis ich da alle möglichen Kombinationen (es gibt auch Sonderzeichen wie / und () und mehr) erstellt und durchgesehen habe ist Weihnachten 2015 - und dann habe ich noch immer nicht die möglichen 3er+ Kombis durch. Wenn mein Chef wüsste was er mir da angetan hat :twisted: \\Edit: Okay, Kombis wie oben genannt kommen so weit nicht vor, so dass ich es mit Zerschneiden versuchen könnte. Neckischerweise aber gibt es durchaus Kombis bei denen 2 Buchstaben 2px Abstand haben, was jedoch kein Leerzeichen ist - das ja auch in 2px vorkommt. "Se" ist z.B. so eine Kombi. Arghs :roteyes: \\Edit2: Ich hab mich vertan. Leerzeichen sind 3-4px breit. Ich sollte wohl doch lieber in den Mai tanzen gehen statt zu arbeiten =) Ich werde das Schnippeln mal ausprogrammieren und schauen ob uns das performance-technisch genügt. Danke euch nochmal! |
Re: OCR für Arme, und ich bin noch ärmer
Ich hätte da noch ne Idee :idea:
Und zwar folgendes: Deine 11pt Schrift dürfte maximal ungefähr 16 Pixel hoch sein. Du könntest jetzt eventuell das Bild spaltenweise durchgehen und für jede Pixelspalte einen Wert berechnen. Der einfachheit halber z.B. eine Zahl. (16 bit Zahl, oberster Pixel ist LSB und unterster Pixel ist MSB) Dann erhältst du für jede Spalte eine Zahl. Jetzt kannst du das Bild als Zahlenabfolge darstellen. (Eine Zahl entspricht einer Bildspalte) Für jeden Buchstaben, den du erkennen möchtest, musst du (vorher) ebenfalls mit dem gleichen verfahren Zahlenfolgen abspeichern. Du hast also eine lange Zahlenfolge und viele kurze (die Buchstaben) - jetzt gehst du jeden Buchstaben durch und schaust, ob dessen Zahlenfolge am Anfang steht. Wenn ja: Buchstaben vorne wegnehmen und weiter gehts. Wenn nein: Vorderste Zahl entfernen und weiter gehts (Buchstabe wurde nicht erkannt) Also statt Pixel vergleichen, Words vergleichen (halt immer 16 Pixel auf einmal) |
Re: OCR für Arme, und ich bin noch ärmer
Um das Problem mit WVXY zu lösen könntest du am Anfang einmal die maximale Breite eines Buchstaben ermitteln und dann spätestens nach dieser maximalen Breite abschneiden. Ich habe mit das mal angesehen, und das ist wirklich ein Ärgernis weil WV und VW sind schwierig auseinander zu halten. Da musst du dann prüfen wenn du bei einer max. breite Abscheidest und keine Übereinstimmung findest (z.B. bei VW weil da noch ein halbes W mit abgeschnitten wird), dann wird es kompliziert, dann musst du halt kucken ob der Anfang deines Bildes mit einem Buchstaben eine Übereinstimmung gibt um dann (hoffentlich) das V findest und da dann abschneidest.
Wenn du bei der Font aber noch mehr solche komischen Konstrukte findest, dann endet dein Algorithmus in einer Sammlung von Sonderfällen. Auch die korrekte Erkennung von (_) könnte problematisch sein auch Kombinationen mit 'II' 'Il' 'lI' und 'll' sehen Problematisch aus. Aber sieh es so, selbst professionelle OCR Programme haben bei 'I' und 'l' Probleme. Aber eine maximale Breite für Buchstaben einführen für die Trennung könnte zumindest helfen. Und ich sehe gerade bei 'LM' klebt auch manchmal zusammen, aber auch nicht immer. Manchmal ist zwischen 'L' und 'M' eine vertikale Linie und manchmal nicht. [EDIT] Die Idee von jfheins ist auch nicht schlecht. Allerdings würde da evtl. bei einem 'W' fälschlicherweise ein 'V' erkannt und hinterher wird kein Treffer mehr gefunden. Idee ist aber gut, muss man vielleicht sogar weniger Sonderfälle behandeln. z.B. bei dem Problem mit dem 'W': Wenn das 'V' erkannt wird, und danach kein Treffer mehr gefunden werden kann, dann einfach das 'V' verwerfen und weitermachen, dann dürfte er das 'W' erkennen und danach (hoffentlich) auch weitere Treffer. das gleiche Problem hat man dann bei einem 'L' weil erst ein 'I' oder 'l' erkannt wird. Wobei das 'I' und 'l' so ein Problem für sich ist. [/EDIT] [EDIT2] Das Problem mit dem 'I' und 'l': 'Ich' und 'lecker' wie willst du das unterscheiden? da brauchst du ja fast schon ein Wörterbuch um das zu erkennen dass 'Iecker' kein dt. Wort ist und es also 'lecker' heißen muss. selbes mit 'lch' und 'Ich'[/EDIT2] |
Re: OCR für Arme, und ich bin noch ärmer
Ich verstehe weiterhin nicht, warum das ganze besser sein soll als den Text direkt bei der Ausgabe abzufangen, aber gut, nicht mein Problem. Bei mir jedenfalls ging das sehr viel besser wie angesprochen mit Hooks.
|
Re: OCR für Arme, und ich bin noch ärmer
@Gargoyl: Wie gesagt, mit etwas Glück stellt sich das Problem mit VW und Konsorten erst garnicht. Da diese Lösung Maßgeschneidert ist muss sie am Ende nirgend wo anders funktionieren.
Die Idee von jfheins klingt auch lustig! Aber auch hier stellt sich das Problem dass man erst zerteilen müsste, da manche Buchstaben mitten drin so aussehen wie ein anderer am Anfang z.B.. Ich glaube der Gewinn durch die gesparten Vergleiche würde hier einer ziemlich komplexen Logik drum herum geopfert. Aaaaaber nun zu dir jaenicke! Ich muss zugeben dein erstes Posting nicht genug beachtet zu haben. Was für Möglichkeiten habe ich da? Das wichtigste ist dabei, dass ich irgendwie auch an die Koordinaten heran komme um den String zuzuordnen. Zudem ist die Frage in welchem Bezug die Koordinaten sind. Zum obersten Fenster, oder zum Control auf dem sie letztlich erscheinen usw. Da ich bislang auch noch keine APIs gehooked habe, würde mich hier auch stückchen Code evtl. schon weiter bringen um zu sehen ob das nicht evtl. wirklich die geniale Lösung wäre. |
Re: OCR für Arme, und ich bin noch ärmer
|
Re: OCR für Arme, und ich bin noch ärmer
Zitat:
![]() Wenn es um eine zeitnahe Lösung gibt, dann bietet sich eine fertige Lösung vermutlich an. Hier gibt es so etwas, auch für Delphi, eine Firmenlizenz kostet im Moment 199€, das sollte machen was du brauchst (getestet habe ich es nicht, ich habe es selbst gerade erst gefunden): ![]() |
Re: OCR für Arme, und ich bin noch ärmer
Danke euch beiden, ich werde mir beide Varianten mal zu Gemüte führen, und dann meinen Chef nochmal fragen ob diese Funktion - da ja egal wie doch recht aufwendig - zwingend ist, bzw. ob es dem Kunden das wert wäre. Ich vermute es ist sich vorher keiner so wirklich bewusst gewesen was das alles mit sich bringt, mir inklusive.
:dp: |
Re: OCR für Arme, und ich bin noch ärmer
Wenn du die genauen Anfangskoordinaten und den Zeichensatz kennst, brauchst du nur jedes sichtbare Zeichen des Zeichensatzes per Pixel zu vergleichen.
Hast du den ersten Buchstaben, dann kennst du die Breite und kannst mit dem nächsten Zeichen fortfahren. Zur Beschleunigung könntest du dir vom Zeichensatz für jedes Zeichen eine Pixel-Checksumme bilden und die Breite vormerken. Dann berechnest du an der aktuellen Position und Breite die Checksumme der Pixel und vergleichst sie mit der vorbereiteten Liste der Checksummen/Zeichenbreiten. Evtl. müsstest du in der Liste manche Zeichen umsortieren, damit zB ein "W" nicht als "V" erkannt wird. Pseudocode:
Delphi-Quellcode:
Nur so eine Idee...
function TextLineAtBitmapPos(Bitmap: TBitmap; XPos, YPos: Integer): String;
result := ''; repeat for width := MINWIDTH to MAXWIDTH do begin checksum := CalcPixelChecksum(Bitmap, XPos, YPos, width); foundChar := FindInList(width, checksum); if foundChar <> #0 then begin result := result + foundChar; Inc(XPos, width); break; end; end; until foundChar = #0; end; |
Re: OCR für Arme, und ich bin noch ärmer
Durch deinen Beitrag kam mir gerade irgendwie in den Sinn, warum mein erster Ansatz performancemäßig seltsam war, obgleich du es nicht explizit geschrieben hast. Ich hatte immer Kandidaten für Zeichen in ein temporäres Bitmap gezeichnet (dass ich vorher immer resizen musste), und dieses dann mit den Templates vom Zeichensatz verglichen. Ich müsste ja lediglich die Koordinaten anpassen und kann direkt im ursprünglichen Bild vergleichen :stupid:
Die Idee mit der Prüfsumme gefällt mir auch gut! Ist ansatzweise ähnlich der Idee von jfheins weiter vorne. Ich bin gespannt was ich letztlich einbauen soll - die Entscheidung muss ich wohl weiter geben. |
Re: OCR für Arme, und ich bin noch ärmer
Interessant ist aber auch der Ansatz von dem vorgeschlagenen Programm, welches für bestimmte Fenster die Windows APIs zum zeichnen von Texten etc. hooked. Das wäre dann ganz weg vom OCR und für dein Anwendungsfall auch eine passende Lösung.
|
Re: OCR für Arme, und ich bin noch ärmer
ich hab jetzt nicht alle Beiträge in voller länge durchgelesen, aber so schwer stelle ich mir das nicht vor:
Schritt Eins: Programm schreibt das Alphabet mit der Schriftart und wendet die Buchstabenextrahiermethode (Weiße Linie) an. Für jeden Buchstaben zählt es die Anzahl der schwarzen Pixel. Ich Denke, Vorrausgesetzt das Schicksal meint es nicht alzu schlecht mit dir, das in jedem Buchstaben eine andere Anzahl schwarzer Pixel ist. Findest du jetzt einen breiteren Buchstaben als gewöhnlich ( Bsp.:WW ) Der darauf zurückzuführen ist, dass sich zwei Buchstaben berühren, so rechnest du die schwarze Pixelzahl aus, und schaust welche beiden Buchstaben das Ergeben könnten. Bei ganz dreisten zusammenhängenden 3er Buchstaben, wird das etwas rechenintensiver. Man bedenke aber die Arbeit am Anfang nur einmal ausgeführt werden muss, weshalb man finde ich sagen kann, dass das auch ein älterer PC packt :) Gruß Spiderpig |
Re: OCR für Arme, und ich bin noch ärmer
Ich bin letztlich wirklich dazu übergegangen alle Font-Bitmaps an einer X-Koordinate mit der Quelle pixelweise zu vergleichen (die G32 macht's performant genug). Den breitesten 100%igen Match nehm ich dann. Im Falle von "l" und "I" kann ich glücklicherweise davon ausgehen, dass Wortanfänge immer groß geschrieben sind. Finde ich also ein kleines L am Wortanfang, muss es ein großes I sein.
Finde ich an einer Stelle keinen Match, gehts ein Pixel weiter rechts von vorne los. Sobald ich 3 Pixel lang nix gefunden hab, kann ich ein Leerzeichen annehmen - das passte bisher bei allen Strings die mir unter kamen in dem Font. Glücklicherweise sind die Zeilen auch nicht so wahnsinnig lang, so dass ich nicht arg viel "nichts-gefunden"-Freiraum vergleiche. Die aktuelle Lösung ist daher völlig fern von etwas generischem, da sie genau den Font in genau der Größe voraussetzt, aber für meinen Zweck ist das völlig ausreichend. Danke dir dennoch für den Beitrag! |
Re: OCR für Arme, und ich bin noch ärmer
Hallo!
Du kannst den Text den du mit OCR lesen willst einfach als präfixfreien code betrachten. Dann sind die Buchstaben die codewörter und die pixelspalten sind das alphabet du kannst sie als integer zahlen betrachten wie jfheins gesagt hat :shock: Da du ja die schrift und die grösse weisst, kannst du den codebaum ganz einfach bauen am anfang deines programms. Also du schreibst dir z.B. ein A hin, gehst seine Pixelspalten durch und fügst diese in den Baum ein. Damit es sicher präfixfrei ist, musst du halt auch auf leerspalten aufpassen die nach einem Buchstaben folgen. Am schluss fügst du noch in das Blatt ein, welcher Buchstabe es war. Das machst du für alle buchstaben die vorkommen. :roll: Wenn du den baum dann hast, kannst du den text ganz einfach scannen. Du gehst die pixelspalten durch und folgst damit dem baum von der wurzel bis in die Blätter. Wenn du in einem blatt bist, hast du ein buchstabe fertig und springst wieder zur wurzel. So musst du also jeden pixel nur einmal anschauen genau wie du wolltest :) Liebe Grüsse Laufi |
Re: OCR für Arme, und ich bin noch ärmer
DAS klingt doch auch nach einer ausgesprochen eleganten Sache! Ich bin ja fast geneigt mein Progrämmchen nochmal umzubauen jetzt :)
|
Re: OCR für Arme, und ich bin noch ärmer
Hallo!
Das freut mich dass es dir gefällt :o Dafür habe ich rasch was extra für dich geschrieben :-D
Delphi-Quellcode:
Man kann es natürlich noch schneller machen mit sortiertem einfügen, scanline und so aber das kannst du sicher selber. du kannst auch noch hamming distanz machen damit er toleranter ist :shock:
unit Main;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TSimpleOCR = class type TPixCol = Cardinal; TNode = class private FPixCol: TPixCol; FChildren: array of TNode; FLetter: Char; public destructor Destroy; override; function Add(PixCol: TPixCol): TNode; function Child(PixCol: TPixCol): TNode; end; private FRoot: TNode; FFont: TFont; protected procedure InsertLetters(Letters: string); function BitmapFromText(const S: string): TBitmap; class function GetPixCol(x: Integer; Bmp: TBitmap): TPixCol; public destructor Destroy; override; procedure Init(Font: TFont); overload; procedure Init(const Alphabet: string; Font: TFont); overload; function Scan(Bmp: TBitmap): string; end; type TForm2 = class(TForm) procedure FormCreate(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } end; var Form2: TForm2; implementation {$R *.dfm} { TSimpleOCR } destructor TSimpleOCR.Destroy; begin FreeAndNil(FRoot); inherited; end; procedure TSimpleOCR.Init(const Alphabet: string; Font: TFont); begin FFont:= Font; FreeAndNil(FRoot); FRoot:= TNode.Create; InsertLetters(Alphabet); end; procedure TSimpleOCR.Init(Font: TFont); var Alphabet: string; ch: Char; begin for ch := #32 to #255 do Alphabet:= Alphabet + ch; Init(Alphabet, Font); end; procedure TSimpleOCR.InsertLetters(Letters: string); var Bmp: TBitmap; I, X, cx: Integer; Node: TNode; begin Bmp:= BitmapFromText(Letters); try X:= 0; for I := 1 to Length(Letters) do begin Node:= FRoot; cx:= Bmp.Canvas.TextWidth(Letters[I]); while cx > 0 do begin Node:= Node.Add(GetPixCol(x, bmp)); Inc(X); Dec(cx); end; Node.FLetter:= Letters[I]; end; finally Bmp.Free; end; end; function TSimpleOCR.Scan(Bmp: TBitmap): string; var X, Y: Integer; PixCol: TPixCol; Node: TNode; M: Cardinal; begin Node:= FRoot; for X := 0 to Pred(Bmp.Width) do begin Node:= Node.Child(GetPixCol(X, bmp)); if Node <> nil then begin if Node.FChildren = nil then begin Result:= Result + Node.FLetter; Node:= FRoot; end; end else begin raise Exception.CreateFmt('Fehler: OCR kann nach "%s" nicht weiterlesen!', [Result]); end; end; end; class function TSimpleOCR.GetPixCol(x: Integer; Bmp: TBitmap): TPixCol; var y: Integer; M: Cardinal; begin Result:= 0; M:= 1; for Y := 0 to Pred(Bmp.Height) do begin if bmp.Canvas.Pixels[x, y] = 0 then Result:= Result or M; M:= M shl 1; end; end; function TSimpleOCR.BitmapFromText(const S: string): TBitmap; begin Result:= TBitmap.Create; Result.Canvas.Font:= FFont; with Result.Canvas.TextExtent(S) do Result.SetSize(cx, cy); Result.Canvas.TextOut(0, 0, S); end; { TSimpleOCR.TNode } destructor TSimpleOCR.TNode.Destroy; var I: Integer; begin for I := High(FChildren) downto Low(FChildren) do FreeAndNil(FChildren[I]); inherited; end; function TSimpleOCR.TNode.Add(PixCol: TPixCol): TNode; begin Result:= Child(PixCol); if Result = nil then begin Result:= TNode.Create; Result.FPixCol:= PixCol; Result.FChildren:= nil; SetLength(FChildren, Length(FChildren) + 1); FChildren[High(FChildren)]:= Result; end; end; function TSimpleOCR.TNode.Child(PixCol: TPixCol): TNode; var I: Integer; begin for I := 0 to High(FChildren) do begin if FChildren[I].FPixCol = PixCol then begin Result:= FChildren[I]; Exit; end; end; Result:= nil; end; { Form2 } procedure TForm2.FormCreate(Sender: TObject); var OCR: TSimpleOCR; Bmp: TBitmap; begin OCR:= TSimpleOCR.Create; try OCR.Init(Canvas.Font); bmp:= OCR.BitmapFromText('Hallo das ist ein text der gescannt wird!!'); try Caption:= OCR.Scan(Bmp); finally FreeAndNil(Bmp); end; finally FreeAndNil(OCR); end; end; end. Liebe Grüsse Laufi |
Re: OCR für Arme, und ich bin noch ärmer
Bist du des Wahnsinns? :shock: Jetzt hab ich ja nicht mal mehr eine Ausrede (für mich selbst) es auf die extrem lange Bank zu schieben ;).
|
Re: OCR für Arme, und ich bin noch ärmer
Hallo,
wenn es sich nur um eine begrenzte Anzahl messages handelt, könnte man ja einen "holistischen" Ansatz versuchen: über das ganze Fenster eine Art Prüfsumme bilden und testen, ob verschiedene Messages (Bilder) sich unterscheiden lassen. Im Endeffekt ist das ein Ähnlichkeitsvergleich für Bilder, dafür sollte man Software finden können, es gibt ja Bildsuchprogramme, die im Internet ähnliche Bilder aufspüren. Gruss Reinhard |
Re: OCR für Arme, und ich bin noch ärmer
Das ist in diesem Fall keine wirkliche Option, da insbesondere auch beliebig zusammengesetzte Zahlenkolonnen dabei sind. Bei nur ein paar zig möglichen Texten wäre das sicherlich erheblich einfacher.
|
Re: OCR für Arme, und ich bin noch ärmer
Hab eine Idee dazu:
Der erste Schritt ist, für alle Zeichen eine Vektorsumme zu erstellen. Dh. alle Koordinaten der schwarzen Pixel zu addieren. Das Ergebnis ist eine Koordinate, die einem Buchstaben zugeordnet werden kann. Diese werden dann in einer Tabelle abgelegt. Das OCR-System selbst muss dann nur noch von links beginnend ein Kästchen immer weiter nach rechts wachsen lassen und aus diesem Kästchen nach dem selben Schema eine Koordinate berechnen- bis eine Übereinstimmung mit der Tabelle gefunden wurde. Das nächste Kästchen beginnt dann nach dem letzten, bzw dort, wo das nächste schwarze Pixel gefunden wird. Das sollte nicht besonders CPU zehrend sein und recht einfach zu programmieren. Vielleicht sollte man das ganze auch aus zwei gegenüber liegenden Ecken machen, weil die am weitesten von der Ecke entfernten Pixel sonst die größte Gewichtung haben. Was haltest Du davon? |
Re: OCR für Arme, und ich bin noch ärmer
Der Punkt von dem aus die Koordinaten berechnet und addiert werden sollte vielleicht doch eher auf halber Höhe liegen um größere Unterschiede zwischen den Zeichen zu bekommen. Ich habs noch nicht ausprobiert, aber meiner Meinung nach sollten dann auch l und I gut Unterscheidbar sein. Der Vorteil dieser Methode ist, dass man sich das aufteilen in Buchstaben spart und die einzelnen Zeichen sehr gut voneinander Unterscheidbar sind.
In der Tabelle könnte man auch Regions um die vorausberechneten Koordinaten legen und die gefundenen Koordinaten mittel PTInRegion überprüfen. Ich werd mal ein Programm schreiben um zu überprüfen wie weit sich die Zeichen von einander unterscheiden. Das werde ich allerdings heute nicht mehr schaffen... vielleicht Morgen. |
Re: OCR für Arme, und ich bin noch ärmer
Das Problem mit l und I in diesem Font ist leider, dass sie Pixel für Pixel identisch sind. Da kann man sich auf den Kopf stellen und mit den Füßen Fliegen fangen - man wird's nicht ohne Semantik auseinanderhalten können. Ist aber wie gesagt in meinem speziellen Fall jetzt auch nicht weiter schlimm.
|
Re: OCR für Arme, und ich bin noch ärmer
Zitat:
hier noch 2 Zeilen damit es auch unter Vista mit Delphi 2009 läuft:
Delphi-Quellcode:
unit Main;
... procedure TSimpleOCR.Init(Font: TFont); var Alphabet: string; ch: Char; begin for ch := #32 to #126 do // <-- Edit 1: statt #255 Alphabet:= Alphabet + ch; Init(Alphabet, Font); end; ... function TSimpleOCR.BitmapFromText(const S: string): TBitmap; begin Result:= TBitmap.Create; Result.PixelFormat := pf1bit; // <-- Edit 2: Zeile eingefügt -> Cleartype/Antialiasing aus Result.Canvas.Font:= FFont; with Result.Canvas.TextExtent(S) do Result.SetSize(cx, cy); Result.Canvas.TextOut(0, 0, S); end; ... |
Alle Zeitangaben in WEZ +1. Es ist jetzt 16:53 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz