Der Einfachheit halber wird in diesem Beispiel ein einseitiges Dokument verwendet.
Bei der Dokumentenverarbeitung gibt es häufig Situationen, in denen es nicht ausreicht, die Position von Elementen relativ zu anderen Elementen anhand von „oberhalb - unterhalb - rechts - links“ zu beschreiben. Das kann zum Beispiel vorkommen, wenn sich im Suchbereich mehrere Objekte befinden, die den Suchbedingungen entsprechen. In solchen Fällen werden zusätzliche Unterscheidungsmerkmale benötigt, insbesondere der Abstand zwischen den Objekten. Für diese Zwecke bietet FlexiLayout Studio die Funktion FuzzyQuality sowie die Funktionen der Gruppe Nearest (Nearest, NearestX, NearestY).
Diese Funktionen werden unterschiedlich eingesetzt.
Die Funktion Nearest kann nur im Feld Advanced pre-search relations verwendet werden. Sie legt fest, dass das Programm unter mehreren Hypothesen eines Elements diejenige auswählen muss, die dem in den Eigenschaften der Funktion Nearest angegebenen Element oder Punkt im Bild am nächsten ist. Im Feld Advanced pre-search relations des Elements kann nur eine Funktion aus der Gruppe Nearest verwendet werden. Nach ihrer Ausführung bleibt nur eine Hypothese übrig; dies geschieht in der Phase der Hypothesengenerierung, also bevor der im Feld Erweiterte Nach-Suchbeziehungen angegebene Code ausgeführt wird. Der Parameter Minimum quality, der die Mindestqualität von Hypothesen für das Element angibt, kann für die Elemente StaticText, Zeichenkette, Paragraph, Date und Separator angegeben werden. Es gibt keine Garantie dafür, dass die verbleibende Hypothese die beste ist (und dem gewünschten Objekt im Bild entspricht), da Erweiterte Nach-Suchbeziehungen für die Zuweisung eines Qualitätswerts an eine Hypothese sehr wichtig sind. Bei Verwendung der Funktion Nearest erfolgt die Auswahl der Hypothese in der Phase der Hypothesengenerierung und basiert auf der Nähe zu einem Punkt, nicht auf der Qualität der Hypothese. Wenn die im Abschnitt Erweiterte Nach-Suchbeziehungen angegebenen Eigenschaften für die korrekte Auswahl der Hypothese wichtig sind, sollte daher statt der Funktionen der Gruppe Nearest die Funktion FuzzyQuality verwendet werden.
Die Funktion FuzzyQuality kann nur im Abschnitt Erweiterte Nach-Suchbeziehungen verwendet werden. Anders als die Funktionen der Gruppe Nearest wählt sie keine einzelne Hypothese aus, sondern beeinflusst die Gesamtqualität aller generierten Hypothesen anhand der Eigenschaften dieser Hypothesen und der Parameter der Funktion FuzzyQuality. Außerdem kann die Funktion FuzzyQuality für ein einzelnes Element im Feld Erweiterte Nach-Suchbeziehungen mehrfach verwendet werden. Das bedeutet, dass auf eine Hypothese mehrere unterschiedliche Bedingungen mit verschiedenen Qualitätswerten angewendet werden können. Alle Werte werden multipliziert, um die Post-search quality der Hypothese zu bestimmen. Die Funktion FuzzyQuality sieht wie folgt aus:
FuzzyQuality: x, {f1, f2, f3, f4};
Der Algorithmus funktioniert wie folgt: Die Funktion prüft, ob der Wert des Parameters x zu dem durch die Parameter f1, f2, f3, f4 definierten unscharfen Intervall gehört. Die Bedeutung dieses unscharfen Intervalls ist ähnlich wie bei den unscharfen Intervallen, die für einige der Parameter des Elements Zeichenkette angegeben werden.
Sehen wir uns an, wie die Funktionen Nearest und FuzzyQuality in den folgenden Bildern verwendet werden können.
Wie auf den Bildern zu sehen ist, ist das Rechnungsdokument semi-strukturiert, da die Anordnung der Felder in den verschiedenen Bildern unterschiedlich ist. Wir werden die Felder „Rechnungsnummer“ und „Rechnungsdatum“ erkennen.
Dazu haben wir das Projekt 1.fsp erstellt (Ordner %public%\ABBYY\FlexiCapture\12.0\Samples\FLS\Tips and Tricks\FuzzyAndNearest \Project1).”
Um die FlexiLayout-Struktur zu optimieren und der Logik der Anordnung der gesuchten Felder im Dokument zu folgen, haben wir alle gesuchten Elemente in einem zusammengesetzten Element, InvoiceGroup, zusammengefasst. Mit der Erstellung des FlexiLayouts können wir beginnen, indem wir ein Element erstellen, das die Suchbedingungen für den Namen des Feldes „Rechnungsnummer“ beschreibt. Die Analyse der Bilder zeigt jedoch, dass das Wort „Invoice“, aus dem der Name besteht, im Dokument mehrfach vorkommt. Da sich die relative Position der Felder jeweils ändert, können wir keine Bedingungen festlegen, die die korrekte Erkennung des Wortes „Invoice“ garantieren. Es kann beispielsweise im Namen „Rechnungsdatum“ vorkommen. Um solche Verwechslungen zu vermeiden, haben wir die Beschreibung des Namens des Datumsfeldes mit einem Element vom Typ Static Text mit dem Namen DateHeader begonnen. Im Feld Search text haben wir zwei Varianten des Namens angegeben: Invoicedate:|Invoicedate (also die Varianten des Namens so, wie sie auf den Bildern vorkommen). Die Groß- und Kleinschreibung des Namens ist dabei unerheblich.
Wir suchen das Datumsfeld anhand des Feldnamens. Im Projekt haben wir eine Gruppe DateAlternative erstellt, die aus zwei Elementen besteht: einem Date-Element, das das Datumsfeld in einem der angegebenen Formate sucht, und einem Element vom Typ Zeichenkette für den Fall, dass das Format des gesuchten Feldes abweicht.
Wie auf den Bildern zu sehen ist, kann sich das Datumsfeld entweder rechts neben dem Namen „Rechnungsdatum“ oder darunter befinden. Wenn wir im Feld Relations die Standardsuchbedingungen festlegen (sie werden im Projekt angezeigt, sind aber deaktiviert), wird der Suchbereich zu groß und kann einige Felder einschließen, die irrtümlich als Datumsfeld erkannt werden könnten (ein Beispiel ist im Bild zu sehen). Dies kann zum Beispiel passieren, wenn das Datum nicht dem für das Element Date angegebenen Format entspricht.
Um zu verhindern, dass das Programm den unerwünschten Bereich analysiert, haben wir eine andere Methode verwendet. Im Feld Advanced pre-search relations haben wir den folgenden Code geschrieben:
let Header = InvoiceGroup.DateHeader;
if not Header.IsNull then
{ let rect1 = Rect (Header.Rect.Right, Header.Rect.Top-20dt,
PageRect.Right, Header.Rect.Bottom+20dt);
let rect2 = Rect (Header.Rect.Left - 200dt, Header.Rect.Bottom,
Header.Rect.Right + 150dt, Header.Rect.Bottom+200dt);
RectArray ar;
ar = RectArray ( rect1 );
ar.Add ( rect2 );
RestrictSearchArea( ar );
}
else
{ Above: PageRect.Top + PageRect.Height/2;
}
Der Code prüft, ob der Name des Datumsfeldes gefunden wurde. Falls ja, geben wir den Suchbereich als Array von Rechtecken an (im Beispiel 2 Rechtecke). Ein Rechteck sucht nach dem Datum rechts vom Namen, das andere darunter. Wird der Name nicht gefunden, wird die Suche in der oberen Hälfte des Bildes ausgeführt.
Bei einer Seite, für die die Suchbedingungen im Abschnitt Relations angegeben wurden, hat der Suchbereich nach Ausführung dieses Codes nicht mehr die Form eines Rechtecks. Wie im Bild zu sehen ist, wurden alle unerwünschten Objekte daraus entfernt.
Die erste Codezeile (let Header = InvoiceGroup.DateHeader;) vereinfacht den Code, indem sie die Variable Header definiert und ihr den Wert des Elements DateHeader zuweist.
Wir haben den obigen Code für das Element DateAsString nicht dupliziert, sondern im Abschnitt Advanced pre-search relations die folgende Suchbedingung geschrieben:
if not Date.IsNull then Dontfind();
else RestrictSearchArea (Date.Rect);
Das bedeutet: Wenn das Element Date nicht erkannt wird, wird die Suche in dem Rechteck ausgeführt, das den Suchbereich des Elements Date umschließt.
Um den Suchbereich des Elements DateAsString als Array von Rechtecken anzugeben, duplizieren Sie statt RestrictSearchArea (Date.Rect) aufzurufen den entsprechenden Code aus dem Abschnitt Advanced pre-search relations des Elements Date.
Anschließend haben wir ein Element vom Typ Static Text (mit dem Namen InvoiceHeader) erstellt, um den Namen des Felds „Invoice number“ zu erkennen, und den gesuchten Wert „Invoice“ angegeben. Da das Dokument nicht strukturiert ist, können wir keine spezifischen Suchbedingungen festlegen.
Sobald das FlexiLayout-Matching abgeschlossen ist, sehen wir, dass der Name nur auf der ersten Seite korrekt erkannt wurde. Auf den Seiten 2 und 4 wurde das Wort „Invoice“ fälschlicherweise im Namen des Datumsfelds erkannt. Auf Seite 3 wurde es am unteren Seitenrand gefunden, und gemäß dem Optimierungsalgorithmus wurden die anderen Hypothesen für den Namen nicht erzeugt, obwohl das Wort „Invoice“ im Bild dreimal vorkommt.
Um diese Probleme zu lösen, haben wir die folgende Methode verwendet. Um die Region mit dem Namen des Datumsfelds aus dem Suchbereich des Feldnamens „Invoice“ auszuschließen, haben wir das Element DateHeader zum Abschnitt Exclude regions of elements hinzugefügt (siehe Abbildung unten).
Wenn wir mit der Erstellung des FlexiLayout nicht mit dem Namen DateHeader, sondern mit dem Namen InvoiceHeader begonnen hätten, hätten wir die Funktion Exclude nicht verwenden können, da diese Funktion nur Elemente ausschließen kann, die im Projektbaum oberhalb des aktuellen Elements liegen.
Um die unerwünschte Erkennung des Worts „Invoice“ am unteren Seitenrand auszuschließen, haben wir den folgenden Code in den Abschnitt Advanced pre-search relations geschrieben.
NearestY: PageRect.Top;
Dieser Code weist das Programm an, nach dem Element zu suchen, das der oberen Seitenkante am nächsten liegt.
Sobald das FlexiLayout abgeglichen wurde, sehen wir, dass unsere Methode auf Seite 2 fehlgeschlagen ist, weil der Name des Datumsfelds hier stark verrauscht ist und nicht erkannt wurde. Auf dieser Seite ist die in der Funktion Nearest angegebene Bedingung für beide „Invoice“-Zeichenketten erfüllt, weil sie sich auf derselben Höhe befinden. Und da die Recognition quality der „Invoice“-Zeichenketten in beiden Fällen gut ist, erzeugte der Optimierungsalgorithmus nur eine einzige Hypothese statt zwei separater. Leider ist diese Hypothese nicht korrekt.
Um das Feld „Invoice number“ zu erkennen, haben wir ein Element vom Typ Zeichenkette mit dem Namen InvoiceNumber erstellt. Ähnlich wie beim Element für das Datumsfeld legen wir die Suchbedingungen für das Feld „Invoice number“ im Abschnitt Advanced pre-search relations fest. Der Suchbereich für dieses Element ist ein Array von Rechtecken.
let Header = InvoiceGroup.InvoiceHeader;
if not Header.IsNull then
{ let rect1 = Rect (Header.Rect.Right, Header.Rect.Top-20dt,
PageRect.Right, Header.Rect.Bottom+20dt);
let rect2 = Rect (Header.Rect.Left - 200dt, Header.Rect.Bottom,
Header.Rect.Right + 150dt, Header.Rect.Bottom+200dt);
RectArray ar;
ar = RectArray( rect1 );
ar.Add( rect2 );
RestrictSearchArea( ar );
}
else
{ Above: PageRect.Top + PageRect.Height/2;
}
Nearest: Header;
Zusätzlich haben wir dem Code noch eine weitere Bedingung hinzugefügt. Wir haben dem Programm mitgeteilt, dass das Element InvoiceNumber dem Namenselement am nächsten ist.
Nach dem Ausführen des Matching-Verfahrens sehen wir, dass das Feld „Invoice number“ auf den Seiten 2 und 4 falsch erkannt wurde. Auf Seite 4 wurde es falsch erkannt, obwohl der Feldname korrekt erkannt wurde.
Als Alternative (für die Bilder des aktuellen Projekts) zu Nearest: Header; könnten wir NearestY: Header.Rect.YCenter; schreiben, um dem Programm mitzuteilen, dass das gesuchte Feld vertikal dem Mittelpunkt des Namens am nächsten ist. Dies könnte das Problem der falschen Erkennung des Felds „Invoice number“ auf Seite 4 lösen. Auf Seite 5 hilft es jedoch nicht, weil das gesuchte Feld nach der falschen Erkennung des Namens „Invoice number“ innerhalb des Datumsfelds erkannt wird.
Sehen wir uns nun an, wie wir die Funktion FuzzyQuality in einer solchen Situation verwenden können.
Um dies zu demonstrieren, haben wir das Projekt 2.fsp (Ordner FuzzyAndNearest\Project2) erstellt.
Die Einstellungen dieses Projekts sind fast identisch mit denen des oben beschriebenen Projekts.
Es gibt jedoch einen wesentlichen Unterschied. Wir haben die Funktion Nearest nicht im Abschnitt Advanced pre-search relations verwendet. Stattdessen haben wir den folgenden Code im Abschnitt Erweiterte Nach-Suchbeziehungen geschrieben:
if not IsNull then
{ FuzzyQuality: Rect.Top - PageRect.Top, {0,0,0,50000} * dt;
FuzzyQuality: 500dt-Width, {0,0,0,100000} * dt;
if not InvoiceHeader.IsNull then
{ FuzzyQuality: Rect.XCenter - InvoiceHeader.Rect.XCenter, {-10000,0,0,50000} *dt;
FuzzyQuality: Rect.YCenter - InvoiceHeader.Rect.YCenter, {-10000,0,0,10000} *dt;
}
}
Mit dieser Methode können wir die Qualität aller Hypothesen beeinflussen, ohne eine davon auszuschließen. Die Auswahl der besten Kette erfolgt für jede Kette einzeln, indem die Qualitätswerte aller zugehörigen Elementhypothesen multipliziert werden.
Die Zeile FuzzyQuality: Rect.Top - PageRect.Top, {0,0,0,50000} * dt; bedeutet, dass bei einer Nicht-Null-Hypothese (zunächst wird if not IsNull geprüft) der Abstand zwischen der Position des Elements und dem oberen Rand der Seite bestimmt wird. Das heißt, die Differenz (Rect.Top - PageRect.Top) wird berechnet, und das Programm prüft, ob diese Differenz zum Intervall {0, 0, 0, 50000}*dt gehört. Eine solche Intervallbeschreibung bedeutet, dass die Qualitätsstrafe direkt vom Abstand zwischen dem Element und dem oberen Seitenrand abhängt – je größer der Abstand, desto höher die Strafe. Wie in Abbildung (a) gezeigt, entspricht bei den angegebenen Parameterwerten die maximale Strafe (1) einem Abstand von 50000dt, während ein Abstand von 1000 dots (1 dot ist 1/300 Zoll) einer Strafe von 0,02 und ein Abstand von 100dt einer Strafe von 0,002 entspricht.
Wenn Sie Parameter wählen, die die Grenzen des Intervalls festlegen, sollten Sie diese so wählen (insbesondere bei mehreren Elementprüfungen mit der Funktion FuzzyQuality), dass die richtige Hypothese nicht so stark bestraft wird, dass ihre endgültige Qualität unter die einer Nullhypothese fällt. Wenn die Qualität aller Hypothesen (einschließlich der richtigen) niedriger ist als der Qualitätswert einer Nullhypothese, kann die Nullhypothese ausgewählt werden, d. h. das Element wird nicht erkannt.
(a)
Die Zeile *FuzzyQuality: 500dt - Width, {0,0,0,100000}dt; bedeutet, dass das Programm die Differenz zwischen 500dt und der Breite des erkannten Objekts berücksichtigt, das der Hypothese entspricht. Das heißt, die Differenz (500dt - Width) wird berechnet, und das Programm prüft, ob diese Differenz zum Intervall {0, 0, 0, 100000}*dt gehört. Je schmaler das Objekt, desto größer die Strafe, sodass längere Rechnungsnummern bevorzugt werden. Diese Einschränkung kann verwendet werden, wenn das Bild verrauscht ist. Wenn das Rauschen als Zeichen aus dem angegebenen alphabet erkannt wird (wie beispielsweise auf Seite 2 zu sehen), sollte seine Hypothese bestraft werden, um sie von der weiteren analysis auszuschließen.
Der Wert 500dt wird auf Grundlage einer visuellen Prüfung gewählt, unter der Annahme, dass die string-Länge im Feld „Rechnungsnummer“ diesen Wert nicht überschreitet. Die hier angegebenen Parameter legen fest, dass die maximale Strafe (0,005) einer Feldbreite von null im Feld „Rechnungsnummer“ entspricht. Für alle anderen Breiten zwischen 0 und 500dt wären die Qualitätsstrafen geringer.
Die Zeile *FuzzyQuality: Rect.XCenter - InvoiceHeader.Rect.XCenter, {-10000,0,0,50000} dt; bedeutet, dass, wenn eine Nicht-Null-Hypothese für das Element des Namens des Felds „Rechnungsnummer“ erzeugt wird (zuerst wird die Prüfung if not InvoiceHeader.IsNull ausgeführt), der Abstand zwischen dem Mittelpunkt des erkannten Elements InvoiceNumber und dem Mittelpunkt des Namens InvoiceHeader bestimmt wird. Die Differenz (Rect.XCenter - InvoiceHeader.Rect.XCenter) wird berechnet, und das Programm prüft, ob diese Differenz zum Intervall {-10000, 0, 0, 50000}*dt gehört. Diese Beschreibung berücksichtigt auch die Möglichkeit, dass sich das Feld „Rechnungsnummer“ unterhalb des Namens befinden kann. In diesem Fall gilt: Je weiter die Elemente voneinander entfernt sind, desto größer ist die Strafe für die entsprechende Hypothese. Hypothesen, die davon ausgehen, dass sich die Nummer rechts vom Namen befindet, werden nicht so stark bestraft wie solche, die davon ausgehen, dass sich die Nummer unterhalb des Namens befindet, da die „rechte“ Anordnung des Felds „Rechnungsnummer“ und seines Namens offensichtlich deutlich häufiger vorkommt.
Wie in Abbildung (b) gezeigt, entspricht bei den angegebenen Parametern der linken und rechten Intervallgrenzen die maximale Strafe (1) einer Verschiebung des Felds „Rechnungsnummer“ relativ zum Namensfeld um 10000dt nach links oder um 50000dt nach rechts. Eine Verschiebung um 1000 Dot wird mit 0.1 bestraft, wenn es sich um eine Verschiebung nach „links“ handelt, oder mit 0.02, wenn es sich um eine Verschiebung nach „rechts“ handelt. Ebenso wird eine Verschiebung um 100 Dot mit 0.01 bestraft, wenn es sich um eine Verschiebung nach „links“ handelt, oder mit 0.002, wenn es sich um eine Verschiebung nach „rechts“ handelt.
(b)
Die Zeile *FuzzyQuality: Rect.YCenter - InvoiceHeader.Rect.YCenter, {-10000,0,0,10000} dt; ist mit der vorherigen identisch. Sie ist jedoch für Fälle vorgesehen, in denen sich das Feld „Rechnungsnummer“ auf derselben horizontalen Ebene oder sogar leicht oberhalb des Namensfelds befindet. Die Strafen sind hier für jede vertikale Verschiebung gleich. Die Intervallgrenzen werden nach derselben Logik festgelegt, um Hypothesen zu bevorzugen, die das Datenfeld rechts von seinem Namen finden. Das Projekt zeigt jedoch, dass diese Einstellungen die korrekte Erkennung der Rechnungsnummer selbst dann nicht verhindert haben, wenn sie sich unterhalb des Namens befand (Seite 3).
Nach dem Matching des FlexiLayout mit allen Seiten sehen wir, dass die beiden gesuchten Felder erfolgreich erkannt wurden.
Zusammenfassend können wir sagen, dass die Funktion FuzzyQuality im Vergleich zu den Funktionen der Gruppe Nearest effizienter und flexibler ist, was besonders bei der Verarbeitung semi-strukturierter Dokumente wichtig ist.