Russian (Русский) - Change language

Использование функций Nearest и FuzzyQuality для поиска элементов

Внимание! Для простоты рассмотрим одностраничный документ.

При обработке форм довольно часто возникают ситуации, когда условий, описывающих месторасположение одних элементов относительно других элементов в терминах “выше-ниже-левее-правее” оказывается недостаточно, поскольку в области поиска может одновременно оказаться несколько объектов, удовлетворяющих заданным условиям. В этом случае их приходится отличать по другим признакам, в частности - по дистанции между объектами. Для этих целей в программе FlexiLayout Studio предусмотрены функции FuzzyQuality и функции из группы Nearest (т.е. Nearest, NearestX, NearestY).

Область действия и назначение этих функций несколько отличается.

Функция Nearest может использоваться только в секции Advanced pre-search relations. Она указывает программе, что из нескольких гипотез элемента при прочих равных условиях необходимо искать ближайшую к некоторому элементу или точке на изображении, заданной параметрами функции Nearest. В секции Advanced pre-search relations элемента можно использовать только одну функцию из группы Nearest. После ее срабатывания из нескольких рассматриваемых гипотез остается только одна, причем это происходит на этапе выдвижения гипотез элемента, т.е. до выполнения кода, указанного в секции Advanced post-search relations. Для элементов StaticText, CharacterString, Paragraph, Date и Separator можно задать параметр Minimum quality, определяющий минимальное качество гипотез элемента. Вообще говоря, нет гарантии, что эта гипотеза окажется лучшей (будет соответствовать искомому фрагменту изображения), поскольку Advanced post-search relations играют важную роль в оценке, и следовательно, выборе гипотезы элемента. А здесь выбор происходит уже на этапе создания гипотез, не на основе качества гипотезы, а на основе близости ее к указанной точке. При использовании функции Nearest нужно иметь это в виду. Если для выбора правильной гипотезы важно учитывать условия, записанные в секции Advanced post-search relations (например, ограничение на ширину найденной гипотезы), то вместо функций группы Nearest рекомендуется использовать функцию FuzzyQuality.

Функция FuzzyQuality может использоваться исключительно в секции Advanced post-search relations. В отличие от функций группы Nearest, она не выбирает одну гипотезу из нескольких, а влияет на оценку качества всех найденных гипотез в зависимости от свойств этих гипотез и параметров, заданных в аргументах функции FuzzyQuality. Отличие от функций группы Nearest заключается также в том, что для одного элемента в секции Advanced post-search relations функция FuzzyQuality может использоваться несколько раз. Т.е. на гипотезу при необходимости может быть наложено несколько различных условий с разными оценками качества. Все полученные оценки будут перемножены, таким образом сформируется Post-search quality гипотезы.

Функция FuzzyQuality имеет следующий синтаксис:

FuzzyQuality: x, {f1, f2, f3, f4};
    

Она работает по такому алгоритму: проверяется вхождение значения параметра х в нечеткий интервал, задаваемый параметрами f1, f2, f3, f4. Смысл данного интервала аналогичен смыслу нечетких интервалов, которые определены для некоторых параметров элемента Character String.

Проиллюстрируем работу функций Nearest и FuzzyQuality на следующих изображениях.

Как видно на изображениях, форма счета относится к разряду неформализованных, поскольку поля меняют свое взаимное расположение. Будем искать на данной форме поля “Номер счета” и “Дата счета”.

Для решения этой задачи мы создали проект 1.fsp (папка %public%\ABBYY\FlexiCapture\12.0\Samples\FLS\Tips and Tricks\FuzzyAndNearest\Project1).

Для оптимальной организации гибкого описания, и следуя логике расположения искомых полей данных на форме, все искомые элементы мы объединим в одном составном элементе InvoiceGroup. Мы можем начать создание гибкого описания с создания элемента, описывающего условия поиска заголовка поля “Номер счета”. Но, при анализе всех изображений видно, что слово “Invoice”, из которого состоит заголовок, присутствует на форме несколько раз. А, поскольку взаимное расположение полей все время разное, мы не можем задать программе условия поиска, которые гарантировали бы правильное нахождение слова “Invoice”. Оно может быть найдено, например, в составе заголовка “Invoice date”. Чтобы исключить эту путаницу, мы начали создание гибкого описания с описания заголовка поля даты с помощью элемента типа Static Text с именем DateHeader. В секции Search text мы задали два значения заголовка Invoicedate:|Invoicedate (перечислили варианты написания заголовка на изображениях). Регистр написания значения заголовка может быть любой.

