matlab - reconstrucción de personajes y relleno para OCR
image-processing edge-detection (2)
Puede aplicar primero un filtro máximo (asigne a cada píxel en una nueva imagen el valor máximo de una vecindad alrededor del mismo píxel en la imagen original), luego un filtro mínimo (asigne un mínimo de vecindad en la imagen máxima). Especialmente si da forma al vecindario un poco más ancho que alto (por ejemplo, 2 o 3 píxeles a la derecha / izquierda, 1 píxel arriba / abajo), debería poder obtener algunos de sus caracteres (su imagen parece mostrar principalmente huecos en la dirección horizontal).
El tamaño y la forma óptimos del vecindario dependen de su problema específico, por lo que tendrá que experimentar algunos. Es posible que experimente pegar caracteres juntos mediante esta operación; posiblemente tenga que detectar los blobs y dividirlos si son demasiado anchos en comparación con los otros blobs.
editar: Además, la configuración de binarización es absolutamente clave. Pruebe varios algoritmos de binarización diferentes (Otsu, Sauvola, ...) para ver cuál (y qué parámetros) funciona mejor para usted.
Estoy trabajando con reconocimiento de texto en llantas. Para usar un OCR, primero debo obtener un mapa binario claro.
He procesado imágenes y el texto aparece con bordes rotos y descontinuados. He intentado la erosión / dilatación estándar con discos circulares y elementos de línea en MATLAB, pero realmente no ayuda.
Pr1- ¿Alguna idea sobre cómo reconstruir estos personajes y llenar el espacio entre los trazos de los personajes?
Pr2- Las imágenes de arriba son de mayor resolución y están bien iluminadas. Sin embargo, si la iluminación es pobre y la resolución es relativamente baja como en la imagen a continuación, ¿cuáles serían las opciones viables para el procesamiento?
Soluciones probadas:
S1: Este es el resultado de la aplicación del filtro mediano a la imagen procesada compartida por Spektre. Para eliminar el ruido, apliqué un filtro mediano (5x5) y posteriormente la dilatación de la imagen con un elemento lineal (5,11). Incluso ahora, el OCR (Matlab 2014b) solo puede reconocer algunos de los personajes
De todos modos, muchas gracias por las sugerencias hasta ahora. Todavía esperaré para ver si alguien puede sugerir algo diferente tal vez pensando fuera de la caja :).
Resultados de la implementación de Matlab de los pasos del código de Spektre a continuación (sin dilatación del trazo (normalización con esquinas en orden de 1,2,3,4:
y con umbral tr0 = 400 y tr1 = 180 y orden de esquina para normalización 1,3,2,4
Atentamente
Wajahat
He jugado un poco con tu aportación
La normalización de la iluminación + la normalización del rango dinámico ayuda un poco a obtener resultados mucho mejores, pero aún lejos del necesario. Me gustaría probar el afilado de derivaciones parciales para aumentar las letras del fondo y eliminar pequeñas protuberancias antes de integrar y volver a colorear para enmascarar la imagen cuando tenga tiempo (no estoy seguro de cuándo tal vez mañana) Editaré esto (y comentaré / notificaré tú)
iluminación normalizada
Calcular la intensidad promedio de las esquinas y reescalar bilinealmente las intensidades para que coincida con el color promedio
detección de bordes
derivación parcial de intensidad
i
por
x
e
y
...
-
i=|i(x,y)/dx|+|i(x,y)/dy|
y luego pisoteado por
treshold=13
[notas]
Para eliminar la mayoría del ruido, apliqué un filtrado suave antes de la detección de bordes
[edit1] después de un análisis encontré que su imagen tiene bordes pobres para mejorar la integración
Aquí ejemplo de gráfico de intensidad después de la primera derivación por x en la línea media de la imagen
Como puede ver, las áreas negras están bien, pero las blancas no son casi reconocibles por el ruido de fondo. Entonces, su única esperanza es utilizar el filtrado mínimo máximo como sugirió la respuesta de @Daniel y tomar más peso en las regiones de borde negro (el blanco no es confiable)
min max filter enfatiza las regiones negra (máscara azul) y blanca (máscara roja). Si las áreas de la cabina fueran confiables, simplemente llene el espacio entre ellas, pero esa no es una opción en su caso, sino que agrandaría las áreas (ponderadas más en la máscara azul) y OCR el resultado con OCR personalizado para dicha entrada de 3 colores.
- Puede hacer su propio OCR personalizado para esto, ver OCR y similitud de caracteres
También puede tomar 2 imágenes con diferentes posiciones de luz y cámara fija y combinarlas para cubrir el área negra reconocible desde todos los lados
[edit2] Código fuente de C ++ para el último método
//---------------------------------------------------------------------------
typedef union { int dd; short int dw[2]; byte db[4]; } color;
picture pic0,pic1,pic2; // pic0 source image,pic1 normalized+min/max,pic2 enlarge filter
//---------------------------------------------------------------------------
void filter()
{
int sz=16; // [pixels] square size for corner avg color computation (c00..c11)
int fs0=5; // blue [pixels] font thickness
int fs1=2; // red [pixels] font thickness
int tr0=320; // blue min treshold
int tr1=125; // red max treshold
int x,y,c,cavg,cmin,cmax;
pic1=pic0; // copy source image
pic1.rgb2i(); // convert to grayscale intensity
for (x=0;x<5;x++) pic1.ui_smooth();
cavg=pic1.ui_normalize();
// min max filter
cmin=pic1.p[0][0].dd; cmax=cmin;
for (y=0;y<pic1.ys;y++)
for (x=0;x<pic1.xs;x++)
{
c=pic1.p[y][x].dd;
if (cmin>c) cmin=c;
if (cmax<c) cmax=c;
}
// treshold min/max
for (y=0;y<pic1.ys;y++)
for (x=0;x<pic1.xs;x++)
{
c=pic1.p[y][x].dd;
if (cmax-c<tr1) c=0x00FF0000; // red
else if (c-cmin<tr0) c=0x000000FF; // blue
else c=0x00000000; // black
pic1.p[y][x].dd=c;
}
pic1.rgb_smooth(); // remove single dots
// recolor image
pic2=pic1; pic2.clear(0);
pic2.bmp->Canvas->Pen ->Color=clWhite;
pic2.bmp->Canvas->Brush->Color=clWhite;
for (y=0;y<pic1.ys;y++)
for (x=0;x<pic1.xs;x++)
{
c=pic1.p[y][x].dd;
if (c==0x00FF0000)
{
pic2.bmp->Canvas->Pen ->Color=clRed;
pic2.bmp->Canvas->Brush->Color=clRed;
pic2.bmp->Canvas->Ellipse(x-fs1,y-fs1,x+fs1,y+fs1); // red
}
if (c==0x000000FF)
{
pic2.bmp->Canvas->Pen ->Color=clBlue;
pic2.bmp->Canvas->Brush->Color=clBlue;
pic2.bmp->Canvas->Ellipse(x-fs0,y-fs0,x+fs0,y+fs0); // blue
}
}
}
//---------------------------------------------------------------------------
int picture::ui_normalize(int sz=32)
{
if (xs<sz) return 0;
if (ys<sz) return 0;
int x,y,c,c0,c1,c00,c01,c10,c11,cavg;
// compute average intensity in corners
for (c00=0,y= 0;y< sz;y++) for (x= 0;x< sz;x++) c00+=p[y][x].dd; c00/=sz*sz;
for (c01=0,y= 0;y< sz;y++) for (x=xs-sz;x<xs;x++) c01+=p[y][x].dd; c01/=sz*sz;
for (c10=0,y=ys-sz;y<ys;y++) for (x= 0;x< sz;x++) c10+=p[y][x].dd; c10/=sz*sz;
for (c11=0,y=ys-sz;y<ys;y++) for (x=xs-sz;x<xs;x++) c11+=p[y][x].dd; c11/=sz*sz;
cavg=(c00+c01+c10+c11)/4;
// normalize lighting conditions
for (y=0;y<ys;y++)
for (x=0;x<xs;x++)
{
// avg color = bilinear interpolation of corners colors
c0=c00+(((c01-c00)*x)/xs);
c1=c10+(((c11-c10)*x)/xs);
c =c0 +(((c1 -c0 )*y)/ys);
// scale to avg color
if (c) p[y][x].dd=(p[y][x].dd*cavg)/c;
}
// compute min max intensities
for (c0=0,c1=0,y=0;y<ys;y++)
for (x=0;x<xs;x++)
{
c=p[y][x].dd;
if (c0>c) c0=c;
if (c1<c) c1=c;
}
// maximize dynamic range <0,765>
for (y=0;y<ys;y++)
for (x=0;x<xs;x++)
c=((p[y][x].dd-c0)*765)/(c1-c0);
return cavg;
}
//---------------------------------------------------------------------------
void picture::rgb_smooth()
{
color *q0,*q1;
int x,y,i;
color c0,c1,c2;
if ((xs<2)||(ys<2)) return;
for (y=0;y<ys-1;y++)
{
q0=p[y ];
q1=p[y+1];
for (x=0;x<xs-1;x++)
{
c0=q0[x];
c1=q0[x+1];
c2=q1[x];
for (i=0;i<4;i++) q0[x].db[i]=WORD((WORD(c0.db[i])+WORD(c0.db[i])+WORD(c1.db[i])+WORD(c2.db[i]))>>2);
}
}
}
//---------------------------------------------------------------------------
Utilizo mi propia clase de imagen para imágenes, por lo que algunos miembros son:
-
xs,ys
tamaño de la imagen en píxeles -
p[y][x].dd
es un píxel en la posición(x,y)
como tipo entero de 32 bits -
clear(color)
: borra toda la imagen -
resize(xs,ys)
: redimensiona la imagen a una nueva resolución -
bmp
- Mapa de bits GDI encapsulado VCL con acceso a Canvas
Agregué la fuente solo para 2 funciones miembro relevantes (no es necesario copiar toda la clase aquí)
[edit3] imagen LQ
La mejor configuración que encontré (el código es el mismo):
int sz=32; // [pixels] square size for corner avg color computation (c00..c11)
int fs0=2; // blue [pixels] font thickness
int fs1=2; // red [pixels] font thickness
int tr0=52; // blue min treshold
int tr1=0; // red max treshold
Debido a las condiciones de iluminación, el área roja no se puede usar (apagada)