- 操作系统:ubuntu22.04
- OpenCV版本:OpenCV4.9
- IDE:Visual Studio Code
- 编程语言:C++11
算法描述
围绕一组2D点拟合一个椭圆。
该函数计算出一个椭圆,该椭圆拟合一组2D点。它返回一个内切于该椭圆的旋转矩形。使用了由[91]提出的直接最小二乘法(Direct)方法。
对于椭圆,这个基集是 χ = ( x 2 , x y , y 2 , x , y , 1 ) \chi= \left(x^2, x y, y^2, x, y, 1\right) χ=(x2,xy,y2,x,y,1),这是一个包含六个自由系数的集合 A T = { A xx , A xy , A yy , A x , A y , A 0 } A^T=\left\{A_{\text{xx}},A_{\text{xy}},A_{\text{yy}},A_x,A_y,A_0\right\} AT={Axx,Axy,Ayy,Ax,Ay,A0}然而,要指定一个椭圆,只需要五个数字;主轴和次轴的长度 ( a , b ) (a,b) (a,b),位置 ( x 0 , y 0 ) (x_0,y_0) (x0,y0),以及方向 θ。这是因为空间中还包含了直线、二次函数、抛物线和双曲线函数作为可能的拟合。直接方法通过确保 4 A x x A y y − A x y 2 > 0 4 A_{xx} A_{yy}- A_{xy}^2 > 0 4AxxAyy−Axy2>0来将拟合限定为椭圆。施加的条件是 4 A x x A y y − A x y 2 = 1 4 A_{xx} A_{yy}- A_{xy}^2=1 4AxxAyy−Axy2=1,这满足了不等式,并且由于系数可以任意缩放,因此这一条件并不过于限制
ϵ 2 = A T D T D A 其中 A T C A = 1 并且 C = ( 0 0 2 0 0 0 0 − 1 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ) \begin{equation*} \epsilon ^2= A^T D^T D A \quad \text{其中} \quad A^T C A =1 \quad \text{并且} \quad C=\left(\begin{matrix} 0 & 0 & 2 & 0 & 0 & 0 \\ 0 & -1 & 0 & 0 & 0 & 0 \\ 2 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 \end{matrix} \right) \end{equation*} ϵ2=ATDTDA其中ATCA=1并且C= 0020000−10000200000000000000000000000
最小成本是通过求解广义特征值问题找到的。
D T D A = λ ( C ) A \begin{equation*} D^T D A = \lambda \left( C\right) A \end{equation*} DTDA=λ(C)A
系统只产生一个正的特征值 λ,该特征值被选作解,并且其特征向量u也被选出。这些值用来找到系数
A = 1 u T C u u \begin{equation*} A = \sqrt{\frac{1}{\mathbf{u}^T C \mathbf{u}}} \mathbf{u} \end{equation*} A=uTCu1u
缩放因子保证了 A T C A = 1 A^T C A =1 ATCA=1
fitEllipseDirect 是 OpenCV 中用于拟合椭圆的一个函数,它使用了直接线性最小二乘法(Direct Least Squares Fit)来进行椭圆拟合。这种方法通常比其他方法更快,但也可能在某些情况下不如其他方法稳健。
函数原型
RotatedRect cv::fitEllipseDirect
(InputArray points
)
参数
- 参数points 输入的2D点集,存储在 std::vector<> 或 Mat 中。
代码示例
#include <iostream>
#include <opencv2/opencv.hpp>using namespace std;
using namespace cv;int main()
{// 创建一个空白图像Mat img( 400, 400, CV_8UC3, Scalar( 255, 255, 255 ) );// 创建一组2D点vector< Point2f > points;points.push_back( Point2f( 100, 100 ) );points.push_back( Point2f( 200, 100 ) );points.push_back( Point2f( 200, 200 ) );points.push_back( Point2f( 100, 200 ) );points.push_back( Point2f( 150, 150 ) );points.push_back( Point2f( 150, 250 ) );points.push_back( Point2f( 250, 150 ) );points.push_back( Point2f( 250, 250 ) );// 拟合椭圆RotatedRect ellipse = fitEllipseDirect( points );// 获取椭圆的四个顶点vector< Point2f > boxPoints;boxPoints.resize( 4 ); // 确保boxPoints至少有4个元素ellipse.points( boxPoints.data() );// 将 Point2f 转换为 Pointvector< Point > intBoxPoints;for ( const auto& pt : boxPoints ){intBoxPoints.push_back( Point( static_cast< int >( pt.x ), static_cast< int >( pt.y ) ) );}// 在原图上绘制椭圆polylines( img, intBoxPoints, true, Scalar( 0, 0, 255 ), 2, LINE_8 );// 绘制点集for ( const auto& pt : points ){circle( img, pt, 5, Scalar( 0, 255, 0 ), -1 );}// 显示结果imshow( "Ellipse Fitting (Direct)", img );waitKey( 0 );return 0;
}