使用归一化互相关匹配对象外形

时间:2014-06-12 13:31:24

标签: matlab templates image-processing template-matching

我在Matlab中使用normxcorr2函数进行模板匹配。但是,我想要做的与normxcorr2的做法不同。内置normxcorr2计算交叉相关性,同时考虑矩形模板中的所有像素。但我只想让某些像素参与规范化的互相关过程。

例如,我想在下面的图像中仅将环状白色区域用作模板,同时关联。 (内部的黑色区域不应用于计算)

enter image description here

因此,如果我将上述模板与下面的图像相关联,我可能会得到大约1.0的标准化值(考虑到两个图像中外圆的直径相同)

enter image description here

我已经检查了这个解决方案: - matlab template matching only for 0 (or 1) in matrix但它不是通用的。

有人可以帮助我在matlab中使用更通用的解决方案吗?这可用于通过外部形状跟踪对象

编辑: - 这是想要查看它的人的整个图片。我想只通过外圆形边缘检测球,而不是内部细节。

enter image description here

2 个答案:

答案 0 :(得分:3)

这是上一篇文章的衍生版,我在这里提供了答案:matlab template matching only for 0 (or 1) in matrix

但是,此解决方案使用for循环,效率非常低。因此,我们可以使用im2colbsxfuncol2im来帮助我们更快地执行此操作。 im2col会在图片中显示重叠区域,并将每个区域放入单独的列中。从本质上讲,这会像您使用任何类型的空间图像过滤一样获取图像的滑动窗口,并收集滑动窗口内的所有像素,并将每个窗口作为单独的列放置。

假设您的模板尺寸为M x N,并且您要搜索的图片尺寸为R x C,并假设您的图片模板在图片时被称为imTemplate如果您要搜索imSearch,我们可以进行以下设置。我们还假设两个图像都是二进制

[M, N] = size(imTemplate);
[R, C] = size(imSearch);

%// Cast to double for precision
imTemplate = im2double(imTemplate);
imSearch = im2double(imSearch);

neigh = im2col(imSearch, [M, N]);
templateCol = imTemplate(:); %// Ensures we place template into single column

现在,您希望排除内部圆形边界的所有像素。因此,我们可以做的是反转图像,使黑色像素变为白色,然后删除边框周围的所有像素。然后,这应该给我们圆圈的内部

imInvert = ~imTemplate;
imInvertNoBorder = imclearborder(imInvert, 8); %// Search 8-pixel neighbourhood

我们将使用它来确定我们要从搜索中删除的像素。这可以通过以下方式完成:

rowsToRemove = imInvertNoBorder(:) == 1;

现在,我们可以做的是最终删除圆圈内部的像素,以便在我们的关联方案中不被搜索。

neigh(rowsToRemove,:) = [];

我们现在可以做的是计算所有这些列的NCC。如果你还记得,两个信号之间的NCC如下:

NCC http://www.jot.fm/issues/issue_2010_03/column2/images/form1.gif

因此,我们需要从每个邻域中减去均值,并且我们还需要从每个列中减去均值。然后我们计算如上所示的公式。我们可以很容易地在MATLAB中实现这样的矢量化:

neighMeanSubtract = bsxfun(@minus, neigh, mean(neigh));
templateMeanSubtract = templateCol - mean(templateCol);

我们可以计算每个邻域的NCC分子(在我们求和之前),如下所示:

numerator = bsxfun(@times, neighMeanSubtract, templateMeanSubtract);

现在,我们所要做的就是对所有列进行求和,这将为我们提供最终的分子:

sumNumerator = sum(numerator);

分母可以这样计算:

denominator1 = sqrt(sum(neighMeanSubtract.*neighMeanSubtract));
denominator2 = sqrt(sum(templateMeanSubtract.*templateMeanSubtract));
sumDenominator = denominator1 .* denominator2;

最后,我们的NCC可以这样计算:

NCC = sumNumerator ./ sumDenominator;

您会注意到这是一行值。每行对应于邻域定义的输出。因此,我们还需要将其重新整形为矩阵,因此您可以使用col2im

finalOutput = col2im(NCC, [M, N], [R, C]);

上述陈述将采用NCC中定义的M x N重叠邻域,并对其进行整形,使其成为R x C矩阵。有时,您会得到除以零错误,尤其是在邻域搜索窗口全部均匀时。因此,您将获得NaN个数字。假设没有变化的区域在图像处理中没有相关性,因此我们将这些位置归零:

finalOutput(isnan(finalOutput)) = 0;

如果要查找最高相关性的位置,只需执行以下操作:

[rowNCC, colNCC] = find(finalOutput == max(finalOutput(:)));

