Saltar al contenido principal
Para simplificar, en este ejemplo se utiliza un documento de una sola página.
En el procesamiento de documentos, a menudo hay situaciones en las que no basta con describir la ubicación de los elementos con respecto a otros elementos en términos de “encima - debajo - a la derecha - a la izquierda”. Esto puede ocurrir, por ejemplo, si en el área de búsqueda hay varios objetos que cumplen las restricciones de búsqueda. En estos casos, necesitamos propiedades distintivas adicionales, en concreto, la distancia entre los objetos. Para ello, FlexiLayout Studio dispone de la función FuzzyQuality, así como de las funciones del grupo Nearest (Nearest, NearestX, NearestY). Estas funciones tienen aplicaciones distintas. La función Nearest solo puede utilizarse en el campo relación avanzada de prebúsqueda. Especifica que, entre las distintas hipótesis del elemento, el programa debe buscar la más cercana a un determinado elemento o punto de la imagen definido en las propiedades de la función Nearest. En el campo relación avanzada de prebúsqueda del elemento, solo puede utilizarse una función del grupo Nearest. Después de ejecutarla, solo queda una hipótesis, y esto ocurre en la etapa de generación de hipótesis, es decir, antes de que se ejecute el código especificado en el campo Relaciones avanzadas de posbúsqueda. El parámetro Minimum quality, que especifica la calidad mínima de las hipótesis del elemento, puede definirse para los elementos StaticText, CharacterString, Paragraph, Date y Separator. No hay garantía de que la hipótesis restante sea la mejor (ni de que corresponda al objeto requerido de la imagen), porque las Relaciones avanzadas de posbúsqueda son muy importantes para asignar un valor de calidad a una hipótesis. Al utilizar la función Nearest, la elección de la hipótesis se realiza en la etapa de generación de hipótesis y se basa en la proximidad a algún punto, no en la calidad de la hipótesis. Siempre debe tenerse en cuenta que, si las propiedades especificadas en la sección Relaciones avanzadas de posbúsqueda son importantes para seleccionar correctamente la hipótesis, debe utilizarse la función FuzzyQuality en lugar de las funciones del grupo Nearest. La función FuzzyQuality solo puede utilizarse en la sección Relaciones avanzadas de posbúsqueda. A diferencia de las funciones del grupo Nearest, no selecciona una única hipótesis, sino que influye en la calidad general de todas las hipótesis generadas en función de las propiedades de dichas hipótesis y de los parámetros de la función FuzzyQuality. Además, la función FuzzyQuality puede utilizarse varias veces para un mismo elemento en el campo Relaciones avanzadas de posbúsqueda. Esto significa que a una hipótesis pueden aplicársele varias restricciones distintas con diferentes valores de calidad. Todos los valores se multiplicarán para determinar la Post-search quality de la hipótesis. La función FuzzyQuality tiene el siguiente aspecto: FuzzyQuality: x, {f1, f2, f3, f4}; Su algoritmo es el siguiente: la función comprueba si el valor del parámetro x pertenece al intervalo definido por los parámetros f1, f2, f3 y f4. El significado de este intervalo difuso es similar al de los intervalos difusos especificados para algunos de los parámetros del elemento cadena de caracteres. Veamos cómo pueden utilizarse las funciones Nearest y FuzzyQuality en las siguientes imágenes.
Como puede verse en las imágenes, el documento de la factura es semiestructurado, ya que la disposición de los campos varía de una imagen a otra. Vamos a detectar los campos “Número de factura” y “Fecha de la factura”. Para ello, creamos el proyecto 1.fsp (carpeta %public%\ABBYY\FlexiCapture\12.0\Samples\FLS\Tips and Tricks\FuzzyAndNearest \Project1). Para optimizar la estructura de FlexiLayout y seguir la lógica de la disposición de los campos buscados en el documento, agrupamos todos los elementos buscados en un elemento compuesto, InvoiceGroup. Podemos comenzar la creación de FlexiLayout creando un elemento que describa las restricciones de búsqueda para el nombre del campo “Invoice number”. Pero el análisis de las imágenes muestra que la palabra “Invoice”, que forma parte del nombre, aparece varias veces en el documento. Como la ubicación relativa de los campos cambia en cada caso, no podemos especificar restricciones de búsqueda que garanticen la detección correcta de la palabra “Invoice”. Puede, por ejemplo, encontrarse en el nombre “Fecha de la factura”. Para evitar esta confusión, empezamos describiendo el nombre del campo de fecha mediante un elemento del tipo texto estático, llamado DateHeader. En el campo Search text especificamos dos valores del nombre: Invoicedate:|Invoicedate (enumerando las variantes del nombre tal como aparecen en las imágenes). El uso de mayúsculas y minúsculas en el nombre es irrelevante.
Consulte Setting multiple static text values. Search for static text with similar values para obtener más información sobre por qué debe especificar ambas variantes.
Vamos a buscar el campo de fecha basándonos en el nombre del campo. En el proyecto hemos creado un grupo DateAlternative, que consta de dos elementos: un elemento Date para buscar el campo de fecha en uno de los formatos especificados y un elemento de tipo cadena de caracteres, por si el formato del campo buscado es diferente.
Consulte Detecting dates in the case of low quality pre-recognition para obtener una descripción detallada de cómo crear un FlexiLayout para la búsqueda de fechas.
Como se ve en las imágenes, el campo de fecha puede estar situado a la derecha del nombre “Fecha de la factura” o debajo de él. Si establecemos restricciones de búsqueda estándar en el campo Relations (se muestran en el proyecto, pero están deshabilitadas), el área de búsqueda será demasiado grande y puede incluir algunos campos que podrían confundirse con el campo de fecha (en la imagen se muestra un ejemplo). Esto puede ocurrir, por ejemplo, si la fecha no coincide con el formato especificado para el elemento Date. Para evitar que el programa analice el área no deseada, utilizamos un método alternativo. En el campo relación avanzada de prebúsqueda escribimos el siguiente código: 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; } El código comprueba si se ha encontrado el nombre del campo de fecha. Si es así, especificamos el área de búsqueda como un array de rectángulos (en el ejemplo, 2 rectángulos). Un rectángulo busca la fecha a la derecha del nombre y el otro, debajo del nombre. Si no se encuentra el nombre, la búsqueda se ejecutará en la mitad superior de la imagen. En el caso de una página en la que las restricciones de búsqueda se especificaron en la sección Relations, la forma del área de búsqueda después de ejecutar este código será distinta de la de un rectángulo. Como se ve en la imagen, se eliminaron de ella todos los objetos no deseados.
La primera línea del código (let Header = InvoiceGroup.DateHeader;) simplifica el código al definir la variable Header y asignarle el valor del elemento DateHeader.
No duplicamos el código anterior para el elemento DateAsString, sino que escribimos la siguiente restricción de búsqueda en la sección relación avanzada de prebúsqueda: if not Date.IsNull then Dontfind(); else RestrictSearchArea (Date.Rect); Esto significa que, si no se detecta el elemento Date, la búsqueda se ejecutará en el rectángulo que encierra el área de búsqueda del elemento Date.
Para especificar el área de búsqueda del elemento DateAsString como un array de rectángulos, en lugar de llamar a RestrictSearchArea (Date.Rect), duplique el código correspondiente de la sección de relación avanzada de prebúsqueda del elemento Date.
A continuación, creamos un elemento de tipo texto estático (llamado InvoiceHeader) para detectar el nombre del campo “Número de factura” y especificamos el valor buscado: “Invoice”. Como el documento no está estructurado, no podemos definir restricciones de búsqueda específicas. Una vez finalizado el procedimiento de emparejamiento de FlexiLayout, podemos ver que el nombre se detectó correctamente solo en la primera página. En las páginas 2 y 4, la palabra “Invoice” se detectó por error en el nombre del campo de fecha. En la página 3, se encontró en la parte inferior de la página y, según el algoritmo de optimización, no se generaron otras hipótesis para el nombre, aunque la palabra “Invoice” aparece tres veces en la imagen.
Consulte Optimizing Group element search para obtener más información sobre la búsqueda óptima de elementos dentro del grupo.
Para resolver estos problemas, usamos el siguiente método. Para excluir la región del nombre del campo de fecha del área de búsqueda del nombre del campo “Invoice”, añadimos el elemento DateHeader a la sección Exclude regions of elements (consulte la imagen a continuación).
Si hubiéramos empezado a crear el FlexiLayout no con el nombre DateHeader, sino con el nombre InvoiceHeader, no habríamos podido usar la función Exclude, ya que esta función solo puede excluir elementos situados por encima del elemento actual en el árbol del proyecto.
Para excluir la detección no deseada de la palabra “Invoice” en la parte inferior de la página, escribimos el siguiente código en la sección de relación avanzada de prebúsqueda. NearestY: PageRect.Top; Este código le indica al programa que busque el elemento más cercano al borde superior de la página. Una vez emparejado el FlexiLayout, vemos que nuestro método falló en la página 2, porque aquí el nombre del campo de fecha tiene mucho ruido y no se detectó. En esta página, la restricción especificada en la función Nearest se cumple para ambas cadenas “Invoice”, porque están situadas al mismo nivel. Y, dado que la calidad de reconocimiento de las cadenas “Invoice” es buena en ambos casos, el algoritmo de optimización generó una sola hipótesis en lugar de dos independientes. Lamentablemente, esta hipótesis no es correcta. Para detectar el campo “Número de factura”, creamos un elemento de tipo cadena de caracteres, llamado InvoiceNumber. Al igual que con el elemento del campo de fecha, especificamos las restricciones de búsqueda para el campo “Número de factura” en la sección de relación avanzada de prebúsqueda. El área de búsqueda de este elemento es un array de rectángulos. 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; Además, añadimos una restricción más al código. Le indicamos al programa que el elemento InvoiceNumber es el más cercano al elemento del nombre. Después de ejecutar el procedimiento de emparejamiento, vemos que el campo “Número de factura” se detectó incorrectamente en las páginas 2 y 4. En la página 4 se detectó incorrectamente aunque el nombre del campo se había detectado correctamente.
Como alternativa (para las imágenes del proyecto actual) a Nearest: Header;, podríamos escribir NearestY: Header.Rect.YCenter; para indicar al programa que el campo buscado es el más cercano verticalmente al centro del nombre. Esto podría resolver el problema de la detección incorrecta del campo “Número de factura” en la página 4. Pero no ayuda en la página 5, porque el campo buscado se detecta dentro del campo de fecha después de la detección incorrecta del nombre “Número de factura”.
Ahora veamos cómo podemos usar la función FuzzyQuality en una situación como esta. Para demostrarlo, creamos el proyecto 2.fsp (FuzzyAndNearest\Project2 folder). La configuración de este proyecto es casi idéntica a la del proyecto descrito anteriormente. Pero hay una diferencia importante. No usamos la función Nearest en la sección de relación avanzada de prebúsqueda. En su lugar, escribimos el siguiente código en la sección Relaciones avanzadas de posbúsqueda: 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; } } Con este método podemos influir en la calidad de todas las hipótesis sin excluir ninguna. La elección de la mejor cadena se hará para cada cadena por separado, multiplicando los valores de calidad de todas las hipótesis que componen los elementos. La línea FuzzyQuality: Rect.Top - PageRect.Top, {0,0,0,50000} * dt; significa que, si se genera una hipótesis no nula (primero se ejecuta la comprobación if not IsNull), se determina la distancia entre la posición del elemento y el borde superior de la página. Es decir, se calcula la diferencia (Rect.Top - PageRect.Top) y el programa comprueba si esta diferencia pertenece al intervalo {0, 0, 0, 50000}*dt. Esta forma de describir el intervalo significa que existe una dependencia directa entre la penalización de calidad y la distancia entre el elemento y el borde superior de la página: cuanto mayor sea la distancia, mayor será la penalización. Como se muestra en la imagen (a), con los valores de parámetros especificados, la penalización máxima (1) corresponde a una distancia de 50000dt, mientras que una distancia de 1000 puntos (1 punto es 1/300 de pulgada) implica una penalización de 0.02, y una distancia de 100dt, una penalización de 0.002.
Al elegir los parámetros que establecen los límites del intervalo, debe seleccionar valores que (en particular, cuando hay varias comprobaciones de elementos con la función FuzzyQuality) no penalicen la hipótesis correcta hasta el punto de que su calidad final sea inferior a la de una hipótesis nula. Si la calidad de todas las hipótesis (incluida la correcta) es inferior al valor de calidad de una hipótesis nula, puede seleccionarse la hipótesis nula; es decir, el elemento no se detectará.
(a) La línea *FuzzyQuality: 500dt - Width, {0,0,0,100000}dt; significa que el programa considera la diferencia entre el ancho del objeto detectado y 500dt, correspondiente a la hipótesis. Es decir, se calcula la diferencia (500dt - Width) y el programa comprueba si esta diferencia pertenece al intervalo {0, 0, 0, 100000}*dt. Cuanto más estrecho sea el objeto, mayor será la penalización, por lo que se preferirán los números de factura más largos. Esta restricción puede utilizarse si la imagen tiene ruido. Si el ruido se reconoce como un carácter del alfabeto especificado (como puede verse, por ejemplo, en la página 2), su hipótesis debe penalizarse para excluirla de análisis posteriores.
El valor de 500dt se elige mediante inspección visual, suponiendo que la longitud de la cadena en el campo “Número de factura” no sea mayor que este valor. Los parámetros especificados aquí definen que la penalización máxima (0.005) correspondería a un ancho cero del campo “Número de factura”. Para cualquier otro ancho entre 0 y 500dt, las penalizaciones de calidad serían menores.
La línea *FuzzyQuality: Rect.XCenter - InvoiceHeader.Rect.XCenter, {-10000,0,0,50000} dt; significa que, si se genera una hipótesis no nula del elemento correspondiente al nombre del campo “Número de factura” (primero se ejecuta la comprobación if not InvoiceHeader.IsNull), se determina la distancia entre el centro del elemento detectado InvoiceNumber y el centro del nombre InvoiceHeader. Se calcula la diferencia (Rect.XCenter - InvoiceHeader.Rect.XCenter) y el programa comprueba si esta diferencia pertenece al intervalo {-10000, 0, 0, 50000}*dt. Esta descripción también tiene en cuenta la posibilidad de que el campo “Número de factura” pueda estar situado debajo del nombre. En este caso, cuanto más alejados estén los elementos entre sí, mayor será la penalización de la hipótesis correspondiente. Las hipótesis que suponen que el número está situado a la derecha del nombre no se penalizarán tanto como las que suponen que el número está debajo del nombre, ya que es evidente que la disposición “correcta” del campo “Número de factura” y su nombre es mucho más habitual. Como se muestra en la imagen (b), con los parámetros especificados para los límites izquierdo y derecho del intervalo, la penalización máxima (1) corresponderá a un desplazamiento del campo “Número de factura” respecto al campo del nombre de 10000dt hacia la izquierda o de 50000dt hacia la derecha. Un desplazamiento de 1000 dot se penalizará con 0.1 si es un desplazamiento “hacia la izquierda”, o con 0.02 si es un desplazamiento “hacia la derecha”. Del mismo modo, un desplazamiento de 100 dot se penalizará con 0.01 si es un desplazamiento “hacia la izquierda”, o con 0.002 si es un desplazamiento “hacia la derecha”. (b) La línea *FuzzyQuality: Rect.YCenter - InvoiceHeader.Rect.YCenter, {-10000,0,0,10000} dt; es idéntica a la anterior. Pero está reservada para los casos en que el campo “Número de factura” está al mismo nivel horizontal o incluso ligeramente por encima del campo del nombre. Aquí las penalizaciones son las mismas para cualquier desplazamiento vertical. Los límites del intervalo se establecen siguiendo la misma lógica: dar prioridad a las hipótesis que encuentran el campo de datos a la derecha de su nombre. Sin embargo, el proyecto muestra que esta configuración no impidió detectar correctamente el número de factura incluso cuando estaba situado debajo del nombre (página 3). Después de hacer coincidir el FlexiLayout con todas las páginas, vemos que los dos campos buscados se han detectado correctamente. En conclusión, podemos decir que la función FuzzyQuality es más eficiente y flexible que las funciones del grupo Nearest, lo cual es especialmente importante al procesar documentos semiestructurados.