Как отслеживать касание нескольких объектов в MATLAB?

У меня есть координаты x, y пикселей нескольких объектов, которые были отслежены с изображения (3744x5616). Координаты хранятся в структуре, называемой объектами, например.

objects(1).centre = [1868 1236]

Каждый объект однозначно идентифицируется числовым кодом, например.

objects(i).code = 33

Я хочу иметь возможность записывать каждый раз, когда любые два объекта находятся в радиусе 300 пикселей друг от друга. Как лучше всего проверить, соприкасаются ли какие-либо объекты, а затем записать идентичность обоих объектов, участвующих во взаимодействии, например, объект 33 взаимодействует с объектом 34.

Спасибо!


person Sam D.    schedule 11.09.2015    source источник


Ответы (1)


Лучшее, что я могу сейчас придумать, это метод грубой силы. Просто проверьте расстояния от центра одного объекта до остальных объектов и вручную проверьте, составляют ли расстояния ‹ 300 пикселей.

Если вы хотите это быстро, мы, вероятно, должны сделать это без каких-либо наборов инструментов. Вы можете разумно сделать это с помощью vanilla MATLAB, используя bsxfun. Сначала создайте отдельные массивы для координат X и Y каждого объекта:

points = reshape([objects.centre], 2, []);
X = points(1,:);
Y = points(2,:);

[objects.centre] получает доступ к отдельным координатам каждого centre поля в вашей структуре и распаковывает их в список, разделенный запятыми. Я изменяю форму этого массива так, чтобы он состоял из 2 строк, где первая строка — это координата X, а вторая строка — это координата Y. Я извлекаю строки и помещаю их в отдельные массивы.

Затем создайте две матрицы различий для каждого X и Y, где строки обозначают одну уникальную координату, а столбцы обозначают другую уникальную координату. Значения внутри этой матрицы представляют собой разницу между точкой i в строке i и точкой j в столбце j:

