Muy bien, aquí hay otra posible solución. Sé que trabajas con Python, yo trabajo con C ++. Te daré algunas ideas y espero que, si lo deseas, puedas implementar esta respuesta.
La idea principal es no utilizar el preprocesamiento en absoluto (al menos no en la etapa inicial) y en su lugar centrarse en cada carácter de destino, obtener algunas propiedades y filtrar cada blob de acuerdo con estas propiedades.
Estoy tratando de no usar el preprocesamiento porque: 1) los filtros y las etapas morfológicas podrían degradar la calidad de las manchas y 2) sus manchas objetivo parecen exhibir algunas características que podríamos explotar, principalmente: relación de aspecto y área .
Míralo, los números y las letras parecen ser más altos que anchos ... además, parecen variar dentro de un cierto valor de área. Por ejemplo, desea descartar objetos "demasiado anchos" o "demasiado grandes" .
La idea es que filtre todo lo que no se encuentre dentro de los valores precalculados. Examiné los caracteres (números y letras) y obtuve valores mínimos, máximos de área y una relación de aspecto mínima (aquí, la relación entre la altura y el ancho).
Trabajemos en el algoritmo. Comienza leyendo la imagen y redimensionándola a la mitad de las dimensiones. Tu imagen es demasiado grande. Convierta a escala de grises y obtenga una imagen binaria a través de otsu, aquí hay un pseudocódigo:
//Read input:
inputImage = imread( "diagram.png" );
//Resize Image;
resizeScale = 0.5;
inputResized = imresize( inputImage, resizeScale );
//Convert to grayscale;
inputGray = rgb2gray( inputResized );
//Get binary image via otsu:
binaryImage = imbinarize( inputGray, "Otsu" );
Frio. Trabajaremos con esta imagen. Debe examinar cada burbuja blanca y aplicar un "filtro de propiedades" . Estoy usando componentes conectados con estadísticas para recorrer cada blob y obtener su área y relación de aspecto, en C ++ esto se hace de la siguiente manera:
//Prepare the output matrices:
cv::Mat outputLabels, stats, centroids;
int connectivity = 8;
//Run the binary image through connected components:
int numberofComponents = cv::connectedComponentsWithStats( binaryImage, outputLabels, stats, centroids, connectivity );
//Prepare a vector of colors – color the filtered blobs in black
std::vector<cv::Vec3b> colors(numberofComponents+1);
colors[0] = cv::Vec3b( 0, 0, 0 ); // Element 0 is the background, which remains black.
//loop through the detected blobs:
for( int i = 1; i <= numberofComponents; i++ ) {
//get area:
auto blobArea = stats.at<int>(i, cv::CC_STAT_AREA);
//get height, width and compute aspect ratio:
auto blobWidth = stats.at<int>(i, cv::CC_STAT_WIDTH);
auto blobHeight = stats.at<int>(i, cv::CC_STAT_HEIGHT);
float blobAspectRatio = (float)blobHeight/(float)blobWidth;
//Filter your blobs…
};
Ahora, aplicaremos el filtro de propiedades. Esto es solo una comparación con los umbrales calculados previamente. Usé los siguientes valores:
Minimum Area: 40 Maximum Area:400
MinimumAspectRatio: 1
Dentro de su for
ciclo, compare las propiedades de blob actuales con estos valores. Si las pruebas son positivas, "pintas" la mancha negra. Continuando dentro del for
bucle:
//Filter your blobs…
//Test the current properties against the thresholds:
bool areaTest = (blobArea > maxArea)||(blobArea < minArea);
bool aspectRatioTest = !(blobAspectRatio > minAspectRatio); //notice we are looking for TALL elements!
//Paint the blob black:
if( areaTest || aspectRatioTest ){
//filtered blobs are colored in black:
colors[i] = cv::Vec3b( 0, 0, 0 );
}else{
//unfiltered blobs are colored in white:
colors[i] = cv::Vec3b( 255, 255, 255 );
}
Después del bucle, construya la imagen filtrada:
cv::Mat filteredMat = cv::Mat::zeros( binaryImage.size(), CV_8UC3 );
for( int y = 0; y < filteredMat.rows; y++ ){
for( int x = 0; x < filteredMat.cols; x++ )
{
int label = outputLabels.at<int>(y, x);
filteredMat.at<cv::Vec3b>(y, x) = colors[label];
}
}
Y ... eso es todo. Filtró todos los elementos que no son similares a lo que está buscando. Al ejecutar el algoritmo obtienes este resultado:
Además, he encontrado los cuadros de límite de los blobs para visualizar mejor los resultados:
Como puede ver, algunos elementos son detectados erróneamente. Puede refinar el "filtro de propiedades" para identificar mejor los caracteres que está buscando. Una solución más profunda, que implica un poco de aprendizaje automático, requiere la construcción de un "vector de características ideal", extraer características de los blobs y comparar ambos vectores a través de una medida de similitud. También puede aplicar algo de procesamiento posterior para mejorar los resultados ...
Lo que sea, hombre, tu problema no es trivial ni fácil de escalar, y solo te estoy dando ideas. Con suerte, podrá implementar su solución.