圆检测 - 可靠性评估

时间:2013-11-23 17:47:18

标签: c++ opencv detection geometry reliability

我正在编写一个支持OpenCV的C ++应用程序。该应用程序基本上分析了手写图像并识别符号(不要指望字母数字符号,它是deaf-specific writing system)。

我的输入图片包含形状各异的符号,但我想专注于圈子。 下图说明了我目前正在处理的一些圆形符号(在降噪和二值化之后)。

Selction of circle-shape characters.

要识别圆形符号,我使用Hough Circle Transform,它可以很好地完成工作。在应用中值滤波器以降低噪声之后应用变换,并将阈值用于二值化图像。

我的问题是,有时候,霍夫圆变换检测到没有圆圈的圆圈(见下图)。

Crazy circle detection.

现在,我一直在寻找一些可靠性评估"对于检测到的圆圈。但我运气不好。 有没有办法查看检测到的圆是否与实际圆相关?

我开始考虑自己的一些解决方案,但也许有人想出更聪明的东西,我可以:

  • 使用实际符号评估检测到的圆的每个点的Husdorff距离,并将其用作某种测量值
  • 使用检测到的中心的坐标,我可以将图像分成4个扇区(见下图),看看Hough Circle Transform是否检测到每个(或大多数)扇区中的某些内容。

Image slit in 4 sectors using the coordinates of the center.

请记住:我正在处理手写(即非常粗略绘制)的符号。

3 个答案:

答案 0 :(得分:0)

我认为某种形式的评估圆圈指向符号的距离是要走的路 - 我怀疑还有更复杂的替代方案也是实用的。

然而,有越来越不复杂和有效的方法来进行距离评估。

由于您的符号图像已经二值化,我建议如下:计算二进制图像上的Distance Transform;这样做efficient algorithms。然后你只需要在圆点处对距离图像进行采样并总结距离(可能相对于圆周进行归一化,得到一些比例不变性)以获得误差分数。

如果您的图像中碰巧有很多小杂乱,那么这会失败,但您发布的内容看起来很干净。

答案 1 :(得分:0)

我解决了设计一个基于@Abhishek想法的函数的问题。

这是伪代码及其C ++实现。

checkCircleSectors(imagePoints, circle, circleExpansionFactor, theta, acceptThreshold)

  Divide the circle in n sectors, using angle theta (so n = 2*PI /theta)

  (Optional) Slightly expand or reduce the radius of the circle by circleExpansionFactor, if needed.

  for each point "curPoint" within the image (imagePoints){

    if the distance between curPoint and center.circle is lesser/equal to circle.radius{

      let "curAngle" be the angle between circle.center and curPoint, calculated using curPoint as origin.
      let "curSector" be the sector of circle which contains curAngle
          upgrade the ranking of curSector by one.
    }
  }

  if there are more than acceptThreshold sectors whose rank is zero
    the circle is not acceptable
  else
    the circle is acceptable

注意:

我发现略微扩大圆的半径非常有用(我使用的是1.25扩展因子),因为有时(特别是在手写圆圈的情况下)检测可能不准确。

这是我对该概念的C ++实现:

boolean ImgCheck::checkCircleSectors(vector<Point> tgtPoints, Point tgtCenter, int tgtRadius, float tgtRadiusExp, int tgtStep, int tgtThreshold){

  vector<int> circleData( 360 / tgtStep, 0);
  int detectionReliability = 0;

  tgtRadius = tgtRadius * tgtRadiusExp;

  /* Analyze the sectors.                               */
  for(size_t i=0; i<tgtPoints.size(); i++){

    Point curCartesianPoint = getCartesianCoordinates(tgtCenter, tgtPoints[i]);

    float angleRad = arctangent2(curCartesianPoint);
    int   angleDeg = angleRad * (180/M_PI);

    if(distance(tgtPoints[i],tgtCenter) <= tgtRadius){
      circleData.at(angleDeg / tgtStep) += 1;
    }

  }

  /* Count the postive-ranked sectors.                 */
  for(size_t i = 0; i< circleData.size(); i++){
    if(circleData[i] > 0)
      detectionReliability += 100.0/(360/tgtStep);
  }

  if(detectionReliability >= tgtThreshold)
    return true;
  }
  return false;
}

注意:

我对最后三个参数的偏好如下:

  • tgtRadiusExp = 1.25(半径扩大了四分之一)
  • tgtStep = 5(theta = 5度 - &gt;圈分为72个扇区)
  • tgtTreshold = 75(通过测试至少需要75个非零扇区)

函数 getCartesianCoordinates (下面的源代码)转换笛卡尔坐标中的OpenCV坐标。它需要两个参数:

  • Point tgtOrigin是必须的点的openCV坐标 用作原产地。
  • Point tgtPoint是另一个点的openCV坐标。

该函数返回tgtPoint的坐标,使用tgtOrigin作为原点进行转换。

Point getCartesianCoordinates(Point tgtOrigin, Point tgtPoint){

  Point resPoint = tgtPoint - tgtOrigin;

  return Point(resPoint.x, -resPoint.y);
}

答案 2 :(得分:0)

花了一些时间手工编码椭圆探测器,我在检测&#34; ellipse-ness&#34;方面遵循了类似的路径。 (适合度是指导)。但最终,对这种探测器进行手工编码是一项脆弱的工作,因为您的输入实际上并没有那么明确;你可能会发现人类读者会成功识别出许多人的圈子和#34;这将失败任何合理的数学定义&#34;循环&#34;。因此,接受角色的实际操作定义不是圆的等式。

我建议你考虑一种完全不同的方法;而不是手动编码探测器,训练他们。这是手写识别的最先进方法,您可以选择流行的经过验证的算法(the MNIST dataset of hand-written arabic numerals is a popular bench-mark)。而不是编码探测器,建立一个非常体面的训练集;你不必为每个角色手动编码探测器,你将构建一些非常强大且经过充分测试的东西,可以应对非常草率的手写,污迹,涂鸦等(比如你可以找到)在MNIST数据集中。