Dobra, oto inne możliwe rozwiązanie. Wiem, że pracujesz z Pythonem - pracuję z C ++. Dam ci kilka pomysłów i mam nadzieję, że jeśli zechcesz, będziesz w stanie zastosować tę odpowiedź.
Główną ideą jest całkowite niestosowanie przetwarzania wstępnego (przynajmniej na początkowym etapie) i skupienie się na każdym znaku docelowym, uzyskanie niektórych właściwości i filtrowanie każdego obiektu blob zgodnie z tymi właściwościami.
Staram się nie używać wstępnego przetwarzania, ponieważ: 1) Filtry i etapy morfologiczne mogą obniżyć jakość obiektów blob oraz 2) Twoje docelowe obiekty blob wydają się wykazywać pewne cechy, które moglibyśmy wykorzystać, głównie: współczynnik kształtu i obszar .
Sprawdź to, wszystkie cyfry i litery wydają się być wyższe niż szersze… ponadto wydają się różnić w obrębie pewnej wartości powierzchni. Na przykład chcesz odrzucić obiekty „za szerokie” lub „za duże” .
Chodzi o to, że będę filtrować wszystko, co nie mieści się w wstępnie obliczonych wartościach. Sprawdziłem znaki (cyfry i litery) i uzyskałem minimalne, maksymalne wartości powierzchni i minimalny współczynnik kształtu (tutaj stosunek wysokości do szerokości).
Pracujmy nad algorytmem. Zacznij od przeczytania obrazu i zmiany jego rozmiaru do połowy wymiarów. Twój obraz jest zdecydowanie za duży. Konwertuj na skalę szarości i uzyskaj obraz binarny przez otsu, oto pseudo-kod:
//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" );
Fajne. Będziemy pracować z tym obrazem. Musisz zbadać każdą białą kroplę i zastosować „filtr właściwości” . Korzystam z połączonych komponentów ze statystykami, aby przeglądać każdą kroplę i uzyskiwać jej powierzchnię i proporcje, w C ++ odbywa się to w następujący sposób:
//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…
};
Teraz zastosujemy filtr właściwości. Jest to tylko porównanie z wstępnie obliczonymi progami. Użyłem następujących wartości:
Minimum Area: 40 Maximum Area:400
MinimumAspectRatio: 1
Wewnątrz for
pętli porównaj bieżące właściwości obiektu blob z tymi wartościami. Jeśli testy są pozytywne, „malujesz” kroplę na czarno. Kontynuując wewnątrz for
pętli:
//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 );
}
Po pętli zbuduj filtrowany obraz:
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];
}
}
I… to wszystko. Przefiltrowano wszystkie elementy, które nie są podobne do tego, czego szukasz. Po uruchomieniu algorytmu otrzymujesz ten wynik:
Dodatkowo znalazłem ramki ograniczające obiektów blob, aby lepiej wizualizować wyniki:
Jak widać, niektóre elementy zostały wykryte. Możesz zawęzić „filtr właściwości”, aby lepiej identyfikować poszukiwane znaki. Głębsze rozwiązanie, wymagające odrobiny uczenia maszynowego, wymaga zbudowania „idealnego wektora cech”, wydobycia cech z obiektów blob i porównania obu wektorów za pomocą miary podobieństwa. Możesz również zastosować postprocessing, aby poprawić wyniki ...
Cokolwiek, człowieku, twój problem nie jest trywialny ani łatwy do skalowania, a ja tylko daję ci pomysły. Mamy nadzieję, że będziesz w stanie wdrożyć swoje rozwiązanie.