An Algorithm for Calculating the Inner and Outer Diameter of a Torus

An Algorithm for Calculating the Inner and Outer Diameter of a Torus

I am working on a project recently, and I need to calculate the inner and outer diameters of the rings on the picture. Similar to the circle below.
write picture description here

write picture description here

The basic characteristics of these rings are
1. Only one ring will appear in each image. Of course there may be cases where the ring deviates from the image and only an incomplete ring is visible.
2. The inside of the ring may be black or bright, but it is different from the color of the ring. The inner boundary of the ring can be clearly seen.
3. The inner and outer walls or surfaces of the ring may be damaged. For example the following picture:
write picture description here

In this case, the inner and outer diameters of the ring should be calculated more accurately.

My development language is C++, and the tools I use are opencv and gsl.

The first step is the canny method to extract image edges. There are many articles about the canny method on the Internet, which will not be introduced here. Then save these edge points to a vector. Similar to the code snippet below.

    cv::Canny(grayImage, grayImage, m_canny_threshold1, m_canny_threshold2);
    std::vector<cv::Point2i> points;
    for(int row = 0; row < grayImage.rows; row++)
    {
        const uchar * pLine = grayImage.ptr<const uchar>(row);
        for(int col = 0; col < grayImage.cols; col++)
        {
            if(pLine[col] > 200)
            {
                points.push_back(cv::Point2i(col, row));
            }
        }
    }

The points extracted here include inner and outer edges, as well as various noise points. Next, you need to separate the inner and outer edges. The following is an example of the code to extract the inner edge:

    circleLeastFit(points, center_x, center_y, radius);
    vector<cv::Point2i> innerCirclePoints; // 圆周一周 360°,每度保留一个数据点
    vector<double> innerCircleDistance;
    for(int i = 0; i < 360; i++)
    {
        innerCirclePoints.push_back(cv::Point2i(-1, -1));
        innerCircleDistance.push_back(INT_MAX);
    }
    for (vector<cv::Point2i>::iterator it = points.begin() ; it != points.end(); ++it)
    {
        int degree = static_cast<int> ((atan2(it->y - center_y, it->x - center_x) + M_PI) / M_PI * 180);
        double dist = hypot(it->y - center_y, it->x - center_x);
        if(dist < innerCircleDistance.at(degree))
        {
            //qDebug() << "degree = "<< degree << ", dist = " << dist << ", point = " << it->x << ", " << it->y;
            innerCircleDistance[degree] = dist;
            innerCirclePoints[degree] = *it; //将这个角度内的离圆心最近的点保留下来
        }
    }
    //qDebug() << "**1**";
    // 如果图像中的圆环不全,有些度数可能就没有点。这时要把这样的点去掉。
    for (vector<cv::Point2i>::iterator it = innerCirclePoints.begin() ; it != innerCirclePoints.end();)
    {
        if(*it == cv::Point2i(-1, -1))
        {
            it = innerCirclePoints.erase(it);
        }
        else
        {
            ++ it;
        }
    }

This code uses circleLeastFit(points, center_x, center_y, radius);
this function is the least squares method to fit a circle. For the fitting method of the circle, please refer to the two articles I wrote before:

Least Squares Fitting
Circle Fitting Algorithm (Minimum Sum of Distances)

The method to obtain the inner boundary is actually very simple, first roughly determine the center of the circle. Then go around the center of the circle to find the closest point to the center of the circle at each angle. Most of these points are inner edges, and there are a few noise points. Then fit one more time to get the inner diameter.

The calculation method of the outer diameter is also similar. Not much to say.

The results of some calculations are given below.

write picture description here

write picture description here

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325601459&siteId=291194637