Сопоставление внешней формы объекта с использованием нормализованной взаимной корреляции

Я работаю с функцией normxcorr2 в Matlab для сопоставления шаблонов. Однако то, что я хочу сделать, отличается от того, что делает normxcorr2. Встроенный normxcorr2 вычисляет взаимную корреляцию, принимая во внимание все пиксели в прямоугольном шаблоне. Но я хочу, чтобы в процессе нормализованной взаимной корреляции участвовали только определенные пиксели.

Например, я хочу, чтобы только кольцеобразная белая область на следующем изображении использовалась в качестве шаблона при корреляции. (черная область внутри не должна использоваться для вычислений)

введите здесь описание изображения

Таким образом, если я сопоставлю приведенный выше шаблон со следующим изображением, я, скорее всего, получу нормализованное значение около 1,0 (учитывая, что внешние круги имеют одинаковый диаметр на обоих изображениях).

введите здесь описание изображения

Я проверил это решение: - шаблон Matlab, соответствующий только для 0 (или 1) в матрице, но не является общим.

Может ли кто-нибудь помочь мне с более общим решением в Matlab? Это можно использовать для отслеживания объектов по их внешним формам.

РЕДАКТИРОВАНИЕ: - Это изображение полностью для тех, кто хочет на него взглянуть. Я хочу обнаружить мяч только по внешнему кольцу, а не по внутренним деталям.

введите здесь описание изображения


person soumyadip.ghosh    schedule 12.06.2014    source источник
comment
Разместите также исходное изображение.   -  person kkuilla    schedule 12.06.2014
comment
@ soumyadip93 soumyadip93: я являюсь первоначальным автором решения для того поста, на который вы ссылаетесь. Почему он не универсальный? Он должен работать для любого типа шаблона, который вы выберете.   -  person rayryeng    schedule 12.06.2014
comment
@rayryeng: Может быть, я не совсем понял ваш код, но вы использовали определенную область для поиска по изображению. Почему вы не использовали весь образ?   -  person soumyadip.ghosh    schedule 12.06.2014
comment
@ soumyadip93 soumyadip93: ОП этого вопроса хотел использовать только пиксели в расчете корреляции, которые находились только в черных областях шаблона. Я также искал все изображение. Ваша ситуация такая же, за исключением того, что вы ищете белые пиксели в своем шаблоне. Таким образом, я считаю, что ваша ситуация и другой вопрос одинаковы... не так ли? Все, что вам нужно сделать в этом случае, это изменить пару строк. В одном случае вы пропускаете, если все пиксели в шаблоне равны 0, а во втором — когда вы пропускаете инвертирование шаблона.   -  person rayryeng    schedule 12.06.2014
comment
@ soumyadip93 soumyadip93 Вам также придется изменить строку в самом верху, где я ищу все пиксели в шаблоне, которые равны нулю. Поскольку у вас противоположный случай, вы можете безопасно удалить эту строку, поскольку пиксели шаблона равны 1.   -  person rayryeng    schedule 12.06.2014
comment
@rayryeng .. Да, я согласен с тем, что эти два поста очень похожи. но вы также упомянули в своем посте, что для этого требуется больше времени вычислений. Мне нужно это отслеживание для решения в реальном времени. На самом деле я думаю, что фрагмент изображения, который вы берете из всего изображения, может быть умножен на подходящую маску (круглый край в моем случае) перед кросс-корреляцией с шаблонным изображением.   -  person soumyadip.ghosh    schedule 12.06.2014
comment
@soumyadip93: Вы не указали режим реального времени! В этом случае мое решение не сработает. Кроме того, я не думаю, что normxcorr2 тоже работает в реальном времени. Этот пост был в основном для новичков в MATLAB, но если вам нужно что-то более реальное, я не вижу никакого способа использовать Image Processing Toolbox, поэтому в OP этого поста собирался использовать MEX.   -  person rayryeng    schedule 12.06.2014
comment
@soumyadip93: Я думаю, мы можем добиться того же, используя colfilt и im2col. Вы хотите, чтобы я написал решение для вас?   -  person rayryeng    schedule 12.06.2014
comment
@rayryeng.. Пожалуйста, напишите решение, я был бы признателен.. также постарайтесь сократить время вычислений как можно меньше   -  person soumyadip.ghosh    schedule 12.06.2014
comment
@soumyadip93: Готово. Посмотри. Это не проверено, но с небольшими размерами выборки, которые я указывал, это должно делать то, что просят. Удачи.   -  person rayryeng    schedule 12.06.2014
comment
Вы пробовали преобразование круга Хафа?   -  person japreiss    schedule 13.06.2014
comment
@japreiss.. Нет, не видел. Это дорого в вычислительном отношении?   -  person soumyadip.ghosh    schedule 13.06.2014
comment
@soumyadip93 - Вам нужна дополнительная помощь?   -  person rayryeng    schedule 16.06.2014
comment
@rayryeng.. Я ценю оба решения моего вопроса. На данный момент я в порядке. Если мне потребуется дополнительная помощь, я спрошу. Пожалуйста, не закрывайте вопрос кстати.   -  person soumyadip.ghosh    schedule 16.06.2014


