matlab image-processing ocr edge-detection

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)