【问题标题】:How to detect image gradient or normal using OpenCV如何使用 OpenCV 检测图像梯度或法线
【发布时间】:2014-04-04 23:29:04
【问题描述】:

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

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

由于一般椭圆有五个参数,因此我需要采样五个点来确定一个。然而,越多的采样点表明获得良好猜测的机会越低,这导致椭圆检测的成功率越低。因此,来自 Mathematica 的答案添加了另一个条件,即图像的梯度必须平行于椭圆方程的梯度。无论如何,我们只需要三个点就可以使用 Mathematica 方法中的最小二乘法确定一个椭圆。结果还不错。

但是,当我尝试在 OpenCV 中使用 Sobel 或 Scharr 算子来寻找图像梯度时,它还不够好,这总是导致结果不好。

如何准确计算图像的梯度或切线?谢谢!


带有渐变的结果,三个点

没有梯度的结果,五分

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

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

一般来说,我的最终目标是检测场景中或物体上的椭圆。像这样的:

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

【问题讨论】:

  • "由于一般椭圆有五个参数,所以我需要采样五个点来确定一个。" > 我有兴趣对此进行详细解释,因为对我来说 3 个 二维 点足以估计 5 个 标量 参数。关于你的问题,你能在你的问题中包含你用来估计梯度的代码吗?
  • 我的想法是一般椭圆是 ax^2+by^2+cxy+dx+ey+1=0。所以如果我想解这个方程,我需要有五个点,(x1,y1)~(x5,y5)。将它们代入椭圆方程的(x,y),我可以解出五个变量的联立方程。这就是为什么我说我需要抽样五个点来确定一个。
  • 代码sn-p是here
  • 考虑到更新后的图像,使用广义霍夫变换。

标签: c++ opencv ellipse


【解决方案1】:

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

findContours[fitEllipse] 在 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);

【讨论】:

  • 这看起来不错!但是如何判断我检测到的椭圆是否足够好呢?例如,在我原来的 RANSAC 方法中,我可以使用接近椭圆的点数作为标准。积分越多越好。在您提出的方法中,如何对检测到的椭圆进行排序?谢谢!
  • 我想对这个答案投赞成票,但我没有足够的声望这样做......对不起。
  • 由于您将找到的每个轮廓传递给 fitellipse() ,这意味着将拟合图像处理后的所有独立对象。 vector 是 SINGLE 轮廓的所有点(vector 的矢量是轮廓的数组/集合,这就是为什么命名为 contourS)。所以你有拟合的椭圆和每个轮廓的原始点。原则上,您可以应用一些标准。
  • 因此您可以使用每个轮廓的大小(点数)来过滤过小/过大的对象。每个轮廓的惯性矩 Ixx/Iyy 与过滤器线段的比率。 fitellipse() 在计算时间上非常昂贵。关于投票,我不是为此而来,所以这不是问题。
【解决方案2】:

如果你关注椭圆(没有其他形状),你可以把椭圆的像素值当作点的质量。

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

那么你可以通过质心找出Xc和Yc。

那么你可以通过 min X 和 min 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

对于 min IYY,d(IYY)/d(theta) = 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 - 新西兰 +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] 放入规范形式

【讨论】:

  • 您可以相当容易地找到主轴:在边缘上随机选取两个点,看看是否可以通过沿边缘交替移动任一点来增加它们之间的距离。长轴的独特之处在于它使两点之间的距离最大化,并且函数是连续的。好的起点候选是 X 或 Y 方向的两个极端。由于像素坐标的四舍五入,对于小的圆形椭圆有些破坏。您还可以轻松确定周长。另一种方法是直接对 6 个参数进行最小二乘。
  • 我已经为此更新了原始帖子。抱歉之前描述的模棱两可。由于我想在相当复杂的场景或物体上检测椭圆,所以有些点不属于椭圆。这就是为什么我使用 RANSAC 来检测椭圆,而不是拟合椭圆。
  • 感谢详细的解释!对于只包含一个椭圆的图像,这种方法似乎相当不错。但是,对于我的应用程序,通常有一个椭圆和其他线条矩形等。但仍然感谢您的回复:)
【解决方案3】:

您以一种不寻常的方式使用术语。

通常对于图像,术语“梯度”被解释为图像是数学函数f(x,y)。这给了我们每个点的(df/dx, df/dy) 向量。

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

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

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

▄▄ ▄▀ ▌ ▀▄

【讨论】:

  • 我的意思是图像渐变,作为第一种情况,每个点的 (df/dx, df/dy) 向量。
  • 我已经更新了我原来的帖子。我的原始测试图像是一个实心椭圆。我做边缘检测以获得边缘点。但是梯度是使用实心椭圆计算的。除了这一切,由于我只想要梯度的方向,所以内梯度向量和外梯度向量还是平行的,应该还是法向量,是吗?
  • 由于我使用 Sobel 算子的 dx 和 dy 计算了每个像素上的图像梯度方向,是否仍会被量化为 45、90,135... 度?
  • @AlbertK:不,Sobel 使用 3x3 内核平滑渐变。
  • 这似乎与here 的答案相矛盾,这是我计算梯度方向所遵循的程序: 1.在 X 方向 (Sx) 上计算 Sobel。 2. 计算 Y 方向的 Sobel (Sy)。 3.用arctan(Sy / Sx)计算梯度方向。
猜你喜欢
  • 2020-08-19
  • 2021-08-14
  • 1970-01-01
  • 1970-01-01
  • 2020-07-29
  • 1970-01-01
  • 1970-01-01
  • 2014-06-21
  • 2019-04-28
相关资源
最近更新 更多