I need to apply blur ignoring zero intensity values. I could not find a good solution yet.
What I’m doing now:
- binary filter to get a mask
- Blur filter original image
- Copy filtered image using the mask.
At the ROI edges it’s noticeable that the blur uses the zero values.
An easy way to test it is using a full white complex shape over a black background (0) image, the borders of the ROI after filtering should still be white. If not, the filter is using data (zeros) outside of the ROI.
I found this: Blurred mask on image - OpenCV Q&A Forum
But it is not good enough. The borders becomes darker, so it still uses data from outside of the mask.
Borders flags seems to work only for the image rectangular region, not zero values.
White portion makes it clear that the borders zeros values are influencing the result. It should be only white.
Some code sample:
// https://answers.opencv.org/question/105994/blurred-mask-on-image/
bool MaskedFilter2(Mat& img, Mat& mask, const double sigmaX, const double sigmaY, double sigmaBorder)
{
if (img.empty()) { return false; }
Mat maskFloat(mask.size(), mask.type());
mask.convertTo(maskFloat, CV_32FC1, 1.0 / 255.0);
if (img.type() != CV_32FC1)
img.convertTo(img, CV_32FC1, 1.0 / 255.0);
Mat filtered;
GaussianBlur(img, filtered, Size(0, 0), sigmaX, sigmaY);
GaussianBlur(maskFloat, maskFloat, Size(0, 0), sigmaBorder);
Mat M1, M2, M3;
subtract(Scalar::all(1.0), maskFloat, M1);
multiply(M1, img, M2);
multiply(maskFloat, filtered, M3);
Mat destDummy(img.size(), img.type());
add(M2, M3, destDummy);
// Apply mask
destDummy.copyTo(img, mask);
return true;
}
int main()
{
// Disable missing dynamically loaded DLL's
cv::utils::logging::setLogLevel(cv::utils::logging::LogLevel::LOG_LEVEL_SILENT);
// Load test image
auto path = R"(D:\Dev\opencv\sources\samples\data\squirrel_cls.jpg)";
Mat img1Orig = imread(path, IMREAD_GRAYSCALE);
// To make sure zeros are not being used. Should have constant color.
rectangle(img1Orig, Rect(Point(0, img1Orig.rows / 2), Size(img1Orig.cols, img1Orig.rows / 2)), 255, FILLED);
Mat originalFloat;
img1Orig.convertTo(originalFloat, CV_32FC1, 1.0 / 255.0);
// Create mask (circle)
const int radius = img1Orig.cols / 3;
Point center(img1Orig.cols / 2, img1Orig.rows / 2);
Mat img2In(img1Orig.size(), img1Orig.type());
Mat mask;
mask.create(img1Orig.size(), CV_8UC1);
mask.setTo(0);
circle(mask, center, radius, 255, FILLED);
// Remove outside mask area
img2In.setTo(0);
img1Orig.copyTo(img2In, mask);
const double sigmaX = 2;
const double sigmaY = 2;
Mat result;
img2In.copyTo(result);
MaskedFilter2(result, mask, sigmaX, sigmaY, sigmaX);
waitKey();
return 0;
}