Сопоставление шаблонов Matlab с использованием БПФ

Я борюсь с сопоставлением шаблонов в области Фурье в Matlab. Вот мои изображения (художник RamalamaCreatures на DeviantArt):

possum.jpg possum_ear.jpg

Моя цель — поместить ограничительную рамку вокруг уха опоссума, как в этом примере (где я выполнил сопоставление с шаблоном с помощью normxcorr2):

Цель: ухо опоссума ограничено

Вот код Matlab, который я использую:

clear all; close all;

template = rgb2gray(imread('possum_ear.jpg'));
background = rgb2gray(imread('possum.jpg'));

%% calculate padding
bx = size(background, 2); 
by = size(background, 1);
tx = size(template, 2); % used for bbox placement
ty = size(template, 1);

%% fft
c = real(ifft2(fft2(background) .* fft2(template, by, bx)));

%% find peak correlation
[max_c, imax]   = max(abs(c(:)));
[ypeak, xpeak] = find(c == max(c(:)));
figure; surf(c), shading flat; % plot correlation 

%% display best match
hFig = figure;
hAx  = axes;
position = [xpeak(1)-tx, ypeak(1)-ty, tx, ty];
imshow(background, 'Parent', hAx);
imrect(hAx, position);

Код не работает должным образом — он не идентифицирует правильный регион. Это неудачный результат — неправильная область обрамлена: Ошибка сопоставления с шаблоном

Это поверхностный график корреляций для неудачного совпадения: график серфинга для неудачного сопоставления с шаблоном

Надеюсь, вы можете помочь! Спасибо.


person Chris Parry    schedule 19.09.2015    source источник
comment
Если вы посмотрите на реализацию normxcorr2 в Image Processing Toolbox, один из кодовых путей вычисляется в частотной области. Вероятно, вам следует взглянуть на это как на ссылку.   -  person Alex Taylor    schedule 21.09.2015


Ответы (2)


То, что вы делаете в своем коде, на самом деле вовсе не корреляция. Вы используете шаблон и выполняете свертку с входным изображением. Если вы помните из преобразования Фурье, умножение спектров двух сигналов эквивалентно свертке двух сигналов во временной/пространственной области.

По сути, вы используете шаблон в качестве ядра и используете его для фильтрации изображения. Затем вы находите максимальный ответ этого вывода, и это то, что считается тем, где находится шаблон. Когда ответ помещается в коробку, это имеет смысл, потому что эта область полностью белая, и использование шаблона в качестве ядра с полностью белой областью даст вам очень большой ответ, поэтому он, скорее всего, идентифицировал эту область как максимальную. отклик. В частности, область будет иметь много высоких значений (~ 255 или около того), и естественное выполнение свертки с патчем шаблона и этой областью даст вам очень большой результат из-за операции, являющейся взвешенной суммой. Таким образом, если вы использовали шаблон в темной области изображения, вывод был бы небольшим, что неверно, поскольку шаблон также состоит из темных пикселей.


Однако вы, безусловно, можете использовать преобразование Фурье, чтобы определить, где находится шаблон, но я бы рекомендовал вам использовать фазовую корреляцию вместо этого. По сути, вместо вычисления умножения двух спектров вы вычисляете спектр перекрестной мощности. Взаимный спектр мощности R между двумя сигналами в частотной области определяется как:

Источник: Википедия

Ga и Gb — исходное изображение и шаблон в частотной области, а * — сопряженное. o — это то, что известно как произведение Адамара или поэлементное произведение. Еще хочу отметить, что деление числителя и знаменателя этой дроби тоже поэлементное. Используя спектр перекрестной мощности, если вы найдете здесь место (x,y), которое дает абсолютный максимальный отклик, это то место, где шаблон должен располагаться на фоновом изображении.

Таким образом, вам просто нужно изменить строку кода, которая вычисляет «корреляцию», чтобы вместо этого она вычисляла перекрестный спектр мощности. Однако я хотел бы отметить нечто очень важное. Когда вы выполняете normxcorr2, корреляция начинается прямо в верхнем левом углу изображения. Сопоставление с шаблоном начинается в этом месте и сравнивается с окном размером с шаблон, где левый верхний угол является источником. При поиске местоположения совпадения шаблона местоположение определяется по отношению к верхнему левому углу сопоставленного окна. После вычисления normxcorr2 вы обычно добавляете половину строк и половину столбцов максимального отклика, чтобы найти центральное местоположение.

