Filter image (Blur) only inside mask region (ignore zeros)

I need to apply blur ignoring zero intensity values. I could not find a good solution yet.

What I’m doing now:

  1. binary filter to get a mask
  2. Blur filter original image
  3. 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.
result

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;
}

what makes you think so ?

(also, please show exact filtering code)

The pixels close to the edge of the ROI will be darkened because of the zero value outside the ROI, so you can just brighten them by the same amount. I think this could be done by blurring the mask and dividing the blurred image over the blurred mask.