初衷
用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[(x−x0)cosθ+(y−y0)sinθ]2+b2[(x−x0)sinθ−(y−y0)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θ(a21−b21)a2sin2θ+b2cos2θ−2[a2cosθ(x0cosθ+y0sinθ)+b2sinθ(x0sinθ−y0cosθ)]−2[a2sinθ(x0cosθ+y0sinθ)−b2cosθ(x0sinθ−y0cosθ)]a2(x0cosθ+y0sinθ)2+b2(x0sinθ−y0cosθ)2−1(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 六个参数。
后记
测试后发现一个问题,计算椭圆方程的方法计算比较复杂,而且计算结果受椭圆大小的影响,难以直观地反应点的偏差值。后来我发现其实可以利用“椭圆上的点到其两个焦点的距离之和不变”这一性质来判断一个点偏离椭圆的程度。
- 椭圆的两个焦点在其长轴上,且与中心的距离为 a 2 − b 2 \sqrt{a^2-b^2} a2−b2;
- 椭圆上的点到两个焦点的距离之和为 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 的差值,即可知道它与椭圆的像素偏差大小。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/69138.html