迭代重加权最小二乘(IRLS)算法

迭代重加权最小二乘算法

1. 概述

对于数据点集合中,如果存在少量离群点,使用标准的最小二乘法进行拟合,往往可以得到满意的结果。但当离群点的比重增加时,例如当需拟合的圆部分被遮挡,使用标准的最小二乘法进行拟合,结果便不可靠,如下图所示,因为即便是离群点,其为偏差 E E E的贡献权值 w w w仍为1。

在这里插入图片描述

针对上述情况,使用==迭代重加权最小二乘法(IRLS)==可以在一定程度上解决上述问题。IRLS方法引入一个距离权重函数 w ( δ ) w(\delta) w(δ),权重可通过已经计算出来的距离来计算。

2. 两个权重函数
2.1 Huber权重函数

w ( δ ) = { 1 , ∣ δ ∣ ≤ γ γ / ∣ δ ∣ , ∣ δ ∣ > γ w(\delta)=\begin{cases} 1 ,\quad \quad|\delta|\le\gamma \\ \gamma/|\delta|,\quad|\delta|\gt\gamma \end{cases} w(δ)={1,δγγ/δ,δ>γ

参数 γ \gamma γ为削波函数,它定义了哪些点为离群点。

2.2 Tukey权重函数

w ( δ ) = { ( 1 − ( ∣ δ ∣ / γ ) 2 ) 2 , ∣ δ ∣ ≤ γ 0 , ∣ δ ∣ > γ w(\delta)=\begin {cases} (1-(|\delta|/\gamma)^2)^2,\quad |\delta|\le\gamma \\ 0,\quad |\delta|\gt\gamma\end {cases} w(δ)={(1(δ/γ)2)2,δγ0,δ>γ

3. 基于IRLS方法拟合圆

圆的方程式为 ( x − a ) 2 + ( y − b ) 2 = c 2 (x-a)^2+(y-b)^2=c^2 (xa)2+(yb)2=c2

建立最小化误差函数 E E E
E = ∑ i = 0 n − 1 ( ( x i − a ) 2 + ( y i − b ) 2 − c 2 ) 2 = ∑ i = 0 n − 1 ( x i 2 + y i 2 − 2 a x i − 2 b y i + a 2 + b 2 − c 2 ) 2 = ∑ i = 0 n − 1 ( x i 2 + y i 2 + A x i + B y i + C ) 2 E=\sum_{i=0}^{n-1}{((x_i-a)^2+(y_i-b)^2-c^2)^2}\\ =\sum_{i=0}^{n-1}(x_i^2+y_i^2-2ax_i-2by_i+a^2+b^2-c^2)^2\\ =\sum_{i=0}^{n-1}(x_i^2+y_i^2+Ax_i+By_i+C)^2 E=i=0n1((xia)2+(yib)2c2)2=i=0n1(xi2+yi22axi2byi+a2+b2c2)2=i=0n1(xi2+yi2+Axi+Byi+C)2
其中, A = − 2 a A=-2a A=2a B = − 2 b B=-2b B=2b C = a 2 + b 2 − c 2 C=a^2+b^2-c^2 C=a2+b2c2

引入距离权值 w i w_i wi
E = ∑ i = 0 n − 1 w i ( x i 2 + y i 2 + A x i + B y i + C ) 2 E=\sum_{i=0}^{n-1}{w_i(x_i^2+y_i^2+Ax_i+By_i+C)^2} E=i=0n1wi(xi2+yi2+Axi+Byi+C)2
基于式(4),对 A A A B B B C C C分别求偏导:
∂ E ∂ A = ∑ w i x i ( x i 2 + y i 2 + A x i + B y i + C ) = A Σ w i x i 2 + B Σ w i x i y i + C Σ w i x i + Σ w i x i 3 + Σ w i x i y i 2 \frac{\partial E}{\partial A}=\sum w_ix_i(x_i^2+y_i^2+Ax_i+By_i+C)\\ =A\Sigma w_ix_i^2+B\Sigma w_ix_iy_i+C\Sigma w_ix_i+\Sigma w_ix_i^3+\Sigma w_ix_iy_i^2 AE=wixi(xi2+yi2+Axi+Byi+C)=AΣwixi2+BΣwixiyi+CΣwixi+Σwixi3+Σwixiyi2

∂ E ∂ B = ∑ w i x i ( x i 2 + y i 2 + A x i + B y i + C ) = A Σ w i x i y i + B Σ w i y i 2 + C Σ w i y i + Σ w i y i 3 + Σ w i x i 2 y i \frac{\partial E}{\partial B}=\sum w_ix_i(x_i^2+y_i^2+Ax_i+By_i+C)\\ =A\Sigma w_ix_iy_i+B\Sigma w_iy_i^2+C\Sigma w_iy_i+\Sigma w_iy_i^3+\Sigma w_ix_i^2y_i BE=wixi(xi2+yi2+Axi+Byi+C)=AΣwixiyi+BΣwiyi2+CΣwiyi+Σwiyi3+Σwixi2yi

∂ E ∂ C = ∑ w i ( x i 2 + y i 2 + A x i + B y i + C ) = A Σ w i x i + B Σ w i y i + C Σ w i + Σ w i x i 2 + Σ w i y i 2 \frac{\partial E}{\partial C}=\sum w_i(x_i^2+y_i^2+Ax_i+By_i+C)\\ =A\Sigma w_ix_i+B\Sigma w_iy_i+C\Sigma w_i+\Sigma w_ix_i^2+\Sigma w_iy_i^2 CE=wi(xi2+yi2+Axi+Byi+C)=AΣwixi+BΣwiyi+CΣwi+Σwixi2+Σwiyi2

式(5~7)变换为矩阵形式:
[ Σ w x 2 Σ w x y Σ w x Σ w x y Σ w y 2 Σ w y Σ w x Σ w y Σ w i ] [ A B C ] = − [ Σ w x 3 + Σ w x y 2 Σ w y 3 + Σ w x 2 y Σ w x 2 + Σ w y 2 ] \begin{bmatrix} \Sigma wx^2&\Sigma wxy&\Sigma wx\\ \Sigma wxy&\Sigma wy^2& \Sigma wy\\ \Sigma wx& \Sigma wy& \Sigma w_i \end{bmatrix} \begin{bmatrix} A\\B\\C \end{bmatrix} =-\begin{bmatrix} \Sigma wx^3+\Sigma wxy^2 \\ \Sigma wy^3+\Sigma wx^2y \\ \Sigma wx^2+\Sigma wy^2 \end{bmatrix} Σwx2ΣwxyΣwxΣwxyΣwy2ΣwyΣwxΣwyΣwiABC=Σwx3+Σwxy2Σwy3+Σwx2yΣwx2+Σwy2
通过上式可求解出 A A A B B B C C C,再进而求解出 a a a b b b c c c

在进行第一次迭代时,采用标准的最小二乘法进行拟合圆,即 w i = 1 w_i=1 wi=1

4. 关键代码
void getCenterPoint(const Mat& src, Point& centerP, int& radius)
{

	Mat meanImg;
	Mat binaryImg;

	boxFilter(src, meanImg, CV_8UC1, Size(11, 11));
	cv::subtract(meanImg, src, binaryImg);

	threshold(binaryImg, binaryImg, 3, 255, THRESH_BINARY);

	ipt.selectLargestRegionByArea(binaryImg);

	vector<vector<Point>> contours;
	findContours(binaryImg, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);

	Mat outCircleImg = Mat::zeros(src.size(), CV_8UC1);
	drawContours(outCircleImg, contours, 0, Scalar(255), 1);

	vector<double> vWeights;
	vector<Point> selectPoints;
	for (size_t i = 0; i < contours[0].size();i=i+3)
	{
		selectPoints.push_back(contours[0].at(i));
		vWeights.push_back(1);
	}

	fitCircle(selectPoints, vWeights, centerP, radius);

	Mat colorImg;
	cvtColor(src, colorImg, CV_GRAY2BGR);
	for (size_t i = 0; i < selectPoints.size(); i = i + 3)
	{
		circle(colorImg, selectPoints[i], 2, Scalar(255, 255, 0), CV_FILLED);
	}
	for (int k = 0; k < 10; k++)
	{
		vector<double> vWeights;
		vector<double> vDist;
		for (size_t i = 0; i < selectPoints.size();i++)
		{
			double dist = (selectPoints[i].x - centerP.x)*(selectPoints[i].x - centerP.x)
				+ (selectPoints[i].y - centerP.y)*(selectPoints[i].y - centerP.y)/* - radius*radius*/;

			dist = abs(dist);
			dist = sqrt(dist);
			dist = abs(dist - radius);

			vDist.push_back(dist);
		}
		vector<double> vDistCopy;
		vDistCopy.assign(vDist.begin(), vDist.end());

		sort(vDistCopy.begin(), vDistCopy.end());
		double sigma = vDistCopy[vDistCopy.size() / 2] / 0.675;
		sigma *= 2;

		vWeights.clear();
		for (size_t i = 0; i < vDist.size(); i++)
		{
			double weight;


			 huber
			//if (vDist[i] <= sigma)
			//{
			//	weight = 1;
			//}
			//else
			//{
			//	weight = sigma / vDist[i];
			//}

			//Tukey
			if (vDist[i] <= sigma)
			{
				double rate = vDist[i] / sigma;
				weight =pow((1-rate*rate),2) ;
			}
			else
			{
				weight = 0;
			}
			vWeights.push_back(weight);
		}	
		fitCircle(selectPoints, vWeights, centerP, radius);
		Mat colorImg;
		cvtColor(src, colorImg, CV_GRAY2BGR);
		circle(colorImg, centerP, radius, Scalar(0, 0, 255), 1);
		for (size_t i = 0; i < selectPoints.size(); i = i + 3)
		{
			circle(colorImg, selectPoints[i], 2 , Scalar(255, 255, 0), CV_FILLED);
		}
	}
}

void fitCircle(const vector<Point>& selectPoint, vector<double> vWeights, Point& centerP, int& radius)
{
	double x1, y1,x2,y2,x3,y3;
	double x1y1, x1y2, x2y1;
	double n = 0;
    
	x1 = y1 = x2 = y2 = x3 = y3 = 0;
	x1y1 = x1y2 = x2y1 = 0;

	for (size_t i = 0; i < selectPoint.size();i++)
	{
		double x = selectPoint[i].x;
		double y = selectPoint[i].y;
		double weight = vWeights[i];

		x1 += weight*x;
		y1 += weight*y;
		x2 += weight*x*x;
		y2 += weight*y*y;
		x3 += weight*x*x*x;
		y3 += weight*y*y*y;
		x1y1 += weight*x*y;
		x1y2 += weight*x*y*y;
		x2y1 += weight*x*x*y;
		n += weight;
	}

	Mat A = (Mat_<float>(3, 3) << x2, x1y1, x1, x1y1, y2, y1, x1, y1, n);
	Vec3f b = { -x3 - x1y2, -x2y1 - y3, -x2 - y2 };
	Vec3f res;
	solve(A, b, res, cv::DECOMP_LU);
	
	double centerX = -res[0] / 2;
	double centerY = -res[1] / 2;

	radius = sqrt(centerY*centerY + centerX*centerX - res[2]);
	centerP = Point(centerX, centerY);
}
5. 拟合效果

第1次迭代:
在这里插入图片描述

第2次迭代:
在这里插入图片描述

第3次迭代:
在这里插入图片描述

第4次迭代:
在这里插入图片描述

第5次迭代:
在这里插入图片描述

从拟合结果可以看出,在第5次迭代时,可以相对较好的得到圆拟合结果。

  • 33
    点赞
  • 149
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值