OpenCV C++ 寻找轮廓内接圆

5
我想找到轮廓的最大内接圆。
我已经使用cv :: findContours检测到了轮廓,并将其表示为vector。
我知道如何检测最小外接圆(cv :: minEnclosingCircle),但不知道如何获取最大内接圆。该如何操作?
问题2:如何获得以重心为中心的内切和外接圆?
为了澄清,我尝试描述一下我所说的这些圆:
1. 最小外接圆:从外部接触对象,中心位置无关紧要,面积最小。 2. 外接圆:从外部接触对象,中心位置在对象的重心上,面积最小。 3. 最大内接圆:从内部接触对象,中心位置无关紧要,面积最大。 4. 内接圆:从内部接触对象,中心位置在对象的重心上,面积最大。

1
您可以使用Hu矩获取轮廓的质心:例如https://docs.opencv.org/2.4/doc/tutorials/imgproc/shapedescriptors/moments/moments.html 至于圆形,只是一个想法。您可以计算从中心到所有轮廓点的所有距离,然后为最大距离绘制一个圆。如果您真的需要内部(内切)圆,您可以绘制两个点之间的线,并使用LineIterator获取相对于轮廓点的最近点。 - Grillteller
1
经过一点搜索,我找到了你第一个问题的重复:https://dev59.com/Im855IYBdhLWcg3wilCD @Miki 的解决方案也非常好。 - Grillteller
3个回答

11
你可以:
1)从你的轮廓创建一个遮罩。

enter image description here

2) 在掩码上计算distanceTransform

enter image description here

3) 最大值是半径,其位置是中心

enter image description here

代码:

#include <opencv2\opencv.hpp>

int main()
{
    // Load image
    cv::Mat1b img = cv::imread("path_to_img", cv::IMREAD_GRAYSCALE);

    // Correct image
    cv::Mat1b bin = img < 127;

    // Find contour
    std::vector<std::vector<cv::Point>> contours;
    cv::findContours(bin, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);

    // Draw on mask
    cv::Mat1b mask(bin.rows, bin.cols, uchar(0));
    cv::drawContours(mask, contours, 0, cv::Scalar(255), cv::FILLED);

    // Distance Trasnsform
    cv::Mat1f dt;
    cv::distanceTransform(mask, dt, cv::DIST_L2, 5, cv::DIST_LABEL_PIXEL);

    // Find max value
    double max_val;
    cv::Point max_loc;
    cv::minMaxLoc(dt, nullptr, &max_val, nullptr, &max_loc);

    // Output image
    cv::Mat3b out;
    cv::cvtColor(img, out, cv::COLOR_GRAY2BGR);
    cv::circle(out, max_loc, max_val, cv::Scalar(0, 255, 0));

    return 0;
}

1

至少我解决了两个圆心在质心上的计算问题(类似于@Grillteller建议的方法):

Point2f p_Contour_first = vp_Contour[0];
double circumCirc_Radius  = norm(p_Centroid - p_Contour_first);
double inscriCirc_Radius  = norm(p_Centroid - p_Contour_first);
for(int p = 0; p < vp_Contour.size(); p++)
{
    Point2f p_Contour_current = vp_Contour[p];
    double r = norm(p_Centroid - p_Contour_current);
    if(r < inscriCirc_Radius) inscriCirc_Radius = r;
    if(r > circumCirc_Radius) circumCirc_Radius = r;
}

但是最初的问题仍然存在(最大面积,中心位置无关紧要)。

0

@Miki的回答非常好,非常有用,我刚刚花了一点时间将其翻译成Python,想在这里留下它

#get maximum inscribed circle
#my input image is called "frame"
#get threshold image from frame

ret, thresh = cv2.threshold(frame, 100, 255, cv2.THRESH_BINARY)
contours,hierarchy = cv2.findContours(thresh, 1, cv2.CHAIN_APPROX_NONE)

#create blank mask
mask = np.zeros(frame.shape[:2], dtype="uint8")
cv2.drawContours(mask, contours, -1, 255, -1)
dist = cv2.distanceTransform(mask, cv2.DIST_L2, 0)
NULL,max_val,NULL,max_indx=cv2.minMaxLoc(dist)

(x,y),radius = max_indx, max_val

#draw circle on original image
cv2.circle(frame, (x,y), radius, (0,255,0), 2)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接