标准椭圆方程推导

初衷

用opencv拟合椭圆后,想评估一下拟合的质量,即被拟合点与拟合结果的接近程度。我首先想到的办法是将被拟合点带入椭圆方程 f ( x , y ) = A x 2 + B x y + C y 2 + D x + E y + F f(x, y) =Ax^2+Bxy+Cy^2+Dx+Ey+F f(x,y)=Ax2+Bxy+Cy2+Dx+Ey+F,如果一个点正好在椭圆上,那么 f ( x , y ) = 0 f(x, y) =0 f(x,y)=0,而一个点偏离椭圆越多,则其计算值越大,因此可以用来评估被被拟合点的偏差。 opencv 中 fitEllipse() 函数计算得到的是椭圆的中心坐标、长短轴的长度和长轴与 + x +x +x 轴的夹角(需要注意的是,拟合结果的 height 才是长轴),需要根据这些信息推导椭圆的标准方程。


推导

中心在原点且长轴在x轴上的椭圆方程为:
x 2 a 2 + y 2 b 2 = 1 (1) \dfrac{x^2}{a^2} + \dfrac{y^2}{b^2} = 1 \tag 1 a2x2+b2y2=1(1)其中, a a a b b b分别是长半轴和短半轴。若椭圆长轴与正x轴夹角为 θ \theta θ(逆时针),且中心坐标为 ( x 0 , y 0 ) (x_0,y_0) (x0,y0)。其任意一点平移 ( − x 0 , − y 0 ) (-x_0,-y_0) (x0,y0)再旋转 − θ -\theta θ即满足标准椭圆方程。

