简体   繁体   English

在 MATLAB 中查找矩阵的 NaN 边界

[英]Finding the NaN boundary of a matrix in MATLAB

I have a very large (2019x1678 double) DEM (digital elevation model) file put as a matrix in MATLAB.我有一个非常大的(2019x1678 双)DEM(数字高程模型)文件作为矩阵放在 MATLAB 中。 The edges of it contain NaN values.它的边缘包含 NaN 值。 In order to account for edge effects in my code, I have to put a 1 cell buffer (same value as adjacent cell) around my DEM.为了在我的代码中考虑边缘效应,我必须在我的 DEM 周围放置一个 1 单元缓冲区(与相邻单元的值相同)。 Where NaNs are present, I need to find the edge of the NaN values in order to build that buffer.在存在 NaN 的地方,我需要找到 NaN 值的边缘才能构建该缓冲区。 I have tried doing this two ways:我尝试过两种方式:

In the first I get the row and column coordinates all non-NaN DEM values, and find the first and last row numbers for each column to get the north and south boundaries, then find the first and last column numbers for each row to get the east and west boundaries.第一个中,我获取所有非 NaN DEM 值的行和列坐标,并找到每列的第一行和最后一行编号以获得南北边界,然后找到每行的第一列和最后一列编号以获得东西边界。 I use these in the sub2ind() to create my buffer.我在sub2ind()中使用这些来创建我的缓冲区。

[r, c] = find(~isnan(Zb_ext)); %Zb is my DEM matrix
idx = accumarray(c, r, [], @(x) {[min(x) max(x)]});
idx = vertcat(idx{:});

NorthBoundary_row = transpose(idx(:,1)); % the values to fill my buffer with
NorthBoundary_row_ext = transpose(idx(:,1) - 1); % My buffer cells
columnmax = length(NorthBoundary_row);
column1 = min(c);
Boundary_Colu = linspace(column1,column1+columnmax-1,columnmax);
SouthBoundary_row = (transpose(idx(:,2))); % Repeat for south Boundary
SouthBoundary_row_ext = transpose(idx(:,2) + 1);

SouthB_Ind = sub2ind(size(Zb_ext),SouthBoundary_row,Boundary_Colu);
SouthB_Ind_ext = sub2ind(size(Zb_ext),SouthBoundary_row_ext, Boundary_Colu);
NorthB_Ind = sub2ind(size(Zb_ext),NorthBoundary_row, Boundary_Colu);
NorthB_Ind_ext = sub2ind(size(Zb_ext),NorthBoundary_row_ext, Boundary_Colu);

Zb_ext(NorthB_Ind_ext) = Zb_ext(NorthB_Ind);
Zb_ext(SouthB_Ind_ext) = Zb_ext(SouthB_Ind);

% Repeat above for East and West Boundary by reversing the roles of row and 
% column

[r, c] = find(~isnan(Zb_ext));
idx = accumarray(r, c, [], @(x) {[min(x) max(x)]});
idx = vertcat(idx{:});

EastBoundary_colu = transpose(idx(:,1)); % Repeat for east Boundary
EastBoundary_colu_ext = transpose(idx(:,1) - 1); 
row1 = min(r);
rowmax = length(EastBoundary_colu);
Boundary_row = linspace(row1,row1+rowmax-1,rowmax);
WestBoundary_colu = transpose(idx(:,2)); % Repeat for west Boundary
WestBoundary_colu_ext = transpose(idx(:,2) + 1);

EastB_Ind = sub2ind(size(Zb_ext),Boundary_row, EastBoundary_colu);
EastB_Ind_ext = sub2ind(size(Zb_ext),Boundary_row, EastBoundary_colu_ext);
WestB_Ind = sub2ind(size(Zb_ext),Boundary_row, WestBoundary_colu);
WestB_Ind_ext = sub2ind(size(Zb_ext),Boundary_row, WestBoundary_colu_ext);

