如何从OpenCV的fitEllipse函数中获取椭圆系数?

如何从OpenCV的fitEllipse函数中获取椭圆系数?,opencv,ellipse,Opencv,Ellipse,我想从一张图片中提取红球,得到图片中检测到的椭圆矩阵 以下是我的例子: 我对图片进行阈值设置,使用findContour()函数找到红球的轮廓,并使用fitEllipse()拟合椭圆 但我想要的是得到这个椭圆的系数。因为fitEllipse()返回一个旋转矩形(RotatedRect),所以我需要重新编写这个函数 一个椭圆可以表示为Ax^2+乘以^2+Cxy+Dx+Ey+F=0;所以我想得到u=(A,B,C,D,E,F)或者u=(A,B,C,D,E),如果F是1(构造一个椭圆矩阵) 我读了fi

我想从一张图片中提取红球,得到图片中检测到的椭圆矩阵

以下是我的例子:

我对图片进行阈值设置,使用findContour()函数找到红球的轮廓,并使用fitEllipse()拟合椭圆

但我想要的是得到这个椭圆的系数。因为fitEllipse()返回一个旋转矩形(RotatedRect),所以我需要重新编写这个函数

一个椭圆可以表示为Ax^2+乘以^2+Cxy+Dx+Ey+F=0;所以我想得到u=(A,B,C,D,E,F)或者u=(A,B,C,D,E),如果F是1(构造一个椭圆矩阵)

我读了fitEllipse()的源代码,一共有三个SVD过程,我想我可以从这三个SVD过程的结果中得到上面的系数。但我很困惑每个SVD过程的每个结果(变量cv::Mat x)代表什么,为什么这里有三个SVD

以下是此函数:

cv::RotatedRect cv::fitEllipse( InputArray _points )
{
   Mat points = _points.getMat();
   int i, n = points.checkVector(2);
   int depth = points.depth();
   CV_Assert( n >= 0 && (depth == CV_32F || depth == CV_32S));

   RotatedRect box;

   if( n < 5 )
        CV_Error( CV_StsBadSize, "There should be at least 5 points to fit the ellipse" );

    // New fitellipse algorithm, contributed by Dr. Daniel Weiss
    Point2f c(0,0);
    double gfp[5], rp[5], t;
    const double min_eps = 1e-8;
    bool is_float = depth == CV_32F;
    const Point* ptsi = points.ptr<Point>();
    const Point2f* ptsf = points.ptr<Point2f>();

    AutoBuffer<double> _Ad(n*5), _bd(n);
    double *Ad = _Ad, *bd = _bd;

    // first fit for parameters A - E
    Mat A( n, 5, CV_64F, Ad );
    Mat b( n, 1, CV_64F, bd );
    Mat x( 5, 1, CV_64F, gfp );

    for( i = 0; i < n; i++ )
    {
        Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y);
        c += p;
    }
    c.x /= n;
    c.y /= n;

    for( i = 0; i < n; i++ )
    {
        Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y);
        p -= c;

        bd[i] = 10000.0; // 1.0?
        Ad[i*5] = -(double)p.x * p.x; // A - C signs inverted as proposed by APP
        Ad[i*5 + 1] = -(double)p.y * p.y;
        Ad[i*5 + 2] = -(double)p.x * p.y;
        Ad[i*5 + 3] = p.x;
        Ad[i*5 + 4] = p.y;
    }

    solve(A, b, x, DECOMP_SVD);

    // now use general-form parameters A - E to find the ellipse center:
    // differentiate general form wrt x/y to get two equations for cx and cy
    A = Mat( 2, 2, CV_64F, Ad );
    b = Mat( 2, 1, CV_64F, bd );
    x = Mat( 2, 1, CV_64F, rp );
    Ad[0] = 2 * gfp[0];
    Ad[1] = Ad[2] = gfp[2];
    Ad[3] = 2 * gfp[1];
    bd[0] = gfp[3];
    bd[1] = gfp[4];
    solve( A, b, x, DECOMP_SVD );

    // re-fit for parameters A - C with those center coordinates
    A = Mat( n, 3, CV_64F, Ad );
    b = Mat( n, 1, CV_64F, bd );
    x = Mat( 3, 1, CV_64F, gfp );
    for( i = 0; i < n; i++ )
    {
        Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y);
        p -= c;
        bd[i] = 1.0;
        Ad[i * 3] = (p.x - rp[0]) * (p.x - rp[0]);
        Ad[i * 3 + 1] = (p.y - rp[1]) * (p.y - rp[1]);
        Ad[i * 3 + 2] = (p.x - rp[0]) * (p.y - rp[1]);
    }
    solve(A, b, x, DECOMP_SVD);

    // store angle and radii
    rp[4] = -0.5 * atan2(gfp[2], gfp[1] - gfp[0]); // convert from APP angle usage
    if( fabs(gfp[2]) > min_eps )
        t = gfp[2]/sin(-2.0 * rp[4]);
    else // ellipse is rotated by an integer multiple of pi/2
        t = gfp[1] - gfp[0];
    rp[2] = fabs(gfp[0] + gfp[1] - t);
    if( rp[2] > min_eps )
        rp[2] = std::sqrt(2.0 / rp[2]);
    rp[3] = fabs(gfp[0] + gfp[1] + t);
    if( rp[3] > min_eps )
        rp[3] = std::sqrt(2.0 / rp[3]);

    box.center.x = (float)rp[0] + c.x;
    box.center.y = (float)rp[1] + c.y;
    box.size.width = (float)(rp[2]*2);
    box.size.height = (float)(rp[3]*2);
    if( box.size.width > box.size.height )
    {
        float tmp;
        CV_SWAP( box.size.width, box.size.height, tmp );
        box.angle = (float)(90 + rp[4]*180/CV_PI);
    }
    if( box.angle < -180 )
        box.angle += 360;
    if( box.angle > 360 )
        box.angle -= 360;

    return box;
}
cv::RotatedRect cv::fitEllipse(输入阵列点)
{
Mat points=_points.getMat();
int i,n=点。检查向量(2);
int depth=points.depth();
CV_断言(n>=0&(深度==CV_32F||深度==CV_32S));
旋转式选框;
if(n<5)
CV_误差(CV_StsBadSize,“应至少有5个点来拟合椭圆”);
//新的fitellipse算法,由Daniel Weiss博士贡献
点2f c(0,0);
双gfp[5],rp[5],t;
常数双最小值=1e-8;
布尔是浮点数=深度==CV\u 32F;
常量点*ptsi=points.ptr();
常量Point2f*ptsf=points.ptr();
自动缓冲区(n*5),自动缓冲区(n);
双*Ad=\U Ad,*bd=\U bd;
//参数A-E的首次拟合
材料A(n,5,CV_64F,Ad);
材料b(n,1,CV_64F,bd);
材料x(5,1,CV_64F,gfp);
对于(i=0;i最小每股收益)
t=gfp[2]/sin(-2.0*rp[4]);
否则//椭圆将旋转pi/2的整数倍
t=gfp[1]-gfp[0];
rp[2]=fabs(gfp[0]+gfp[1]-t);
如果(rp[2]>最小eps)
rp[2]=std::sqrt(2.0/rp[2]);
rp[3]=fabs(gfp[0]+gfp[1]+t);
如果(rp[3]>最小eps)
rp[3]=std::sqrt(2.0/rp[3]);
box.center.x=(float)rp[0]+c.x;
box.center.y=(浮动)rp[1]+c.y;
box.size.width=(浮动)(rp[2]*2);
box.size.height=(浮动)(rp[3]*2);
如果(box.size.width>box.size.height)
{
浮动tmp;
CV_交换(box.size.width、box.size.height、tmp);
箱角=(浮动)(90+rp[4]*180/CV_-PI);
}
如果(长方体角度<-180)
box.angle+=360;
如果(长方体角度>360)
box.angle-=360;
返回框;
}

