
Algoritmo de Matlab para la lectura a través del OCR Tesseract de códigos en imágenes de crotales tomadas en una cinta transportadora.
ALGORITMO
La función prepararcrotales tiene como objetivo mejorar las imágenes de los crotales, a fin de que estos puedan ser fácilmente decodificados posteriormente a través del OCR Tesseract.
Entrada: imagen original.
Salida: imagen con únicamente el código del crotal a analizar.
La primera mejora se realiza sobre la orientación de la imagen, de modo que si el crotal apareciese rotado, se llevase a su correcta posición, es decir, con los números en paralelo al borde inferioro, para una correcta lectura por parte de Tesseract. Este proceso se realiza de la siguiente manera:
- Binarización mediante Otsu y detección de bordes de la imagen.
- Cálculo sobre dichos bordes, mediante transformada de Hough, de la línea más larga, que corresponderá al borde inferior del crotal.
- Cálculo del ángulo de dicha línea, y rotación de la imagen para corregirlo.
![]() |
Figura 1
Arriba: imagen original.
Centro: imagen binarizada y detección de bordes. En azul, línea más larga obtenida con Hough.
Abajo: imagen original rotada para corregir la orientación.
|
Tras el proceso de binarización, puede darse el caso de que los dígitos se hayan mezclado con el fondo. Para solucionarlo, se vuelve a obtener mediante Hough el borde inferior del crotal, con unos parámetros tales que se busque una línea más o menos horizontal, y recortamos lo que esté debajo.
La siguiente mejora consiste en seleccionar el área correspondiente al código a analizar y desechar el resto de la imagen. Para ello:
- Se invierte la imagen y se etiquetan los elementos de la imagen.
- Se elimina el elemento mayor, que corresponde al fondo, y se obtienen los siguientes cuatro mayores, que corresponderán a los dígitos aunque haya ruido o sean parciales.
- Se obtienen las bounding box de los dígitos y, a partir del de más a la izquierda y del de más a la derecha, se calcula un recuadro que los contenga a todos. Se desecha el resto de la imagen.
![]() |
Figura 4
Arriba: imagen binarizada y recortada inferiormente.
Abajo: sólo los dígitos, desechando el resto, e invertida para que Tesseract pueda trabajar con ella.
|
Por último, se añade un borde blanco a la imagen de los dígitos, para que Tesseract pueda trabajar con ella.
![]() |
Figura 5
Arriba: imagen sólo dígitos.
Abajo: borde blanco añadido para que Tesseract pueda trabajar con la imagen.
|
Leer los crotales
La función leercrotales efectua las llamadas necesarias para leer, a través de Tesseract, el código de los crotales.
Entrada: imagen de crotal a analizar.
Salida: genera una imagen en formato .tif con los dígitos analizados y un archivo .txt con la lectura obtenida por tesseract.
El proceso que sigue es el siguiente:
- Llama a la función preparacrotales, pasándole la imagen del crotal (en escala de grises, un único canal) y obteniendo la imagen con sólo los dígitos a analizar.
- Se guarda dicha imagen en formato .tif en el directorio utilizado.
- Se llama al OCR Tesseract, pasándole el archivo .tif. Este lee la imagen y devuelve la lectura en un archivo .txt en el directorio utilizado.
I = imread('crotal.tif');
function leercrotales(I)
imprep=prepararcrotales(I);end
imwrite(imprep,'Imagen.tif','tiff');
!tesseract Imagen.tif Codigo;
RESULTADOS
Se han realizado pruebas sobre una base de datos con imágenes de crotales. Los resultados son correctos en un 90% de los casos, sin embargo se han detectado dos tipos de errores:
En el primero, la umbralización elimina los detalles buscados. Es posible que mediante otro método diferente de Otsu, o controlando los parámetros de la umbralización, pudiera obtenerse una binarización correcta en todas las imágenes, y ese sería el primer punto sobre el que trabajar. Sin embargo, hay que tener en cuenta que este suele ser un objetivo complicado y muchas veces inalcanzable.
En el segundo, la imagen del código se procesa correctamente, pero el OCR Tesseract falla (por ejemplo, reconociendo un '1' como '|'). Este es un problema ajeno al algoritmo creado.
En el primero, la umbralización elimina los detalles buscados. Es posible que mediante otro método diferente de Otsu, o controlando los parámetros de la umbralización, pudiera obtenerse una binarización correcta en todas las imágenes, y ese sería el primer punto sobre el que trabajar. Sin embargo, hay que tener en cuenta que este suele ser un objetivo complicado y muchas veces inalcanzable.
En el segundo, la imagen del código se procesa correctamente, pero el OCR Tesseract falla (por ejemplo, reconociendo un '1' como '|'). Este es un problema ajeno al algoritmo creado.
CÓDIGO
function leercrotales(I)
% Esta función devuelve, a partir de la imagen de un crotal y a través del
% OCR Tesseract, el cógido del crotal.
%
% Entrada:
% -I: imagen de un crotal.
%
% Salidas:
% genera una imagen en formato .tif con los dígitos analizados y un
% archivo .txt con la lectura obtenida por tesseract.
%% Preprocesado de la imagen
imprep=prepararcrotales(I);
%% Lectura del código
imwrite(imprep,'Imagen.tif','tiff');
!tesseract Imagen.tif Codigo;
end
function imprep=prepararcrotales(I)
% Esta función preprocesa imágenes de crotales, quedándose únicamente con
% la región de la imagen que contiene el código numérico de los mismos.
%
% Entrada:
% -I: imagen de un crotal.
%
% Salida:
% -imprep: subimagen conteniendo únicamente la región del código, con
% dígitos en negro sobre fondo blanco.
%% Eliminación borde inferior
% Se elimina el borde inferior de la imagen (un 2%) que no cincierne al crotal:
borde=round(size(I,1)-0.02*size(I,1));
I = I(1:borde,:);
%% Corrección de ángulo
% Se binariza y se detectan los bordes:
level = graythresh(I);
BW = im2bw(I,level);
se = strel('disk', 6, 0);
BW=imclose(BW,se);
BWed = edge(BW,'canny');
% Se calcula la transformada de Hough y se obtienen los picos:
[H,T,R] = hough(BWed);
P = houghpeaks(H,5,'threshold',ceil(0.3*max(H(:))));
x = T(P(:,2)); y = R(P(:,1));
% Se extraen las líneas de la imagen
lines = houghlines(BWed,T,R,P,'FillGap',40,'MinLength',7);
% Se calculan los puntos de los extremos de la línea más larga
max_len = 0;
for k = 1:length(lines)
xy = [lines(k).point1; lines(k).point2];
len = norm(lines(k).point1 - lines(k).point2);
if ( len > max_len)
max_len = len;
xy_long = xy;
end
end
% Se calcula en ángulo de la línea y se gira la imagen para corregir
angle_in_deg=atan2(xy_long(4)-xy_long(3),xy_long(2)-xy_long(1))*180/pi;
rotBW = imrotate(BW,angle_in_deg,'crop');
rotBWed = imrotate(BWed,angle_in_deg,'crop');
%% Obtención área de interés
% Calculamos la nueva línea más larga tras el giro
[H,T,R] = hough(rotBWed,'Theta', -90:-80);
P = houghpeaks(H,5,'threshold',ceil(0.3*max(H(:))));
x = T(P(:,2)); y = R(P(:,1));
lines = houghlines(rotBWed,T,R,P,'FillGap',20,'MinLength',2);
max_len = 0;
for k = 1:length(lines)
xy = [lines(k).point1; lines(k).point2];
len = norm(lines(k).point1 - lines(k).point2);
if ( len > max_len)
max_len = len;
xy_long = xy;
end
end
% Recorte de la parte inferior de la imagen para evitar posibles mezclas de
% dígitos con fondo.
BW2=rotBW(1:xy_long(4)-5,:);
BW2=1-BW2;
% Se etiquetan los elementos de la imagen.
L = bwlabel(BW2,8);
[L, num] = bwlabel(BW2,8);
% Se extrae el área de los elementos y se elimina el elemento de área mayor
% (el fondo).
CC = bwconncomp(BW2);
S = regionprops(CC,'Area');
A=[S(:).Area];
% Se seleccionan los siguientes 4 elementos de área mayor (los dígitos)
indi=[];
H=0;
for i=1:length(S)
H=H+1;
if H <= 4
[maxi,ind]=max([S.Area]);
if maxi>8000
S(ind).Area=NaN;
H=H-1;
else
indi=[indi,ind];
S(ind).Area=NaN;
end
end
end
% Se extraen la BoundingBox de cada dígito y a partir de ellas los límites
% que contienen a los cuatro.
T = regionprops(CC, 'BoundingBox');
min_x=min([
T(indi(1)).BoundingBox(1),
T(indi(2)).BoundingBox(1),
T(indi(3)).BoundingBox(1),
T(indi(4)).BoundingBox(1)]);
max_x=max([
T(indi(1)).BoundingBox(1)+T(indi(1)).BoundingBox(3),
T(indi(2)).BoundingBox(1)+T(indi(2)).BoundingBox(3),
T(indi(3)).BoundingBox(1)+T(indi(3)).BoundingBox(3),
T(indi(4)).BoundingBox(1)+T(indi(4)).BoundingBox(3)]);
min_y=min([
T(indi(1)).BoundingBox(2),
T(indi(2)).BoundingBox(2),
T(indi(3)).BoundingBox(2),
T(indi(4)).BoundingBox(2)]);
% Se recorta la imagen para quedarse únicamente con la región de los
% dígitos.
[alto, ancho]=size(BW2);
imaux=rotBW(:,min_x:max_x);
imaux=imaux(min_y:alto,:);
[alto2, ancho2]=size(imaux);
imprep=ones(alto2+round(alto2/4),ancho2+round(ancho2/4));
% Se añade un borde blanco alrededor de la imagen para un posterior correcto
% funcionamiento de Tesseract.
filainicio = round(alto2/8);
coluinicio = round(ancho2/8);
imprep(filainicio:filainicio+size(imaux,1)-1,coluinicio:coluinicio+size(imaux,2)-1) = imaux;
imshow(imprep)
end