Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I calculate the curvature of an extracted contour by opencv?

Tags:

c++

opencv

I did use the findcontours() method to extract contour from the image, but I have no idea how to calculate the curvature from a set of contour points. Can somebody help me? Thank you very much!

like image 565
kookoo121 Avatar asked Sep 17 '15 11:09

kookoo121


People also ask

How do you match contours in OpenCV?

OpenCV comes with a function cv. matchShapes() which enables us to compare two shapes, or two contours and returns a metric showing the similarity. The lower the result, the better match it is.

What does cv2 contourArea do?

To get the area of the contours, we can implement the function cv2. contourArea() . Why don't we try several contours here? If you input the first, second and third contours, you'll get the decreasing values as shown below.

How do I know if contour is closed on OpenCV?

Just use findContours() in your image, then decide whether the contour is closed or not by examining the hierarchy passed to the findContours() function.


2 Answers

While the theory behind Gombat's answer is correct, there are some errors in the code as well as in the formulae (the denominator t+n-x should be t+n-t). I have made several changes:

  • use symmetric derivatives to get more precise locations of curvature maxima
  • allow to use a step size for derivative calculation (can be used to reduce noise from noisy contours)
  • works with closed contours

Fixes: * return infinity as curvature if denominator is 0 (not 0) * added square calculation in denominator * correct checking for 0 divisor

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;
}
like image 140
Philipp Avatar answered Sep 23 '22 12:09

Philipp


For me curvature is:

where t is the position inside the contour and x(t) resp. y(t) return the related x resp. y value. See here.

So, according to my definition of curvature, one can implement it this way:

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;
}

It works on non-closed pointlists as well. For closed contours, you may would like to change the boundary behavior (for the first iterations).

UPDATE:

Explanation for the derivatives:

A derivative for a continuous 1 dimensional function f(t) is:

But we are in a discrete space and have two discrete functions f_x(t) and f_y(t) where the smallest step for t is one.

The second derivative is the derivative of the first derivative:

Using the approximation of the first derivative, it yields to:

There are other approximations for the derivatives, if you google it, you will find a lot.

like image 25
Gombat Avatar answered Sep 24 '22 12:09

Gombat