Замечание. О том, почему необходимо задавать оба варианта, несмотря на незначительное отличие заголовков, можно подробнее узнать в разделе Задание нескольких вариантов значений статического текста. Поиск статического текста со схожими значениями.

Поле даты мы будем искать относительно заголовка. В проекте создана группа DateAlternative, состоящая из двух элементов: специализированного элемента Date, предназначенного для поиска даты в одном из допустимых стандартных форматов, и элемента типа Character String с именем DateAsString, созданного на случай, если формат искомого поля по каким-то причинам будет отличаться.

Замечание. Подробнее о принципах организации гибкого описания для поиска полей даты можно посмотреть в разделе Поиск даты в случае хорошего и плохого распознавания.

Как видно на изображениях, поле даты может находиться как правее заголовка “Invoice date”, так и под ним. Если мы воспользуемся стандартным способом описания условий поиска элемента на вкладке Relations (эти условия приведены в проекте в иллюстративных целях, но отключены), то область поиска будет слишком большой, и в нее могут попасть поля, которые при некоторых условиях могут быть приняты за дату (пример приведен на изображении). Это может произойти, например, если дата не соответствует формату элемента Date.

Чтобы исключить рассмотрение лишней области мы поступили по-другому. В секции Advanced pre-search relations мы задали следующий код:

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;
}
    

В этом коде мы проверяем, был ли найден заголовок поля даты. Если он найден, мы формируем область поиска даты в виде массива прямоугольников (в нашем случае – из двух). Один прямоугольник “отвечает” за поиск даты правее заголовка, другой – под ним. Если заголовок не был найден, поиск поля даты будет идти в верхней полуплоскости изображения.

Применительно к той странице, изображение которой было приведено для случая задания условий поиска на вкладке Relations , область поиска после срабатывания данного кода будет иметь уже иную, непрямоугольную форму. Как видно на изображении, из нее были исключены все лишние объекты.

Замечание. Первая строка кода (let Header = InvoiceGroup.DateHeader;) позволяет упростить последующий код за счет определения переменной Header и присваивания ей значения элемента DateHeader.

Для элемента DateAsString мы не стали дублировать вышеприведенный код, а записали следующее условие поиска в секции Advanced pre-search relations:

if not Date.IsNull then Dontfind();
else RestrictSearchArea (Date.Rect);
    

Это означает, что если элемент Date не будет найден, поиск элемента DateAsString будет вестись в прямоугольнике, описывающем область поиска элемента Date.

Замечание. Если необходимо, чтобы область поиска элемента DateAsString была не прямоугольной формы (состояла из массива прямоугольников), как в случае элемента Date, то вместо вызова RestrictSearchArea (Date.Rect) следует продублировать соответствующий код, указанный в секции Advanced pre-search relations элемента Date.

Далее мы создали элемент типа Static Text с именем InvoiceHeader для поиска заголовка поля “Номер счета“ и задали искомое значение “Invoice”. Поскольку форма не формализованная, мы не можем задать никакие конкретные условия поиска.

Запустив процедуру наложения гибкого описания на всех изображениях, мы видим, что правильно заголовок был найден только на первой странице. На страницах 2 и 4 слово “Invoice” было ошибочно обнаружено в заголовке поля даты. На странице 3 оно было найдено внизу страницы, а в силу срабатывания алгоритма оптимизации, остальные гипотезы заголовка не были сформированы, несмотря на то, что слово “Invoice” присутствует трижды на данном изображении.

Замечание. Более подробная информация об оптимизации поиска элементов в группе приведена в разделе Оптимизация поиска составного элемента.

Для решения возникших проблем мы поступили следующим образом. Для исключения из области поиска заголовка “Invoice” области заголовка поля даты, мы добавили элемент DateHeader в секции Exclude regions of elements (см. изображение ниже).

