5

我正在寻找一种在更大的矩阵(任意维数)中找到矩阵(模式)的“好”方法。

例子:

total = rand(3,4,5);
sub = total(2:3,1:3,3:4);

现在我希望发生这种情况:

loc = matrixFind(total, sub)

在这种情况下loc应该变成[2 1 3].

现在我只对找到一个点(如果存在)感兴趣,并不担心舍入问题。可以假设sub“适合”在total.


这是我如何在 3 维上做到这一点,但感觉好像有更好的方法:

total = rand(3,4,5);
sub = total(2:3,1:3,3:4);
loc = [];
for x = 1:size(total,1)-size(sub,1)+1
    for y = 1:size(total,2)-size(sub,2)+1
        for z = 1:size(total,3)-size(sub,3)+1
            block = total(x:x+size(sub,1)-1,y:y+size(sub,2)-1,z:z+size(sub,3)-1);
            if isequal(sub,block)
                loc = [x y z]
            end
        end
    end
end

我希望为任意数量的维度找到一个可行的解决方案。

4

3 回答 3

4

这是低性能但(据说)任意维度的函数。它用于find创建潜在匹配位置的(线性)索引列表,total然后仅检查大小合适的total匹配子块sub

function loc = matrixFind(total, sub)
%matrixFind find position of array in another array

    % initialize result
    loc = [];

    % pre-check: do all elements of sub exist in total?
    elements_in_both = intersect(sub(:), total(:));
    if numel(elements_in_both) < numel(unique(sub))
        % if not, return nothing
        return
    end

    % select a pivot element
    % Improvement: use least common element in total for less iterations
    pivot_element = sub(1);

    % determine linear index of all occurences of pivot_elemnent in total
    starting_positions = find(total == pivot_element);

    % prepare cell arrays for variable length subscript vectors
    [subscripts, subscript_ranges] = deal(cell([1, ndims(total)]));


    for k = 1:length(starting_positions)
        % fill subscript vector for starting position
        [subscripts{:}] = ind2sub(size(total), starting_positions(k));

        % add offsets according to size of sub per dimension
        for m = 1:length(subscripts)
            subscript_ranges{m} = subscripts{m}:subscripts{m} + size(sub, m) - 1;
        end

        % is subblock of total equal to sub
        if isequal(total(subscript_ranges{:}), sub)
            loc = [loc; cell2mat(subscripts)]; %#ok<AGROW>
        end
    end
end
于 2013-09-16T16:03:07.710 回答
2

这是基于对原始矩阵进行所有可能的移位,并将移位后的total左上角等子矩阵total与寻找的模式进行比较subs。使用字符串生成移位,并使用circshift.

大部分工作都是矢量化的。仅使用一级循环。

该函数查找所有匹配项,而不仅仅是第一个。例如:

>> total = ones(3,4,5,6);
>> sub = ones(3,3,5,6);
>> matrixFind(total, sub)
ans =

     1     1     1     1
     1     2     1     1

这是功能:

function sol = matrixFind(total, sub)

nd = ndims(total);
sizt = size(total).';
max_sizt = max(sizt);
sizs = [ size(sub) ones(1,nd-ndims(sub)) ].'; % in case there are
% trailing singletons

if any(sizs>sizt)
    error('Incorrect dimensions')
end

allowed_shift = (sizt-sizs);
max_allowed_shift = max(allowed_shift);
if max_allowed_shift>0
    shifts = dec2base(0:(max_allowed_shift+1)^nd-1,max_allowed_shift+1).'-'0';
    filter = all(bsxfun(@le,shifts,allowed_shift));
    shifts = shifts(:,filter); % possible shifts of matrix "total", along 
    % all dimensions
else
    shifts = zeros(nd,1);
end

for dim = 1:nd
    d{dim} = 1:sizt(dim); % vectors with subindices per dimension
end
g = cell(1,nd);
[g{:}] = ndgrid(d{:}); % grid of subindices per dimension
gc = cat(nd+1,g{:}); % concatenated grid
accept = repmat(permute(sizs,[2:nd+1 1]), [sizt; 1]); % acceptable values
% of subindices in order to compare with matrix "sub"
ind_filter = find(all(gc<=accept,nd+1));

sol = [];
for shift = shifts
    total_shifted = circshift(total,-shift);
    if all(total_shifted(ind_filter)==sub(:))
        sol = [ sol; shift.'+1 ];
    end
end
于 2013-09-16T17:04:04.420 回答
1

对于任意数量的维度,您可以尝试convn.

C = convn(total,reshape(sub(end:-1:1),size(sub)),'valid'); % flip dimensions of sub to be correlation
[~,indmax] = max(C(:));
% thanks to Eitan T for the next line
cc = cell(1,ndims(total)); [cc{:}] = ind2sub(size(C),indmax); subs = [cc{:}]

感谢 Eitan T 建议对通用 ind2sub 使用逗号分隔列表。

最后,您应该测试结果,isequal因为这不是归一化的互相关,这意味着局部子区域中的较大数字会夸大相关值,可能会产生误报。如果您的total矩阵与大值区域非常不均匀,您可能需要在 中搜索其他最大值C

于 2013-09-16T16:08:27.217 回答