我在Matlab中使用normxcorr2
函数进行模板匹配。但是,我想要做的与normxcorr2
的做法不同。内置normxcorr2
计算交叉相关性,同时考虑矩形模板中的所有像素。但我只想让某些像素参与规范化的互相关过程。
例如,我想在下面的图像中仅将环状白色区域用作模板,同时关联。 (内部的黑色区域不应用于计算)
因此,如果我将上述模板与下面的图像相关联,我可能会得到大约1.0的标准化值(考虑到两个图像中外圆的直径相同)
我已经检查了这个解决方案: - matlab template matching only for 0 (or 1) in matrix但它不是通用的。
有人可以帮助我在matlab中使用更通用的解决方案吗?这可用于通过外部形状跟踪对象
编辑: - 这是想要查看它的人的整个图片。我想只通过外圆形边缘检测球,而不是内部细节。
答案 0 :(得分:3)
这是上一篇文章的衍生版,我在这里提供了答案:matlab template matching only for 0 (or 1) in matrix
但是,此解决方案使用for
循环,效率非常低。因此,我们可以使用im2col
,bsxfun
和col2im
来帮助我们更快地执行此操作。 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问题。
归一化互相关的公式为:
在这种情况下,您希望更改每个窗口的集成边界。这会影响标准偏差和相关性本身。让我们分几步解决它:
步骤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
函数。这段代码可以很容易地针对内存使用进行优化,而且会降低可读性。
% 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)