Замечание. Если бы мы начали создавать гибкое описание не с заголовка DateHeader, а с заголовка InvoiceHeader, мы бы не имели возможность использовать функцию Exclude, поскольку с ее помощью можно исключать только элементы, описанные в дереве проекта выше текущего элемента.

А для исключения ошибочного нахождения слова “Invoice” внизу страницы поместили следующий код в секции Advanced pre-search relations:

NearestY: PageRect.Top;
    

Этот код сообщает программе, что необходимо искать элемент, ближайший к верхнему краю страницы.

После наложения гибкого описания видим, что на странице 2 используемый нами прием не помог, в силу того, что на данной странице заголовок поля даты не был найден из-за наличия мусора на изображении. В то же время, на данной странице условие, заданное в функции Nearest, соблюдается для обеих строк “Invoice”, поскольку они располагаются на одном уровне. А поскольку качество распознавания строки “Invoice” в обоих случаях хорошее, благодаря алгоритму оптимизации вместо двух гипотез была сформирована одна. И, к сожалению, она оказалась неправильной.

Для поиска поля “Номер счета” мы создали в гибком описании элемент типа Character String с именем InvoiceNumber. По аналогии с элементом поиска поля даты, в секции 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;
    

Кроме того, мы задали в этом коде еще одно условие. Мы сообщили программе, что элемент InvoiceNumber является ближайшим к элементу заголовка.

После запуска процедуры наложения гибкого описания мы видим, что на 2 и 4 страницах поле “Номер счета” найдено ошибочно. Причем на 4 странице – несмотря на то, что сам заголовок поля был найден правильно.

Замечание. В качестве альтернативы (имеется в виду для изображений, которые присутствуют в данном проекте) вместо Nearest: Header; можно было бы записать NearestY: Header.Rect.YCenter; т.е. этим мы бы сообщили программе, что искомое поле является ближайшим к центру заголовка по вертикали. Это могло бы решить проблему ошибочного нахождения поля “Номер счета” для страницы 4. Но таким способом на странице 2 проблему ошибочного нахождения поля решить не удается, поскольку из-за ошибочного нахождения заголовка “Номер счета” обнаруживается в поле даты.

Теперь посмотрим, как можно использовать в данном случае функцию FuzzyQuality.

Для этого мы создали проект 2.fsp (папка FuzzyAndNearest\Project2).

Этот проект с точки зрения настроек является практически полным аналогом одноименного проекта, рассмотренного ранее.

Но есть одно существенное отличие. В секции 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;
}
}
    

Таким способом мы можем влиять на качество всех рассматриваемых гипотез, не исключая ни одну из них. Выбор лучшей цепочки будет осуществляться для каждой сформированной цепочки путем перемножения показателей качества всех входящих в нее гипотез элементов.

Запись FuzzyQuality: Rect.Top - PageRect.Top, {0,0,0,50000} * dt; означает, что если была сформирована ненулевая гипотеза элемента (предварительно идет проверка if not IsNull), то рассматривается расстояние между местом нахождения элемента и верхним краем страницы. Т.е. рассчитывается разница (Rect.Top - PageRect.Top) и проверяется вхождение этой разницы в нечеткий интервал {0, 0, 0, 50000}*dt. Такая запись нечеткого интервала означает прямую линейную зависимость между штрафом, налагаемым на качество гипотезы, и расстоянием между элементом и верхним краем страницы. Чем дальше элемент от верха, тем больше штрафуется соответствующая гипотеза. Как условно показано на схеме (а), при выбранном значении параметра, отвечающего за правую границу нечеткого интервала, максимальный штраф (1) будет соответствовать расстоянию 50000dt, а расстоянию в 1000 dot (1 dot = 1/300 дюйма) будет соответствовать штраф 0,02, в 100dt – штраф 0,002.

Замечание. При подборе параметров, соответствующих границам интервала, следует оценивать соответствующие фактические данные на тестовых изображениях. Чем меньше значение параметра, тем больше угол наклона прямой, тем больше будет штраф на единицу длины. Необходимо выбирать такие значения параметров (особенно, когда для элемента пишется несколько проверок с участием функции FuzzyQuality), при которых штраф правильной гипотезы не приведет к снижению ее качества до такой степени, что оно окажется ниже качества нулевой гипотезы. Если в результате штрафа качество всех гипотез (в том числе правильной гипотезы), окажется ниже качества нулевой гипотезы, то может быть выбрана нулевая гипотеза, т.е. элемент не будет найден.

