Josep Portella

Cómo funciona Magia DNI

Diciembre de 2011
Actualizado: febrero de 2017

© 2011, 2017 Josep Portella Florit
Esta obra está bajo una licencia de
Atribución-SinDerivadas 3.0 Creative Commons.

Contenido

Introducción

Magia DNI es una aplicación para Android que usa la cámara del dispositivo para leer los datos OCR del DNI español, tanto el electrónico como el tradicional, y calcular el dígito de control, aplicando lo explicado en mi artículo Desmitificando los números del DNI. El propósito de la aplicación es demostrar de forma gráfica que lo que se dice del dígito de control es un mito, y para ello hice que funcionase igualmente con el dígito de control tapado.

Al publicar Magia DNI varias personas se interesaron en saber cómo había hecho el reconocimiento de caracteres. En este artículo pretendo explicar el método utilizado.

No usé bibliotecas de terceros (si no contamos el SDK de Android) pero, para hacerlo más fácil de implementar, más preciso y más rápido, aproveché las peculiaridades de los datos OCR del DNI que los sistemas OCR genéricos no pueden tener en cuenta.

El código fuente de Magia DNI está disponible bajo licencia GPL.

Localización de las filas de caracteres

La imagen de la cámara se trata en Magia DNI como un array de 2 dimensiones con valores de 0 a 255 que representan la luminosidad de cada píxel, siendo 0 totalmente oscuro y 255 el valor máximo de luz.

[Foto en escala de grises de unas líneas de datos OCR de un DNI
inventado]

Para cada línea horizontal de píxeles se obtiene la media de luminosidad. El resultado de este proceso es un gráfica parecida a esta:

[Gráfica que muestra 3 caídas correspondientes a la posición vertical
de las líneas]

A continuación, cada valor entre el valor máximo y mínimo de la gráfica es utilizado como umbral para calcular una gráfica de colisiones. Las colisiones son una serie de segmentos de los cuales se sabe la posición, longitud y el hecho de si están por encima o por debajo del umbral, es decir, si colisionan o no colisionan con la gráfica. Por ejemplo, estableciendo el umbral en una posición entre el valor máximo y mínimo de la gráfica anterior:

[La gráfica anterior con una línea horizontal que atraviesa las
caídas]

se obtiene esta gráfica de colisiones:

[Línea horizontal con unas muescas que representan las subidas y
bajadas respecto al umbral]

En cada gráfica de colisiones se busca el patrón que suelen dejar las filas de datos OCR del DNI; si se encuentra el patrón se guardan las posiciones de las filas a partir los datos de los segmentos correspondientes, pero si anteriormente ya se había encontrado una coincidencia, esta sólo se substituye por la actual en el caso de que la actual sea mejor.

El patrón que dejan las filas de datos OCR del DNI consiste en tres segmentos que no colisionan, llamémosles filas, separados por segmentos más pequeños que colisionan, llamémosles separadores. Se comprueba que las filas tengan cierto tamaño como mínimo, proporcial al tamaño de la imagen, y que la desviación estándar de los tamaños de las filas no exceda el 12% del promedio de los tamaños, y se hace lo mismo con los separadores. Además se comprueba que el tamaño máximo de las filas sea mayor que el tamaño máximo de los separadores, pero menor que las colisiones a la izquierda de la primera fila y a la derecha de la última fila.

Una coincidencia del patrón se considera mejor que otra cuando la suma del tamaño de sus filas es mayor que la de la otra.

Una vez acabado el proceso, se descarta la última de las filas de la mejor coincidencia, ya que no sirve para el propósito de la aplicación; también se descartan los separadores, y las dos filas restantes se ajustan a izquierda y derecha según la separación entre ambas.

[Secuencia de gráficos que representa el ajuste de las 2 primeras
filas]

De esta forma se disminuye la posibilidad de que se corte parte de los caracteres.

Localización de las columnas de caracteres

Teniendo en cuenta la posición y el tamaño de las dos primeras filas, vemos la imagen así:

[La foto recortada para incluir sólo las 2 primeras
filas]

Para cada línea vertical de la imagen recortada se encuentran el valor mínimo y el valor máximo y se restan, obteniendo una gráfica similar a esta:

[Gráfica con caídas que indican la posición de las columnas de
caracteres]

Igual que con las filas, se han de obtener gráficas de colisiones para cada valor entre el valor máximo y el valor mínimo de la gráfica. En cada gráfica de colisiones se buscan coincidencias con el patrón de las columnas para quedarse con la mejor de todas las coincidencias.

Llamemos columnas a los segmentos que no colisionan y coinciden con la posición de las columnas de caracteres. El patrón a buscar es parecido al de las filas; la diferencia está en que el valor máximo de la desviación estándar de los tamaños de las columnas y separadores será el promedio de los tamaños. Se da por bueno si hay 24 columnas (las necesarias para el cálculo del dígito de control), e igual que con las filas, las columnas tienen cierto tamaño como mínimo y el tamaño máximo de las columnas es mayor que el tamaño máximo de los separadores.

Para que una coincidencia sea considerada mejor que otra, además de que su suma de tamaños de columnas sea mayor, ha de empezar en la misma posición o estar más a la izquierda.

Finalmente se descartan los separadores de la mejor coincidencia y se ajustan las columnas de la misma forma en que se ajustaron las filas.

Conversión de caracteres a 2 bits

Habiendo localizado las filas y las columnas de caracteres ya disponemos de posición, altura y anchura de los caracteres que nos interesan. Antes de poder reconocer los caracteres hay que transformarlos de escala de grises a 2 bits, eliminando el máximo ruido posible. Para ello se calcula el umbral óptimo: el 70% del valor medio de los píxeles del carácter a procesar.

[Secuencia de imágenes con un carácter representado con diversos
umbrales]

Reconocimiento de caracteres

Sabiendo el umbral óptimo de un carácter recortamos los márgenes de forma que quede ajustado.

[Comparació d'un caràcter amb marges i sense
marges]

A la hora de reconocer un carácter se tienen en cuenta los diferentes valores que puede tomar según la fila y la columna del carácter. Por ejemplo, las fechas de la segunda fila siempre serán dígitos. De esta forma el proceso se hace más preciso y más rápido.

La aplicación dispone de una plantilla para cada carácter posible. Para cada carácter posible se comparan píxel a píxel la imagen a reconocer con la correspondiente plantilla, teniendo en cuenta el ratio porque lo normal será que no coincidan en tamaño. Se empieza asignando la puntuación de la plantilla a cero. Si coincide un píxel, se suma 1 a la puntuación; si no coincide, se resta 1. El resultado será el valor de la plantilla con mayor puntuación.

Reconocimiento del tipo de DNI

Para saber si los datos OCR que se están leyendo pertenecen a un DNI electrónico o a un DNI tradicional, se reconoce el carácter en la columna 23 de la fila 1. Si el carácter es un dígito, entonces se trata de un DNI electrónico; si el carácter es el símbolo de menor-que, entonces se trata de un DNI tradicional.

El tipo de DNI se tiene en cuenta a la hora de calcular el dígito de control, porque varían los datos que se utilizan para ello.

Detección de errores

Antes de mostrar el dígito de control calculado, se comprueban los demás dígitos de control, incluyendo la letra del DNI, para evitar mostrar el dígito incorrecto. Aún así, puede ocurrir que 2 o más errores de reconocimiento inoportunos hagan posible superar las comprobaciones, resultando en un dígito de control final incorrecto. Para hacer esto menos probable, no se muestra el dígito hasta que se calcula el mismo por segunda vez, descartándose si se obtiene otro dígito.