メインコンテンツへスキップ
このメソッドは、複数の要素に対して同じ複雑な検索制約を確認する必要がある場合に使用できます。基本的な考え方は、制約の確認と計算を補助要素内で一度だけ実行することです。制約の確認結果に応じて、対象の要素を検索するかどうかが決まり、見つかった仮説または見つからなかった仮説が、制約が満たされているかどうかを示すマーカーになります。この補助要素は、DontFind() 関数 (つまり「検索を停止する」) が有効化されていない限り、画像上で常に何らかのものを検出できるように作成します。仮説の数は、仮説ツリーが過度に広がったり、FlexiLayout のマッチング時間が長くなったりしないよう、多くなりすぎないようにする必要があります。そのため、たとえば Object Collection 型や Paragraph 型の要素を使用できます。これらの要素は、検索領域内にある指定型のすべての object を含む単一の仮説を常に生成します。ダミー要素の Advanced pre-search relations セクションには、プロジェクトツリー内でダミー要素の下にあるいくつかの要素に対して確認したい条件の set を記述します。すべての条件が満たされたら、ダミー要素に対して DontFind() 関数を呼び出します。この場合、ダミー要素に対してヌル仮説が生成され、プログラムが他の要素の検索を開始したときに、すべての条件が満たされていることを示すマーカーとして機能します。これにより、複数の要素に対して同じ複雑な制約を毎回確認する代わりに、ダミー要素の IsNull チェックだけを実行するようプログラムに指示できます。
このメソッドを使うと、コードがよりわかりやすくなります。さらに、制約を編集する必要がある場合も、ダミー要素の記述だけを修正すれば済みます。これにより、コードを複製する際の論理エラーや構文エラーの発生を抑えられます。
将来の製品バージョンでは、groups の subelements 領域で変数を作成できるようにする予定です。そうなれば、制約の確認結果はさまざまな変数の値に保存されます。現在のメソッドは、現行バージョンの FlexiLayout Studio において Advanced セクションのコードを簡素化するための一時的な回避策 (workaround) です。
このメソッドが、プロジェクト 1.fsp (フォルダー %public%\ABBYY\FlexiCapture\12.0\Samples\FLS\Tips and Tricks\Auxiliary element) でどのように機能するかを見てみましょう。 これらの画像では、次の field を検索します: 「請求書番号」、「請求日」、「会社名」、「会社住所」。 2 種類の異なる請求書を処理しなければならないと仮定します。
  • 「会社名」と「会社住所」の field は、「請求書番号」の field の上にある。
  • 「会社名」と「会社住所」の field は、「請求書番号」の field の下にある。
