如何使用OpenCV检测图像梯度或正常

时间:2014-04-02 07:53:26

标签: c++ opencv ellipse

我想检测图像中的椭圆。由于我当时正在学习Mathematica,我问了一个问题here并从下面的答案得到了一个令人满意的结果,它使用RANSAC算法来检测椭圆。

但是,最近我需要将它移植到OpenCV,但有些功能只存在于Mathematica中。其中一个关键功能是“GradientOrientationFilter”功能。

由于一般椭圆有五个参数,我需要采样五个点来确定一个。但是,采样点越多表示猜测的机会越小,这导致椭圆检测的成功率越低。因此,Mathematica的答案增加了另一个条件,即图像的梯度必须与椭圆方程的梯度平行。无论如何,我们只需要三个点来使用Mathematica方法中的最小二乘法确定一个椭圆。结果非常好。

但是,当我尝试在OpenCV中使用Sobel或Scharr运算符查找图像渐变时,它不够好,这总会导致不良结果。

如何准确计算图像的渐变或切线?谢谢!


结果有渐变,三分 Result with gradient, three points

没有渐变的结果,五分 Result without gradient, five points

---------- ----------更新

我事先做了一些边缘检测和中值模糊,并在边缘图像上绘制结果。我原来的测试图像是这样的:

original test image

通常,我的最终目标是检测场景或对象中的椭圆。像这样:

coffee cup

这就是我选择使用RANSAC从边缘点拟合椭圆的原因。

3 个答案:

答案 0 :(得分:1)

至于你的最终目标,你可以试试

OpenCV中的

findContours [fitEllipse]

伪代码将是

1)一些图像处理

2)找到所有轮廓

3)通过fitEllipse

拟合每个轮廓

这是我之前使用的代码的一部分

[... image process ....you get a bwimage ]

vector<vector<Point> > contours;
findContours(bwimage, contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);


for(size_t i = 0; i < contours.size(); i++)
{
    size_t count = contours[i].size();

    Mat pointsf;
    Mat(contours[i]).convertTo(pointsf, CV_32F);
    RotatedRect box = fitEllipse(pointsf);

    /* You can put some limitation about size and aspect ratio here */
    if( box.size.width > 20 && 
        box.size.height > 20 && 
        box.size.width < 80 && 
        box.size.height < 80 )
    {
    if( MAX(box.size.width, box.size.height) > MIN(box.size.width, box.size.height)*30 )
        continue;
    //drawContours(SrcImage, contours, (int)i, Scalar::all(255), 1, 8);

    ellipse(SrcImage, box, Scalar(0,0,255), 1, CV_AA);
    ellipse(SrcImage, box.center, box.size*0.5f, box.angle, 0, 360, Scalar(200,255,255), 1, CV_AA);
    }
}

imshow("result", SrcImage);

答案 1 :(得分:0)

您以不寻常的方式使用字词。

通常对于图片,术语&#34;渐变&#34;被解释为图像是数学函数f(x,y)。这为每个点提供了(df/dx, df/dy)向量。

然而,您正在查看图片,好像它是一个函数y = f(x),渐变将是f(x)/dx

现在,如果你看一下你的形象,你会发现这两种解释肯定是相关的。您的椭圆被绘制为一组对比像素,因此图像中有两个锐利渐变 - 内部和外部。这些当然对应于两个法向量,因此方向相反。

另请注意,您的图片有像素。渐变也是像素化的。使用单个像素宽度绘制椭圆的方式意味着局部渐变仅采用45度的倍数值:

▄▄ ▄▀ ▌ ▀▄

答案 2 :(得分:0)

如果您专注于椭圆(没有其他形状),您可以将椭圆像素的值视为点的质量。

然后你可以计算惯性矩Ixx,Iyy,Ixy找出角度θ,它可以将一般椭圆旋转回规范形式(X-Xc)^ 2 / a +(Y-Yc) ^ 2 / b = 1。

然后你可以通过质心找到Xc和Yc。

然后你可以通过最小X和最小Y找到a和b。

---------------更新-----------

此方法也可以应用于填充椭圆。

单个图像上的多个椭圆将失败,除非您先将它们分段。

让我解释一下, 我将使用C来表示cos(theta),使用S来表示sin(theta)

旋转到规范形式后,新的X是[eq0] X = xC-yS,Y是Y = xS + yC,其中x和y是原始位置。

旋转会给你最小的IYY。

[EQ1]

IYY = Sum(m * Y * Y)= Sum {m *(xS + yC)(xS + yC)} = Sum {m (xxSS + yyCC + xySC)= Ixx * S ^ 2 + Iyy * C ^ 2 + Ixy * S * C

对于最小IYY,d(IYY)/ d(θ)= 0

2IxxSC - 2IyySC + Ixy(CC-SS)= 0

2(Ixx-Iyy)/ Ixy =(SS-CC)/ SC = S / C + C / S = Z + 1 / Z

编程时,LHS只是一个数字,让我们说N

Z ^ 2 - NZ +1 = 0

所以有两个Z因此theta的根,让我们说Z1和Z2,一个将使IYY缩小,另一个将最大化IYY。

-----------伪代码--------

计算Ixx,Iyy,Ixy为空心或填充椭圆。

计算theta1 = atan(Z1)和theta2 = atan(Z2)

将这两个theta放入eq1中查找哪个更小。然后你得到theta。

回到那些非零像素,用你找到的theta将它们传送到新的X和Y.

通过sort()找到质心Xc Yc和min X和min Y.

--------------手工-----------

如果需要椭圆的原始方程

只需将[eq0]放入规范表格

即可