问题重现 MATLAB 的 imfilter 函数
Issue reproducing MATLAB's imfilter function
我想了解 MATLAB 的 imfilter
函数是如何工作的。
im = imread("cameraman.tif");
% Kernel for sharpening the image
kernel = [
0 -1 0;
-1 5 -1;
0 -1 0];
im2 = zeros(size(im));
for y = 1 : size(im,1) - 3
for x = 1 : size(im,2) - 3
sum = 0;
for ky = 1:3
for kx = 1:3
xx = x + kx - 1;
yy = y + ky - 1;
sum = sum + im(yy,xx)*kernel(ky,kx);
end
end
im2(y,x) = sum;
end
end
% Map im2 to 0 - 255
im2 = im2 - min(im2(:));
im2 = im2 / max(im2(:)) * 255;
im2 = uint8(im2);
subplot(131), imshow(im), title('Original Image')
subplot(132), imshow(imfilter(im,kernel)), title('Matlab imfilter')
subplot(133), imshow(im2), title('My filter')
边界上的差异不是我关心的问题,但我的结果(右侧的子图)明显不同于 MATLAB 生成的结果(中间的子图),尽管使用了相同的内核。
请问哪里有偏差?据我所知,内核补丁将按元素应用于图像并在结果上求和。有人能让我知道我错过了什么吗?谢谢
你的错误在这一行:
sum = sum + im(yy,xx)*kernel(ky,kx);
这并不明显,但是 MATLAB 在对不同数据类型进行算术运算时做出了一个奇怪的选择。在 MATLAB 中,默认情况下所有数值数组(所有值)都是双精度数。 sum
在初始化时是双* (sum = 0
),kernel
和 im2
也是。但是 im
是一个 8 位无符号整数数组。所以 im(yy,xx)*kernel(ky,kx)
将 uint8 与 double 相乘。这是在一个操作中组合不同数据类型的奇怪情况。
对整数数组进行算术运算时,另一个操作数必须是同一类型,除非它是双精度标量(1x1 双精度数组)。在这种情况下,标量值被转换为整数类型,然后应用该操作。此外,整数运算是饱和的,这意味着整数范围之外的任何结果都被限制在该范围内(没有其他语言中的溢出)。
所以 im(yy,xx)*kernel(ky,kx)
结果是一个 uint8。接下来,sum + <uint8 result>
也是一个uint8值,赋值给sum
。现在 sum
是 uint8!
要解决此问题,请执行
sum = sum + double(im(yy,xx)) * kernel(ky,kx);
* 另请注意,所有内容都是一个数组。 0
是一个 1x1 数组。
不请自来的建议:
你不应该缩放im2
图像,你应该直接将它投射到uint8
。 MATLAB 将为您将值限制在 [0,255] 范围内。缩放会导致对比度下降。
不要使用 sum
作为变量名。 sum
是一个 built-in 函数,您可以使用此变量隐藏(使其不可用)。在 运行 你的代码之后,你不能再做 sum(im(:))
,例如。
内部两个循环很容易向量化:
tmp = double(im(x + (0:2), y + (0:2))) .* kernel;
im2(y,x) = sum(tmp(:));
或者,在最新版本的 MATLAB 中,
im2(y,x) = sum(double(im(x + (0:2), y + (0:2))) .* kernel, 'all');
(注意这里需要 sum
函数!)
我想了解 MATLAB 的 imfilter
函数是如何工作的。
im = imread("cameraman.tif");
% Kernel for sharpening the image
kernel = [
0 -1 0;
-1 5 -1;
0 -1 0];
im2 = zeros(size(im));
for y = 1 : size(im,1) - 3
for x = 1 : size(im,2) - 3
sum = 0;
for ky = 1:3
for kx = 1:3
xx = x + kx - 1;
yy = y + ky - 1;
sum = sum + im(yy,xx)*kernel(ky,kx);
end
end
im2(y,x) = sum;
end
end
% Map im2 to 0 - 255
im2 = im2 - min(im2(:));
im2 = im2 / max(im2(:)) * 255;
im2 = uint8(im2);
subplot(131), imshow(im), title('Original Image')
subplot(132), imshow(imfilter(im,kernel)), title('Matlab imfilter')
subplot(133), imshow(im2), title('My filter')
边界上的差异不是我关心的问题,但我的结果(右侧的子图)明显不同于 MATLAB 生成的结果(中间的子图),尽管使用了相同的内核。
请问哪里有偏差?据我所知,内核补丁将按元素应用于图像并在结果上求和。有人能让我知道我错过了什么吗?谢谢
你的错误在这一行:
sum = sum + im(yy,xx)*kernel(ky,kx);
这并不明显,但是 MATLAB 在对不同数据类型进行算术运算时做出了一个奇怪的选择。在 MATLAB 中,默认情况下所有数值数组(所有值)都是双精度数。 sum
在初始化时是双* (sum = 0
),kernel
和 im2
也是。但是 im
是一个 8 位无符号整数数组。所以 im(yy,xx)*kernel(ky,kx)
将 uint8 与 double 相乘。这是在一个操作中组合不同数据类型的奇怪情况。
对整数数组进行算术运算时,另一个操作数必须是同一类型,除非它是双精度标量(1x1 双精度数组)。在这种情况下,标量值被转换为整数类型,然后应用该操作。此外,整数运算是饱和的,这意味着整数范围之外的任何结果都被限制在该范围内(没有其他语言中的溢出)。
所以 im(yy,xx)*kernel(ky,kx)
结果是一个 uint8。接下来,sum + <uint8 result>
也是一个uint8值,赋值给sum
。现在 sum
是 uint8!
要解决此问题,请执行
sum = sum + double(im(yy,xx)) * kernel(ky,kx);
* 另请注意,所有内容都是一个数组。 0
是一个 1x1 数组。
不请自来的建议:
你不应该缩放
im2
图像,你应该直接将它投射到uint8
。 MATLAB 将为您将值限制在 [0,255] 范围内。缩放会导致对比度下降。不要使用
sum
作为变量名。sum
是一个 built-in 函数,您可以使用此变量隐藏(使其不可用)。在 运行 你的代码之后,你不能再做sum(im(:))
,例如。内部两个循环很容易向量化:
tmp = double(im(x + (0:2), y + (0:2))) .* kernel; im2(y,x) = sum(tmp(:));
或者,在最新版本的 MATLAB 中,
im2(y,x) = sum(double(im(x + (0:2), y + (0:2))) .* kernel, 'all');
(注意这里需要
sum
函数!)