var surf = OpenCvSharp.XFeatures2D.SURF.Create(2000);
var keyPoints = surf.Detect(src);
Cv2.DrawKeypoints(src, keyPoints, src);
surf.Dispose();
Vector2[] vector2s = new Vector2[keyPoints.Length];
Point[] points = new Point[keyPoints.Length];
for (int i = 0; i < keyPoints.Length; i++)
{
var point = (Point)keyPoints[i].Pt;
points[i] = point;
vector2s[i].X = point.X;
vector2s[i].Y = point.Y;
}
最小二乘法在opencvsharp里有现成的api调用,这里就不多说了,很简单:
var line = Cv2.FitLine(points, DistanceTypes.L1, 0, 0, 0);
line.FitSize(src.Width, src.Height, out var p1, out var p2);
Cv2.Line(src, p1, p2, Scalar.Blue, 5);
然后是Ransac随机抽样一致法,和网络上的办法一样,也是用距离来判断点是否在给定阈值范围内。
结果图,红色的线是Ransac,蓝色的线是最小二乘法:
完整代码:
public Form1()
{
InitializeComponent();
var src = Cv2.ImRead("C:\\Users\\Administrator\\Desktop\\test2.png", ImreadModes.Grayscale);
TestFitLine(src);
Cv2.ImShow("dst", src);
}
public static void TestFitLine(Mat src)
{
var surf = OpenCvSharp.XFeatures2D.SURF.Create(2000);
var keyPoints = surf.Detect(src);
Cv2.DrawKeypoints(src, keyPoints, src);
surf.Dispose();
Vector2[] vector2s = new Vector2[keyPoints.Length];
Point[] points = new Point[keyPoints.Length];
for (int i = 0; i < keyPoints.Length; i++)
{
var point = (Point)keyPoints[i].Pt;
points[i] = point;
vector2s[i].X = point.X;
vector2s[i].Y = point.Y;
}
//最小二乘法
var line = Cv2.FitLine(points, DistanceTypes.L1, 0, 0, 0);
line.FitSize(src.Width, src.Height, out var p1, out var p2);
Cv2.Line(src, p1, p2, Scalar.Blue, 5);
//Ransac随机抽样一致法
var r = FitLineRansac(vector2s, 1000, 10);
r.FitSize(src.Width, src.Height, out var p3, out var p4);
Cv2.Line(src, p3, p4, Scalar.Red, 5);
}
public static Line2D FitLineRansac(Vector2[] points, int iterations, double sigma)
{
int bestScore = -1;
Line2D result = null;
Random random = new Random();
for (int i = 0; i < iterations; i++)
{
if (bestScore > points.Length * 0.5)
break;
var indexes = GetRandomIndexes(random, 0, points.Length);
var p1 = points[indexes.Item0];
var p2 = points[indexes.Item1];
var dir = Vector2.Normalize(p2 - p1);
var line = new Line2D(dir.X, dir.Y, p2.X, p2.Y);
int score = 0;
for (int j = 0; j < points.Length; j++)
{
if (line.Distance(points[j].X, points[j].Y) < sigma)
score += 1;
}
if (score > bestScore)
{
bestScore = score;
result = line;
}
}
return result;
}
private static Vec2i GetRandomIndexes(Random random, int min, int max)
{
var index1 = random.Next(min, max);
var index2 = random.Next(min, max);
if (index1 == index2)
GetRandomIndexes(random, min, max);
return new Vec2i(index1, index2);
}