Zum Hauptinhalt springen
Diese Methode kann verwendet werden, wenn Sie dieselben umfangreichen Suchbedingungen für mehrere Elemente prüfen müssen. Die Grundidee besteht darin, dass die Bedingungen geprüft und die Berechnungen nur einmal in einem Hilfselement durchgeführt werden. Als Ergebnis dieser Prüfung wird nach dem betreffenden Element entweder gesucht oder nicht; die gefundene bzw. nicht gefundene Hypothese dient also als Marker, der signalisiert, ob die Bedingungen erfüllt sind oder nicht. Dieses Hilfselement wird so erstellt, dass es auf den Bildern immer etwas erkennen kann, sofern die Funktion DontFind() (d. h. „Suche beenden“) nicht aktiviert ist. Die Anzahl der Hypothesen sollte nicht zu groß sein, um den Hypothesenbaum nicht unnötig aufzublähen und die Dauer des Abgleichs des FlexiLayouts nicht zu verlängern. Um dies zu erreichen, können Sie beispielsweise Elemente der Typen Object Collection oder Paragraph verwenden. Diese Elemente erzeugen immer genau eine Hypothese, die alle Objekte des angegebenen Typs aus dem Suchbereich enthält. Im Abschnitt Advanced pre-search relations des Dummy-Elements schreiben wir die Bedingungen, die wir für einige der Elemente prüfen möchten, die sich im Projektbaum unterhalb des Dummy-Elements befinden. Wenn alle Bedingungen erfüllt sind, rufen wir für das Dummy-Element die Funktion DontFind() auf. In diesem Fall wird für das Dummy-Element eine Nullhypothese erzeugt, die beim Start der Suche nach anderen Elementen als Marker dafür dient, dass alle Bedingungen erfüllt wurden. Auf diese Weise weisen wir das Programm an, nur die IsNull-Prüfung des Dummy-Elements auszuführen, anstatt dieselben umfangreichen Bedingungen für mehrere Elemente zu prüfen.
Diese Methode macht den Code leichter verständlich. Wenn Sie die Bedingungen bearbeiten müssen, können Sie dies außerdem an nur einer Stelle tun, nämlich in der Beschreibung des Dummy-Elements. Dadurch verringert sich die Wahrscheinlichkeit logischer und syntaktischer Fehler beim Duplizieren des Codes.
In zukünftigen Versionen des Produkts planen wir, die Erstellung von Variablen im Bereich der Subelemente von Gruppen zu unterstützen. Die Ergebnisse der Prüfung der Bedingungen werden dann in den Werten verschiedener Variablen gespeichert. Die aktuelle Methode ist eine temporäre Lösung (workaround), die dabei hilft, den Code in den erweiterten Abschnitten in der aktuellen Version von FlexiLayout Studio zu vereinfachen.
Sehen wir uns an, wie diese Methode im Projekt 1.fsp funktioniert (Ordner %public%\ABBYY\FlexiCapture\12.0\Samples\FLS\Tips and Tricks\Auxiliary element). Auf diesen Bildern suchen wir nach den folgenden Feldern: „Rechnungsnummer“, „Rechnungsdatum“, „Firmenname“ und „Firmenadresse“. Nehmen wir an, dass wir Rechnungen von zwei verschiedenen Typen verarbeiten müssen:
  • Die Felder „Firmenname“ und „Firmenadresse“ befinden sich oberhalb des Feldes „Rechnungsnummer“;
  • Die Felder „Firmenname“ und „Firmenadresse“ befinden sich unterhalb des Feldes „Rechnungsnummer“.
