# 用C++实现最小二乘法，最小区域法，最小包容圆拟合圆的算法和在Qt中的运行结果

``````#ifndef LEASTSQUAREMETHODFITTINGCIRCLE_H
#define LEASTSQUAREMETHODFITTINGCIRCLE_H

#include <QList>

typedef struct pointInfo
{
double x;
double y;
}pointInfo;

pointInfo LSFit(QList<pointInfo> &Point);   //最小二乘圆拟合函数

#endif // LEASTSQUAREMETHODFITTINGCIRCLE_H
``````
``````#include "leastsquaremethodfittingcircle.h"

/* 最小二乘法圆拟合函数 */
pointInfo LSFit(QList<pointInfo> &Point)
{

pointInfo tempPoint;        //定义临时变量，存储最小二乘圆心坐标

double X1=0,Y1=0,X2=0,Y2=0,X3=0,Y3=0;
double X1Y1=0,X1Y2=0,X2Y1=0;
int N=Point.size();

for(int i=0;i<Point.size();i++)
{
double x=Point.at(i).x;
double y=Point.at(i).y;
double x2=x*x,y2=y*y;
X1+=x;
Y1+=y;
X2+=x2;
Y2+=y2;
X3+=x*x2;
Y3+=y*y2;
X1Y1+=x*y;
X1Y2+=x*y2;
X2Y1+=x2*y;
}

double a=0,b=0,c=0;
double C1=0,D1=0,E1=0,G1=0,H1=0;

C1=N*X2-X1*X1;
D1=N*X1Y1-X1*Y1;
E1=N*X3+N*X1Y2-(X2+Y2)*X1;
G1=N*Y2-Y1*Y1;
H1=N*X2Y1+N*Y3-(X2+Y2)*Y1;

a=(H1*D1-E1*G1)/(C1*G1-D1*D1);
b=(H1*C1-E1*D1)/(D1*D1-G1*C1);
c=-(a*X1+b*Y1+X2+Y2)/N;

tempPoint.x=a/(-2);
tempPoint.y=b/(-2);

return tempPoint;
}
``````

``````#ifndef MINIMALENCLOSINGCIRCLE_H
#define MINIMALENCLOSINGCIRCLE_H

#include <QList>
#include <algorithm>
#include <qmath.h>
#include <random>

struct Point
{
double x;
double y;

Point subtract(const Point &p) const;

double distance(const Point &p) const;

double cross(const Point &p) const;// Signed area / determinant thing
};

struct Circle
{
public:
static const Circle INVALID;
Point c;
double r;

bool contains(const Point &p) const;

private:
static const double MULTIPLICATIVE_EPSILON;
};

Circle MakeSmallestEnclosingCircle(QList<Point> &points);

Circle MakeSmallestEnclosingCircleOnePoint(QList<Point> &points, int end, Point &p);

Circle MakeSmallestEnclosingCircleTwoPoints(QList<Point> &points, int end, Point &p, Point &q);

Circle MakeDiameter(const Point &a, const Point &b);

Circle MakeCircumcircle(const Point &a, const Point &b, const Point &c);

#endif // MINIMALENCLOSINGCIRCLE_H
``````
``````#include "minimalenclosingcircle.h"

/* 求取坐标差值函数 */
Point Point::subtract(const Point &p) const
{
return Point{x - p.x, y - p.y};      //q点与P点横纵坐标的差值
}

/* 求取两点间距离函数 */
double Point::distance(const Point &p) const
{
//hypot求斜边的长度，返回两点的距离值
return hypot(x - p.x,y-p.y);         //求p点的坐标与圆c的圆心的距离
}

/* 求向量的叉乘 */
double Point::cross(const Point &p) const
{
return x * p.y - y * p.x;
}

/*定义INVALID*/
const Circle Circle::INVALID{Point{0, 0}, -1};

/* 定义MULTIPLICATIVE_EPSILON */
const double Circle::MULTIPLICATIVE_EPSILON = 1 + 1e-14;

bool Circle::contains(const Point &p) const
{
return c.distance(p) <= r * MULTIPLICATIVE_EPSILON; //r为圆c的半径，如果距离点p到圆c的距离小于半径（也就是说，点p在圆c内）返回真
}

