作为我项目的一部分,我需要使用在rxr窗口上执行中值过滤的代码,并忽略nan值 .
我目前使用MATLAB的nlfilter函数 . 问题是它非常慢:300x300示例需要将近5秒,而MATLAB的medfilt2需要0.2秒 . 有没有人有更高效和优雅的解决方案?
注意:在我的情况下,在边界上的行为并不重要 . 在这个例子中,nlfilter自动用零填充数组,但其他解决方案,如边界重复也是可以的 .
code example:
%Initialize input
r = 3; % window size is 3x3
I = [9,1,6,10,1,5,4;2,4,3,8,8,NaN,5;4,5,8,6,2,NaN,3;5,NaN,6,4,NaN,4,9;3,1,10,9,4,3,2;10,9,10,10,6,NaN,5;10,9,4,1,2,7,2];
%perform median filter on rxr window, igonre nans
f = @(A)median(A(~isnan(A)));
filteredRes = nlfilter(I, [r r], f);
filteredRes(nanMask) = nan;
Expected Results
在过滤之前:
I =
9 1 6 10 1 5 4
2 4 3 8 8 NaN 5
4 5 8 6 2 NaN 3
5 NaN 6 4 NaN 4 9
3 1 10 9 4 3 2
10 9 10 10 6 NaN 5
10 9 4 1 2 7 2
过滤后:
filteredRes =
0 2.0000 3.0000 3.0000 3.0000 2.5000 0
2.0000 4.0000 6.0000 6.0000 6.0000 NaN 3.0000
3.0000 4.5000 5.5000 6.0000 5.0000 NaN 3.0000
2.0000 NaN 6.0000 6.0000 NaN 3.0000 2.5000
2.0000 7.5000 9.0000 7.5000 4.0000 4.0000 2.5000
3.0000 9.0000 9.0000 6.0000 5.0000 NaN 2.0000
0 9.0000 4.0000 2.0000 1.5000 2.0000 0
谢谢!
1 回答
您可以首先使用padarray填充图像,在每侧填充
floor(r/2)
像素,然后使用im2col重新构建填充图像,以便像素的每个邻域放置在单独的列中 . 接下来,您需要首先将所有nan
值设置为虚拟值,这样您就不会干扰中值计算...也许就像零一样 . 之后,找到每列的中位数,然后重新塑造成适当大小的图像 .这样的事情应该有效:
我们得到:
使用上述方法,与预期结果略有不同的是,我们将所有
nan
值设置为0,并且这些值包含在中位数中 . 另外,如果元素的数量在中位数中是偶数,那么我只选择模糊度右侧的元素作为最终输出 .这可能不是你想要的具体 . 更有效的方法是单独 sort 所有列,同时保持
nan
值不变,然后确定每列有效的最后一个元素,并确定每个元素,确定中途点的位置,并从排序中选择那些列 . 使用sort
的好处是nan
值被推向数组的末尾 .像这样的东西可以工作:
我们现在得到:
次要注意事项
最新版本的MATLAB有一个名为
nanflag
的可选第三个输入,您可以在其中明确确定遇到nan
时要执行的操作 . 如果将标志设置为omitnan
,则将忽略其计算中的所有nan
元素,其中默认值为includenan
,您不必指定第三个参数 . 如果您在中值过滤器调用中指定omitnan
以及在第一步中跳过nan
值的设置为0部分,您将从nlfilter
的输出中得到您想要的内容:我们得到:
更高效的im2col解决方案
用户Divakar实现了更快的版本
im2col
,他已经对其进行了基准测试,并且显示出比MATLAB提供的im2col
解决方案快得多's image processing toolbox. If you'将多次调用此代码,请考虑使用他的实现:Efficient Implementation ofim2col
andcol2im
时间测试
为了确定提议的方法是否更快,我将使用timeit执行时序测试 . 首先,我将创建一个设置公共变量的函数,创建两个函数,其中第一个是
nlfilter
的原始方法,第二个方法是建议的方法 . 我将使用'omitnan'
使用该方法,因为它可以产生您想要的结果 .这里's the function I wrote. I' ve生成了一个300 x 300的输入,就像你如何制作它一样,这个输入中约有20%的数字有
nan
. 我还设置了你正在使用nlfilter
的匿名函数来过滤没有nan
的中位数以及邻域大小,即3 x 3.然后我在这段代码中定义了两个函数 - 代码执行的原始方法使用nlfilter
进行过滤以及我在上面使用omitnan
选项提出的建议:我目前的机器是HP ZBook G5,配备16 GB RAM和Intel Core i7 @ 2.80 GHz . 当您运行此代码时,我得到以下结果:
如您所见,新方法的运行速度约为(1.033838 / 0.038697)=比
nlfilter
快26.7162倍 . 不错!