如何通过opencv计算提取轮廓的曲率?

koo*_*121 8 c++ opencv

我确实使用该findcontours()方法从图像中提取轮廓,但我不知道如何从一组轮廓点计算曲率.有人能帮助我吗?非常感谢你!

Gom*_*bat 5

对我来说曲率是:

t轮廓内的位置在哪里x(t)y(t)返回相关的x响应。y价值。见这里

所以,根据我对曲率的定义,可以这样实现:

std::vector< float > vecCurvature( vecContourPoints.size() );

cv::Point2f posOld, posOlder;
cv::Point2f f1stDerivative, f2ndDerivative;   
for (size_t i = 0; i < vecContourPoints.size(); i++ )
{
    const cv::Point2f& pos = vecContourPoints[i];

    if ( i == 0 ){ posOld = posOlder = pos; }

    f1stDerivative.x =   pos.x -        posOld.x;
    f1stDerivative.y =   pos.y -        posOld.y;
    f2ndDerivative.x = - pos.x + 2.0f * posOld.x - posOlder.x;
    f2ndDerivative.y = - pos.y + 2.0f * posOld.y - posOlder.y;

    float curvature2D = 0.0f;
    if ( std::abs(f2ndDerivative.x) > 10e-4 && std::abs(f2ndDerivative.y) > 10e-4 )
    {
        curvature2D = sqrt( std::abs( 
            pow( f2ndDerivative.y*f1stDerivative.x - f2ndDerivative.x*f1stDerivative.y, 2.0f ) / 
            pow( f2ndDerivative.x + f2ndDerivative.y, 3.0 ) ) );
    }
    
    vecCurvature[i] = curvature2D;
    
    posOlder = posOld;
    posOld = pos;
}
Run Code Online (Sandbox Code Playgroud)

它也适用于非封闭点列表。对于闭合轮廓,您可能希望更改边界行为(对于第一次迭代)。

更新:

衍生品说明:

连续一维函数的导数f(t)是:

但是我们在一个离散空间中,有两个离散函数f_x(t)f_y(t)其中最小的步长为t1。

二阶导数是一阶导数的导数:

使用一阶导数的近似,它产生:

导数还有其他近似值,如果你用谷歌搜索,你会发现很多。


Phi*_*ipp 5

虽然Gombat的答案背后的理论是正确的,但代码和公式中都存在一些错误(分母t+n-x应该是t+n-t).我做了几处改动:

  • 使用对称导数来获得更精确的曲率最大值位置
  • 允许使用步长进行微分计算(可用于减少噪声等值线的噪声)
  • 适用于封闭的轮廓

修正:*如果分母为0(不为0),则返回无穷大作为曲率*在分母中添加平方计算*正确检查0除数

std::vector<double> getCurvature(std::vector<cv::Point> const& vecContourPoints, int step)
{
  std::vector< double > vecCurvature( vecContourPoints.size() );

  if (vecContourPoints.size() < step)
    return vecCurvature;

  auto frontToBack = vecContourPoints.front() - vecContourPoints.back();
  std::cout << CONTENT_OF(frontToBack) << std::endl;
  bool isClosed = ((int)std::max(std::abs(frontToBack.x), std::abs(frontToBack.y))) <= 1;

  cv::Point2f pplus, pminus;
  cv::Point2f f1stDerivative, f2ndDerivative;
  for (int i = 0; i < vecContourPoints.size(); i++ )
  {
      const cv::Point2f& pos = vecContourPoints[i];

      int maxStep = step;
      if (!isClosed)
        {
          maxStep = std::min(std::min(step, i), (int)vecContourPoints.size()-1-i);
          if (maxStep == 0)
            {
              vecCurvature[i] = std::numeric_limits<double>::infinity();
              continue;
            }
        }


      int iminus = i-maxStep;
      int iplus = i+maxStep;
      pminus = vecContourPoints[iminus < 0 ? iminus + vecContourPoints.size() : iminus];
      pplus = vecContourPoints[iplus > vecContourPoints.size() ? iplus - vecContourPoints.size() : iplus];


      f1stDerivative.x =   (pplus.x -        pminus.x) / (iplus-iminus);
      f1stDerivative.y =   (pplus.y -        pminus.y) / (iplus-iminus);
      f2ndDerivative.x = (pplus.x - 2*pos.x + pminus.x) / ((iplus-iminus)/2*(iplus-iminus)/2);
      f2ndDerivative.y = (pplus.y - 2*pos.y + pminus.y) / ((iplus-iminus)/2*(iplus-iminus)/2);

      double curvature2D;
      double divisor = f1stDerivative.x*f1stDerivative.x + f1stDerivative.y*f1stDerivative.y;
      if ( std::abs(divisor) > 10e-8 )
        {
          curvature2D =  std::abs(f2ndDerivative.y*f1stDerivative.x - f2ndDerivative.x*f1stDerivative.y) /
                pow(divisor, 3.0/2.0 )  ;
        }
      else
        {
          curvature2D = std::numeric_limits<double>::infinity();
        }

      vecCurvature[i] = curvature2D;


  }
  return vecCurvature;
}
Run Code Online (Sandbox Code Playgroud)