源代码链接:

函数
fitEllipse
返回包含椭圆所有参数的
RotatedRect

椭圆由5个参数定义:

ellipse(res, Point(xc, yc), Size(a, b), theta, 0.0, 360.0, Scalar(0,255,0));
  • xc:中心的x坐标
  • yc:中心的y坐标
  • a:主半轴
  • b:短半轴
  • θ:旋转角度
您可以获得以下参数:

RotatedRect e = fitEllipse(points);

float xc    = e.center.x;
float yc    = e.center.y;
float a     = e.size.width  / 2;    // width >= height
float b     = e.size.height / 2;
float theta = e.angle;              // in degrees
您可以使用
rotatedlect
,使用函数
eliple
绘制椭圆:

ellipse(image, e, Scalar(0,255,0)); 
或者,等效使用椭圆参数:

ellipse(res, Point(xc, yc), Size(a, b), theta, 0.0, 360.0, Scalar(0,255,0));
如果需要隐式方程的系数值,可以执行以下操作(从):

因此,您可以从
rotatedlect
获取所需的参数,而无需更改函数
fitEllipse
。 该函数用于求解线性系统或最小二乘问题。使用SVD分解方法,系统可能被过度定义和/或矩阵src1可能是奇异的


有关该算法的更多详细信息,请参见提出该拟合椭圆方法的示例

这里有一些代码对我有用,我是根据这个线程上的其他响应编写的

def getConicCoeffFromEllipse(e):
    # ellipse(Point(xc, yc),Size(a, b), theta)
    xc = e[0][0]
    yc = e[0][1]
    a = e[1][0]/2
    b = e[1][1]/2
    theta = math.radians(e[2])
    # See https://en.wikipedia.org/wiki/Ellipse
    # Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0 is the equation
    A = a*a*math.pow(math.sin(theta),2) + b*b*math.pow(math.cos(theta),2)
    B = 2*(b*b - a*a)*math.sin(theta)*math.cos(theta)
    C = a*a*math.pow(math.cos(theta),2) + b*b*math.pow(math.sin(theta),2)
    D = -2*A*xc - B*yc
    E = -B*xc - 2*C*yc
    F = A*xc*xc + B*xc*yc + C*yc*yc - a*a*b*b
    coef = np.array([A,B,C,D,E,F]) / F
    return coef

def getConicMatrixFromCoeff(c):
    C = np.array([[c[0], c[1]/2, c[3]/2], # [ a, b/2, d/2 ]
                 [c[1]/2, c[2], c[4]/2],  # [b/2,  c, e/2 ]
                 [c[3]/2, c[4]/2, c[5]]])  # [d/2], e/2, f ]
    return C