C#:线段与轮廓之间的距离distance_lc

  • 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
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容