1

我有从图像 (3744x5616) 跟踪的多个对象的 x,y 像素坐标。坐标存储在称为对象的结构中,例如

objects(1).centre = [1868 1236]

每个对象都由一个数字代码唯一标识,例如

objects(i).code = 33

我希望能够记录任何两个对象每次进入彼此 300 像素的半径范围内。检查是否有任何对象正在接触然后记录交互中涉及的两个对象的身份的最佳方法是什么,例如对象 33 与对象 34 交互。

谢谢!

4

1 回答 1

3

我现在能想到的最好的事情是蛮力方法。只需检查一个对象中心与其他对象的距离,然后手动检查距离是否小于 300 像素。

如果你想要这么快,我们可能应该在没有任何工具箱的情况下做到这一点。您可以使用 vanilla MATLAB 智能地做到这一点bsxfunX首先,为每个对象的和Y坐标创建单独的数组:

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

[ objects.centre] 访问结构中每个字段的各个坐标centre并将它们解压缩到逗号分隔的列表中。我重塑了这个数组,使其成为 2 行,其中第一行是X坐标,第二行是Y坐标。我提取行并将它们放入单独的数组中。

接下来,为每个矩阵创建两个差分矩阵XY其中行表示一个唯一坐标,列表示另一个唯一坐标。该矩阵内的值是iij和列点之间的差异j

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

bsxfun代表B inary Singleton E X pansion FUN ction。如果您熟悉该repmat函数,它实际上会在底层复制矩阵和向量,以便您操作的两个输入具有相同的大小。在这种情况下,我正在做的是指定XY作为两个输入。一个是另一个的转置版本。通过这样做会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。如果你对XY点都这样做,你会得到一个点相对于对称矩阵中所有其他点的分量距离。


您会注意到这是一个反对称矩阵,其中对角线全部为 0 ... 是有道理的,因为一点的一维距离相对于自身的距离应该为 0。矩阵的左下三角形部分是右边的相反符号......因为减法的顺序。如果你用点 2 减去点 1,做相反的减法会给你相反的符号。但是,让我们假设行表示第一个对象,列表示第二个对象,因此您需要专注于下半部分。

现在,计算距离,并确保将上三角或下三角设置为,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);

IJ是确定矩阵中哪些行和列的值 < 300 的行/列对,因此在我们的例子中,数组中的每一对IJ都会为您提供彼此靠近的对象位置。

要最终找出正确的目标代码,您可以执行以下操作:

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

这使用IJ访问逗号分隔列表中相似的那些对象的相应代码,并将它们并排放置到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) 和 (33,35) 配对的对象 33、34 和 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循环也可以接受矩阵,并且索引变量从左到右一次为您提供每个矩阵的一列。因此,我转置了codes数组,使每对唯一代码成为一列。我只是访问每列的第一个和第二个元素并将其打印出来。

我们得到:

Object with code 33 interacted with object with code 34
Object with code 33 interacted with object with code 35
于 2015-09-11T15:41:02.693 回答