Поскольку мы выполняем более или менее те же операции для сопоставления шаблонов (скользящие окна, корреляция и т. д.) с БПФ/частотной областью, когда вы закончите поиск пика в этом массиве корреляции, вы должны также принять это во внимание. . Однако ваш вызов imrect для рисования прямоугольника вокруг того места, где совпадает шаблон, в любом случае занимает верхний левый угол ограничивающей рамки, поэтому здесь нет необходимости делать смещение. Таким образом, мы собираемся немного изменить этот код, но помните о логике смещения при использовании этого кода на будущее, если хотите найти центральное местоположение совпадения.


Я также изменил ваш код, чтобы читать изображения непосредственно из StackOverflow, чтобы он воспроизводился:

clear all; close all;

template = rgb2gray(imread('http://i.stack.imgur.com/6bTzT.jpg'));
background = rgb2gray(imread('http://i.stack.imgur.com/FXEy7.jpg'));

%% calculate padding
bx = size(background, 2); 
by = size(background, 1);
tx = size(template, 2); % used for bbox placement
ty = size(template, 1);

%% fft
%c = real(ifft2(fft2(background) .* fft2(template, by, bx)));

%// Change - Compute the cross power spectrum
Ga = fft2(background);
Gb = fft2(template, by, bx);
c = real(ifft2((Ga.*conj(Gb))./abs(Ga.*conj(Gb))));

%% find peak correlation
[max_c, imax]   = max(abs(c(:)));
[ypeak, xpeak] = find(c == max(c(:)));
figure; surf(c), shading flat; % plot correlation    

%% display best match
hFig = figure;
hAx  = axes;

%// New - no need to offset the coordinates anymore
%// xpeak and ypeak are already the top left corner of the matched window
position = [xpeak(1), ypeak(1), tx, ty];
imshow(background, 'Parent', hAx);
imrect(hAx, position);

При этом я получаю следующее изображение:

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

Я также получаю следующее, когда показываю поверхностный график перекрестного спектра мощности:

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

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


Надеюсь это поможет!

person rayryeng    schedule 19.09.2015
comment
@ChrisParry - С удовольствием! Кстати, я знаю, почему сопоставление дало вам нижний левый угол. Я добавил это к своему ответу, но поскольку вы используете шаблон в качестве ядра, выполнение свертки с этим ядром и очень белыми областями, естественно, даст вам большой ответ, и поэтому поиск максимального ответа будет более естественным. в этой области. Вот почему фазовая корреляция здесь лучше подходит, потому что она естественным образом определяет, насколько сместились два изображения. Я рад, что помог. Удачи! - person rayryeng; 19.09.2015
comment
@ChrisParry - я также добавил, как выглядит график поверхности спектра мощности. Лучше увидеть его, чтобы вы могли убедиться, что место, которое мы получили, определенно находится там, где находится шаблон. - person rayryeng; 19.09.2015
comment
Отлично, спасибо! Я уверен, что этот ответ поможет и многим другим людям. - person Chris Parry; 19.09.2015
comment
@ChrisParry - была небольшая ошибка в том, как я интерпретировал местоположение соответствия шаблона. Совпадающие координаты — это верхний левый угол окна, поэтому просто сместите половину строк и столбцов, чтобы получить центр. Однако вашему вызову imrect в любом случае нужен верхний левый угол, поэтому я изменил эту логику. Результаты все те же, и я отредактировал сообщение. - person rayryeng; 19.09.2015
comment
Это работает хорошо для меня, спасибо. Поскольку сопоставление происходит очень быстро, а ориентация шаблона неизвестна, я могу просто перебирать различные повороты. Однако сопоставление оказывается также очень чувствительным к масштабу шаблона. Есть ли хороший способ справиться с этим, кроме перебора различных преобразований шаблона? - person Chris Parry; 19.09.2015
comment
Сопоставление шаблонов, к сожалению, непростое дело. В этом конкретном случае у вас нет другого выбора, кроме как перебрать каждый размер шаблона и использовать тот же подход. На самом деле, именно так работает большинство детекторов больших двоичных объектов. Они используют пространство масштаба Гаусса или разности Гаусса и обнаруживают объекты в каждом масштабе... Таким образом, зацикливание является естественным. Однако, поскольку это довольно быстро, вы должны быть в состоянии сделать это в кратчайшие сроки, или, возможно, вы можете посмотреть на parfor конструкции, чтобы делать что-то параллельно. Кстати, я рад, что это работает и быстро! - person rayryeng; 19.09.2015
comment
К вашему сведению, регистрация изображений на основе фазовой корреляции уже реализована в Image Processing Toolbox как imregcorr: mathworks.com/help/images/ref/imregcorr.html - person Alex Taylor; 21.09.2015
comment
Хорошо, просто подумал, что стоит упомянуть, поскольку фазовая корреляция - полезный алгоритм. - person Alex Taylor; 21.09.2015
comment
@Alex - imregcorr был представлен только в R2014a, которого у меня сейчас нет. Мне также нравится реализовывать вещи с нуля для лучшего понимания ... такое впечатление я получил от ОП ... поэтому я решил, что хорошо ... давайте также сделаем это с нуля. Вы по достоинству оцените алгоритмы, если сделаете это с самого начала!... и да, это, безусловно, полезно. Спасибо за комментарии! - person rayryeng; 21.09.2015
comment
Что делать, если необходимо извлечь несколько пиковых точек, пожалуйста, помогите - person Rajnikant Sharma; 15.06.2016
comment
@RajnikantSharma Это становится сложнее. Вам придется изменить оператор find, чтобы вместо поиска максимального пикового отклика вы искали пики выше определенного порога. Это может стать очень сложной задачей, потому что установка правильного порога — непростая задача. Одно предложение состоит в том, чтобы, возможно, использовать процент от максимального пика в качестве порога... что-то вроде 0.8*max_c, но это эвристика, и вам просто нужно попробовать. Логика рисования ограничивающих рамок того места, где был обнаружен шаблон, может быть изменена для зацикливания после того, как вы найдете пики. - person rayryeng; 15.06.2016
comment
Спасибо Rayryeng, мне удалось использовать цикл, но в некоторых случаях я получаю c=NaN, для очень маленького шаблона (60x80), вырезанного из исходного изображения (3500x2500), этот шаблон содержит 4 линии, пересекающиеся друг с другом под определенным углом и образующие шаблон. - person Rajnikant Sharma; 19.08.2016
comment
@RajnikantSharma Вероятно, у шаблона недостаточно различительной способности для получения хороших результатов (т. е. его преобразование Фурье недостаточно сложно). В результате вы также можете получить ошибки деления на ноль, поэтому, возможно, вы получаете NaN. Кстати, если вам понравился этот ответ, пожалуйста, заплатите за него вперед и проголосуйте. Удачи. - person rayryeng; 19.08.2016

