使用大半径/标准差对图像进行高斯模糊时的奇怪行为
Strange behavior when gaussian blurring an image using big radius / standard deviation
见编辑
我尝试在 MATLAB 中自己实现高斯模糊算法,而不是使用内置算法,以便详细了解它。
我找到了 interesting implementation and someone has already asked how to code that kind of algorithm。所以这不是生意。
此外,我使用以下公式计算给定半径的标准偏差,就像 GIMP 所做的那样:
stdDeviation = sqrt(-(radius^2) / (2*log10(1 / 255)));
我的算法适用于较小的半径值(例如 3、5、7),没有任何问题(您至少看不出差异)。如果我尝试模糊半径为 21 的图像,输出如下:
与 GIMP/MATLAB 的 imgaussfilt(A,sigma)
输出相比:
显然,这些算法计算的输出图像不同(或至少相似)。 GIMP / MATLAB imgaussfilt(A,sigma)
除此之外还有什么功能?
图像的边框可以忽略。我知道那个问题。但是我不明白输出图像中 'strange pixel stripes' 的来源。
为了完整起见,这里是源代码:
function y = gaussianBlurSepTest(inputImage)
% radius in pixel; RADIUS MUST BE ODD!
radius = 21;
% approximate value for standard deviation
stdDeviation = sqrt(-(radius^2) / (2*log10(1 / 255)));
ind = -floor(radius/2):floor(radius/2);
[X, Y] = meshgrid(ind, ind);
h = exp(-(X.^2 + Y.^2) / (2*stdDeviation*stdDeviation));
h = h / sum(h(:));
redChannel = inputImage(:,:,1);
greenChannel = inputImage(:,:,2);
blueChannel = inputImage(:,:,3);
redBlurred = conv2(redChannel, h);
greenBlurred = conv2(greenChannel, h);
blueBlurred = conv2(blueChannel, h);
y = cat(3, uint8(redBlurred), uint8(greenBlurred), uint8(blueBlurred));
编辑:
为了完整性和帮助他人:我应用了 erfan 的修改。结果现在好多了,但是仍然可以看到与 gimp 的计算有明显的不同。 GIMP 的结果看起来 'smoother'.
实现的算法:
function y = gaussianBlurSepTest(inputImage)
radius = 21;
stdDeviation = sqrt(-(radius^2) / (2*log10(1 / 255)));
ind = -floor(radius/2):floor(radius/2);
[X, Y] = meshgrid(ind, ind);
[~, R] = cart2pol(X, Y); % Here R is defined
h = exp(-R.^2 / (2*stdDeviation*stdDeviation));
h = h / sum(h(:));
h(R > radius/2) = 0;
h = h / sum(h(:));
redChannel = inputImage(:,:,1);
greenChannel = inputImage(:,:,2);
blueChannel = inputImage(:,:,3);
redBlurred = conv2(redChannel, h);
greenBlurred = conv2(greenChannel, h);
blueBlurred = conv2(blueChannel, h);
y = cat(3, uint8(redBlurred), uint8(greenBlurred), uint8(blueBlurred));
结果:
GIMP 的结果:
就完整回答问题和帮助其他有相同问题的人而言,我认为询问差异的来源可能会有用。
谢谢!
我认为罪魁祸首是 conv2
指令,这是因为您的图像类型是 uint8
或 uint16
,当您过滤图像时,您应该使用 float
或 double
类型。
您的滤波器大小如此之大,以至于在像素上应用系数 window 的结果超过了 8 位或 16 位整数范围(255 或 65535)。我建议尝试在应用滤镜之前将您的图像投射到 double
,并在应用滤镜之后将其投射回 uint8
,如下所示:
Output=gaussianBlurSepTest(double(inputImage));
Output=uint8(Output);
祝你好运。
h
是出现横竖条纹的原因。尽管您已定义 h
具有 angular 对称性,但如下图所示,它的边界打破了这种对称性:
为了使事情正确,您可以在正确的半径上截断 h
:
我将修改应用于您的函数,现在它应该会提供更好的结果:
function y = gaussianBlurSepTest(inputImage, radius)
stdDeviation = sqrt(-(radius^2) / (2*log10(1 / 255)));
ind = -floor(radius/2):floor(radius/2);
[X, Y] = meshgrid(ind, ind);
[~, R] = cart2pol(X, Y); % Here R is defined
h = exp(-R.^2 / (2*stdDeviation*stdDeviation));
h = h / sum(h(:));
h(R > radius/2) = 0; % And here h is truncated. The rest is the same.
这是我的测试。我的形象:
在你的 gaussianBlurSepTest
(半径 = 35)之后:
修改后的功能:
注意: 输出会变暗一点。如果这是一个问题,您可以重新规范化 stdDeviation
或扩大您的网格。
见编辑
我尝试在 MATLAB 中自己实现高斯模糊算法,而不是使用内置算法,以便详细了解它。
我找到了 interesting implementation and someone has already asked how to code that kind of algorithm。所以这不是生意。
此外,我使用以下公式计算给定半径的标准偏差,就像 GIMP 所做的那样:
stdDeviation = sqrt(-(radius^2) / (2*log10(1 / 255)));
我的算法适用于较小的半径值(例如 3、5、7),没有任何问题(您至少看不出差异)。如果我尝试模糊半径为 21 的图像,输出如下:
与 GIMP/MATLAB 的 imgaussfilt(A,sigma)
输出相比:
显然,这些算法计算的输出图像不同(或至少相似)。 GIMP / MATLAB imgaussfilt(A,sigma)
除此之外还有什么功能?
图像的边框可以忽略。我知道那个问题。但是我不明白输出图像中 'strange pixel stripes' 的来源。
为了完整起见,这里是源代码:
function y = gaussianBlurSepTest(inputImage)
% radius in pixel; RADIUS MUST BE ODD!
radius = 21;
% approximate value for standard deviation
stdDeviation = sqrt(-(radius^2) / (2*log10(1 / 255)));
ind = -floor(radius/2):floor(radius/2);
[X, Y] = meshgrid(ind, ind);
h = exp(-(X.^2 + Y.^2) / (2*stdDeviation*stdDeviation));
h = h / sum(h(:));
redChannel = inputImage(:,:,1);
greenChannel = inputImage(:,:,2);
blueChannel = inputImage(:,:,3);
redBlurred = conv2(redChannel, h);
greenBlurred = conv2(greenChannel, h);
blueBlurred = conv2(blueChannel, h);
y = cat(3, uint8(redBlurred), uint8(greenBlurred), uint8(blueBlurred));
编辑:
为了完整性和帮助他人:我应用了 erfan 的修改。结果现在好多了,但是仍然可以看到与 gimp 的计算有明显的不同。 GIMP 的结果看起来 'smoother'.
实现的算法:
function y = gaussianBlurSepTest(inputImage)
radius = 21;
stdDeviation = sqrt(-(radius^2) / (2*log10(1 / 255)));
ind = -floor(radius/2):floor(radius/2);
[X, Y] = meshgrid(ind, ind);
[~, R] = cart2pol(X, Y); % Here R is defined
h = exp(-R.^2 / (2*stdDeviation*stdDeviation));
h = h / sum(h(:));
h(R > radius/2) = 0;
h = h / sum(h(:));
redChannel = inputImage(:,:,1);
greenChannel = inputImage(:,:,2);
blueChannel = inputImage(:,:,3);
redBlurred = conv2(redChannel, h);
greenBlurred = conv2(greenChannel, h);
blueBlurred = conv2(blueChannel, h);
y = cat(3, uint8(redBlurred), uint8(greenBlurred), uint8(blueBlurred));
结果:
GIMP 的结果:
就完整回答问题和帮助其他有相同问题的人而言,我认为询问差异的来源可能会有用。
谢谢!
我认为罪魁祸首是 conv2
指令,这是因为您的图像类型是 uint8
或 uint16
,当您过滤图像时,您应该使用 float
或 double
类型。
您的滤波器大小如此之大,以至于在像素上应用系数 window 的结果超过了 8 位或 16 位整数范围(255 或 65535)。我建议尝试在应用滤镜之前将您的图像投射到 double
,并在应用滤镜之后将其投射回 uint8
,如下所示:
Output=gaussianBlurSepTest(double(inputImage));
Output=uint8(Output);
祝你好运。
h
是出现横竖条纹的原因。尽管您已定义 h
具有 angular 对称性,但如下图所示,它的边界打破了这种对称性:
为了使事情正确,您可以在正确的半径上截断 h
:
我将修改应用于您的函数,现在它应该会提供更好的结果:
function y = gaussianBlurSepTest(inputImage, radius)
stdDeviation = sqrt(-(radius^2) / (2*log10(1 / 255)));
ind = -floor(radius/2):floor(radius/2);
[X, Y] = meshgrid(ind, ind);
[~, R] = cart2pol(X, Y); % Here R is defined
h = exp(-R.^2 / (2*stdDeviation*stdDeviation));
h = h / sum(h(:));
h(R > radius/2) = 0; % And here h is truncated. The rest is the same.
这是我的测试。我的形象:
在你的 gaussianBlurSepTest
(半径 = 35)之后:
修改后的功能:
注意: 输出会变暗一点。如果这是一个问题,您可以重新规范化 stdDeviation
或扩大您的网格。