Image 二维中值滤波器,忽略nan值

Image 二维中值滤波器,忽略nan值,image,matlab,image-processing,vectorization,median,Image,Matlab,Image Processing,Vectorization,Median,作为我项目的一部分,我需要使用在rxr窗口上执行中值滤波并忽略nan值的代码 我目前使用MATLAB的nlfilter函数。问题是速度非常慢: 300x300示例几乎需要5秒,而MATLAB的medfilt2需要0.2秒。 有人有更高效、更优雅的解决方案吗 注意:在我的例子中,边界上的行为并不重要。 在本例中,nlfilter会自动用零填充阵列,但其他解决方案(如边界复制)也可以 代码示例: 预期结果 筛选前: I = 9 1 6 10 1 5 4

作为我项目的一部分,我需要使用在rxr窗口上执行中值滤波并忽略nan值的代码

我目前使用MATLAB的nlfilter函数。问题是速度非常慢: 300x300示例几乎需要5秒,而MATLAB的medfilt2需要0.2秒。 有人有更高效、更优雅的解决方案吗

注意:在我的例子中,边界上的行为并不重要。 在本例中,nlfilter会自动用零填充阵列,但其他解决方案(如边界复制)也可以

代码示例:

预期结果

筛选前:

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
过滤后:

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

谢谢

您可以首先使用要在每侧填充floorr/2像素的位置填充图像,然后使用重新构造填充图像,以便将每个像素邻域放置在单独的列中。接下来,您需要首先将所有nan值设置为虚拟值,这样您就不会干扰中值计算。。。大概是零。之后,找到每列的中间值,然后重新塑造成适当大小的图像

像这样的方法应该会奏效:

r = 3;
nanMask = isnan(I); % Define nan mask
Ic = I;
Ic(nanMask) = 0; % Create new copy of image and set nan elements to zero
IP = padarray(Ic, floor([r/2 r/2]), 'both'); % Pad image
IPc = im2col(IP, [r r], 'sliding'); % Transform into columns
out = reshape(median(IPc, 1), size(I,1), size(I,2)); % Find median of each column and reshape back
out(nanMask) = nan; % Set nan elements back
r = 3;
nanMask = isnan(I); % Define nan mask
IP = padarray(I, floor([r/2 r/2]), 'both'); % Pad image
IPc = im2col(IP, [r r], 'sliding'); % Transform into columns
IPc = sort(IPc, 1, 'ascend'); % Sort the columns
[~,ind] = max(isnan(IPc), [], 1); % For each column, find the last valid number
ind(ind == 1) = r*r; % Handles the case when there are all valid numbers per column
ind = ceil(ind / 2); % Find the halfway point
out = reshape(IPc(sub2ind(size(IPc), ind, 1:size(IPc,2))), size(I,1), size(I,2)); % Find median of each column and reshape back
out(nanMask) = nan; % Set nan elements back
我们得到:

>> out

out =

     0     2     3     3     1     1     0
     2     4     6     6     5   NaN     0
     2     4     5     6     4   NaN     0
     1   NaN     6     6   NaN     3     2
     1     6     9     6     4     4     2
     3     9     9     6     4   NaN     2
     0     9     4     2     1     2     0
>> out

out =

         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
使用上述方法,与预期结果略有不同的是,我们已将所有nan值设置为0,这些值包含在中值中。此外,如果元素的数量在中间值中为偶数,那么我只需选择歧义右边的元素作为最终输出

这可能不是您特别想要的。更有效的方法是对所有列进行单独排序,同时保留nan值不变,然后确定每个列的最后一个有效元素,对于这些元素中的每个元素,确定中间点的位置,并从已排序的列中选择这些元素。使用sort的一个好处是将nan值推送到数组的末尾

类似这样的方法可能会奏效:

