提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
众所周知,曲线拟合是图像识别中的重要技术,其中常用的就是直线拟合,拟合方式大概分为切比雪夫法和最小二乘法的方式,其中最小二乘法会有更多人使用
,在近期我也接触了这方面项目的设计和研究,以下是我的设计过程。
提示:以下是本篇文章正文内容,下面案例可供参考
一、最小二乘法是什么?
相信很多人和我一样听到了最小二乘法一开始是懵逼的状态,什么是最小二乘法?其实就是类似于线性回归方程,把散乱的点用一条直线方程表示出来,我不清楚这样描述是否正确,但是我大概清楚了自己要做什么了。
二、使用步骤
1.winform窗口设计
窗口设计如下:
点击加载可以将你需要拟合的图像,加入图像后图像会出现在右侧,由于我做的是激光扫描后的图像,所以得到的都是黑点,每个黑点可以视为一个坐标,由此可以进行直线拟合,点击计算可以获取直线的方程表达式。
2.代码
代码如下:
(1)Argorithm.cs
获取黑点并进行拟合
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Drawing; using System.Drawing.Drawing2D; namespace DEMO { class Argorithm { /// <summary> /// 获得黑色点 /// </summary> /// <param name="img"></param> /// <returns></returns> static public List<Point> GetBlakePoints(Image img) { List<Point> blakePoints = new List<Point>(); Point point = new Point(); int height = img.Height; int width = img.Width; Bitmap bitmap = new Bitmap(img); for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { Color color = bitmap.GetPixel(i, j); if (color.R < 0x1F && color.G < 0x1F && color.B < 0x1F) { point.X = i; point.Y = j; blakePoints.Add(point); } } } return blakePoints; } /* /// <summary> /// 最小二乘法直线拟合,直线方程 y = kx + b, Ax + By + C = 0 /// </summary> /// <param name="points"></param> /// <param name="k"></param> /// <param name="b"></param> static public void CalculateLineViaLeastSquaresFitting(List<Point> points, ref int k, ref int b)//int *k { k = 1; b = 80; } * */ /// <summary> /// 最小二乘法直线拟合,直线方程 y = kx + b, Ax+By+C = 0 /// </summary> /// <param name="points"></param> /// <param name="k"></param> /// <param name="b"></param> static public void CalculateLineViaLeastSquaresFitting(List<Point> points, ref double k, ref double b) { if (points.Count < 2) { throw new Exception("最小二乘法直线拟合失败"); ; } k = 0.0; b = 0.0; double Xavr = 0.0; double Yavr = 0.0; int N = points.Count; foreach (var pt in points) { Xavr += pt.X; Yavr += pt.Y; } Xavr = Xavr / N; Yavr = Yavr / N; double A = 0, B = 0, C = 0; for (int i = 0; i < N; i++) { double x = points[i].X - Xavr; double y = points[i].Y - Yavr; A += x * y; B += x * x - y * y; C += -x * y; } double k1 = (-B + Math.Sqrt(B * B - 4 * A * C)) / (2 * A); double k2 = (-B - Math.Sqrt(B * B - 4 * A * C)) / (2 * A); k = k1; b = Yavr - Xavr * k ; return; } } }
(2)MainForm.cs 加入图片,画出直线,显示直线方程using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace DEMO { public partial class MainForm : Form { public MainForm() { InitializeComponent(); } private void MainForm_Load(object sender, EventArgs e) { } /// <summary> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnLoad_Click(object sender, EventArgs e) { OpenFileDialog dialog = new OpenFileDialog(); dialog.Multiselect = false; dialog.Title = "请选择BMP文件"; dialog.Filter = "BMP文件(*.bmp)|*.bmp"; if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) { string filename = dialog.FileName; Image orignalImg = Image.FromFile(filename); pbxMap.Image = orignalImg; //pbxMap.Height = orignalImg.Height; //pbxMap.Width = orignalImg.Width; MessageBox.Show(orignalImg.Width + "," + orignalImg.Height); } } /// <summary> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnCal_Click(object sender, EventArgs e) { if (null == pbxMap.Image) { MessageBox.Show("加载图片"); return; } Graphics gra = pbxMap.CreateGraphics(); List<Point> points = Argorithm.GetBlakePoints(pbxMap.Image); SolidBrush myBrush = new SolidBrush(Color.Blue); lbxMsg.Items.Add("共" + points.Count + "个点"); //把所有黑点描成蓝点 foreach (var tem in points) { gra.FillRectangle(myBrush, tem.X, tem.Y, 1, 1); } double k = 0; double b = 0; //调用函数 Argorithm.CalculateLineViaLeastSquaresFitting(points, ref k, ref b); if (b >= 0) { lbxMsg.Items.Add("拟合方程: y = " + k + "x + " + b); } else { lbxMsg.Items.Add("拟合方程: y = " + k + "x - " + Math.Abs(b)); } //开始点 Point start = new Point(); start.X = 200; start.Y = (int)(k * start.X + b); //结束点 Point end = new Point(); end.X = 20; end.Y = (int)(k * end.X + b); //画线 myBrush = new SolidBrush(Color.Red); Pen pen = new Pen(myBrush); gra.DrawLine(pen, start, end); } } }
(3)Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace DEMO
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
}
效果图:
总结
以上就是今天要讲的内容,本文仅仅简单介绍了最小二乘法直线拟合的算法和应用,实际上我还想请教各位大佬怎么用这个winform设计直线的分段拟合,这一直在困扰我。初次发表还请多多指教,也希望能够帮助到有帮助的人。