opencv/opencv_contrib

Guided filter might produce wrong results when radius is small

Closed this issue ยท 8 comments

System information (version)
  • OpenCV => 3.1 (with opencv_contrib compiled)
  • Operating System / Platform => Windows 10 64 Bit
  • Compiler => Visual Studio 2013
Detailed description

I have tested OpenCV's implementation of guided filter. And I found that, when the radius is set to a small value, the filtered result is not as expected, compared with the author's MATLAB implementation of guided filter.

Steps to reproduce
  • The C++ code I used to test OpenCV's implementation of guided filter
#include <opencv2\opencv.hpp>
#include <opencv2\ximgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;

int main()
{
    Mat guide = imread("guide.png");
    Mat input = imread("guide.png", CV_LOAD_IMAGE_GRAYSCALE);
        // Whether input and guide is same or not, the problem both exists.
        // In my case,
        // guide.png is a three-channel RGB image
        // and input is supposed to be an one-channel grayscale image to be filterd
    Mat output = Mat(guide.size(), CV_8UC1, Scalar(0));

    ximgproc::guidedFilter(guide, input, output, 3, 1e-6);
    imwrite("output.bmp", output);

    return 0;
}

And the author's MATLAB implementation is here.

I found that when the radius is set to 1, 3, 5, 7, 9, the output is incorrect.

For example, when the radius is set to 7, the OpenCV's implementation of guided filter produced the result below. Only part of the image has the correct values.

output7

With the author's MATLAB code, the result is correct while radius and eps is the same.

matlab7

I have read the author's paper about guided filter and implemented guided filter on my own. Although my implementation is slow and lacks optimization, the result I got is the same as the author's MATLAB code. So I believe there must be something wrong in OpenCV's implementation.

@ludv1x Hope you can take a look at this issue at your convenience.

@clarkzjw Ok, thank you for your feedback ๐Ÿ‘ .
I will review it as soon as possible.

@clarkzjw
This problem appears when radius is small and regulariztion parameter is small also.
When the radius is small then covariance matrix is poorly conditioned (its determinant is zero) since when radius is small we have a few pixel samples.
It means that its invariance contain a NaN or Inf values and results will contain a lot of NaN values.
So, white areas in provided examples is actually NaNs which were clamped to 255 during imwrite().

Regularization parameter (eps) helps to avoid this problem.
But since OpenCV's implementation uses single precision floating point numbers, too small eps cannot achieve such effect (If you set eps to 1e-2 instead of 1e-6 it will work perfectly).

Original author's implementation uses doubles therefore you can use more small eps there.

I make a hotfix of this problem which checks and fix "overflows" (please, not to pay attention to trailing whitespaces).
But I don't assure that described use case (small radius and too small eps) is critical since it is very rare and particularly don't change input image.
What do you think about it?

@ludv1x ๐Ÿ‘ Well done!

Indeed, I also believe the described usage scenario is rare. In my practical usage, I usually choose a more larger radius.

I am just curious that why OpenCV's implementation of guided filter uses single precision floating numbers? For compatibility reasons?

I am just curious that why OpenCV's implementation of guided filter uses single precision floating numbers? For compatibility reasons?

Single precision floating numbers are ~2x faster than double precision ones.

Oh, I see.

And I will try to apply your patch on my local code. If any new usage cases fail, I will let you know. Thank you! ๐Ÿ˜„

@clarkzjw Ok.
FYI Authors of orignal paper use small eps (0.1^2) assuming that images are in [0; 1] range.
So, if you process images in [0; 255] range you should use eps = (0.1 * 255)^2 in such case.

All right, thanks for the information.

@ludv1x can you provide a PR with this patch?