Laplacian of Gaussian (LoG) blob detector c++
Пишу blob detector на основе LoG. Использую библиотеку openCV. Задача детектировать светлые круги на темном фоне. Тип картинки CV_8UC1
Исходная картинка (зашумленная специально)
Все началось с того, что почему то минимальный отклик давал блоб большего радиуса, чем сам искомый круг. Дошёл до того, что ошибка заключается в согласовании размеров ядер cv::GaussianBlur
и cv::Laplacian
.
Например, первая картинка со всеми найденными блобами (отсортированы чтобы убрать ложные срабатывания), вторая картинка - это оставленные блобы с минимальным откликом. Для нахождения блобов разных размеров я использую пирамиду масштабов cv::pyrDown и нормализацю отклика LoG sigma^2
Из документации не понял какой размер нужно указывать в параметрах к функциям - размер крыла или реальный размер ядра. Например, если я укажу в параметрах ksize = 5. Размер гауссовского ядра будет равен 5? А размер ядра Лапласа будет равен 11 - по формуле 2*n + 1?
Ответ пытался найти с помощью получения ядер. То есть использовал функции cv::getGaussianKernel и cv::getDerivKernels. Но по каким-то причинам результат применения cv::Laplacian к полученному ядру гаусса, даёт немного другой результат. А именно в случае применения cv::GaussianBlur и cv::Laplacian минимальный отклик я получил в точке [155, 710] со значением -144004136960.000000, а в случае применения cv::getGaussianKernel и cv::Laplacian минимальный отклик я получил в точке [155, 710] со значением -9097119744.000000. Размеры ядер использовал одинаковые. Ядро полученное cv::getGaussianKernel я умножил на транспонированное ядро, так как функция cv::getGaussianKernel даёт одномерное ядро.
Не понимаю причины такого поведения.
Первая картинка полученная классическим методом, вторая полученная с помощью применения cv::Laplacian к ядру гаусса cv::getGaussianKernel.
Так выглядит LoG при нахождении ядра гаусса cv::getGaussianKernel
Далее я пытался получить ядро Лапласа. Для этого использовал
cv::getDerivKernels(sobelKernelX, sobelKernelY, 2, 2, kernelsizes);
cv::Mat sobelKernel = sobelKernelX * sobelKernelX.t() + sobelKernelY * sobelKernelY.t();
Но получал совсем ужасный и неправильный результат
//используемые значения
double sigmakz = 3.2;
int kernelsizes = static_cast<int>(6*sigmakz);
//классический вариант
image.convertTo(temprrrr_reverse, CV_32F);
cv::GaussianBlur(temprrrr_reverse, tryinguff, cv::Size(kernelsizes, kernelsizes), sigmakz);
cv::Laplacian(tryinguff, outtt, CV_32F, kernelsizes);
//вариант с получением ядра гаусса
cv::Mat gausKernel = cv::getGaussianKernel(kernelsizes, sigmakz, CV_32F);
cv::Mat temprrrr = gausKernel * gausKernel.t();
cv::Mat smth;
cv::Laplacian(temprrrr, smth, CV_32F, kernelsizes);
cv::Mat hrrr;
cv::filter2D(image, hrrr, CV_32F, smth);
//вариант с получением ядра второй производной
cv::Mat sobelKernelX;
cv::Mat sobelKernelY;
cv::getDerivKernels(sobelKernelX, sobelKernelY, 2, 2, kernelsizes);
cv::Mat sobelKernel = sobelKernelX * sobelKernelX.t() + sobelKernelY * sobelKernelY.t();
cv::Mat tryinguff;
//temprr = gausKernel * gausKernel.t();
cv::filter2D(temprrrr, tryinguff, CV_32F, sobelKernel);
cv::Mat outtt;
cv::filter2D(image, outtt, CV_32F, tryinguff);
Главный вопрос как правильно с технической точки зрения согласовать размеры ядер? Какое значения указывать в параметрах ksize для Laplacian и Gaussian?