Xdiff = bsxfun(@minus, X.', X);
Ydiff = bsxfun(@minus, Y.', Y);

bsxfun означает Binary Singleton EXpansion FUNction. Если вы знакомы с функцией repmat, она, по сути, копирует матрицы и векторы под капотом, так что оба входных параметра, с которыми вы работаете, имеют одинаковый размер. В данном случае я указываю X или Y в качестве обоих входных данных. Один является транспонированной версией другого. Делая это, bsxfun автоматически транслирует каждый вход, чтобы входы совпадали по измерению. В частности, первый вход представляет собой вектор-столбец X, поэтому он повторяется и складывается по горизонтали столько раз, сколько значений содержится в X.

Аналогично это делается для значения Y. После этого вы выполняете поэлементное вычитание для обоих выходов и получаете покомпонентное вычитание между одной точкой и другой точкой для X и Y, где строка дает вам первую точку, а столбец дает вам вторую точку. В качестве игрушечного примера представьте, что у нас есть X = [1 2 3]. Выполнение вызова bsxfun с использованием приведенного выше кода дает:

>> Xdiff = bsxfun(@minus, [1 2 3].', [1 2 3])

Xdiff =

##  |  1     2     3  
----------------------
 1  |  0    -1    -2
 2  |  1     0    -1
 3  |  2     1     0

Я поместил в вывод несколько дополнительных символов, но они используются исключительно для иллюстрации и в качестве ориентира. Взяв значение строки из столбца ## и вычтя из значения столбца из строки ##, вы получите желаемое вычитание. Например, второй столбец первой строки иллюстрирует 1 - 2 = -1. Вторая строка, третий столбец иллюстрирует 2 - 3 = -1. Если вы сделаете это как для точек X, так и для Y, вы получите покомпонентные расстояния для одной точки по отношению ко всем другим точкам в симметричной матрице.


Вы заметите, что это антисимметричная матрица, где вся диагональ равна 0... имеет смысл, поскольку расстояние одного измерения одной точки относительно самой себя должно быть равно 0. Нижняя левая треугольная часть матрицы - это противоположный знак справа... из-за порядка вычитания. Если вы вычтете точку 1 из точки 2, то обратное вычитание даст вам противоположный знак. Однако давайте предположим, что строки обозначают первый объект, а столбцы — второй объект, поэтому вам следует сосредоточиться на нижней половине.

Теперь вычислите расстояние и убедитесь, что вы установили либо верхнюю, либо нижнюю половину треугольника на NaN, потому что при вычислении расстояния знак игнорируется. Если вы не проигнорируете это, мы обнаружим повторяющиеся взаимодействующие объекты, поэтому взаимодействие объекта 3 и объекта 1 будет отличаться от взаимодействия объекта 1 и объекта 3. или опустите треугольную половину до NaN для следующего шага. Предполагая евклидово расстояние:

dists = sqrt(Xdiff.^2 + Ydiff.^2);
dists(tril(ones(numel(objects))==1)) = NaN;

В первой строке вычисляется евклидово расстояние для всех пар точек, и мы используем tril для извлечения нижней треугольной части матрицы, состоящей из всех логических 1. Извлекая эту матрицу, мы используем ее для установки нижней половины матрицы в NaN. Это позволяет нам пропускать элементы, которые нас не интересуют. Обратите внимание, что я также установил диагональ на 0, потому что нас не интересует расстояние от одного объекта до самого себя.

Теперь, когда вы, наконец, здесь, найдите объекты размером ‹ 300 пикселей:

[I,J] = find(dists < 300);

I и J — это пары строк и столбцов, которые определяют, какие строки и столбцы в матрице имеют значения ‹ 300, поэтому в нашем случае каждая пара I и J в массиве дает вам расположение объектов, которые находятся близко друг к другу.

Чтобы наконец выяснить правильные коды объектов, вы можете сделать:

codes = [[objects(I).code].' [objects(J).code].'];

Это использует I и J для доступа к соответствующим кодам тех объектов, которые были похожи в списке, разделенном запятыми, и помещает их рядом в матрицу N x 2. Таким образом, каждая строка codes дает вам уникальные пары объектов, которые удовлетворяют требованиям расстояния.


Для копирования и вставки:

points = reshape([objects.centre], 2, []);
X = points(1,:);
Y = points(2,:);
Xdiff = bsxfun(@minus, X.', X);
Ydiff = bsxfun(@minus, Y.', Y);
dists = sqrt(Xdiff.^2 + Ydiff.^2);
dists(tril(ones(numel(objects))==1)) = NaN;
[I,J] = find(dists < 300);    
codes = [[objects(I).code].' [objects(J).code].'];

Пример игрушки

Вот пример, который мы можем использовать, чтобы проверить правильность того, что у нас есть:

objects(1).centre = [1868 1236];
objects(2).centre = [2000 1000];
objects(3).centre = [1900 1300];
objects(4).centre = [3000 2000];
objects(1).code = 33;
objects(2).code = 34;
objects(3).code = 35;
objects(4).code = 99;

Я инициализировал 4 объекта с разными центроидами и разными кодами. Посмотрим, что нам даст массив dists после того, как мы его вычислим:

>> format long g
>> dists

dists =

                       NaN          270.407100498489          71.5541752799933          1365.69396278961
                       NaN                       NaN          316.227766016838           1414.2135623731
                       NaN                       NaN                       NaN          1303.84048104053
                       NaN                       NaN                       NaN                       NaN

Я намеренно сделал последнюю точку дальше любой из трех других точек, чтобы мы могли показать случаи, когда есть точки, расположенные не рядом с другими.

Как видите, точки (1,2) и (1,3) находятся рядом друг с другом, что мы и получим, когда закончим остальную часть кода. Это соответствует объектам 33, 34 и 35 с парами (33,34) и (33,35). Точки с кодами 34 и 35 я сделал немного меньше, но они все равно больше порога в 300 пикселей, поэтому они тоже не учитываются:

>> codes

codes =

    33    34
    33    35

Теперь, если вы хотите отобразить это в предварительном формате, возможно, используйте цикл for:

for vec = codes.'
    fprintf('Object with code %d interacted with object with code %d\n', vec(1), vec(2));
end

Этот цикл for немного сложен. Малоизвестный факт, что циклы for могут также принимать матрицы< /a>, а переменная index дает вам по одному столбцу каждой матрицы за раз слева направо. Поэтому я транспонировал массив codes так, чтобы каждая пара уникальных кодов стала столбцом. Я просто получаю доступ к первому и второму элементу каждого столбца и распечатываю его.

Мы получили:

Object with code 33 interacted with object with code 34
Object with code 33 interacted with object with code 35
person rayryeng    schedule 11.09.2015
comment
Отличный ответ спасибо! Я очень ценю объяснение каждого шага, все имеет смысл и работает так, как я ожидал. Лучшее, что я мог придумать, это перебрать все возможные перестановки пар и проверить расстояние каждой пары объектов, но гораздо эффективнее использовать матрицы различий, я бы никогда не подумал об этом. - person Sam D.; 14.09.2015
comment
Только одна вещь с формулировкой: когда вы говорите, убедитесь, что для верхней половины треугольника установлено значение NaN, вы имели в виду установить для верхнего или нижнего треугольника значение NaN, потому что при вычислении расстояний оба треугольника будут одинаковыми? - person Sam D.; 14.09.2015
comment
(1) Не за что :) Я тоже думал перебрать все пары, но матрицы расстояний вступили в игру, как только я начал делать кое-что на бумаге. Это было лучшее, что я мог придумать, и это, безусловно, будет увеличиваться для больших очков. (2) Да, это именно то, что я имел в виду по поводу настройки NaN. Я редактировал свой пост и забыл удалить часть текста. Я изменю это сейчас. Спасибо! - person rayryeng; 14.09.2015