Хорошо, вот еще одно возможное решение. Я знаю, что вы работаете с Python - я работаю с C ++. Я дам вам несколько идей и, надеюсь, если вы захотите, вы сможете реализовать этот ответ.
Основная идея заключается в том, чтобы вообще не использовать предварительную обработку (в по крайней мере, не на начальном этапе), и вместо этого сосредоточьтесь на каждом целевом символе, получите несколько свойств и фильтра каждого шарика в соответствии с этими свойствами.
Я пытаюсь не использовать предварительную обработку, потому что: 1) фильтры и морфологические стадии могут ухудшить качество сгустков и 2) ваши целевые сгустки обладают некоторыми характеристиками, которые мы могли бы использовать, в основном: форматное соотношение и площадь .
Проверьте это, все цифры и буквы кажутся выше, чем шире ... кроме того, они кажутся разными в пределах определенного значения площади. Например, вы хотите отбросить объекты "слишком широкий" или "слишком большой" .
Идея состоит в том, что я буду фильтровать все, что не попадает в предварительно рассчитанные значения. Я проверил символы (цифры и буквы) и пришел с минимальными, максимальными значениями площади и минимальным соотношением сторон (здесь, соотношение между высотой и шириной).
Давайте работать над алгоритмом. Начните с чтения изображения и изменения его размера до половины размеров. Ваше изображение слишком велико. Преобразовать в оттенки серого и получить двоичное изображение через отсу, вот в псевдокоде:
//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" );
Круто. Мы будем работать с этим изображением. Вам нужно изучить каждый белый шарик и применить «фильтр свойств» . Я использую подключенных компонентов со статистикой к l oop через каждый шарик и получаю его площадь и соотношение сторон, в C ++ это делается следующим образом:
//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…
};
Теперь мы будет применять фильтр свойств. Это всего лишь сравнение с заранее рассчитанными порогами. Я использовал следующие значения:
Minimum Area: 40 Maximum Area:400
MinimumAspectRatio: 1
Внутри вашего for
l oop сравните текущие свойства BLOB-объектов с этими значениями. Если тесты положительные, вы «рисуете» черную каплю. Продолжая в for
l oop:
//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 );
}
После l oop, создайте отфильтрованное изображение:
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];
}
}
И… это в значительной степени все. Вы отфильтровали все элементы, которые не похожи на то, что вы ищете. Запустив алгоритм, вы получите следующий результат:
Я также нашел Bounding Boxs для BLOB-объектов, чтобы лучше визуализировать результаты:
Как видите, некоторые элементы обнаружены с ошибками. Вы можете уточнить «фильтр свойств», чтобы лучше идентифицировать символы, которые вы ищете. Более глубокое решение, включающее немного машинного обучения, требует построения «идеального вектора признаков», извлечения признаков из BLOB-объектов и сравнения обоих векторов с помощью меры сходства. Вы также можете применить некоторую post -обработку для улучшения результатов ...
Как бы то ни было, мужик, твоя проблема не тривиальна и не легко масштабируема, и я просто даю тебе идеи. Надеюсь, вы сможете реализовать свое решение.