【问题标题】:Calculate distance point to line segment with special cases用特殊情况计算点到线段的距离
【发布时间】:2018-02-15 04:48:53
【问题描述】:

我需要实现以下逻辑: 给定一组 2d 样本点(以 x-y 坐标对形式给出)和一组线段(也是 x-y 坐标对)。 编辑 1:如何计算(矢量化)点 pi 到线 Li 的距离?

这些点大约在线附近,我想获得每个样本点到最近线段的距离。可能是点,有点“偏离”(参见第一张图片中的 p6),这些点可以通过以下算法找到:

  1. 案例:投影到线段的样本点“左外”。在这种情况下,我需要从 p1 到 x1 的欧几里得距离。
  2. 案例:投影到线段的样本点位于线段“内部”。在这种情况下,我需要从 p2 到从 x1 到 x2 的线的距离。
  3. 案例:投影到线段的样本点在“正外”。在这种情况下,我需要从 p3 到 x2 的欧式距离。

有一个矢量化解决方案(感谢用户Andy),它使用“全局”投影,始终假设每个点-线段对的情况2。但是,这将返回 p1 ... p3 的距离 [1 1 1],其中所需的距离为 [1.4142 1 1.4142]。可以根据这些需求修改此代码吗?

ptsx = [1 3 5];
ptsy= [1 1 1];

linesx = [2 4];
linesy = [0 0];

points = [ptsx;ptsy];
lines = [linesx;linesy];

% start of lines
l = [linesx(1:end-1);linesy(1:end-1)];
% vector perpendicular on line
v = [diff(linesy);-diff(linesx)];
% make unit vector
v = v ./ hypot (v(1,:),v(2,:));
v = repmat (v, 1, 1, size (points, 2));
% vector from points (in third dimension) to start of lines (second dimension)
r = bsxfun (@minus, permute (points, [1 3 2]), l);
d = abs (dot (v, r));
dist = squeeze (min (d, [], 2))

从数学上讲,可以通过查看vec(pi-x1)vec(x2-x1) 的投影长度来区分案例。如果这个长度因子 1,那么点在线段的“右边” .

编辑 1:我将添加一个伪代码来解释如何使用双 for 循环解决此问题,但由于我有大约 6000 个样本和 10000 行,循环解决方案不适合我。

for each sample point pi
  for each linesegment Li
    a = vector from start of Li to end of Li
    b = vector from pi to start of Li
    relLength = dot(a,b)/norm(a)^2
    if relLength < 0: distance = euclidean distance from start of Li to pi
    if relLength > 1: distance = euclidean distance from end of Li to pi
    else: distance = perpendicular distance from pi to Li
  endfor 
endfor

编辑 2 / 2017-09-07:我设法矢量化了该算法的第一部分。 relLength 现在包含每个pi-startOfLi 在每个线段上的投影的相对长度。

ptsx = [0.5 2 3 5.5 8 11];
ptsy= [1  2 -1.5 0.5 4 5];

linesx = [0 2 4 6 10 10 0 0];
linesy = [0 0 0 0  0  4 4 0];

points = [ptsx;ptsy];
lines = [linesx;linesy];

% Start of all lines
L1 = [linesx(1:end-1); linesy(1:end-1)];
% Vector of each line segment
a = [diff(linesx); diff(linesy)];
b = bsxfun(@minus, permute(points, [1 3 2]), L1);
aRep = repmat(a, 1, 1, length(b(1,1,:)));
relLength = dot(aRep,b)./norm(a, 'cols').^2

【问题讨论】:

  • 您只显示只有一行开头和结尾的情况。如果您有一个“V”形多边形,其中的点可以“投影”到多条线上怎么办?据我了解您之前的问题,案例 1 和案例 3 只是第一个和最后一个线段的边界案例,不是吗。顺便说一句,这听起来很像 XY 问题。如果你没听过xyproblem.info真的值得一读
  • 更重要的是:到目前为止,您尝试过什么?该代码已记录在案,显然您知道数学
  • 我已经阅读了 xyproblem 并在此基础上给出了一个简单的“任务描述”以及我解决问题的方法。我希望我能澄清我需要实现的目标。我已经实现了我在开篇文章中编辑的伪代码,它可以工作,但我需要一个矢量化版本。
  • 你总是有一个多边形(闭合的折线)吗?在您的情况下,线条是否总是水平或垂直?据我了解您的问题,您总是希望从点到折线的最小距离(如果您称它为“线内”,则它垂直于折线)
  • 我没有关于这些行的任何信息。线条可以在空间中任意分布,不必是水平或垂直的(因此情况不同)。我唯一可以肯定的是,线条中没有“间隙”,例如线段的结束总是下一个的开始(显然,除了第一个和最后一个)。是的,我需要最小距离。

标签: vectorization octave


【解决方案1】:

在 GNU Octave 中:

points = [1 4.3 3.7 2.9;0.8 0.8 2.1 -0.5];
lines = [0 2 4 3.6;0 -1 1 1.75];

% plot them
hold off
plot (points(1,:), points(2,:), 'or')
hold on
plot (lines(1,:), lines(2,:), '-xb')
text (points(1,:), points(2,:),...
      arrayfun (@(x) sprintf('  p%i',x),...
                1:columns(points),'UniformOutput', false))
axis ('equal')
grid on
zoom (0.9);

% some intermediate vars
s_lines = lines (:,1:end-1);    % start of lines
d_lines = diff(lines, 1, 2);    % vectors between line points
l_lines = hypot (d_lines(1,:),
                 d_lines(2,:)); % length of lines

现在做“真正的”工作:

% vectors perpendicular on lines
v = [0 1;-1 0] * d_lines;
vn = v ./ norm (v, 2, 'cols');   %make unit vector

% extend to number of points
vn = repmat (vn, 1, 1, columns (points));
points_3 = permute (points, [1 3 2]);

% vectors from points (in third dimension) to start of lines (second dimension)
d =  dot (vn, points_3 - s_lines);

% check if the projection is on line
tmp = dot (repmat (d_lines, 1, 1, columns (points)),...
           points_3 - s_lines)./l_lines.^2;
point_hits_line = tmp > 0 & tmp < 1;

% set othogonal distance to Inf if there is no hit
d(~ point_hits_line) = Inf;

dist = squeeze (min (abs(d), [], 2));

% calculate the euclidean distance from points to line start/stop
% if the projection doesn't hit the line
nh = isinf (dist);
tmp = points_3(:,:,nh) - lines;
tmp = hypot(tmp(1,:,:),tmp(2,:,:));
tmp = min (tmp, [], 2);
% store the result back
dist (nh) = tmp

将结果绘制为点周围的黄色圆圈

% use for loops because this hasn't to be fast
t = linspace (0, 2*pi, 40);
for k=1:numel(dist)
  plot (points (1, k) + cos (t) * dist(k),
        points (2, k) + sin (t) * dist(k),
        '-y')
end

【讨论】:

  • 看起来这是一个不错的解决方案,欣赏最后的视觉效果。我认为如果您插入几行来解释该方法可能会有所帮助,因为我不确定正在发生什么(当然是为我自己说话)
【解决方案2】:

使用八度几何包

Octaves geometry 包包含解决所请求问题的所有必要工具。有两个函数可以解决您的问题:

函数distancePointPolylinedistancePointPolygon 都应该能够计算请求的距离。多边形是闭合的折线。

示例

以下脚本演示了函数的使用。有关图形结果,请参见 figure

% Load octave geometry package (package is also available for matlab)
pkg load geometry
% Define points
points = [1,4.3,3.7,2.9; 0.8, 0.8, 2.1, -0.5]';
% Define polyline
lines  = [0, 2, 4, 3.6;   0, -1, 1, 1.75]';

% Calculate distance
d = distancePointPolyline (points,lines);

% Produce figure
figure('name','Distance from points to polyline');
hold all
drawPoint(points);
drawPolyline(lines);
drawCircle(points, d);
axis equal

结果

【讨论】:

    猜你喜欢
    • 2013-01-26
    • 1970-01-01
    • 1970-01-01
    • 2010-11-20
    • 2020-12-08
    • 1970-01-01
    • 2014-11-11
    • 2021-02-17
    • 1970-01-01
    相关资源
    最近更新 更多