OpenCV结构分析与形状描述符(12)椭圆拟合函数fitEllipseAMS()的使用
- 操作系统:ubuntu22.04
- OpenCV版本:OpenCV4.9
- IDE:Visual Studio Code
- 编程语言:C++11
算法描述
围绕一组2D点拟合一个椭圆。
该函数计算出一个椭圆,该椭圆拟合一组2D点。它返回一个内切于该椭圆的旋转矩形。使用了由[260]提出的近似均方差(AMS)方法。
对于椭圆,这个基集是
χ
=
(
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)以及方向 θ。这是因为基集包含了直线、二次函数、抛物线和双曲线函数以及椭圆函数作为可能的拟合。如果发现拟合是一个抛物线或双曲线函数,则使用标准的 fitEllipse 方法。AMS 方法通过施加条件
A
T
(
D
x
T
D
x
+
D
y
T
D
y
)
A
=
1
A^T ( D_x^T D_x + D_y^T D_y) A = 1
AT(DxTDx+DyTDy)A=1 来限制拟合为抛物线、双曲线和椭圆曲线,其中矩阵
D
x
Dx
Dx和
D
y
Dy
DyDy 是设计矩阵D关于x 和y的偏导数。矩阵是通过逐行应用以下规则来形成的,针对点集中的每一个点:
D
(
i
,
:
)
=
{
x
i
2
,
x
i
y
i
,
y
i
2
,
x
i
,
y
i
,
1
}
D
x
(
i
,
:
)
=
{
2
x
i
,
y
i
,
0
,
1
,
0
,
0
}
D
y
(
i
,
:
)
=
{
0
,
x
i
,
2
y
i
,
0
,
1
,
0
}
\begin{align*} D(i,:)&=\left\{x_i^2, x_i y_i, y_i^2, x_i, y_i, 1\right\} & D_x(i,:)&=\left\{2 x_i,y_i,0,1,0,0\right\} & D_y(i,:)&=\left\{0,x_i,2 y_i,0,1,0\right\} \end{align*}
D(i,:)={xi2,xiyi,yi2,xi,yi,1}Dx(i,:)={2xi,yi,0,1,0,0}Dy(i,:)={0,xi,2yi,0,1,0}
AMS 方法最小化成本函数:
ϵ
2
=
A
T
D
T
D
A
A
T
(
D
x
T
D
x
+
D
y
T
D
y
)
A
T
\begin{equation*} \epsilon ^2=\frac{ A^T D^T D A }{ A^T (D_x^T D_x + D_y^T D_y) A^T } \end{equation*}
ϵ2=AT(DxTDx+DyTDy)ATATDTDA
通过求解广义特征值问题找到最小成本。
D
T
D
A
=
λ
(
D
x
T
D
x
+
D
y
T
D
y
)
A
\begin{equation*} D^T D A = \lambda \left( D_x^T D_x + D_y^T D_y\right) A \end{equation*}
DTDA=λ(DxTDx+DyTDy)A
fitEllipseAMS 是 OpenCV 中用于拟合椭圆的一个函数,它使用了“代数方法”(Algebraic Method)来进行椭圆拟合。相比于 fitEllipse 函数使用的最小二乘法,fitEllipseAMS 更侧重于代数意义上的拟合,适用于某些特定的应用场景。
函数原型
RotatedRect cv::fitEllipseAMS
(
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 = fitEllipseAMS( points );
// 获取椭圆的四个顶点
vector< Point2f > boxPoints;
boxPoints.resize( 4 ); // 确保boxPoints至少有4个元素
ellipse.points( boxPoints.data() );
// 将 Point2f 转换为 Point
vector< 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 (AMS)", img );
waitKey( 0 );
return 0;
}