/*初始化圆没有边界，圆为坐标（0,0），半径-1*/
Circle MakeSmallestEnclosingCircle(QList<Point> &points)
{
QList<Point> shuffled=points;
Circle c(Circle::INVALID);           //初始化为坐标(0,0)  半径为-1的圆   显然不存在{Point{0, 0}, -1};

for (int i = 0; i < shuffled.size(); i++)
{
Point p = shuffled.at(i);
if (c.r < 0 || !c.contains(p))   //如果半径小于0或者点p不在圆c内，执行下面的语句。不在圆内就有了第一个点
{
c = MakeSmallestEnclosingCircleOnePoint(shuffled, i + 1, p);
}
}
return c;
}

/*知道了圆的一个点，要确定第二个点*/
Circle MakeSmallestEnclosingCircleOnePoint(QList<Point> &points, int end, Point &p)
{
Circle c{p, 0};
for (int i = 0; i < end; i++)
{
Point q = points.at(i);
if (!c.contains(q))            //p不在圆c内就执行以下语句，有了第二个点
{
if (c.r == 0)
c = MakeDiameter(p, q);//第一次的r等于0.以p,q两点为直径当做圆
else
{
c = MakeSmallestEnclosingCircleTwoPoints(points, i + 1, p, q);
}
}
}
return c;
}

/* 知道了圆的两个点 */
Circle MakeSmallestEnclosingCircleTwoPoints(QList<Point> &points, int end,  Point &p,  Point &q)
{
Circle circ = MakeDiameter(p, q);
Circle left = Circle::INVALID;
Circle right = Circle::INVALID;

//有任意一点不在以p和q为圆的内部
Point pq = q.subtract(p);
for (int i = 0; i < end; i++)
{
const Point &r = points.at(i);
if (circ.contains(r))//如果以圆p，q为直径的圆 包括点r  跳出这次循环
{
continue;
}

//找一个外接圆，并在左边或右边分类
double cross = pq.cross(r.subtract(p));//可以用向量的方式判断第三点在q的左边还是右边
Circle c = MakeCircumcircle(p, q, r);
if (c.r < 0)
{
continue;
}
else if (cross > 0 && (left.r < 0 || pq.cross(c.c.subtract(p)) > pq.cross(left.c.subtract(p))))//点r在q的左边
{
left = c;
}
else if (cross < 0 && (right.r < 0 || pq.cross(c.c.subtract(p)) < pq.cross(right.c.subtract(p))))//点r在q的右边
{
right = c;
}
}

//选择返回哪个圆
if (left.r < 0 && right.r < 0)
return circ;
else if (left.r < 0)
return right;
else if (right.r < 0)
return left;
else
return left.r <= right.r ? left : right;//返回半径较小的那个圆
}

/*两点为直径画圆*/
Circle MakeDiameter(const Point &a, const Point &b)
{
Point c{(a.x + b.x) / 2, (a.y + b.y) / 2};//求 a,b两点连线的中点，当做圆心
return Circle{c, qMax(c.distance(a),c.distance(b))};//返回较大值
}

/*三点确定一个圆*/
Circle MakeCircumcircle(const Point &a, const Point &b, const Point &c)
{
//数学上的外接圆算法
double ox= (qMin(qMin(a.x,b.x),c.x)+qMax(qMin(a.x,b.x),c.x))/2;//求一条连线的中点
double oy= (qMin(qMin(a.y,b.y),c.y)+qMax(qMin(a.y,b.y),c.y))/2;

double ax = a.x - ox, ay = a.y - oy;
double bx = b.x - ox, by = b.y - oy;
double cx = c.x - ox, cy = c.y - oy;
double d = (ax * (by - cy) + bx * (cy - ay) + cx * (ay - by)) * 2;
if (d == 0)
return Circle::INVALID;
double x = ((ax * ax + ay * ay) * (by - cy) + (bx * bx + by * by) * (cy - ay) + (cx * cx + cy * cy) * (ay - by)) / d;
double y = ((ax * ax + ay * ay) * (cx - bx) + (bx * bx + by * by) * (ax - cx) + (cx * cx + cy * cy) * (bx - ax)) / d;
Point p{ox + x, oy + y};
double r=qMax(qMax(p.distance(a),p.distance(b)),p.distance(c));
return Circle{p, r};
}
``````