opencv c++ find inscribing circle of a contour
Asked Answered
G

3

5

I want to find the maximum inscribing circle of contour.

I have detected the contour with cv::findContours and it is there as a vector<Point>.

I know how to detect the minimum enclosing circle (cv::minEnclosingCircle), but not how to get the maximum inclosing circle. How to do this?

Question2: How do i get the inscribing and circumscribing circles centered on the center of mass?


For clarification, i try to describe, what i mean with these circels:

  1. min enclosing circle: touching object from outside, center position doesn't matter, minimum area.
  2. circumscribing circle: touching object from outside, center position on the center of mass of the object, minimum area.
  3. max inclosing circle: touching object from inside, center position doesn't matter, maximum area.
  4. inscribing circle: touching object from inside, center position on the center of mass of the object, maximum area.
Gaffrigged answered 6/12, 2018 at 6:53 Comment(2)
You can get the mass center of your contour using the Hu moments: e.g.docs.opencv.org/2.4/doc/tutorials/imgproc/shapedescriptors/… As for the circle, just an idea. You could calculate all distances from the center to all points of the contours and then draw a circle for the largest distance. If you really need the inner (inscribing) circle you could draw the line between the two points and use a LineIterator to get the nearest point in relation to the contour point.Shillelagh
After a little bit of searching I found a duplicate to your first question: #4279978 @Miki 's solution is also very nice.Shillelagh
K
11

You can:

1) create a mask from your contour

enter image description here

2) Compute the distanceTransform on the mask

enter image description here

3) The highest value is the radius, its position is the center

enter image description here

Code:

#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;
}
Kuehn answered 6/12, 2018 at 10:7 Comment(0)
G
1

At least i solved the calculation of the two circles with the center on the center of mass (in a way similar to @Grillteller suggested):

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

But the original question remeains (max area, center pos doesn't matter).

Gaffrigged answered 6/12, 2018 at 9:43 Comment(0)
C
1

@Miki's answer is great and extremely useful, I just spent a bit of time translating it into python, thought I should leave it here as well

#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)
Cnidoblast answered 12/8, 2022 at 21:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.