OpenCvSharp function: FitLine straight line fitting

FitLine straight line fitting

Function description: Fit a straight line to a given set of two-dimensional or three-dimensional points based on the M-Estimator algorithm.
//函数原型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)

parameter

illustrate

InputArray points

IEnumerable<Point> points

IEnumerable<Point2f> points

IEnumerable<Point3i> points

IEnumerable<Point3f> points

The set of coordinate points of the straight line to be fitted (two-dimensional or three-dimensional)

DistanceTypes distType

Distance calculation method: generally use L2 or L1

double param

The value of parameter C: used when distType is Fair, Welsch, or Huber. The default value is 0, and the optimal value will be automatically taken.

double reps

The radius (the distance from the coordinate origin to the straight line) is accurate. Recommended is 0.01

double aeps

The angle is precise. Recommended is 0.01

Return value (Line2D, Line3D)

Returns the fitted straight line.

a. When Vy=1, it is perpendicular to the vertical X axis. When Vx=1, it is perpendicular to the Y axis

b. Straight slope k=Vy/Vx

c.Intercept b=Y1-k*X1

Image example

Source code example

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 function examples (Table of Contents)

Guess you like

Origin blog.csdn.net/TyroneKing/article/details/129439353