Tudo bem, aqui está outra solução possível. Eu sei que você trabalha com Python - trabalho com C ++. Vou lhe dar algumas idéias e, se desejar, você poderá implementar esta resposta.
A idéia principal é não usar pré-processamento (pelo menos não no estágio inicial) e, em vez disso, focar em cada caractere de destino, obter algumas propriedades e filtrar cada blob de acordo com essas propriedades.
Estou tentando não usar o pré-processamento porque: 1) Os filtros e os estágios morfológicos podem degradar a qualidade dos blobs e 2) os blobs de destino parecem exibir algumas características que poderíamos explorar, principalmente: proporção e área .
Confira, todos os números e letras parecem mais altos que largos ... além disso, eles parecem variar dentro de um determinado valor da área. Por exemplo, você deseja descartar objetos "muito grandes" ou "muito grandes" .
A ideia é filtrar tudo o que não se enquadra nos valores pré-calculados. Examinei os caracteres (números e letras) e vim com valores mínimos e máximos de área e uma proporção mínima (aqui, a proporção entre altura e largura).
Vamos trabalhar no algoritmo. Comece lendo a imagem e redimensionando-a para metade das dimensões. Sua imagem é muito grande. Converta em escala de cinza e obtenha uma imagem binária via otsu, aqui está no pseudo-có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" );
Legal. Vamos trabalhar com esta imagem. Você precisa examinar todos os blobs brancos e aplicar um "filtro de propriedades" . Estou usando componentes conectados com estatísticas para percorrer cada blob e obter sua área e proporção, em C ++, isso é feito da seguinte maneira:
//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…
};
Agora, aplicaremos o filtro de propriedades. Esta é apenas uma comparação com os limites pré-calculados. Eu usei os seguintes valores:
Minimum Area: 40 Maximum Area:400
MinimumAspectRatio: 1
Dentro do seu for
loop, compare as propriedades atuais do blob com esses valores. Se os testes forem positivos, você "pinta" o blob de preto. Continuando dentro do for
loop:
//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 );
}
Após o loop, construa a imagem 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];
}
}
E ... é praticamente isso. Você filtrou todos os elementos que não são semelhantes ao que você está procurando. Executando o algoritmo, você obtém este resultado:
Além disso, encontrei as caixas delimitadoras dos blobs para visualizar melhor os resultados:
Como você vê, alguns elementos são detectados erroneamente. Você pode refinar o "filtro de propriedades" para identificar melhor os caracteres que está procurando. Uma solução mais profunda, envolvendo um pouco de aprendizado de máquina, requer a construção de um "vetor de recurso ideal", extraindo recursos dos blobs e comparando os dois vetores por meio de uma medida de similaridade. Você também pode aplicar algum pós- processamento para melhorar os resultados ...
Seja como for, cara, seu problema não é trivial nem fácil de ser dimensionado, e só estou lhe dando idéias. Felizmente, você poderá implementar sua solução.