如果您想解释负相关,那完全取决于您的应用。如果您想确保模板匹配算法旋转不变,那么您应该实际检查绝对值的最大值。负相关仅仅意味着模板和邻域之间的匹配被简单地旋转。因此,找到最佳社区的更好方法是:

maxCoeff = max(abs(finalOutput(:)));
[rowNCC, colNCC] = find(abs(finalOutput) == maxCoeff);

为了您的复制和粘贴乐趣,以下是完整的代码:

function [rowNCC, colNCC] = testCorr(imTemplate, imSearch)
    [M, N] = size(imTemplate);
    [R, C] = size(imSearch);

    %// Cast to double for precision
    imTemplate = im2double(imTemplate);
    imSearch = im2double(imSearch);

    neigh = im2col(imSearch, [M, N]);
    templateCol = imTemplate(:); %// Ensures we place template into single column

    imInvert = ~imTemplate;
    imInvertNoBorder = imclearborder(imInvert, 8); %// Search 8-pixel neighbourhood
    rowsToRemove = imInvertNoBorder(:) == 1;
    neigh(rowsToRemove,:) = [];

    neighMeanSubtract = bsxfun(@minus, neigh, mean(neigh));
    templateMeanSubtract = templateCol - mean(templateCol);

    numerator = bsxfun(@times, neighMeanSubtract, templateMeanSubtract);
    sumNumerator = sum(numerator);

    denominator1 = sqrt(sum(neighMeanSubtract.*neighMeanSubtract));
    denominator2 = sqrt(sum(templateMeanSubtract.*templateMeanSubtract));
    sumDenominator = denominator1 .* denominator2;

    NCC = sumNumerator ./ sumDenominator;    

    finalOutput = col2im(NCC, [M, N], [R, C]);
    finalOutput(isnan(finalOutput)) = 0;

    maxCoeff = max(abs(finalOutput(:)));
    [rowNCC, colNCC] = find(abs(finalOutput) == maxCoeff);
end
祝你好运!

答案 1 :(得分:1)

好的,我们试一试......这个解决方案试图使用现有的normxcorr2实现并修改它来解决yoru问题。

归一化互相关的公式为:

enter image description here

在这种情况下,您希望更改每个窗口的集成边界。这会影响标准偏差和相关性本身。让我们分几步解决它:

步骤1:获得正确的关联

我们可以通过修改模板图像来实现:

template_fix = template;
mean_template_mask = mean(template(mask == 1));
template_fix(mask == 0) = mean_template_mask;
result = normxcorr2(template_fix, query)

请注意,通过进行此更改,我们使模板的平均值等于掩码中模板的平均值。这样,掩码外的所有模板像素都不会对积分做出贡献,因为它们等于平均值​​。

步骤2:修复模板标准

size_mask = sum(mask(:));
size_template = numel(template);
std_template = std2(template);
std_template_masked = sqrt(sum((template_fix(:) - mean_template_mask).^2)/size_mask);
result = result * (std_template/std_template_masked);

步骤3:修复查询标准

sum_filt = ones(size(template));
std_query = filter2(query.^2, sum_filt) - filter2(query, sum_filt).^2/size_template;
std_query = sqrt(std_query/size_template);

std_query_mask = filter2(query.^2, mask) - filter2(query, mask).^2/size_mask;    
std_query_mask = sqrt(std_query_mask/size_mask);

result = result .* std_query ./ std_query_mask;

我的Matlab没有回复,所以我没有机会在实践中测试它。除非我错过了一些错误,否则应该在数学上等同。

此解决方案会进行一些额外的卷积,但它不会多次处理重叠像素。

如果您多次使用相同的模板图像,那么您可以重构步骤1& 2只运行一次进行预处理。虽然两者都不应该在计算上昂贵。

不同的方法:直接前进

这是一种不同的,直接的方法,不使用原始的normxcorr2函数。这段代码可以很容易地针对内存使用进行优化,而且会降低可读性。

enter image description here

enter image description here

% q for query, t for template and mask for mask
% shape = 'full' or 'same' or 'valid'

t_mask = t .* mask;
n      = numel(mask);
tq_sum = filter2(t_mask,q, shape);

q_sum  = filter2(mask, q, shape);    
q_mean = q_sum/n;
t_sum  = sum(t_mask(:));
t_mean = t_sum/n;

res1 = tq_sum - t_mean*q_sum - t_sum*q_mean + t_mean*q_mean*n;

t_std = sqrt((sum(t_mask(:).^2) - sum(t_mask(:)).^2/n)/n);
q_std = sqrt((filter2(mask, q.^2, shape) - q_sum.^2/n)/n);

res = res1 ./ (n * t_std * q_std)