OpenCvSharp函数:FitLine直线拟合

文章介绍了如何使用OpenCV中的FitLine函数进行二维和三维点集的直线拟合,该函数基于M-估计量算法。参数包括距离计算方式、C的取值、半径精确度和角度精确度。代码示例展示了生成随机点并进行直线拟合的过程,以及如何绘制拟合直线和原始数据点。

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

FitLine直线拟合

函数说明:基于M-估计量(M-Estimator)算法对给定的一组二维或三维点坐点集拟合直线。
//函数原型1
void FitLine(InputArray points,
    OutputArray line,
    DistanceTypes distType,
    double param,
    double reps,
    double aeps)

//函数原型2
Line2D FitLine(IEnumerable<Point> points,
    DistanceTypes distType,
    double param,
    double reps,
    double aeps)

//函数原型3
Line2D FitLine(IEnumerable<Point2f> points,
    DistanceTypes distType,
    double param,
    double reps,
    double aeps)

//函数原型4
Line3D FitLine(IEnumerable<Point3i> points,
    DistanceTypes distType,
    double param,
    double reps,
    double aeps)

//函数原型5
Line3D FitLine(IEnumerable<Point3f> points,
    DistanceTypes distType,
    double param,
    double reps,
    double aeps)

参数

说明

InputArray points

IEnumerable<Point> points

IEnumerable<Point2f> points

IEnumerable<Point3i> points

IEnumerable<Point3f> points

待拟合直线的坐标点集(二维或三维)

DistanceTypes distType

距离计算方式:一般用L2或L1

double param

参数C的取值:distType为Fair、Welsch、Huber时用到,默认为0即可,会自动取最优值

double reps

半径(坐标原点到直线的距离)精确。建议为0.01

double aeps

角度精确。建议为0.01

返回值(Line2D,Line3D)

返回拟合直线。

a.Vy=1时,与垂直X轴垂直。Vx=1时,与Y轴垂直

b.直率斜率k=Vy/Vx

c.截距b=Y1-k*X1

图像示例

源码示例

private string winName = "FitLine Demo";

public void Run() {

    Cv2.NamedWindow(winName, WindowFlags.Normal);
    Cv2.ResizeWindow(winName, new Size(800, 800));

    Cv2.CreateTrackbar("偏移", winName, 100, OnChanged);
    Cv2.SetTrackbarPos("偏移", winName, 50);

    Cv2.WaitKey();
    Cv2.DestroyAllWindows();
}

private void OnChanged(int pos, IntPtr userData) {
    //生成随机点
    List<List<Point>> curves = new List<List<Point>>();
    List<Point> rndPoints = new List<Point>();
    Random random = new Random();
    for (int col = 30; col < 450; col += 20) {
        rndPoints.Add(new Point(500 - col, 50 + col / 2 + random.Next(-pos, pos)));
    }
    curves.Add(rndPoints);
    rndPoints = new List<Point>();
    for (int row = 30; row < 450; row += 20) {
        rndPoints.Add(new Point(100 + row / 2 + random.Next(-pos, pos), row));
    }
    curves.Add(rndPoints);

    rndPoints = new List<Point>();
    for (int xy = 30; xy < 450; xy += 20) {
        if (pos % 2 == 0) {
            rndPoints.Add(new Point(xy, pos));
        }
        else {
            rndPoints.Add(new Point(pos, xy));
        }
    }
    curves.Add(rndPoints);


    using Mat src = Mat.Zeros(new Size(500, 500), MatType.CV_8UC3);
    foreach (var points in curves) {
        foreach (var point in points) {
            Cv2.Circle(src, point, 2, Scalar.Blue, -1);
        }
        //直线拟合
        var line = Cv2.FitLine(points, DistanceTypes.L2, 0, 0.01, 0.01);

        GetStartEndPoint(src, points, line, out Point startPoint, out Point endPoint);

        Cv2.Line(src, startPoint, endPoint, Scalar.Red, lineType: LineTypes.AntiAlias);
        Cv2.Circle(src, (int)line.X1, (int)line.Y1, 3, Scalar.Yellow, -1);
    }
    Cv2.ImShow(winName, src);
}

/// <summary>
/// 获取待拟合点集的起始与终止点
/// </summary>
/// <param name="src"></param>
/// <param name="points">待拟合点集</param>
/// <param name="line">拟合直线</param>
/// <param name="p1">起点</param>
/// <param name="p2">终止</param>
private void GetStartEndPoint(Mat src, List<Point> points, Line2D line, out Point p1, out Point p2) {
    if (1 - line.Vx < 0.0001) {//水平线
        var sort = points.OrderBy(z => z.X);
        p1 = new Point(sort.First().X, line.Y1);
        p2 = new Point(sort.Last().X, line.Y1);
    }
    else if (1 - line.Vy < 0.0001) {//垂直线
        var sort = points.OrderBy(z => z.Y);
        p1 = new Point(line.X1, sort.First().Y);
        p2 = new Point(line.X1, sort.Last().Y);
    }
    else {
        //原直线斜率,注意Y轴是向下的(斜率正负号与通常的坐标系相反)
        var k1 = line.Vy / line.Vx;
        //直线截距
        var b1 = line.Y1 - k1 * line.X1;

        IOrderedEnumerable<Point> sort;
        if (k1 > 0) {//与普通坐标系统符号相反
            //左上到右下
            sort = points.OrderBy(z => z.Y).ThenBy(z => z.X);
        }
        else {
            //左下到右上
            sort = points.OrderBy(z => z.X).ThenBy(z => z.Y);
        }
        p1 = GetCrossPoint(sort.First(), k1, b1);
        p2 = GetCrossPoint(sort.Last(), k1, b1);

        Cv2.Line(src, p1, sort.First(), Scalar.Yellow);
        Cv2.Line(src, p2, sort.Last(), Scalar.Yellow);

        Cv2.PutText(src, $"k={k1.ToString("0.0000")},b={b1.ToString("0.0")}", p1, HersheyFonts.HersheySimplex, 0.5, Scalar.White);
    }
}

/// <summary>
/// 获取经过point的与直线(斜率为k1,截距为b1)的垂直直线的交点
/// </summary>
/// <param name="point"></param>
/// <param name="k1">直线斜率</param>
/// <param name="b1">直线截距</param>
/// <returns></returns>
private Point GetCrossPoint(Point point,double k1,double b1) {
    //与原直线垂直的直线的斜率 。斜率相乘等于-1
    var k2 = -1.0D / k1;

    //与原直线垂直的直线的截距
    var b2 = point.Y - k2 * point.X;

    //交点
    Point crossPoint = new Point();
    crossPoint.X = (int)((b2 - b1) / (k1 - k2));
    crossPoint.Y = (int)((b2 * k1 - b1 * k2) / (k1 - k2));
    return crossPoint;
}

OpenCvSharp函数示例(目录)

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

图南科技

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值