(а)

Запись FuzzyQuality: 500dt - Width, {0,0,0,100000}*dt; означает, что рассматривается отличие ширины найденного объекта, соответствующего гипотезе от 500dt. Т.е. рассчитывается разница (500dt - Width) и проверяется вхождение этой разницы в нечеткий интервал {0, 0, 0, 100000}*dt. Чем уже найденный объект, тем больше штрафуется соответствующая гипотеза, т.е. при прочих равных условиях предпочтение отдается более длинным номерам счета. Это условие можно использовать на случай, когда на изображении в области поиска поля присутствует мусор. Если при распознавании программой он воспринимается как символ, соответствующий заданному алфавиту (это можно увидеть, например, на странице 2), то для него также может быть сформирована гипотеза, которую необходимо исключить из дальнейшего рассмотрения путем более сильного штрафования.

Замечание. Значение 500dt выбрано на глаз, исходя из того, что максимально возможная длина строки в поле “Номер счета” не превысит этого значения. Заданные в этом выражении параметры определяют, что максимальный штраф (0,005) соответствовал бы нулевой ширине поля “Номер счета”. Для всех прочих значений ширины между 0 и 500dt штраф будет ниже.

Запись FuzzyQuality: Rect.XCenter - InvoiceHeader.Rect.XCenter, {-10000,0,0,50000} *dt; означает, что если была сформирована ненулевая гипотеза элемента заголовка поля “Номер счета” (предварительно идет проверка if not InvoiceHeader.IsNull), то рассматривается расстояние между центром найденного элемента InvoiceNumber и центром заголовка InvoiceHeader по горизонтали. Т.е. рассчитывается разница (Rect.XCenter - InvoiceHeader.Rect.XCenter) и проверяется вхождение этой разницы в нечеткий интервал {-10000, 0, 0, 50000}*dt. Такая запись нечеткого интервала учитывает те случаи, когда поле “Номер счета” может располагаться под заголовком. В этом случае, чем дальше элементы будут друг от друга (причем допускается отклонение в любую сторону от центра заголовка), тем больше будет штрафоваться соответствующая гипотеза. Максимальное качество (штраф = 0) будет в случае, когда поле и заголовок четко центрированы по вертикали. Неравенство левой и правой границ нечеткого интервала объясняется простым наблюдением – номер счета обычно пишут рядом с полем заголовка и правее него, гораздо реже – под ним. Поэтому “правые” гипотезы, мы будем штрафовать меньше.

Как условно показано на схеме (б), при выбранных значениях параметров, отвечающих за левую и правую границы нечеткого интервала, максимальный штраф (1) будет соответствовать смещению поля “Номер счета” влево относительно заголовка на 10000dt и вправо относительно поля заголовка на 50000dt. Смещению в 1000 dot для “левого” интервала будет соответствовать штраф 0,1, а в 100dt – штраф 0,01. Для “правого” интервала размер штрафа составит 0,02 и 0,002 соответственно.

(b)

Запись FuzzyQuality: Rect.YCenter - InvoiceHeader.Rect.YCenter, {-10000,0,0,10000} *dt; аналогична предыдущей. Но она предусмотрена для случаев, когда поле “Номер счета” может находиться на одном уровне по горизонтали или даже чуть выше поля заголовка. Размер штрафа в данном случае одинаков при любом смещении относительно горизонтали. Границы интервала выбраны, исходя из той же логики, что и раньше – мы хотим обеспечить преимущество гипотез, соответствующих схеме “поле правее заголовка”. Однако, как видно в проекте, заданные настройки не помешали правильно найти номер счета, и в случае его размещения под заголовком (страница 3).

После наложения гибкого описания на все страницы видим, что оба искомых поля найдены успешно.

Таким образом, можно сделать вывод, что функция FuzzyQuality является более эффективным и гибким инструментом по сравнению с функциями группы Nearest, что особенно важно при обработке неформализованных форм.

11/10/2020 12:08:08 PM


Please leave your feedback about this article