I am working with normxcorr2
function in Matlab for template matching. However, what I want to do is different from what normxcorr2
does. The built-in normxcorr2
computes cross-correlation taking into account all the pixels in a rectangular template. But I only want certain pixels to participate in the normalized cross correlation process.
For example, I want only the ring-like white region in the following image to be used as a template while correlating. (the black region inside should not be used for computation)
And thus if I correlate the above template with the following image, I am likely to get a normalized value of around 1.0 (considering the outer circles are of same diameter in both images)
I have checked out this solution:- matlab template matching only for 0 (or 1) in matrix but it is not generic.
Can anybody help me with a more general solution in matlab? This can be used to track objects by their outer shapes
EDIT:- This is the entire image for those who want to have a look at it. I want to detect the ball only by the outer circular rim, not the inner details.
Ok, let's give it a try... This solution tries to use existing normxcorr2
implementation and modify it to solve yoru problem.
The formula for normalized cross correlation is:
In this case you want to change the integration boundaries for every window. This is turn affects both standard deviations and the correlation itself. Lets tackle it in several steps:
Step #1: Get the correlation right
We can do this by modifying the template image:
template_fix = template;
mean_template_mask = mean(template(mask == 1));
template_fix(mask == 0) = mean_template_mask;
result = normxcorr2(template_fix, query)
Notice that by making this change we make the mean value of the template to be equal to the mean value of the template in side the mask. this way all template pixels outside the mask don't contribute to the integration as they are equal to the mean value.
Step #2: Fix template std
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);
Step #3: Fix query std
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;
My Matlab is not responding so I didn't have the chance to test it in practice. Unless I missed some errors it should be mathematically equivalent.
This solution does some extra convolutions but it doesn't process overlapping pixels more than once.
If you use the same template image multiple times then you could refactor steps 1 & 2 to run only once for preprocessing. Although both shouldn't be computationally expensive.
Different approach: Straight forward
Here is a different, straightforward approach that doesn't use the original normxcorr2
function. This code can be easily optimized for memory usage in expense of readability.
% 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)