Wie Sie auf den Bildern sehen können, haben die Felder „Firmenname“ und „Firmenadresse“ keine Bezeichnungen, was uns daran hindert, das Standardverfahren zur Suche nach dem Datumsfeld anhand seines Namens zu verwenden. Allerdings können wir ein bestimmtes Muster erkennen: Wenn sich das Datumsfeld rechts von der Rechnungsnummer befindet, liegen Firmenname und Firmenadresse unterhalb des Feldes „Rechnungsnummer“ (Seiten 1 und 3); befindet sich das Datumsfeld dagegen unterhalb des Feldes „Rechnungsnummer“, dann stehen die Firmendaten oberhalb des Feldes „Rechnungsnummer“ (Seiten 2 und 4). Unter Berücksichtigung des erkannten Musters ist es am sinnvollsten, zuerst die Position der Felder „Rechnungsnummer“ und „Rechnungsdatum“ zu suchen. Anschließend erkennen wir die anderen Felder anhand dieser beiden, indem wir ihre gegenseitigen Positionen festlegen. Wir haben ein Gruppenelement InvoiceGroup erstellt, das die Elemente InvoiceHeader, InvoiceNum, DateHeader und ein Gruppenelement DateGroup enthält. Diese Subelemente sind erforderlich, um Feldnamen sowie die Felder „Invoice Number“ und „Rechnungsdatum“ zu erkennen.
Unter Erkennen von Datumsangaben bei Voraberkennung geringer Qualität finden Sie weitere Details zu den Methoden der Datumssuche. Hier beschreiben wir nur die Bedingungen für die Datumssuche im aktuellen Projekt.
Wie Sie sehen, haben die Datumsfelder in den Bildern nicht immer Bezeichnungen. Daher müssen Sie bei der Suche nach einem Datumsfeld zwei Sätze von Suchbedingungen angeben: für Fälle, in denen die Bezeichnung erkannt wurde, und für Fälle, in denen die Bezeichnung nicht erkannt wurde (ein Sonderfall wäre, wenn die Bezeichnung auf der Seite vorhanden ist, aber nicht erkannt wurde, z. B. aufgrund von Rauschen). if not DateHeader.IsNull then { RightOf: DateHeader.Rect.Right; Below: DateHeader.Rect.Top - 30dt; Above: DateHeader.Rect.Bottom + 30dt; } else { RectArray ar; Let ar1 = Rect (InvoiceNum.Rect.Right, InvoiceNum.Rect.Top-30dt, PageRect.Right, InvoiceNum.Rect.Bottom + 30dt); Let ar2 = Rect (InvoiceHeader.Rect.Left, InvoiceHeader.Rect.Bottom, InvoiceHeader.Rect.Right + 300dt, InvoiceHeader.Rect.Bottom + 150dt); ar = RectArray (ar1); ar.Add (ar2); RestrictSearchArea (ar); } Wenn der Name des Datumsfelds erkannt wird (d. h. die Bedingung if not DateHeader.IsNull erfüllt ist), wird die Suche relativ zum Namen des Datumsfelds durchgeführt: rechts vom Namen, auf derselben horizontalen Ebene, mit einem gewissen Spielraum für vertikale Abweichungen: { RightOf: DateHeader.Rect.Right; Below: DateHeader.Rect.Top - 30dt; Above: DateHeader.Rect.Bottom + 30dt; } Andernfalls wird der Suchbereich in zwei Rechtecke unterteilt: rechts von der Rechnungsnummer und auf derselben Höhe wie die Rechnungsnummer sowie unterhalb des Rechnungsfelds.
Der Einfachheit halber nehmen wir an, dass Bilder von guter Qualität vorliegen, bei denen das Feld „Rechnungsnummer“ und seine Bezeichnung immer erkannt werden. In der Praxis müssen wir vor dem Aufruf der Eigenschaften dieser Elemente eine IsNull-Prüfung ausführen, denn wenn die Elemente nicht erkannt werden, wird die weitere Suche relativ zu den Suchbereichen der entsprechenden Elemente durchgeführt.
Im Abschnitt Erweiterte Nach-Suchbeziehungen des Elements Date haben wir den folgenden Code geschrieben: if (DateHeader.IsNull) and (not IsNull) then {if (not InvoiceHeader.IsNull) then { FuzzyQuality: Rect.Left - InvoiceHeader.Rect.Right, {0, 0, 0, 50000}*dt; FuzzyQuality: Rect.Top - InvoiceHeader.Rect.Bottom, {-50000, 0, 0, 50000}*dt; } if (not InvoiceNum.IsNull) then { FuzzyQuality: Rect.Left - InvoiceNum.Rect.Right, {0, 0, 0, 50000}*dt; FuzzyQuality: Rect.Top - InvoiceNum.Rect.Bottom, {-50000, 0, 0, 50000}*dt; } } Dadurch lässt sich die Quality der Datumshypothese abhängig von ihrer Distanz zur Bezeichnung des Rechnungsfelds und zum Feld „Rechnungsnummer“ selbst beeinflussen, wenn der Name des Datumsfelds nicht erkannt wird. Je größer die Distanz zu den angegebenen Feldern ist, desto höher ist die Strafe für die entsprechenden Hypothesen; das heißt, wir suchen nach dem Datumsfeld, das dem Feld „Rechnungsnummer“ am nächsten liegt. Hinweis. Siehe Using Nearest and FuzzyQuality to look for elements für weitere Details zur Verwendung dieser Funktionen. Für das Element DateAsString geben wir identische Suchbedingungen an, fügen jedoch im Abschnitt Erweiterte Nach-Suchbeziehungen noch eine weitere Zeile zum oben genannten Code hinzu: FuzzyQuality: 600dt - Width, {0, 0, 0, 50000}*dt; Diese Zeile wird bei der Suche nach dem Datum benötigt, damit eine Hypothese mit einer längeren Zeichenfolge aus alphabetischen Zeichen gegenüber einer kürzeren bevorzugt wird. Zusätzlich haben wir auf der Registerkarte Suchbedingungen des Elements DateAsString festgelegt, dass beim Suchen nach dem Datum als Zeichenfolge die Region des Elements InvoiceNum ausgeschlossen werden muss. Der Grund dafür ist, dass wir der Einfachheit halber für das Element DateAsString nicht dieselben Suchbedingungen duplizieren wollten, die wir im Abschnitt Advanced pre-search relations des Elements Date angegeben haben. Den Suchbereich haben wir als RestrictSearchArea (Date.Rect); festgelegt. Damit haben wir dem Programm mitgeteilt, nach dem Objekt des Elements DateAsString im Bereich des unscharfen Rechtecks des Elements Date zu suchen. Der Suchbereich des Elements Date kann als Array von Rechtecken dargestellt werden. Wenn für das Element Date eine Nullhypothese erzeugt wird, wird das den Suchbereich umschließende Rechteck als Rechteck (Rect) des aktuellen Elements verwendet. Wie Sie in der Abbildung unten sehen können, umschließt es auch das durch das Element InvoiceNum beschriebene Feld „Rechnungsnummer“. Unter bestimmten Bedingungen (zum Beispiel, wenn das Datumsfeld stark verrauscht ist) kann es vorkommen, dass das Element DateAsString statt des Datumsfelds das Feld mit der Rechnungsnummer erkennt, da die für dieses Element angegebenen Zeichen (einschließlich Ziffern) keinerlei Formateinschränkungen unterliegen. Nachdem die für die Suche nach den Feldern „Rechnungsnummer“ und „Rechnungsdatum“ erforderlichen Elemente beschrieben wurden, können wir mit der Suche nach den Feldern „Firmenname“ und „Firmenadresse“ fortfahren. Wir erstellen ein Element vom Typ Paragraph und nennen es ShamElement. Dieses Element dient als Hilfselement und wird ausschließlich verwendet, um die gegenseitige Position der Felder „Rechnungsnummer“ und „Rechnungsdatum“ zu überprüfen. Im Abschnitt Advanced pre-search relations des Hilfselements haben wir den folgenden Code geschrieben: Let Date1 = InvoiceGroup.DateGroup.Date; Let Date2 = InvoiceGroup.DateGroup.DateAsString; Let DateGroup = InvoiceGroup.DateGroup; Let InvoiceHeader = InvoiceGroup.InvoiceHeader; Let InvoiceNumber = InvoiceGroup.InvoiceNum; if ((not InvoiceHeader.IsNull) or (not InvoiceNumber.IsNull)) and ( ((Date1.IsNull == FALSE) and ((max (InvoiceHeader.Rect.YCenter - Date1.Rect.YCenter, Date1.Rect.YCenter - InvoiceHeader.Rect.YCenter ) < 30dt) or (max (InvoiceNumber.Rect.YCenter - Date1.Rect.YCenter, Date1.Rect.YCenter - InvoiceNumber.Rect.YCenter ) < 30dt)) and ((Date1.Rect.Left - InvoiceHeader.Rect.Right > 100dt) or (Date1.Rect.Left - InvoiceNumber.Rect.Right > 50dt))) or ((Date2.IsNull == FALSE) and ((max (InvoiceHeader.Rect.YCenter - Date2.Rect.YCenter, Date2.Rect.YCenter - InvoiceHeader.Rect.YCenter ) < 30dt) or (max (InvoiceNumber.Rect.YCenter - Date2.Rect.YCenter, Date2.Rect.YCenter - InvoiceNumber.Rect.YCenter ) < 30dt)) and ((Date2.Rect.Left - InvoiceHeader.Rect.Right > 100dt) or (Date2.Rect.Left - InvoiceNumber.Rect.Right > 50dt))) ) then { Dontfind(); } else { Below: PageRect.Top; } Obwohl die von diesem Code geprüften Bedingungen recht einfach sind, ist der Code selbst ziemlich umfangreich geraten. Die Idee dahinter ist, dass wir dem Programm mitteilen, für das Hilfselement eine Nullhypothese zu erzeugen, wenn das mit einem der Elemente Date oder DateAsString erkannte Datumsfeld rechts vom Feld mit der Rechnungsnummer, aber auf derselben Höhe liegt (mit einer vertikalen Toleranz von 30 dt). In allen anderen Fällen wird nach dem Element ShamElement unterhalb der oberen Kante der Seite gesucht. Da wir ein Hilfselement vom Typ Paragraph ohne zusätzliche Suchbedingungen erstellt haben, wird es alle Textobjekte auf der Seite umschließen und es wird eine einzige Hypothese erzeugt. Nahezu alle zu prüfenden Bedingungen sind intuitiv verständlich. Deshalb werden wir nur die komplizierteste davon beschreiben. max (InvoiceHeader.Rect.YCenter - Date1.Rect.YCenter, Date1.Rect.YCenter - InvoiceHeader.Rect.YCenter ) < 30dt Dieser Teil des Codes prüft, ob das Datumsfeld (in diesem Fall, ob es vom Element Date erkannt wurde) auf derselben horizontalen Ebene wie das Element mit dem Namen des Feldes “Rechnungsnummer” liegt. Das Datum kann geringfügig höher oder niedriger als der Name liegen (aufgrund möglicher Fehler beim Scannen oder beim Ausfüllen des Dokuments). Wir haben festgelegt, dass der maximale Unterschied 30 dt beträgt. Die gegenseitige vertikale Position der Felder “Rechnungsdatum” und “Rechnungsnummer” wird mit derselben Methode geprüft. Date1.Rect.Left - InvoiceHeader.Rect.Right > 100dt Diese Zeile prüft, ob das Datumsfeld rechts vom Namen des Feldes “Rechnungsnummer” liegt (die Koordinate der linken Begrenzung des Datums ist größer als die der rechten Begrenzung des Namens). Wir haben eine Toleranz von 100dt angegeben, da sich zwischen dem Namen und dem Datum die eigentliche Rechnungsnummer befinden muss. Das Datumsfeld liegt rechts vom Feld “Rechnungsnummer”. Alle Bedingungen der Prüfung werden dann für das Element DateAsString dupliziert. Um zu überprüfen, ob unser Code korrekt ist, führen wir das FlexiLayout-Matching-Verfahren auf allen Seiten aus. Wir sehen, dass auf den Seiten 1 und 3, auf denen sich das Datumsfeld rechts von der Rechnungsnummer befindet, eine Null-Hypothese für das Element ShamElement generiert wurde. Auf den anderen beiden Seiten wurden Textfragmente erkannt. Um die Unternehmensdetails zu erkennen, erstellen wir ein Gruppenelement CompanyGroup. Es gruppiert die Elemente CompanyName vom Typ Zeichenkette (der Unternehmensname auf den Testbildern steht in einer einzigen Zeile) und Address vom Typ Paragraph. Das Element wird verwendet, um den Block zu suchen, der die Unternehmensadresse enthält. Die Verwendung des Hilfselements vereinfacht den Code, der die Suchbedingungen des Elements im Abschnitt „Advanced pre-search relations” beschreibt. Wenn das Hilfselement nicht erkannt wird (d. h. eine Null-Hypothese dafür generiert wird), bedeutet dies, dass sich das Datumsfeld rechts vom Rechnungsfeld befindet. In diesem Fall wird das Feld „Company name” unterhalb des Felds „Rechnungsnummer” gesucht. Wird das Hilfselement erkannt, wird das Feld „Company name” oberhalb des Felds „Rechnungsnummer” gesucht. if ShamElement.IsNull then { Below: InvoiceGroup.InvoiceHeader; Below: InvoiceGroup.InvoiceNum; NearestY: PageRect.Top; } else { Above: InvoiceGroup.InvoiceHeader; Above: InvoiceGroup.InvoiceNum; }
In diesem Fall können wir die Funktion Nearest verwenden, da sie es ermöglicht, das Feld mit dem Firmennamen eindeutig und präzise zu identifizieren (es gibt keine Objekte, die denselben Suchbedingungen entsprechen).
Im Abschnitt „Erweiterte Nach-Suchbeziehungen” des Elements CompanyName fügen wir den folgenden Code ein. if not IsNull then { FuzzyQuality: Rect.Left - PageRect.Left, {0, 0, 0, 50000}*dt; FuzzyQuality: Rect.Top - PageRect.Top, {0, 0, 0, 50000}*dt; FuzzyQuality: 100dt - Height, {0, 0, 0, 10000}*dt; } Dieser Code funktioniert, wenn das einzeilige Feld „Company name” oberhalb des Felds der Rechnungsnummer gesucht wird. Die gesuchten Felder haben keine Namen. Gleichzeitig ist die relative Position der Felder „Company Name” und „Company address” auf den Seiten 2 und 4 unterschiedlich. Die Bedingung Above: InvoiceGroup.InvoiceHeader; und Above: InvoiceGroup.InvoiceNum; wird nicht nur durch die Zeichenfolge mit dem Firmennamen erfüllt, sondern auch durch die Zeichenfolgen im Adressfeld. Um die einzig korrekte Hypothese unter allen generierten Hypothesen auszuwählen, wird der folgende Code im Abschnitt „Erweiterte Nach-Suchbeziehungen” verwendet. Durch Eingabe des Codes FuzzyQuality: Rect.Left - PageRect.Left, {0, 0, 0, 50000}*dt; und FuzzyQuality: Rect.Top -PageRect.Top, {0, 0, 0, 50000}*dt; weisen wir das Programm an, Hypothesen mit Objekten nahe dem unteren und rechten Rand des Bildes stärker zu penalisieren. Auf Seite 2 befindet sich das Feld „Firmenname” jedoch links vom Adressfeld, aber unterhalb von dessen oberer Zeile. Die beschriebenen Einschränkungen reichen daher nicht aus, um das gesuchte Feld „Firmenname” zu erkennen, weshalb eine weitere Einschränkung hinzugefügt werden muss. FuzzyQuality: 100dt - Height, {0, 0, 0, 10000}*dt; Diese Zeile weist das Programm an, die Höhe aller Zeilen für die generierten Hypothesen zu überprüfen. Je höher die Zeichen in einer Zeile sind, desto höher ist die Qualität der entsprechenden Hypothese. Nachdem wir den Abgleich des FlexiLayouts durchgeführt haben, sehen wir, dass das Feld „Firmenname“ auf allen Seiten erfolgreich erkannt wurde. Um das Feld „Adresse“ zu erkennen, haben wir den folgenden Code im Abschnitt Advanced pre-search relations des Elements Adresse geschrieben: if ShamElement.IsNull then { Below: CompanyName; Above: TotalSumHeader.Rect.Top; } else { Above: InvoiceGroup.InvoiceHeader; Above: InvoiceGroup.InvoiceNum; RightOf: CompanyName.Rect.Left; Exclude: CompanyName; } Auch hier verwenden wir wieder das Hilfselement ShamElement, das den Code vereinfacht. Die Erstellung eines FlexiLayouts ist nun abgeschlossen. Sobald wir das Matching des FlexiLayouts ausgeführt haben, sehen wir, dass alle Felder erfolgreich erkannt wurden.