Вот еще одно возможное решение, реализованное на C ++ и использующее k-означает в качестве основного метода сегментации. Идея этой сегментации заключается в том, что k-means (метод кластеризации) будет группировать цвета одинакового значения. Здесь я устанавливаю k-means, чтобы найти кластеры 2 цветов: цвет фона и цвет переднего плана.
Давайте посмотрим на код:
std::string imageName = "C://opencvImages/LSl42.jpg";
cv::Mat testImage = cv::imread( imageName );
//apply Gaussian Blur to smooth out the input:
cv::GaussianBlur( testImage, testImage, cv::Size(3,3), 0, 0 );
Ваше изображение имеет шумный (высокочастотный) фон. Вы можете немного размыть его, чтобы получить более плавный градиент и улучшить сегментацию. Я применил Gaussian Blur со стандартным размером ядра 3 x 3. Проверьте разницу между входным и сглаженным изображением:
Очень круто. Теперь я могу передать это изображение K-means. imageQuantization
является функцией, взятой из здесь , которая реализует сегментацию на основе K-средних. Как я уже упоминал, он может группировать цвета одинакового значения в кластеры. Это очень удобно! Давайте сгруппируем цвета в 2 группы: передний план объект и фон .
int segmentationClusters = 2; //total number of clusters in which the input will be segmented...
int iterations = 5; // k-means iterations
cv::Mat segmentedImage = imageQuantization( testImage, segmentationClusters, iterations );
Результат:
Довольно хорошо, а?
Вы можете применить обнаружение края непосредственно к этому изображению, но я хочу улучшить его, используя немного морфология . Сначала я преобразовываю изображение в градации серого, применяю пороговое значение Outsu, а затем выполняю морфологическое закрытие:
//compute grayscale image of the segmented output:
cv::Mat grayImage;
cv::cvtColor( segmentedImage, grayImage, cv::COLOR_RGB2GRAY );
//get binary image via Otsu:
cv::Mat binImage;
cv::threshold( grayImage, binImage, 0, 255, cv::THRESH_OTSU );
//Perform a morphological closing to lose up holes in the target blob:
cv::Mat SE = cv::getStructuringElement( cv::MORPH_RECT, cv::Size(3, 3) );
cv::morphologyEx( binImage, binImage, cv::MORPH_CLOSE, SE, cv::Point(-1,-1), 10 );
Я использую прямоугольный angular структурирующий элемент размером 3x3 и 10 итераций операции закрытия, это результат:
Затем определите края, используя детектор контуров Кэнни:
cv::Mat testEdges;
//setup lower and upper thresholds for Canny’s edge detection:
float lowerThreshold = 30;
float upperThreshold = 3 * lowerThreshold;
cv::Canny( binImage, testEdges, lowerThreshold, upperThreshold );
Наконец, получите контур капли:
std::vector<std::vector<cv::Point> > contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours( testEdges, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0) );
for( int i = 0; i< contours.size(); i++ )
{
cv::Scalar color = cv::Scalar( 0,255,0 );
cv::drawContours( resizedImage, contours, i, color, 2, 8, hierarchy, 0, cv::Point() );
}
Это конечный результат, который я получаю:
Хотите улучшить результат, расширив контур? Попробуйте расширить двоичное изображение за несколько итераций, прежде чем передать его в обнаружение края Кэнни. Это тест, расширение изображения в 5 раз: