- Halcon 的 distance_lc 算子用于计算一条线与一个轮廓之间的距离,通常用于测量一个几何元素与轮廓之间的距离。
一、任务描述
实现计算从线段到轮廓的最近距离。
输入:
一条线段,由两点 (x1, y1) 和 (x2, y2) 定义。
一个轮廓,通常由一系列的点组成,可以视为一个多边形的边界。目标:
计算这条线与轮廓之间的最小距离。
二、计算步骤:
- 线段定义:线段由两个端点定义,即 (x1, y1) 和 (x2, y2)。
- 轮廓定义:轮廓是一个点集,可以是闭合或开口的。
- 距离计算:我们需要计算线段到轮廓的最小距离。可以通过以下步骤来计算:
- 遍历轮廓上的每一条边(即相邻的点对),计算线段到该边的距离。
- 通过投影方法计算线段到该边的最短距离。
- 返回最小距离:找到与线段最近的轮廓边并返回其距离。
三、程序
using OpenCvSharp;
using System;
using System.Collections.Generic;
namespace OpenCVSharpExamples
{
public class DistanceCalculator
{
/// <summary>
/// 计算一条线段和一个轮廓之间的最小距离
/// </summary>
/// <param name="lineStart">线段的起始点 (x1, y1)。</param>
/// <param name="lineEnd">线段的结束点 (x2, y2)。</param>
/// <param name="contour">轮廓,表示为一系列的点。</param>
/// <returns>返回线段到轮廓的最小距离。</returns>
public static double CalculateDistanceToContour(Point lineStart, Point lineEnd, List<Point> contour)
{
double minDistance = double.MaxValue;
// 遍历轮廓上的每一条边(每两个相邻的点)
for (int i = 0; i < contour.Count; i++)
{
// 获取当前边的两个端点
Point contourStart = contour[i];
Point contourEnd = contour[(i + 1) % contour.Count]; // 闭合轮廓
// 计算线段到轮廓边的距离
double distance = LineToSegmentDistance(lineStart, lineEnd, contourStart, contourEnd);
// 更新最小距离
minDistance = Math.Min(minDistance, distance);
}
return minDistance;
}
/// <summary>
/// 计算一条线段到另一条线段的最短距离
/// </summary>
/// <param name="line1Start">第一条线段的起始点 (x1, y1)。</param>
/// <param name="line1End">第一条线段的结束点 (x2, y2)。</param>
/// <param name="line2Start">第二条线段的起始点 (x3, y3)。</param>
/// <param name="line2End">第二条线段的结束点 (x4, y4)。</param>
/// <returns>返回两条线段的最短距离。</returns>
private static double LineToSegmentDistance(Point line1Start, Point line1End, Point line2Start, Point line2End)
{
// 线段1的方向向量
double v1x = line1End.X - line1Start.X;
double v1y = line1End.Y - line1Start.Y;
// 线段2的方向向量
double v2x = line2End.X - line2Start.X;
double v2y = line2End.Y - line2Start.Y;
// 线段1的起点到线段2起点的向量
double v3x = line2Start.X - line1Start.X;
double v3y = line2Start.Y - line1Start.Y;
// 计算两个线段的方向向量的叉积(用于计算是否相交)
double cross = v1x * v2y - v1y * v2x;
// 如果叉积为零,则表示线段平行
if (Math.Abs(cross) < 1e-6)
{
// 计算线段端点到线段的距离
double distance1 = PointToSegmentDistance(line1Start, line2Start, line2End);
double distance2 = PointToSegmentDistance(line1End, line2Start, line2End);
return Math.Min(distance1, distance2);
}
// 否则,计算线段与线段之间的最短距离
double t = ((v3x * v2y) - (v3y * v2x)) / cross;
double u = ((v3x * v1y) - (v3y * v1x)) / cross;
// 如果t和u都在0到1之间,则线段相交,返回距离为0
if (t >= 0 && t <= 1 && u >= 0 && u <= 1)
{
return 0;
}
// 否则,计算端点到线段的距离
double distance1 = PointToSegmentDistance(line1Start, line2Start, line2End);
double distance2 = PointToSegmentDistance(line1End, line2Start, line2End);
return Math.Min(distance1, distance2);
}
/// <summary>
/// 计算点到线段的最短距离
/// </summary>
/// <param name="point">待计算的点。</param>
/// <param name="segmentStart">线段的起点。</param>
/// <param name="segmentEnd">线段的终点。</param>
/// <returns>返回点到线段的最短距离。</returns>
private static double PointToSegmentDistance(Point point, Point segmentStart, Point segmentEnd)
{
// 线段的方向向量
double dx = segmentEnd.X - segmentStart.X;
double dy = segmentEnd.Y - segmentStart.Y;
// 点到线段起点的向量
double px = point.X - segmentStart.X;
double py = point.Y - segmentStart.Y;
// 线段的长度的平方
double segmentLengthSquared = dx * dx + dy * dy;
// 如果线段长度为零,返回点到起点的距离
if (segmentLengthSquared == 0)
{
return Math.Sqrt(px * px + py * py);
}
// 投影点 t,限制在[0, 1]区间
double t = Math.Max(0, Math.Min(1, (px * dx + py * dy) / segmentLengthSquared));
// 计算投影点的坐标
double projectionX = segmentStart.X + t * dx;
double projectionY = segmentStart.Y + t * dy;
// 计算点到投影点的距离
double distX = point.X - projectionX;
double distY = point.Y - projectionY;
return Math.Sqrt(distX * distX + distY * distY);
}
public static void Main(string[] args)
{
// 定义一条线段
Point lineStart = new Point(0, 0);
Point lineEnd = new Point(5, 0);
// 定义一个轮廓(例如一个正方形)
List<Point> contour = new List<Point>
{
new Point(1, 1),
new Point(4, 1),
new Point(4, 4),
new Point(1, 4)
};
// 计算线段到轮廓的最小距离
double distance = CalculateDistanceToContour(lineStart, lineEnd, contour);
// 输出结果
Console.WriteLine($"The minimum distance from the line to the contour is: {distance}");
}
}
}
四、代码解释
1、CalculateDistanceToContour
- 计算线段和轮廓之间的最小距离。
- 轮廓是一个 List<Point>,包含一系列点。
- 对每一条轮廓的边(即每对相邻的点),调用 LineToSegmentDistance 来计算线段到该边的距离,并更新最小距离。
2、LineToSegmentDistance:
- 计算两条线段之间的最短距离。
- 线段通过两个端点定义(line1Start, line1End 和 line2Start, line2End)。
- 通过向量叉积判断线段是否相交,如果相交则返回 0。否则计算端点到另一条线段的距离。
3、 PointToSegmentDistance
- 该函数计算一个点到一条线段的最短距离。它的基本思想是通过将点投影到线段上,然后计算点到投影点的距离。具体步骤如下:
- 首先计算线段的方向向量 dx 和 dy,以及点与线段起点之间的向量 px 和 py。
- 然后根据点与线段的方向向量的内积,计算点到线段的投影位置。投影点通过参数 t 确定,t 的取值范围在 [0, 1] 之间,表示点在这条线段上的相对位置。
- 如果 t 不在 [0, 1] 之间,那么意味着投影点超出了线段的端点范围,我们就计算点到线段端点的距离。
- 如果 t 在 [0, 1] 之间,则投影点落在线段上,计算点到投影点的欧几里得距离。
五、改进与扩展:
处理多种形状的轮廓:该实现只考虑了轮廓边缘为线段的情况。如果轮廓形状更加复杂(例如圆形或曲线),可以采用更复杂的几何计算方法来处理。
优化性能:当前实现对轮廓中的每一条边进行逐一计算,若轮廓点数较多时,性能可能不理想。对于一些特殊情况(例如规则形状),可以考虑通过优化计算或预计算一些几何参数来提升性能。
其他几何计算方法:该方法使用了线段到点的距离计算,也可以考虑通过向量投影或其他几何算法来进一步改进。
六、资料
技能拾荒者《3-OpenCVSharp 中实现 Halcon 的 distance_lc 算子(计算一条线与一个轮廓之间的距离)》
https://blog.csdn.net/weixin_45590420/article/details/143872664