2

I'm trying to implement a function that gives me the Gaussian kernel given a certain standard deviation and dimensions. The output of the function seems correct, but when I apply it using the cv::filter2D function the result I obtain is quite strange. The values saturate to maximum, giving a white color.

The implementation of the function that generates the kernel is the following:

    cv::Mat gaussKernel(int kernel_size, float stdDev)
    {
        // For now, only odd kernel_size values
        std::default_random_engine generator;
        std::normal_distribution<float> distribution(0, stdDev);
        float sum_term {0};
        cv::Mat kernel = cv::Mat::zeros(kernel_size, kernel_size, CV_32F);


        // Create kernel
        for (int i = -kernel_size/2; i <= kernel_size/2; i++)
        {
            for (int j = -kernel_size/2; j <= kernel_size/2; j++)
            {
                kernel.at<float>(i + kernel_size/2,j + kernel_size/2) = std::exp(-(pow(i,2) + pow(j,2))/(2*pow(stdDev,2)));
                sum_term += kernel.at<float>(i + kernel_size/2,j + kernel_size/2);
            }
        }

        kernel /= sum_term;

        std::cout << "The kernel is:   " << std::endl;
        for (int i = 0; i < kernel.rows; i++)
        {
            std::cout << "    ";
            for (int j = 0; j < kernel.cols; j++)
            {
                std::cout << kernel.at<float>(i,j) << ", ";
            }
            std::cout << std::endl;
        }

        return kernel;
    }

All is being done with greyscale pictures. I expect the error to be related to the codification used in the kernel matrix, but I have tested some stuff and received no result.

--Edit: Value of stdDev = 1; Value of kernel_size = 3;

Image: Link to image

Part of the code related to reading or modifications of the image:

std::string dir {"../resources/ORings/ORing01.jpg"};
cv::Mat original, customKernel;
original = cv::imread(dir);

cv::Mat noisy = original.clone();
cv::Mat clean_1 = original.clone();

customKernel = gauss::gaussKernel(kernel_size, 1);
noisy = gauss::addGaussianNoise(noisy, SNR);
cv::filter2D(clean_1, clean_1, CV_32F, customKernel, cv::Point(-1,-1), 0, cv::BORDER_DEFAULT);

  • The problem is not reproducible. Please post the values of `kernel_size` and `stdDev`. Please post a sample input image, and the code you are using for reading the image and applying the filter. – Rotem Sep 26 '21 at 20:23
  • @Rotem I just added the required information. – pdaranda661 Sep 26 '21 at 20:40

1 Answers1

2

The cause of the problem is setting the ddepth argument of cv::filter2D to CV_32F.

Setting the argument value to -1 solves the issue:

cv::filter2D(clean_1, clean_1, -1, customKernel, cv::Point(-1,-1), 0, cv::BORDER_DEFAULT);

Setting ddepth argument to CV_32F causes the output type of cv::filter2D to be CV_32F (pixels of type float).

The values of the output are in range [0, 255] (same range as the input).
OpenCV convention for pixels of type CV_32F (float) is that black is 0 and white is 1. Values above 1.0 are also white.

There are 3 relevant solutions I can think of:

  • Keeping the type of the output CV_8U (same as the input), by setting ddepth to -1.
  • Dividing the kernel by 255: customKernel /= 255.0f;.
  • Dividing the output image by 255: clean_1 /= 255.0f;.
Rotem
  • 30,366
  • 4
  • 32
  • 65
  • 1
    Indeed. And a 4th solution: use a better image display utility that doesn’t saturate images unexpectedly. ;) – Cris Luengo Sep 26 '21 at 23:00
  • @CrisLuengo You are right, saturating pixels above 1 is the behavior of `cv::imshow`. I keep forgetting it's not MATLAB :) – Rotem Sep 27 '21 at 08:53