Только что реализовал то же самое с python с теми же идеями, что и @rayryeng, используя функции scipy.fftpack.fftn() / ifftn() со следующим результатом для тех же целевых и шаблонных изображений:

import numpy as np
import scipy.fftpack as fp
from skimage.io import imread
from skimage.color import rgb2gray, gray2rgb
import matplotlib.pylab as plt
from skimage.draw import rectangle_perimeter

im = 255*rgb2gray(imread('http://i.stack.imgur.com/FXEy7.jpg'))    # target
im_tm = 255*rgb2gray(imread('http://i.stack.imgur.com/6bTzT.jpg')) # template

# FFT 
F = fp.fftn(im)                   
F_tm = fp.fftn(im_tm, shape=im.shape)

# compute the best match location
F_cc = F * np.conj(F_tm)
c = (fp.ifftn(F_cc/np.abs(F_cc))).real
i, j = np.unravel_index(c.argmax(), c.shape)
print(i, j)
# 214 317

# draw rectangle around the best match location
im2 = (gray2rgb(im)).astype(np.uint8)
rr, cc = rectangle_perimeter((i,j), end=(i + im_tm.shape[0], j + im_tm.shape[1]), shape=im.shape)
for x in range(-2,2):
    for y in range(-2,2):
        im2[rr + x, cc + y] = (255,0,0)

# show the output image
plt.figure(figsize=(10,10))
plt.imshow(im2)
plt.axis('off')
plt.show()

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

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

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

Следует отметить одну вещь: вывод очень сильно зависит от сходства размера и формы объекта, который должен быть сопоставлен с шаблоном, если он сильно отличается от изображения шаблона, шаблон может вообще не совпадать. .

person Sandipan Dey    schedule 24.05.2020
comment
Было бы полезно, если бы вы могли включить исходный код Python. Вопрос, конечно, в MATLAB, но было бы неплохо закрыть пробел и посмотреть еще и реализацию на Python. Меня часто просили перевести приведенную выше логику на Python, но у меня нет времени. - person rayryeng; 24.05.2020