首先考虑点 ( x , y ) (x,y) (x,y)绕原点顺时针旋转角度 θ \theta θ ( x ′ , y ′ ) (x',y') (x,y)
用参数方程表示点 ( x , y ) (x,y) (x,y)和点 ( x ′ , y ′ ) (x',y') (x,y)
{ x = t cos ⁡ ϕ y = t sin ⁡ ϕ (2) \begin{cases} x = t\cos\phi\\ y = t\sin\phi \end{cases} \tag 2 {x=tcosϕy=tsinϕ(2) { x ′ = t cos ⁡ ( ϕ − θ ) = t ( cos ⁡ ϕ cos ⁡ θ + sin ⁡ ϕ sin ⁡ θ ) y ′ = t sin ⁡ ( ϕ − θ ) = t ( sin ⁡ ϕ cos ⁡ θ − cos ⁡ ϕ sin ⁡ θ ) (3) \begin{cases} x' = t\cos(\phi-\theta)=t(\cos\phi\cos\theta+\sin\phi\sin\theta)\\ y' = t\sin(\phi-\theta)=t(\sin\phi\cos\theta-\cos\phi\sin\theta) \end{cases} \tag 3 {x=tcos(ϕθ)=t(cosϕcosθ+sinϕsinθ)y=tsin(ϕθ)=t(sinϕcosθcosϕsinθ)(3)因此:
{ x ′ = x cos ⁡ θ + y sin ⁡ θ y ′ = − x sin ⁡ θ + y cos ⁡ θ (4) \begin{cases} x' = x\cos\theta+y\sin\theta\\ y' = -x\sin\theta+y\cos\theta \end{cases} \tag 4 {x=xcosθ+ysinθy=xsinθ+ycosθ(4)继而可得一般椭圆方程为:
[ ( x − x 0 ) cos ⁡ θ + ( y − y 0 ) sin ⁡ θ ] 2 a 2 + [ ( x − x 0 ) sin ⁡ θ − ( y − y 0 ) cos ⁡ θ ] 2 b 2 = 1 \dfrac{\left[(x-x_0)\cos\theta+(y-y_0)\sin\theta\right]^2}{a^2}+ \dfrac{\left[(x-x_0)\sin\theta-(y-y_0)\cos\theta\right]^2}{b^2}=1 a2[(xx0)cosθ+(yy0)sinθ]2+b2[(xx0)sinθ(yy0)cosθ]2=1化简为 A x 2 + B x y + C y 2 + D x + E y + F = 0 Ax^2+Bxy+Cy^2+Dx+Ey+F=0 Ax2+Bxy+Cy2+Dx+Ey+F=0 的形式可得:
A = cos ⁡ 2 θ a 2 + sin ⁡ 2 θ b 2 B = 2 sin ⁡ θ cos ⁡ θ ( 1 a 2 − 1 b 2 ) C = sin ⁡ 2 θ a 2 + cos ⁡ 2 θ b 2 D = − 2 [ cos ⁡ θ ( x 0 cos ⁡ θ + y 0 sin ⁡ θ ) a 2 + sin ⁡ θ ( x 0 sin ⁡ θ − y 0 cos ⁡ θ ) b 2 ] E = − 2 [ sin ⁡ θ ( x 0 cos ⁡ θ + y 0 sin ⁡ θ ) a 2 − cos ⁡ θ ( x 0 sin ⁡ θ − y 0 cos ⁡ θ ) b 2 ] F = ( x 0 cos ⁡ θ + y 0 sin ⁡ θ ) 2 a 2 + ( x 0 sin ⁡ θ − y 0 cos ⁡ θ ) 2 b 2 − 1 (5) \begin{aligned} A=&\dfrac{\cos^2\theta}{a^2}+\dfrac{\sin^2\theta}{b^2} \\ B=&2\sin\theta\cos\theta\left(\frac{1}{a^2}-\frac{1}{b^2}\right) \\ C=&\dfrac{\sin^2\theta}{a^2}+\dfrac{\cos^2\theta}{b^2} \\ D=&-2\left[\dfrac{\cos\theta(x_0\cos\theta+y_0\sin\theta)}{a^2}+ \dfrac{\sin\theta(x_0\sin\theta-y_0\cos\theta)}{b^2}\right] \\ E=&-2\left[\dfrac{\sin\theta(x_0\cos\theta+y_0\sin\theta)}{a^2}- \dfrac{\cos\theta(x_0\sin\theta-y_0\cos\theta)}{b^2}\right] \\ F=&\dfrac{(x_0\cos\theta+y_0\sin\theta)^2}{a^2}+ \dfrac{(x_0\sin\theta-y_0\cos\theta)^2}{b^2}-1 \end{aligned} \tag 5 A=B=C=D=E=F=a2cos2θ+b2sin2θ2sinθcosθ(a21b21)a2sin2θ+b2cos2θ2[a2cosθ(x0cosθ+y0sinθ)+b2sinθ(x0sinθy0cosθ)]2[a2sinθ(x0cosθ+y0sinθ)b2cosθ(x0sinθy0cosθ)]a2(x0cosθ+y0sinθ)2+b2(x0sinθy0cosθ)21(5)


代码

在 C++ 中实现上述计算的函数为:

cv::Mat normEllipseParams(cv::RotatedRect box)
{
    double params[6];
    cv::Mat rst(6, 1, CV_64FC1, params);
    double theta = box.angle / 180 * CV_PI;
    double st = sin(theta);
    double ct = cos(theta);
    double a = box.size.width / 2;
    double b = box.size.height / 2;
    double a2 = a * a;
    double b2 = b * b;
    double x0 = box.center.x;
    double y0 = box.center.y;
    double xcys = x0 * ct + y0 * st;
    double xsyc = x0 * st - y0 * ct;
    params[0] = ct * ct / a2 + st * st / b2;
    params[1] = 2 * st * ct * (1 / a2 - 1 / b2);
    params[2] = st * st / a2 + ct * ct / b2;
    params[3] = -2 * (ct * xcys / a2 + st * xsyc / b2);
    params[4] = -2 * (st * xcys / a2 - ct * xsyc / b2);
    params[5] = xcys * xcys / a2 + xsyc * xsyc / b2 - 1;
    return rst.clone();
}

返回的 Mat 即 A~F 六个参数。


后记

测试后发现一个问题,计算椭圆方程的方法计算比较复杂,而且计算结果受椭圆大小的影响,难以直观地反应点的偏差值。后来我发现其实可以利用“椭圆上的点到其两个焦点的距离之和不变”这一性质来判断一个点偏离椭圆的程度。

  1. 椭圆的两个焦点在其长轴上,且与中心的距离为 a 2 − b 2 \sqrt{a^2-b^2} a2b2 ;
  2. 椭圆上的点到两个焦点的距离之和为 2 a 2a 2a.

计算椭圆焦点的函数为:

std::array<cv::Point2f, 2> calcFocal(cv::RotatedRect& ellipse)
{
    if (ellipse.size.width < ellipse.size.height) {
        swap(ellipse.size.width, ellipse.size.height);
        ellipse.angle -= 90;
    }
    float a = ellipse.size.width / 2;
    float b = ellipse.size.height / 2;
    float c = sqrt(a * a - b * b);
    float theta = ellipse.angle / 180 * CV_PI;
    cv::Point2f delta(c * cos(theta), c * sin(theta));
    return {ellipse.center + delta, ellipse.center - delta};
}

对于任意一个被拟合的点,只要计算其到两焦点的距离之和与 2 a 2a 2a 的差值,即可知道它与椭圆的像素偏差大小。

### 最小二乘法椭圆拟合的数学推导 #### 椭圆的一般方程形式 一般情况下,可以表示为五参数模型: \[ Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0 \] 其中 \(A, B, C\) 不全为零。为了简化问题并减少退化解的存在,在实际应用中常采用约束条件使上述表达标准化[^1]。 对于给定的数据点集\((x_i,y_i)\),目标是最小化这些点到所求椭圆的距离平方和。由于直接测量几何距离较为复杂,常用代数距离作为替代指标来进行优化处理[^2]。 #### 构建残差函数 定义第\(i\)个样本点对应的代数误差为: \[ f(x_i,y_i)=Ax_i^2+Bx_iy_i+Cy_i^2+Dx_i+Ey_i+F \] 则总的误差平方和可写作: \[ S=\sum_{i=1}^{N}(f(x_i,y_i))^2 \] 这里\(N\)代表总共有多少个观测点参与计算[^3]。 #### 参数估计 为了让这个二次多项式的系数能够最好地描述输入数据中的趋势,需要调整六个未知量使得整体偏差尽可能的小。即寻找最优解向量\(\theta=[A,B,C,D,E,F]^T\)让\(S(\theta)\)达到极小值。这可以通过设置偏微分为零的方式获得必要条件: \[ \frac{\partial{S}}{\partial{\theta_j}}=0,\quad j=A,...,F \] 从而形成线性方程组,进一步得到关于待估参数的具体数值解答。值得注意的是,因为存在尺度不变性和旋转自由度等问题,所以在具体实施过程中还需要引入额外限制以确保唯一解的存在性以及稳定性。 ```cpp // 下面是一个简单的C++伪代码片段展示如何构建矩阵并解决该问题 MatrixXd A(N,6); // N是数据点的数量 VectorXd b(N); for(int i=0;i<N;++i){ double xi=data[i].first; double yi=data[i].second; A(i,0)=xi*xi; // 对应于A项 A(i,1)=xi*yi; // 对应于B项 A(i,2)=yi*yi; // 对应于C项 A(i,3)=xi; // 对应于D项 A(i,4)=yi; // 对应于E项 A(i,5)=1.0; // 对应于F项 b(i)=0.0; // 假设右侧恒等于0 } VectorX theta=(A.transpose()*A).ldlt().solve(A.transpose()*b); ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值