簡単にするため、このサンプルでは 1 ページの文書を使用します。
文書処理では、要素の位置をほかの要素に対して「上 - 下 - 右 - 左」のような関係だけで表すのでは不十分な場合がよくあります。たとえば、検索領域内に検索制約に一致するオブジェクトが複数存在する場合です。このような場合には、それらのオブジェクトを区別するための追加の属性、具体的にはオブジェクト間の距離が必要になります。そのために、FlexiLayout Studio には FuzzyQuality 関数と、Nearest グループの関数 (Nearest、NearestX、NearestY) が用意されています。
これらの関数の用途は異なります。
Nearest 関数は、Advanced pre-search relations field でのみ使用できます。この関数は、要素の複数の仮説のうち、Nearest 関数のプロパティで設定された画像上の特定の要素または点に最も近いものをプログラムが選択することを指定します。要素の Advanced pre-search relations field では、Nearest グループの関数は 1 つしか使用できません。これを実行すると、残る仮説は 1 つだけになり、これは仮説生成の段階、つまり Advanced post-search relations field で指定されたコードが実行される前に行われます。要素の仮説の最小 quality を指定する Minimum quality parameter は、StaticText、CharacterString、Paragraph、Date、Separator の各要素に対して指定できます。ただし、残った仮説が最良であり (画像内の目的のオブジェクトに対応している) とは限りません。というのも、仮説に quality 値を割り当てるうえで Advanced post-search relations は非常に重要だからです。Nearest 関数を使用する場合、仮説の選択は仮説生成の段階で行われ、その基準は特定の点への近さであり、仮説の quality ではありません。そのため、Advanced post-search relations セクションで指定するプロパティが仮説を正しく選択するうえで重要である場合は、Nearest グループの関数ではなく FuzzyQuality 関数を使用する必要があります。
FuzzyQuality 関数は、Advanced post-search relations セクション でのみ使用できます。Nearest グループの関数とは異なり、単一の仮説を選択するのではなく、生成されたすべての仮説のプロパティと FuzzyQuality 関数の parameter に基づいて、各仮説の overall quality に影響を与えます。さらに、FuzzyQuality 関数は、1 つの要素に対して Advanced post-search relations field 内で複数回使用できます。これは、異なる quality 値を持つ複数の制約を 1 つの仮説に適用できることを意味します。仮説の Post-search quality を決定するために、すべての値が乗算されます。FuzzyQuality 関数は次のようになります。
FuzzyQuality: x, {f1, f2, f3, f4};
アルゴリズムは次のとおりです。この関数は、parameter x の値が、パラメーター f1、f2、f3、f4 で定義される区間に含まれるかどうかを確認します。このファジー区間の意味は、Character String 要素の一部のパラメーターに指定されるファジー区間と似ています。
以下の画像で、Nearest 関数と FuzzyQuality 関数をどのように使用できるかを見てみましょう。
画像からわかるように、この請求書文書は画像ごとに field の配置が異なるため、半構造化です。ここでは、「請求書番号」と「請求日」の field を検出します。
これを行うために、1.fsp project (フォルダー %public%\ABBYY\FlexiCapture\12.0\Samples\FLS\Tips and Tricks\FuzzyAndNearest \Project1) を作成しました。
FlexiLayout の構造を最適化し、ドキュメント内で対象となる field の配置ロジックに沿うため、対象となるすべての要素を複合要素 InvoiceGroup にまとめました。FlexiLayout の作成は、field 名「請求書番号」の検索制約を記述する要素の作成から始めることもできます。しかし、画像を解析すると、この名称を構成する単語「Invoice」がドキュメント内に複数回現れることがわかります。さらに、field の相対位置は毎回変わるため、単語「Invoice」を確実に正しく検出できるような制約は指定できません。たとえば、「Invoice date」という名称の一部として現れることがあります。このような混同を避けるため、日付 field 名の記述は、Static Text 型の要素 DateHeader を使って開始しました。Search text field には、名称の 2 つの値 Invoicedate:|Invoicedate を指定しました (画像上で実際に現れる表記の候補を列挙しています) 。名称の大文字と小文字は区別されません。
日付 field は、field 名を手がかりに検索します。プロジェクトでは DateAlternative というグループを作成しました。これは 2 つの要素で構成されています。1 つは指定された形式のいずれかで日付 field を検索する Date 要素、もう 1 つは対象 field の形式が異なる場合に備えた Character String 型の要素です。
画像からわかるように、日付 field は「Invoice date」という名称の右側にある場合もあれば、その下にある場合もあります。Relations field に標準の検索制約を設定すると (これらはプロジェクト内には表示されていますが、無効になっています) 、検索領域が広くなりすぎて、日付 field と誤って認識される可能性のある field まで含まれてしまうことがあります (例は図を参照) 。これは、たとえば日付が要素 Date に指定された形式と一致しない場合に起こりえます。
プログラムが不要な領域を解析しないようにするため、別の方法を使用しました。Advanced pre-search relations field に次のコードを記述しました。
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;
}
このコードは、日付 field 名が見つかったかどうかを確認します。見つかった場合は、検索領域を矩形の配列 (この例では 2 つの矩形) として指定します。1 つの矩形では名称の右側の日付を検索し、もう 1 つでは名称の下側を検索します。名称が見つからない場合は、画像の上半分で検索が実行されます。
ページに対して検索制約が Relations セクションで指定されていた場合、このコードの実行後の検索領域の形状は矩形ではなくなります。図のとおり、不要な object はすべてそこから除外されています。
コードの 1 行目 (let Header = InvoiceGroup.DateHeader;) は、変数 Header を定義して要素 DateHeader の値を割り当てることで、コードを簡潔にしています。
要素 DateAsString には上記のコードをそのまま繰り返し記述せず、代わりに Advanced pre-search relations セクションに次の検索制約を記述しました。
if not Date.IsNull then Dontfind();
else RestrictSearchArea (Date.Rect);
これは、要素 Date が検出されなかった場合、要素 Date の検索領域を囲む矩形内で検索が実行されることを意味します。
要素 DateAsString の検索領域を矩形の配列として指定するには、RestrictSearchArea (Date.Rect) を呼び出す代わりに、要素 Date の Advanced pre-search relations セクションにある対応するコードを複製します。
次に、field 名「請求書番号」を検出するために、Static Text 型の要素 (名前は InvoiceHeader) を作成し、検索する値として「Invoice」を指定しました。この文書は構造化されていないため、具体的な 検索制約 は指定できません。
FlexiLayout のマッチングが完了すると、名前が正確に検出されたのは最初のページだけであることが分かります。2 ページ目と 4 ページ目では、単語「Invoice」が日付 field の名前内で誤って検出されました。3 ページ目ではページ下部で見つかり、画像内に「Invoice」が 3 回出現しているにもかかわらず、最適化アルゴリズムにより名前の他の仮説は生成されませんでした。
この問題を解決するために、次のメソッドを使用しました。日付 field の名前の領域を field 名「Invoice」の検索領域から除外するため、Exclude regions of elements セクションに要素 DateHeader を追加しました (下の図を参照) 。
もし FlexiLayout の作成を DateHeader ではなく InvoiceHeader から始めていた場合、Exclude 関数は使用できませんでした。これは、この関数ではプロジェクトツリー内で現在の要素より上にある要素しか除外できないためです。
ページ下部で単語「Invoice」が不要に検出されるのを防ぐために、Advanced pre-search relations セクションに次のコードを記述しました。
NearestY: PageRect.Top;
このコードは、ページの上端に最も近い要素を検索するようプログラムに指示します。
FlexiLayout をマッチングすると、2 ページ目ではこの方法がうまくいかなかったことが分かります。これは、このページでは日付 field の名前にノイズが非常に多く、検出されなかったためです。このページでは、Nearest 関数で指定した制約が 2 つの「Invoice」文字列の両方に当てはまります。これは、それらが同じ高さに配置されているためです。また、どちらの「Invoice」文字列も認識精度が良好なため、最適化アルゴリズムは 2 つの別々の仮説ではなく、1 つの仮説を生成しました。残念ながら、この仮説は正しくありません。
「請求書番号」field を検出するために、Character String 型の要素 InvoiceNumber を作成しました。日付 field の要素と同様に、「請求書番号」field の 検索制約 は Advanced pre-search relations セクションで指定します。この要素の検索領域は矩形の配列です。
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;
さらに、コードにもう 1 つ制約を追加しました。要素 InvoiceNumber が名前の要素に最も近いことをプログラムに指示しました。
マッチング手順を実行すると、2 ページ目と 4 ページ目で「請求書番号」field が誤って検出されたことが分かります。4 ページ目では、field 名が正しく検出されていたにもかかわらず、誤検出されました。
Nearest: Header; の代替として (現在のプロジェクトの画像に対しては) 、NearestY: Header.Rect.YCenter; と記述し、検索対象の field が名前の中央に垂直方向で最も近いことをプログラムに指示することもできます。これにより、4 ページ目の field「請求書番号」の誤検出の問題は解決できる可能性があります。しかし、5 ページ目では役に立ちません。これは、名前「Invoice number」の誤検出の後、検索対象の field が日付 field 内で検出されてしまうためです。
では、このような状況で FuzzyQuality 関数をどのように使用できるかを見てみましょう。
これを示すために、プロジェクト 2.fsp (FuzzyAndNearest\Project2 フォルダー) を作成しました。
このプロジェクトの設定は、上で説明したプロジェクトの設定とほぼ同じです。
ただし、1 つ重要な違いがあります。Advanced pre-search relations セクションでは Nearest 関数を使用していません。代わりに、Advanced post-search relations セクションに次のコードを記述しました。
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;
}
}
このメソッドでは、どの仮説も除外することなく、すべての仮説の quality に影響を与えることができます。最適なチェーンは、各要素を構成するすべての仮説の quality 値を掛け合わせたうえで、チェーンごとに個別に選択されます。
行 FuzzyQuality: Rect.Top - PageRect.Top, {0,0,0,50000} * dt; は、null ではない仮説が生成された場合 (最初に if not IsNull チェックが実行されます) に、要素の位置とページ上端との距離を求めることを意味します。つまり、差 (Rect.Top - PageRect.Top) を計算し、その差が区間 {0, 0, 0, 50000}*dt に含まれるかどうかをプログラムが確認します。このような区間の指定は、quality penalty が要素とページ上端との距離に比例して増えることを意味します。つまり、距離が長いほど penalty も大きくなります。図 (a) に示すように、指定した パラメーター 値では、最大 penalty (1) は距離 50000dt に対応し、1000 ドットの距離 (1 ドットは 1/300 インチ) では penalty は 0.02、100dt の距離では 0.002 になります。
区間の boundaries を設定する パラメーター を選ぶ際は、適切な仮説に過度な penalty を与えて、その最終的な quality が null 仮説の quality より低くならないようにする必要があります (特に FuzzyQuality 関数による複数の要素チェックがある場合) 。すべての仮説 (正しいものを含む) の quality が null 仮説の quality 値より低いと、null 仮説が選択される可能性があります。つまり、その要素は検出されません。
(a)
行 *FuzzyQuality: 500dt - Width, {0,0,0,100000}dt; は、プログラムが、仮説に対応する検出対象の幅と 500dt との差を考慮することを意味します。つまり、差 (500dt - Width) を計算し、その差が区間 {0, 0, 0, 100000}*dt に含まれるかどうかをプログラムが確認します。対象の幅が狭いほど penalty は大きくなるため、より長い請求書番号が優先されます。この制約は、画像にノイズがある場合に使用できます。ノイズが指定されたアルファベットの文字として認識された場合 (たとえば 2 ページ目で確認できます) 、その仮説に penalty を与えることで、以降の解析から除外できます。
500dt という値は、field「Invoice number」の文字列長がこの値を超えないと想定して、目視で選択されています。ここで指定した パラメーター では、field「Invoice number」の幅が 0 のときに最大 penalty (0.005) になります。幅が 0 から 500dt の範囲内であれば、それ以外の場合の quality penalty はこれより小さくなります。
行 *FuzzyQuality: Rect.XCenter - InvoiceHeader.Rect.XCenter, {-10000,0,0,50000} dt; は、「請求書番号」field の名前要素についてヌルでない仮説が生成された場合 (最初に if not InvoiceHeader.IsNull チェックが実行されます) 、検出された要素 InvoiceNumber の中心と、名前 InvoiceHeader の中心との距離を求めることを意味します。差分 (Rect.XCenter - InvoiceHeader.Rect.XCenter) を計算し、その差分が区間 {-10000, 0, 0, 50000}*dt に含まれるかどうかをプログラムが確認します。この記述では、「請求書番号」field が名前の下に配置される可能性も考慮されています。この場合、要素同士の距離が離れるほど、対応する仮説に対するペナルティは大きくなります。数値が名前の右側にあるとみなす仮説は、数値が名前の下にあるとみなす仮説ほど大きなペナルティを受けません。これは、「請求書番号」field とその名前では、数値が右側に配置されるほうがはるかに一般的だからです。
図 (b) に示されているように、区間の左境界と右境界に指定されたパラメーターでは、最大ペナルティ (1) は、「請求書番号」field が名前 field から左に 10000dt、または右に 50000dt ずれた場合に対応します。1000 dot のずれには、それが「左」へのずれであれば 0.1、「右」へのずれであれば 0.02 のペナルティが課されます。同様に、100 dot のずれには、それが「左」へのずれであれば 0.01、「右」へのずれであれば 0.002 のペナルティが課されます。
(b)
行 *FuzzyQuality: Rect.YCenter - InvoiceHeader.Rect.YCenter, {-10000,0,0,10000} dt; は前のものと同じです。ただし、これは「請求書番号」field が名前の field と同じ水平位置にある場合、またはそれよりわずかに上にある場合のために用意されています。ここでのペナルティは、垂直方向のずれであればどの場合も同じです。区間の境界も同じ考え方に従って設定されており、名前の右側に data field を見つける仮説を優先するようになっています。ただし、実際のプロジェクトでは、請求書番号が名前の下に配置されていた場合でも (3 ページ) 、これらの設定が正しい検出を妨げなかったことが示されています。
すべてのページに対して FlexiLayout をマッチングすると、目的の 2 つの field が正常に検出されていることがわかります。
結論として、FuzzyQuality function は Nearest group の functions と比べて、より効率的で柔軟であると言えます。これは、半構造化文書の処理において特に重要です。