You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 07:36:04