[Python] Detecting inflection points/local minima in openCv Python contours objects to...

Discussão em 'Python' iniciado por Stack, Junho 19, 2025.

  1. Stack

    Stack Membro Participativo

    I am trying to answer Detecting circular/elliptical shape with Python openCv (or any other lib). In trying to get the best results I would like to find a way to differentiate between the following two kinds of shapes (red contours):

    [​IMG].


    My first attempt uses as input:
    ​

    bud_1.jpg :

    [​IMG]

    and this code :

    import cv2
    import numpy as np

    def getCurvature(vec_contour_points, step):
    vec_curvature = [0.0] * len(vec_contour_points)

    if len(vec_contour_points) < step:
    return vec_curvature

    front_to_back = vec_contour_points[0] - vec_contour_points[-1]
    # print(front_to_back) # Assuming CONTENT_OF is a print statement
    is_closed = max(abs(front_to_back[0]), abs(front_to_back[1])) <= 1

    for i in range(len(vec_contour_points)):
    pos = vec_contour_points

    max_step = step
    if not is_closed:
    max_step = min(min(step, i), len(vec_contour_points) - 1 - i)
    if max_step == 0:
    vec_curvature = float('inf')
    continue

    iminus = i - max_step
    iplus = i + max_step
    pminus = vec_contour_points[iminus if iminus >= 0 else iminus + len(vec_contour_points)]
    pplus = vec_contour_points[iplus if iplus < len(vec_contour_points) else iplus - len(vec_contour_points)]

    f1st_derivative = [(pplus[0] - pminus[0]) / (iplus - iminus), (pplus[1] - pminus[1]) / (iplus - iminus)]
    f2nd_derivative = [
    (pplus[0] - 2 * pos[0] + pminus[0]) / ((iplus - iminus) / 2 * (iplus - iminus) / 2),
    (pplus[1] - 2 * pos[1] + pminus[1]) / ((iplus - iminus) / 2 * (iplus - iminus) / 2)
    ]

    divisor = f1st_derivative[0] ** 2 + f1st_derivative[1] ** 2
    if abs(divisor) > 1e-8:
    curvature_2d = abs(f2nd_derivative[1] * f1st_derivative[0] - f2nd_derivative[0] * f1st_derivative[1]) / (divisor ** (3.0 / 2.0))
    else:
    curvature_2d = float('inf')

    vec_curvature = curvature_2d

    return vec_curvature

    # Load image and find contours
    img = cv2.imread('bud_1.jpg')

    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    _, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

    cv2.imshow("Image threshold", gray)
    cv2.waitKey(0)



    black_img = np.zeros((img.shape[0], img.shape[1]))

    print('img size : ', img.shape, img.shape[1])

    print('black_img size : ', black_img.shape, img.shape[1])


    contours, _ = cv2.findContours(thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)



    # Select the largest contour
    if contours:

    print('contours lenght : ', len(contours))
    # largest_contour = max(contours, key=cv2.contourArea)

    cnt = sorted(contours, key=cv2.contourArea, reverse=True)

    print('cnt lenght : ', len(cnt))

    largest_contour = cnt[1]

    print('largest_contour area : ', cv2.contourArea(largest_contour))
    cv2.drawContours(img, [largest_contour] , 0, (0,255,0) , 1)


    cv2.imshow("Image contour", img)
    cv2.imwrite('contour.jpg' , img)
    cv2.waitKey(0)


    filled_contours = cv2.drawContours(black_img, [largest_contour], 0 , 1 , cv2.FILLED)



    cv2.imshow("Black Image", black_img)
    cv2.waitKey(0)

    cv2.imwrite('black.tiff', black_img)

    thinned_img = np.zeros((img.shape[0], img.shape[1]))

    thinned_contours = cv2.drawContours(thinned_img, [largest_contour], 0 , 1 , 1)

    cv2.imshow("Thinned Image", thinned_img)
    cv2.waitKey(0)

    cv2.imwrite('thinned_contours.tiff', thinned_img)

    # Extract contour points
    # contour_points = np.squeeze(largest_contour)

    # this is tocheck that contours is bad as it is even if looks like a nice one pixel line
    contour_points = np.where(thinned_contours == 1)

    contour_points = np.squeeze(largest_contour)

    # for i in contour_points :

    # print(i)


    print('largest_contour lenght : ', len(largest_contour))
    print('contour points lenght : ', len(contour_points))

    # # Calculate slopes and curvatures (using finite differences)
    ###### Using SO 32629806 answer for curvatures
    # slopes = np.zeros(len(contour_points) - 1)
    # curvatures = np.zeros(len(contour_points) - 2)

    q_value = np.zeros(len(contour_points) - 1)


    k = 2
    ###### Using SO 32629806 answer for curvatures
    # for i in range(len(contour_points) -k -1):
    # # slopes = (contour_points[i+k][1] - contour_points[1]) / (contour_points[i+k][0] - contour_points[0])

    # # slopes = (contour_points[1]) - contour_points[i+k][1] / (contour_points[i+k][0] - contour_points[0])


    # if (contour_points[0] - contour_points[i+k][0]) != 0:


    # slopes = (contour_points[1] - contour_points[i+k][1]) / (contour_points[0] - contour_points[i+k][0])

    # else :

    # slopes = 0

    ###### Using SO 32629806 answer for curvatures
    # for i in range(len(contour_points) -k -1):
    # if (contour_points[0] - contour_points[i+k][0]) != 0:
    # curvatures = -(slopes - slopes[i+k]) / (contour_points[0] - contour_points[i+k][0])
    # else :
    # curvatures = 0


    curvatures = getCurvature(contour_points , 1)

    # print('curvatures : ', curvatures)

    print('len(contour_points) :' ,len(contour_points))

    for i in range(len(contour_points)-k-1):

    q_value = contour_points[i][1] - curvatures[i]*(contour_points[i][0])



    # Identify inflection points (curvature change sign)
    inflection_points = []


    for i in range(len(curvatures) - 1):

    if np.sign(curvatures[i]) != np.sign(curvatures[i + 1 ]) :

    mid_point = (contour_points[i])


    if True:

    img_copy = img.copy()

    cv2.circle(img_copy, (mid_point[0], mid_point[1]), 5, (0, 0, 255), -1)


    if curvatures[i] != float('inf'):

    # print('------------', np.isnan(curvatures[i]) , curvatures[i])


    if np.isnan(curvatures[i]) :

    print(np.isnan(curvatures[i]))



    cv2.line(img_copy , ( 0 , int(curvatures[i]*0 + q_value[i])) , (img.shape[1]-1 , int(curvatures[i]*img.shape[1]-1 + q_value[i])), (0, 255, 0), 1)

    cv2.imshow("Image Copy", img_copy)

    # cv2.imwrite('finale.jpg__'+str(i), img_copy)

    cv2.waitKey(0)

    inflection_points.append([mid_point])




    # # print('\ninflection points[0] : ' , inflection_points)

    # # Draw the inflection points on the image
    for point in inflection_points:

    cv2.circle(img, (point[0][0], point[0][1]), 5, (0, 0, 255), -1)

    cv2.imshow("Image with Inflection Points", img)

    cv2.imwrite('finale.jpg', img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()


    What I am trying to achieve is to get all the tangents to the contour shape and figure out a way to detect the two local minima in the shape:

    [URL='https://i.sstatic.net/bZBJtc3U.jpg'][IMG]https://i.sstatic.net/bZBJtc3U.jpg[/IMG][/URL]

    Apart from having difficulty retrieving only real tangents to the curve (I guess it has to do with the finite and bumpy contour profile that is not a continuous numerical function) I need to devise a strategy to isolate these kinds of tangents:

    [URL='https://i.sstatic.net/eAHQyqzv.png'][IMG]https://i.sstatic.net/eAHQyqzv.png[/IMG][/URL]

    [URL='https://i.sstatic.net/WiAfVonw.png'][IMG]https://i.sstatic.net/WiAfVonw.png[/IMG][/URL]

    to have a way to assess that the found contour is a bilobated shape versus a circular or elliptical one.

    My strategy would be to check if the [B]contour[/B] area is similar to the calculated cv2.minEnclosingCircle(contour) or cv2.fitEllipse(contour) and for the shape showing too big difference evaluate the presence of at least 2 local minimal (I guess they could be more because of the results of my algorithm that retrieve wrong tangents i.e. :

    [URL='https://i.sstatic.net/xFk7v6Wi.png'][IMG]https://i.sstatic.net/xFk7v6Wi.png[/IMG][/URL]

    My idea would be to check if any of the points on the tangent at the right of the contour contact point (inflection point) belongs to the blue shape and test for the same condition on the right. Any idea about how to achieve this ? Any idea on how to get only proper real tangent to the contour ?

    [url="https://stackoverflow.com/questions/79672095/detecting-inflection-points-local-minima-in-opencv-python-contours-objects-to-di"]Continue reading...[/url][/i][/i][/i][/i][/i][/i][/i][/i][/i][/i][/i][/i][/i][/i]

Compartilhe esta Página