Ответы (2)


Хорошо, давайте попробуем... Это решение пытается использовать существующую реализацию normxcorr2 и изменить ее, чтобы решить вашу проблему.

Формула нормализованной взаимной корреляции:

введите здесь описание изображения

В этом случае вы хотите изменить границы интеграции для каждого окна. Это, в свою очередь, влияет как на стандартные отклонения, так и на саму корреляцию. Разберемся с этим в несколько шагов:

Шаг 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)
person Xyand    schedule 12.06.2014
comment
Мне нравится логика этого решения. Я тестирую это сейчас. Не могли бы вы объяснить, почему он делает некоторые дополнительные свертки? - person soumyadip.ghosh; 13.06.2014
comment
Он не делает лишних сверток намеренно. Но он использует filter2 для вычисления исходного стандартного и модифицированного. Я называю это дополнительным, потому что оно уже было рассчитано где-то внутри normxcorr21, и мы фактически переоцениваем его. - person Xyand; 13.06.2014
comment
Выдает много ошибок. Прежде всего, для двумерной матрицы необходимо использовать std2, а не std. Кроме того, когда вы вычисляете стандартное отклонение матрицы запроса, оно показывает несоответствие размеров матрицы. Вам не кажется, что при вычислении std_query sum_query нужно заменить на sum_query^2? - person soumyadip.ghosh; 13.06.2014
comment
Вы правы, я написал это поздно ночью :) . Я внес некоторые изменения, дайте мне знать, если это работает. - person Xyand; 13.06.2014
comment
Еще есть несоответствие размеров в последней строке. Это связано с тем, что размеры запроса отличаются от std_query (и std_query_mask). Также 2-я строка должна быть '=' вместо '==' - person soumyadip.ghosh; 13.06.2014
comment
Как я уже сказал, у меня нет рабочего Matlab. Разница в размерах должна быть результатом различий в реализациях по умолчанию filter2 и normxcorr. Попробуйте добавить 'full' к filter2 вызовам. Если вам удалось решить это по-другому, не стесняйтесь обновлять ответ. Актуальный вопрос: stackoverflow.com/questions/9145107/ - person Xyand; 13.06.2014
comment
Давайте продолжим обсуждение в чате. - person Xyand; 13.06.2014

Это производная от предыдущего сообщения, где я дал ответ здесь: шаблон Matlab, соответствующий только 0 (или 1) в матрице

Однако в этом решении использовались циклы 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
(источник: www.jot.fm)

Таким образом, нам нужно вычесть среднее значение из каждой окрестности, а также нам нужно вычесть среднее значение из каждого из столбцов. Затем мы вычисляем формулу, как показано выше. Мы можем легко сделать это векторизованным в 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]);

Приведенный выше оператор возьмет перекрывающиеся окрестности M x N, определенные в NCC, и изменит их форму, чтобы она стала матрицей 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

Удачи!

person rayryeng    schedule 12.06.2014
comment
Этот пост посвящается Луису Мендо и Дивакару за то, что они bsxfun мастера :) - person rayryeng; 13.06.2014
comment
Может ли NCC получить одно значение сходства вместо строки или матрицы? Мне нужно, чтобы NCC вычислил сходство, подобное взаимной информации (MI). подсказка: для серых изображений. - person Mohammad nagdawi; 11.08.2018
comment
@Mohammadnagdawi Прости, я понятия не имею, о чем ты спрашиваешь. - person rayryeng; 11.08.2018
comment
Я получаю сообщение об ошибке при использовании im2col в этой функции. Он говорит, что массив превышает максимальный размер массива. Мой imSearch - 644 * 3456, а imTemplate - 321 * 2566. - person Afzal; 28.02.2020
comment
@Afzal Это изображение и шаблон истощат вашу память. Такое количество окрестностей для большого изображения и шаблона не будет работать с этим кодом. - person rayryeng; 28.02.2020
comment
@rayryeng Что бы вы порекомендовали в этом случае? - person Afzal; 29.02.2020
comment
См. изображения по адресу uk.mathworks.com/matlabcentral/answers. / - person Afzal; 29.02.2020