最小二乘法拟合圆(Python&C++实现)

圆的方程可以表示为:
R 2 = ( x − A ) 2 + ( y − B ) 2 (1) R^2 = (x-A)^2+(y-B)^2\tag{1} R2=(xA)2+(yB)2(1)
展开后为:
R 2 = x 2 − 2 A x + A 2 + y 2 − 2 B y + B 2 R^2 = x^2-2Ax+A^2+y^2-2By+B^2 R2=x22Ax+A2+y22By+B2
a = − 2 A , b = − 2 B , c = A 2 + B 2 − R 2 a=-2A,b=-2B,c=A^2+B^2-R^2 a=2A,b=2B,c=A2+B2R2:
则式 ( 1 ) (1) (1)可表示为:
x 2 + y 2 + a x + b y + c = 0 x^2+y^2+ax+by+c=0 x2+y2+ax+by+c=0
− a x − b y − c = x 2 + y 2 -ax-by-c = x^2+y^2 axbyc=x2+y2
矩阵形式可表示为:
[ − x 1 − y 1 − 1 − x 2 − y 2 − 1 ⋯ ⋯ ⋯ − x n − y n − 1 ] [ a b c ] = [ x 1 2 + y 1 2 x 2 2 + y 2 2 ⋯ x n 2 + y n 2 ] \begin{gathered} \quad \begin{bmatrix} -x_1 & -y_1 & -1 \\ -x_2 & -y_2 & -1 \\ \cdots & \cdots & \cdots \\ -x_n & -y_n & -1 \end{bmatrix} \begin{bmatrix} a \\ b \\ c \end{bmatrix} = \begin{bmatrix} x_1^2+y_1^2 \\ x_2^2+y_2^2 \\ \cdots \\ x_n^2+y_n^2 \end{bmatrix} \end{gathered} x1x2xny1y2yn111 abc = x12+y12x22+y22xn2+yn2
得到 R ⋅ A = Y R \cdot A = Y RA=Y的矩阵形式后,便可利用 A = ( R T ⋅ R ) − 1 ⋅ R T ⋅ Y A = (R^T \cdot R)^{-1} \cdot R^T \cdot Y A=(RTR)1RTY得到 a 、 b 、 c a、b、c abc的值。
Python代码为:

def Fit_Circle(x=None, y=None):
    if x is None:
        x = [1, 0, -1, 0]
    if y is None:
        y = [0, 1, 0, -1]

    Y = []
    R = []

    for i in range(len(x)):
        R.append([-x[i], -y[i], -1])
        Y.append(np.square(x[i]) + np.square(y[i]))

    R = np.mat(R)
    A = np.dot(np.dot(np.linalg.inv(np.dot(R.T, R)), R.T), Y)
    A = np.array(A, dtype='float32').flatten()
    return round(-(A[0] / 2), 2), round(-(A[1] / 2), 2), round(np.sqrt(np.square(A[0]) + np.square(A[1]) - 4 * A[2]) / 2, 2)

根据圆心坐标和半径画出圆的代码为:

def Show_Circle(x=0, y=0, r=2):
    theta = np.arange(0, 2 * np.pi, 0.01)
    x = x + r * np.cos(theta)
    y = y + r * np.sin(theta)
    fig = plt.figure()
    axes = fig.add_subplot(111)
    axes.plot(x, y)
    axes.axis('equal')
    plt.show()

Python完整代码为:

import matplotlib.pyplot as plt
import numpy as np


def Show_Circle(x=0, y=0, r=2):
    theta = np.arange(0, 2 * np.pi, 0.01)
    x = x + r * np.cos(theta)
    y = y + r * np.sin(theta)
    fig = plt.figure()
    axes = fig.add_subplot(111)
    axes.plot(x, y)
    axes.axis('equal')
    plt.show()


def Fit_Circle(x=None, y=None):
    if x is None:
        x = [1, 0, -1, 0]
    if y is None:
        y = [0, 1, 0, -1]

    Y = []
    R = []

    for i in range(len(x)):
        R.append([-x[i], -y[i], -1])
        Y.append(np.square(x[i]) + np.square(y[i]))

    R = np.mat(R)
    A = np.dot(np.dot(np.linalg.inv(np.dot(R.T, R)), R.T), Y)
    A = np.array(A, dtype='float32').flatten()
    return round(-(A[0] / 2), 2), round(-(A[1] / 2), 2), round(np.sqrt(np.square(A[0]) + np.square(A[1]) - 4 * A[2]) / 2, 2)


if __name__ == '__main__':
    a = [2, 0, -2, 0]
    b = [0, 2, 0, -2]
    X, Y, R = Fit_Circle(x=a, y=b)
    Show_Circle(X, Y, R)

C++完整代码为:

#include
#include 
#include

using namespace std;

