我想检测图像中的椭圆。由于我当时正在学习Mathematica,我问了一个问题here并从下面的答案得到了一个令人满意的结果,它使用RANSAC算法来检测椭圆。
但是,最近我需要将它移植到OpenCV,但有些功能只存在于Mathematica中。其中一个关键功能是“GradientOrientationFilter”功能。
由于一般椭圆有五个参数,我需要采样五个点来确定一个。但是,采样点越多表示猜测的机会越小,这导致椭圆检测的成功率越低。因此,Mathematica的答案增加了另一个条件,即图像的梯度必须与椭圆方程的梯度平行。无论如何,我们只需要三个点来使用Mathematica方法中的最小二乘法确定一个椭圆。结果非常好。
但是,当我尝试在OpenCV中使用Sobel或Scharr运算符查找图像渐变时,它不够好,这总会导致不良结果。
如何准确计算图像的渐变或切线?谢谢!
结果有渐变,三分
没有渐变的结果,五分
---------- ----------更新
我事先做了一些边缘检测和中值模糊,并在边缘图像上绘制结果。我原来的测试图像是这样的:
通常,我的最终目标是检测场景或对象中的椭圆。像这样:
这就是我选择使用RANSAC从边缘点拟合椭圆的原因。
答案 0 :(得分:1)
至于你的最终目标,你可以试试
OpenCV中的伪代码将是
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]放入规范表格
即可