Zb_ext(NorthB_Ind_ext) = Zb_ext(NorthB_Ind);
Zb_ext(SouthB_Ind_ext) = Zb_ext(SouthB_Ind);
Zb_ext(EastB_Ind_ext) = Zb_ext(EastB_Ind);
Zb_ext(WestB_Ind_ext) = Zb_ext(WestB_Ind);

This works well on my small development matrix, but fails on my full sized DEM.这在我的小型开发矩阵上效果很好,但在我的全尺寸 DEM 上却失败了。 I do not understand the behavior of my code, but looking at the data there are gaps in my boundary.我不了解我的代码的行为,但查看数据时我的边界存在差距。 I wonder if I need to better control the order of max/min row/column values, though in my test on a smaller dataset, all seemed in order....我想知道我是否需要更好地控制最大/最小行/列值的顺序,尽管在我对较小数据集的测试中,一切似乎都是有序的......

The second method I got from a similar question to this and basically uses a dilation method.我从与此类似的问题中得到的第二种方法基本上使用了膨胀方法。 However, when I transition to my full dataset, it takes hours to calculate ZbDilated .但是,当我转换到我的完整数据集时,计算ZbDilated需要几个小时。 Although my first method does not work, it at least calculates within seconds.虽然我的第一种方法不起作用,但它至少可以在几秒钟内计算出来。

[m, n] = size(Zb); % 
Zb_ext = nan(size(Zb)+2);
Zb_ext(2:end-1, 2:end-1) = Zb; % pad Zb with zeroes on each side
ZbNANs = ~isnan(Zb_ext);
ZbDilated = zeros(m + 2, n + 2); % this will hold the dilated shape.

for i = 1:(m+2)
    if i == 1 %handling boundary situations during dilation
        i_f = i;
        i_l = i+1;
    elseif i == m+2
        i_f = i-1;
        i_l = i;
    else
        i_f = i-1;
        i_l = i+1;
    end
    for j = 1:(n+2)
        mask = zeros(size(ZbNANs));
        if j == 1 %handling boundary situations again
            j_f = j;
            j_l = j+1;
        elseif j == n+2
            j_f = j-1;
            j_l = j;
        else
            j_f = j-1;
            j_l = j+1;
        end
        
        mask(i_f:i_l, j_f:j_l) = 1; % this places a 3x3 square of 1's around (i, j)
        ZbDilated(i, j) = max(ZbNANs(logical(mask)));
    end
end

Zb_ext(logical(ZbDilated)) = fillmissing(Zb_ext(logical(ZbDilated)),'nearest');

Does anyone have any ideas on making either of these usable?有没有人有任何想法使这些中的任何一个可用?

Here is what I start out with:这是我开始的:

   NaN   NaN     2     5    39    55    44     8   NaN   NaN
   NaN   NaN   NaN     7    33    48    31    66    17   NaN
   NaN   NaN   NaN    28   NaN    89   NaN   NaN   NaN   NaN

Here is the matrix buffered on the limits with NaNs:这是使用 NaN 缓冲在极限上的矩阵:

   NaN   NaN   NaN   NaN   NaN   NaN   NaN   NaN   NaN   NaN   NaN   NaN
   NaN   NaN   NaN     2     5    39    55    44     8   NaN   NaN   NaN
   NaN   NaN   NaN   NaN     7    33    48    31    66    17   NaN   NaN
   NaN   NaN   NaN   NaN    28   NaN    89   NaN   NaN   NaN   NaN   NaN
   NaN   NaN   NaN   NaN   NaN   NaN   NaN   NaN   NaN   NaN   NaN   NaN

