C# Windows Forms 中点集列表离群点(异常值)去除方法求助
解决方案
实现思路
- 采用RANSAC鲁棒估计算法拟合两个点集之间的相似变换参数,避免离群点对参数估计的干扰
- 基于拟合得到的最优变换参数,过滤listB中误差超过设定阈值的离群点,保留和listA形状匹配的内点
- 直接复用你定义的
Transformation类即可适配两个点集的旋转、缩放、平移映射关系
完整实现代码
将RANSAC相关方法添加到Form1类中,修改按钮点击逻辑即可运行:
using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; class Form1 : Form { List<Point> listA = new List<Point>(); List<Point> listB = new List<Point>(); // 可按需调整的RANSAC参数 const double RansacThreshold = 8.0; // 误差阈值,单位为像素 const int RansacIterations = 200; // 迭代次数 private void button1_click(object sender, EventArgs e) { listA.Clear(); listB.Clear(); Point p1a = new Point(20, 30); Point p2a = new Point(120, 50); Point p3a = new Point(160, 80); Point p4a = new Point(180, 300); Point p5a = new Point(100, 220); Point p6a = new Point(50, 280); Point p7a = new Point(20, 140); Point[] mypoints = new Point[] { p1a, p2a, p3a, p4a, p5a, p6a, p7a }; listA.AddRange(mypoints); Transformation t2 = new Transformation(); t2.A = 1.05; t2.B = 0.05; t2.T1 = 15; t2.T2 = 22; listB = applytransformation(t2, listA); // 模拟添加离群点 listB[2] = new Point(listB[2].X + 30, listB[2].Y + 20); listB[4] = new Point(listB[4].X - 25, listB[4].Y + 15); // 调用RANSAC过滤离群点 List<Point> filteredListB = RansacFilter(listA, listB); Pen penA = new Pen(Brushes.Blue, 3); Pen penFilteredB = new Pen(Brushes.Green, 3); Graphics g = panel1.CreateGraphics(); g.Clear(Color.White); DisplayShape(listA, penA, g); DisplayShape(filteredListB, penFilteredB, g); } // RANSAC过滤核心方法 List<Point> RansacFilter(List<Point> source, List<Point> target) { if (source.Count != target.Count || source.Count < 2) return target; Random rand = new Random(); int maxInlierCount = 0; List<bool> bestInlierMask = new List<bool>(); for (int iter = 0; iter < RansacIterations; iter++) { // 随机选取2组不重复的对应点求解变换参数 int idx1 = rand.Next(source.Count); int idx2 = rand.Next(source.Count); while (idx2 == idx1) idx2 = rand.Next(source.Count); Point s1 = source[idx1], s2 = source[idx2]; Point t1 = target[idx1], t2 = target[idx2]; double dxS = s2.X - s1.X; double dyS = s2.Y - s1.Y; double dxT = t2.X - t1.X; double dyT = t2.Y - t1.Y; double denominator = dxS * dxS + dyS * dyS; if (denominator == 0) continue; // 求解相似变换参数A、B、T1、T2 double A = (dxS * dxT + dyS * dyT) / denominator; double B = (dxS * dyT - dyS * dxT) / denominator; double T1 = t1.X - A * s1.X - B * s1.Y; double T2 = t1.Y + B * s1.X - A * s1.Y; Transformation trans = new Transformation() { A = A, B = B, T1 = T1, T2 = T2 }; // 统计当前变换下的内点数量 int inlierCount = 0; List<bool> inlierMask = new List<bool>(); for (int i = 0; i < source.Count; i++) { Point pPredict = ApplyTransSingle(trans, source[i]); double error = Math.Sqrt(Math.Pow(pPredict.X - target[i].X, 2) + Math.Pow(pPredict.Y - target[i].Y, 2)); bool isInlier = error < RansacThreshold; inlierMask.Add(isInlier); if (isInlier) inlierCount++; } // 保存最优内点筛选规则 if (inlierCount > maxInlierCount) { maxInlierCount = inlierCount; bestInlierMask = inlierMask; } } // 过滤得到最终的无离群点点集 List<Point> result = new List<Point>(); for (int i = 0; i < target.Count; i++) { if (bestInlierMask.Count > 0 && bestInlierMask[i]) result.Add(target[i]); } return result; } // 单个点的变换方法 Point ApplyTransSingle(Transformation x, Point c) { double xprime = x.A * c.X + x.B * c.Y + x.T1; double yprime = -x.B * c.X + x.A * c.Y + x.T2; return new Point((int)Math.Round(xprime), (int)Math.Round(yprime)); } List<Point> applytransformation(Transformation x, List<Point> shape) { List<Point> Tlist = new List<Point>(); foreach (Point c in shape) { Tlist.Add(ApplyTransSingle(x, c)); } return Tlist; } void DisplayShape(List<Point> Shp, Pen pen, Graphics G) { if (Shp.Count < 2) return; Point? prevPoint = null; foreach (Point pt in Shp) { G.DrawEllipse(pen, new Rectangle(pt.X - 2, pt.Y - 2, 4, 4)); if (prevPoint != null) { G.DrawLine(pen, (Point)prevPoint, pt); } prevPoint = pt; } G.DrawLine(pen, Shp[0], Shp[Shp.Count - 1]); } // 窗体控件初始化,如已通过设计器拖拽控件可删除本段 Panel panel1 = new Panel() { Dock = DockStyle.Fill }; Button button1 = new Button() { Text = "绘制", Dock = DockStyle.Top }; public Form1() { Controls.Add(panel1); Controls.Add(button1); button1.Click += button1_click; } } public class Transformation { public double A { get; set; } public double B { get; set; } public double T1 { get; set; } public double T2 { get; set; } }
参数调整说明
- 如果离群点偏差较大,可适当调大
RansacThreshold阈值 - 如果点集数量较多,可增加
RansacIterations迭代次数提升拟合准确率 - 示例中过滤后的点集用绿色绘制,可按需调整显示逻辑
内容的提问来源于stack exchange,提问作者user16612111
相关产品推荐
相关产品推荐