r = 3;
nanMask = isnan(I); % Define nan mask
Ic = I;
Ic(nanMask) = 0; % Create new copy of image and set nan elements to zero
IP = padarray(Ic, floor([r/2 r/2]), 'both'); % Pad image
IPc = im2col(IP, [r r], 'sliding'); % Transform into columns
out = reshape(median(IPc, 1), size(I,1), size(I,2)); % Find median of each column and reshape back
out(nanMask) = nan; % Set nan elements back
r = 3;
nanMask = isnan(I); % Define nan mask
IP = padarray(I, floor([r/2 r/2]), 'both'); % Pad image
IPc = im2col(IP, [r r], 'sliding'); % Transform into columns
IPc = sort(IPc, 1, 'ascend'); % Sort the columns
[~,ind] = max(isnan(IPc), [], 1); % For each column, find the last valid number
ind(ind == 1) = r*r; % Handles the case when there are all valid numbers per column
ind = ceil(ind / 2); % Find the halfway point
out = reshape(IPc(sub2ind(size(IPc), ind, 1:size(IPc,2))), size(I,1), size(I,2)); % Find median of each column and reshape back
out(nanMask) = nan; % Set nan elements back
我们现在得到:

>> out

out =

     0     2     3     3     5     4     0
     2     4     6     6     6   NaN     3
     4     5     6     6     6   NaN     3
     3   NaN     6     6   NaN     3     3
     3     9     9     9     4     4     3
     3     9     9     6     6   NaN     2
     0     9     4     2     2     2     0
小调 MATLAB的最新版本有一个可选的第三个输入,名为nanflag,您可以在其中明确确定遇到Nan时要做什么。如果将标志设置为“忽略nan”,则将忽略其计算中的所有nan元素,其中默认值为includenan,而不必指定第三个参数。如果在中值滤波器调用中指定omitnan,并在第一步中跳过将nan值设置为0部分,则可以从nlfilter的输出中获得所需的结果:

我们得到:

>> out

out =

     0     2     3     3     1     1     0
     2     4     6     6     5   NaN     0
     2     4     5     6     4   NaN     0
     1   NaN     6     6   NaN     3     2
     1     6     9     6     4     4     2
     3     9     9     6     4   NaN     2
     0     9     4     2     1     2     0
>> out

out =

         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
一种更有效的im2col溶液 用户已经实现了一个更快的im2col版本,他已经对该版本进行了基准测试,结果表明该版本比MATLAB的图像处理工具箱提供的im2col解决方案快得多。如果要多次调用此代码,请考虑使用他的实现:

定时测试 为了确定建议的方法是否更快,我将使用执行计时测试。首先,我将创建一个设置公共变量的函数,创建两个函数,其中第一个是使用nlfilter的原始方法,第二个是使用建议的方法。我将使用使用'omitnan'的方法,因为它会产生您想要的结果

这是我写的函数。我已经生成了一个300 x 300的输入,就像你的设置一样,它包含0到1之间的所有随机数。我这样做是为了在这个输入中大约有20%的数字是nan。我还设置了你在nlfilter中使用的匿名函数,用于过滤没有NAN的中位数以及邻域大小(3 x 3)。然后,我在此代码中定义了两个函数—代码使用nlfilter进行过滤的原始方法,以及我在上面提出的使用omitnan选项的方法:


如您所见,新方法的运行速度大约比nlfilter快1.033838/0.038697=26.7162倍。不错

你有图像处理工具箱吗?你试过这个功能吗?@hbaderts是的,我试过,而且速度快得多。问题是medfilt2在nans上没有定义。嗯,对吧。。。我唯一的猜测是Matlab文件交换中的函数。根据一条评论,它需要两倍于medfilt2的时间,但明显比其他手动方法快…感谢您的接受!我还没有时间对代码进行基准测试,但与nlfilter相比,它快了多少?我将对奥米特南进行第二次尝试,因为这正是你所追求的。我决定亲自测试一下。我建议的方法大约快26倍。该死的雷,你又做过头了。干得好