Here is what I want to get after using fillmissing (though I have noticed some irregularities with how buffer values are filled...):这是使用fillmissing后我想要得到的结果(尽管我注意到缓冲区值的填充方式存在一些不规则性......):

   NaN   NaN     2     2     5    39    55    44     8    17   NaN   NaN
   NaN   NaN     2     2     5    39    55    44     8    17    17   NaN
   NaN   NaN     2     2     7    33    48    31    66    17    17   NaN
   NaN   NaN   NaN     2    28    33    89    31    66    17    17   NaN
   NaN   NaN   NaN     5    28    55    89     8   NaN   NaN   NaN   NaN

To try and clear up any confusion about what I am doing, here is the logical I get from dilation I use for fillmissing为了尝试消除对我正在做的事情的任何困惑,这是我从用于填充缺失的膨胀中得到的逻辑

       0   0   1     1    1    1    1    1   1   1   0   0
       0   0   1     1    1    1    1    1   1   1   1   0
       0   0   1     1    1    1    1    1   1   1   1   0
       0   0   0     1    1    1    1    1   1   1   1   0
       0   0   0     1    1    1    1    1   0   0   0   0

A faster way to apply a 3x3 dilation would be as follows.应用 3x3 膨胀的更快方法如下。 This does involve some large intermediate matrices, which make it less efficient than, say applying imdilate .这确实涉及一些大型中间矩阵,这使其效率低于应用imdilate

[m, n] = size(Zb); % 
Zb_ext = nan(size(Zb)+2);
Zb_ext(2:end-1, 2:end-1) = Zb; % pad A with zeroes on each side
ZbNANs = ~isnan(Zb_ext);
ZbDilated = ZbNANs; % this will hold the dilated shape.

% up and down neighbors
ZbDilated(2:end, :) = max(ZbDilated(2:end, :), ZbNANs(1:end-1, :));
ZbDilated(1:end-1, :) = max(ZbDilated(1:end-1, :), ZbNANs(2:end, :));

% left and right neighbors
ZbDilated(:, 2:end) = max(ZbDilated(:, 2:end), ZbNANs(:, 1:end-1));
ZbDilated(:, 1:end-1) = max(ZbDilated(:, 1:end-1), ZbNANs(:, 2:end));

% and 4 diagonal neighbors
ZbDilated(2:end, 2:end) = max(ZbDilated(2:end, 2:end), ZbNANs(1:end-1, 1:end-1));
ZbDilated(1:end-1, 2:end) = max(ZbDilated(1:end-1, 2:end), ZbNANs(2:end, 1:end-1));
ZbDilated(2:end, 1:end-1) = max(ZbDilated(2:end, 1:end-1), ZbNANs(1:end-1, 2:end));
ZbDilated(1:end-1, 1:end-1) = max(ZbDilated(1:end-1, 1:end-1), ZbNANs(2:end, 2:end));

This is a tedious way to write it, I'm sure there's a loop that can be written that is shorter, but this I think makes the intention clearer.这是一种乏味的编写方式,我确信可以编写更短的循环,但我认为这会使意图更清晰。

[Edit: Because we're dealing with a logical array here, instead of max(A,B) we could also do A | B [编辑:因为我们在这里处理的是一个逻辑数组,所以我们也可以做A | B而不是max(A,B) A | B . A | B I'm not sure if there would be any difference in time.]我不确定是否会有任何时间差异。]


What @beaker said in a comment was to not use @beaker 在评论中说的是不要使用

mask = zeros(size(ZbNANs));
mask(i_f:i_l, j_f:j_l) = 1; % this places a 3x3 square of 1's around (i, j)
ZbDilated(i, j) = max(ZbNANs(logical(mask)));

but rather do而是做

ZbDilated(i, j) = max(ZbNANs(i_f:i_l, j_f:j_l), [], 'all');

[Edit: Because we're dealing with a logical array here, instead of max(A,[],'all') we could also do any(A,'all') , which should be faster. [编辑:因为我们在这里处理的是一个逻辑数组,所以我们也可以做any(A,'all')而不是max(A,[],'all') all') ,这应该会更快。 See @beaker's other comment .]请参阅@beaker 的其他评论。]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM