OpenCvSharp函数:FitLine直线拟合

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函数示例(目录)

猜你喜欢

转载自blog.csdn.net/TyroneKing/article/details/129439353