给定矩阵 M(n,m) 的线性索引,您可以确信元素的左上角邻居M(i,j) = M(i-1, j-1) = M(i-1 + n * (j-2))
在“线性索引”空间中,这意味着该元素的偏移量是
-n-1
对所有其他位置执行此操作,我们发现
-n-1 | -1 | n-1
-n | x | n => [-n-1, -n, -n+1, -1, +1, +n-1, +n, +n+1]
-n+1 | +1 | n+1
因此,您可以使用上述值创建向量偏移(将 n 替换为第一个维度)。例如,如果 M 为 (5x4),则
offset = [-6 -5 -4 -1 1 4 5 6];
然后您创建所有索引:
indices = bsxfun(@plus, (1:m*n), offset(:));
bsxfun 是“在这些元素上执行此功能;其中一个元素具有单一维度而另一个元素没有,相应地扩展”的一种很酷的简写。您可以对repmat 执行相同的操作,但这会创建不必要的中间矩阵(有时可能非常大)。
该命令将创建一个(8 x m*n)
所有 8 个邻居的索引矩阵,包括那些可能不是真正邻居的索引...您需要修复的问题。
几种可能的方法:
- 在开始之前填充矩阵
- 不关心包装,只需去掉从边缘掉落的元素即可
- 为所有“超出边缘”的蒙版创建一个蒙版。
我更喜欢后者。 “边缘”的意思是:
- 上升到顶行
- 在左栏向左走
- 到最下面一排
- 在右栏中向右走
在这四种情况中,每一种都有 3 个“无效”索引。它们在上述矩阵中的位置可以确定如下:
mask = zeros(size(M));
mask(:,1) = 1;
left = find(mask == 1);
mask(:,end) = 2;
right = find(mask == 2);
mask(1,:) = 3;
top = find(mask == 3);
mask(end,:) = 4;
bottom = find(mask == 4);
edgeMask = ones(8,m*n);
edgeMask(1:3, top) = 0;
edgeMask([1 4 6], left) = 0;
edgeMask([3 5 8], right) = 0;
edgeMask(6:8, bottom) = 0;
现在您已拥有所需的一切 - 所有索引以及“无效”索引。无循环。
如果您雄心勃勃,您可以将其转换为元胞数组,但它会比使用完整数组+掩码慢。例如,如果你想找到一个值的所有邻居的平均值,你可以这样做
meanNeighbor = reshape(sum(M(indices).*edgeMask, 1)./sum(edgeMask, 1), size(M));
EDIT重新阅读你的问题,我发现你想要一个 M*N,8 维。我的代码已转置。我相信你能弄清楚如何适应它......
归因@Tin 对上述帖子提出了许多很棒的修改建议,但在审核过程中被拒绝了。我无法完全消除这种不公正——但我想在此表达我的谢意。
延伸至不同区域、多维度
如果你有一个 N 维图像矩阵 M,你可以按如下方式找到邻居:
temp = zeros(size(M));
temp(1:3,1:3,1:3) = 1;
temp(2,2,2) = 2;
offsets = find(temp==1) - find(temp==2);
如果你想要一个大小一定半径的区域,你可以这样做
sz = size(M);
[xx yy zz] = meshgrid(1:sz(1), 1:sz(2), 1:sz(3));
center = round(sz/2);
rr = sqrt((xx - center(1)).^2 + (yy - center(2)).^2 + (zz - center(3)).^2);
offsets = find(rr < radius) - find(rr < 0.001);
您可能可以按照前面所示的 2D 情况找出如何处理边缘问题。
未经测试 - 请查看您是否发现上述问题。