画像からわかるように、「会社名」と「会社住所」の field には名前がないため、名前を手がかりに日付 field を探す標準的な方法は使えません。しかし、あるパターンが見て取れます。日付 field が請求書番号の右側にある場合、会社名と住所は「請求書番号」の field の下にあります (1 ページ目と 3 ページ目) 。一方、日付 field が請求書番号 field の下にある場合は、会社情報は「請求書番号」の field の上にあります (2 ページ目と 4 ページ目) 。 このパターンを踏まえると、まず「請求書番号」と「請求日」の field の位置を検索するのが最適です。その後、この 2 つを基準に相互の位置関係を指定しながら、他の field を検出します。 要素 InvoiceHeaderInvoiceNumDateHeader、および グループ要素 DateGroup を含む グループ要素 InvoiceGroup を作成しました。これらの subelements は、field 名と「Invoice Number」および「請求日」の field を検出するために必要です。
日付の検索メソッドの詳細については、前認識の品質が低い場合の日付の検出を参照してください。ここでは、現在のプロジェクトにおける日付検索の制約のみを説明します。
ご覧のとおり、画像上の日付 field には名前が付いていないこともあります。そのため、日付 field を検索する際には、名前が検出された場合用と、名前が検出されなかった場合用の 2 組の制約を指定する必要があります (特殊なケースとして、たとえば noise が原因で、名前がページ上に存在していても検出されないことがあります)。 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); } 日付 field の名前が検出された場合 (制約 if not DateHeader.IsNull が評価される場合) 、検索は日付 field 名を基準に実行されます。つまり、名前の右側で、同じ高さにあり、垂直方向のずれにはある程度の許容幅を持たせます。 { RightOf: DateHeader.Rect.Right; Below: DateHeader.Rect.Top - 30dt; Above: DateHeader.Rect.Bottom + 30dt; } それ以外の場合、検索領域は 2 つの矩形に分割されます。1 つは請求書番号の右側で請求書番号と同じ高さ、もう 1 つは invoice field の下です。
簡単のため、ここでは画質の良い画像を使用しており、field “請求書番号” とその名前は常に検出されると仮定します。実際の状況では、これらの要素のプロパティを呼び出す前に IsNull チェックを実行する必要があります。要素が検出されない場合、その後の検索は対応する要素の検索領域を基準に行われるためです。
要素 Date の Advanced post-search relations セクションには、次のコードを記述しました。 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; } } これにより、日付 field 名が検出されない場合に、invoice field の名前および field “請求書番号” 自体からの距離に応じて、日付仮説の Quality に影響を与えることができます。指定した fields までの距離が長いほど、対応する仮説に対するペナルティは大きくなります。つまり、field “請求書番号” に最も近い日付 field を検索します。 注。 これらの function の使い方の詳細については、Using Nearest and FuzzyQuality to look for elements を参照してください。 要素 DateAsString に対しても同じ検索制約を指定しますが、Advanced post-search relations セクションでは、上記のコードにさらに 1 行追加します。 FuzzyQuality: 600dt - Width, {0, 0, 0, 50000}*dt; この行は日付の検索時に必要で、短いものよりも長いアルファベット文字列を持つ仮説が優先されるようにします。 さらに、要素 DateAsString の Search Constraints タブでは、日付を文字列として検索する際に、要素 InvoiceNum の Region を除外するよう指定しました。これは、簡略化のため、要素 Date の Advanced pre-search relations セクションで指定したものと同じ検索制約を、要素 DateAsString に重ねて設定しないことにしたためです。検索領域には RestrictSearchArea (Date.Rect); を指定しました。つまり、要素 DateAsString の object を、要素 Date の Fuzzy Rectangle の領域内で検索するようプログラムに指示したことになります。要素 Date の検索領域は、矩形の array として表すことができます。要素 Date に対してヌル仮説が生成されると、検索領域を囲む矩形が現在の要素の矩形 (Rect) として扱われます。下の画像からわかるように、この矩形には要素 InvoiceNum で表される field「請求書番号」も含まれます。条件によっては (たとえば日付 field のノイズが非常に多い場合) 、日付 field ではなく、要素 DateAsString が請求書番号の field を検出してしまうことがあります。これは、この要素に指定された文字 (数字を含む) には書式上の制約がないためです。 field「請求書番号」と「請求日」を検索するために必要な要素の記述が終わったので、次は field「会社名」と「会社住所」の検索に進みます。 Paragraph の要素を作成し、ShamElement という名前を付けます。この要素は補助要素として機能し、field「請求書番号」と「請求日」の相対的な位置関係を確認するためだけに使用されます。 補助要素の Advanced pre-search relations セクションには、次のコードを記述しました。 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; } このコードで確認している制約自体は単純ですが、コードそのものはかなり長くなっています。考え方としては、要素 Date または DateAsString のいずれかで検出された日付 field が、請求書番号 field の右側にあり、かつ同じ高さ (垂直方向の許容差は 30 dt) にある場合、補助要素に対してヌル仮説を生成するようプログラムに指示する、というものです。それ以外の場合、要素 ShamElement はページ上端より下の領域で検索されます。追加の検索制約を設定せずに型 Paragraph の補助要素を作成したため、この要素はページ上のすべての text objects を囲み、単一の仮説が生成されます。 確認する制約のほとんどは直感的に理解できます。そこで、ここではその中で最も複雑なものだけを説明します。 max (InvoiceHeader.Rect.YCenter - Date1.Rect.YCenter, Date1.Rect.YCenter - InvoiceHeader.Rect.YCenter ) < 30dt このコードの部分では、日付フィールド (この場合、要素 Date によって検出されたかどうか) が、フィールド名「Invoice number」の要素と同じ水平ライン上に位置しているかを確認します。日付は、フィールド名よりもわずかに上下にずれている場合があります (スキャンや文書への記入時に生じるエラーの可能性があるため) 。最大差は 30 dt と指定しています。同じメソッドを使用して、フィールド「Invoice date」と「Invoice number」の垂直方向の相対位置も確認します。 Date1.Rect.Left - InvoiceHeader.Rect.Right > 100dt この行は、日付fieldが「請求書番号」fieldの名前の右側に位置していることを確認します (日付の左境界のcoordinateが名前の右境界のcoordinateより大きい値を持つ) 。名前と日付の間には請求書番号の値自体が存在する必要があるため、許容誤差を100dtに指定しています。日付fieldの位置は「請求書番号」fieldの右側になります。 チェックのすべての条件が、要素 DateAsString に対して複製されます。 コードが正しいことを確認するために、すべてのページでFlexiLayoutのマッチング処理を実行してみましょう。ページ1とページ3では、日付fieldが請求書番号の右側にあるため、要素ShamElementに対してヌル仮説が生成されていることがわかります。残りの2ページでは、テキストフラグメントが検出されました。 会社の詳細を検出するために、Group element CompanyGroup を作成します。これは、Character String 型の CompanyName (テスト画像上の会社名は1行で記載されています) と、Paragraph 型の Address をグループ化します。このelementは、会社の住所を含むブロックを検索するために使用されます。 補助要素を使用することで、「Advanced pre-search relations」セクションにおける要素の検索条件を記述するコードを簡略化できます。補助要素が検出されない場合 (つまり、ヌル仮説が生成された場合) 、日付fieldはinvoice fieldの右側に位置することを意味します。この場合、field「Company name」はfield「Invoice number」の下方で検索されます。補助要素が検出された場合、field「Company name」はfield「Invoice number」の上方で検索されます。 if ShamElement.IsNull then { Below: InvoiceGroup.InvoiceHeader; Below: InvoiceGroup.InvoiceNum; NearestY: PageRect.Top; } else { Above: InvoiceGroup.InvoiceHeader; Above: InvoiceGroup.InvoiceNum; }
この場合は、関数 Nearest を使用できます。これにより、会社名を含む field を一意かつ正確に検出できます (同じ検索条件に一致するオブジェクトはありません) 。
要素 CompanyName の「Advanced post-search relations」セクションに、次のコードを記述します。 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; } このコードは、単一行のfield「Company name」が請求書番号のfieldの上方で検索される場合に機能します。検索対象のfieldsには名前がありません。また、ページ2とページ4では、「Company Name」と「Company address」のfieldsの相対位置が異なります。制約 Above: InvoiceGroup.InvoiceHeader; および Above: InvoiceGroup.InvoiceNum; は、会社名を含む文字列だけでなく、住所fieldの文字列によっても満たされます。 生成されたすべての仮説の中から唯一の正しい仮説を選択するために、「Advanced post-search relations」セクションで以下のコードを使用します。 コードを入力することで FuzzyQuality: Rect.Left - PageRect.Left, {0, 0, 0, 50000}*dt; および FuzzyQuality: Rect.Top -PageRect.Top, {0, 0, 0, 50000}*dt; 画像の下端および右端の境界に近いオブジェクトを持つ仮説に対して、より強いペナルティを課すようプログラムに指示します。ただし、2ページ目では、field「Company name」は住所fieldの左側に位置していますが、その上端ラインよりも下にあります。そのため、説明した制約だけでは目的のfield「Company name」を検出するには不十分であり、制約をもう1つ追加する必要があります。 FuzzyQuality: 100dt - Height, {0, 0, 0, 10000}*dt; この行は、生成された仮説について、すべての行の高さをチェックするようプログラムに指示しています。行内の文字の高さが高いほど、対応する仮説の品質も高くなります。 FlexiLayout のマッチングを行うと、field「会社名」がすべてのページで正常に検出されていることがわかります。 住所fieldを検出するため、要素 Address の Advanced pre-search relations セクションに次のコードを記述しました。 if ShamElement.IsNull then { Below: CompanyName; Above: TotalSumHeader.Rect.Top; } else { Above: InvoiceGroup.InvoiceHeader; Above: InvoiceGroup.InvoiceNum; RightOf: CompanyName.Rect.Left; Exclude: CompanyName; } ここでも、コードを簡略化するのに役立つ補助要素 ShamElement を使用しています。 これで FlexiLayout の作成は完了です。FlexiLayout のマッチング手順を実行すると、すべての field が正常に検出されていることがわかります。