double a[][2] = {{2,0},{0,2},{-2,0},{0,-2}};
int M = sizeof(a) / sizeof(a[0]);

void matrix_inverse(double(*a)[3], double(*b)[3]);
	
int main() 
{
	double R[M][3] = {0};
	double R_T[3][M] = {0};
	double RR[3][3] = {0};	// R_T * R
	double RR_1[3][3] = {0};
	double RR_1R_T[3][M] = {0};	// RR_1 * R_T
	double Y[M][1] = {0};
	double A[3][1] = {0};
	
	for(int i = 0; i < M; i++)
	{
		R[i][0] = -a[i][0];
		R[i][1] = -a[i][1];
		R[i][2] = -1;
		
		R_T[0][i] = -a[i][0];
		R_T[1][i] = -a[i][1];
		R_T[2][i] = -1;
		
		Y[i][0] = pow(a[i][0], 2) + pow(a[i][1], 2);
	}
	
	for(int i = 0; i < 3; ++i)		// 矩阵相乘 (k的取值来源于左边矩阵的列数或右边矩阵的行数) 
		for(int j = 0; j < 3; ++j)
		{
			RR[i][j] = 0;
			for(int k = 0; k < M; ++k)
				RR[i][j] += R_T[i][k] * R[k][j];
		}
			
	matrix_inverse(RR, RR_1);
	
	for(int i = 0; i < 3; ++i)		// 矩阵相乘 (k的取值来源于左边矩阵的列数或右边矩阵的行数) 
		for(int j = 0; j < M; ++j)
		{
			RR_1R_T[i][j] = 0; 
			for(int k = 0; k < 3; ++k)
				RR_1R_T[i][j] += RR_1[i][k] * R_T[k][j];
		}
				
	for(int i = 0; i < 3; ++i)		// 矩阵相乘 (k的取值来源于左边矩阵的列数或右边矩阵的行数) 
		for(int j = 0; j < 1; ++j)
		{
			A[i][j] = 0;
			for(int k = 0; k < M; ++k)
				A[i][j] += RR_1R_T[i][k] * Y[k][j];
		}
	
	double x = -(A[0][0] / 2);
	double y = -(A[1][0] / 2);
	double r = pow(pow(x, 2) + pow(y, 2) - A[2][0], 0.5);
	
	cout.setf(ios::fixed);
	cout << "圆心坐标:(" << setprecision(2) << x << "," << setprecision(2) << y << ")" << endl;
	cout << "半径:" << setprecision(2) << r << endl;
	
	return 0;
}

void matrix_inverse(double(*a)[3], double(*b)[3])			// 矩阵求逆 
{
	using namespace std;
	int i, j, k;
	double max, temp;
	// 定义一个临时矩阵t
	double t[3][3];
	// 将a矩阵临时存放在矩阵t[n][n]中
	for (i = 0; i < 3; i++)
		for (j = 0; j < 3; j++)
			t[i][j] = a[i][j];
	// 初始化B矩阵为单位矩阵
	for (i = 0; i < 3; i++)
		for (j = 0; j < 3; j++)
			b[i][j] = (i == j) ? (double)1 : 0;
	// 进行列主消元,找到每一列的主元
	for (i = 0; i < 3; i++)
	{
		max = t[i][i];
		// 用于记录每一列中的第几个元素为主元
		k = i;
		// 寻找每一列中的主元元素
		for (j = i + 1; j < 3; j++)
		{
			if (fabs(t[j][i]) > fabs(max))
			{
				max = t[j][i];
				k = j;
			}
		}
		//cout<<"the max number is "<
		// 如果主元所在的行不是第i行,则进行行交换
		if (k != i)
		{
			// 进行行交换
			for (j = 0; j < 3; j++)
			{
				temp = t[i][j];
				t[i][j] = t[k][j];
				t[k][j] = temp;
				// 伴随矩阵B也要进行行交换
				temp = b[i][j];
				b[i][j] = b[k][j];
				b[k][j] = temp;
			}
		}
		if (t[i][i] == 0)
		{
			cout << "\nthe matrix does not exist inverse matrix\n";
			break;
		}
		// 获取列主元素
		temp = t[i][i];
		// 将主元所在的行进行单位化处理
		//cout<<"\nthe temp is "<
		for (j = 0; j < 3; j++)
		{
			t[i][j] = t[i][j] / temp;
			b[i][j] = b[i][j] / temp;
		}
		for (j = 0; j < 3; j++)
		{
			if (j != i)
			{
				temp = t[j][i];
				//消去该列的其他元素
				for (k = 0; k < 3; k++)
				{
					t[j][k] = t[j][k] - temp * t[i][k];
					b[j][k] = b[j][k] - temp * b[i][k];
				}
			}
		}
	}
}

你可能感兴趣的:(python,最小二乘法)