Search code examples
pythonopencvimage-processingcomputer-visioncurve-fitting

Fitting curves in an image and finding intersection points


I am working on some images to extract the two curves and find their intersection points. The images vary widely in their appearance as you can see in the attached images.

As for now, doing a sobel and then applying canny edge filter pops them out but not quite sure how to fit curves to them and find the two intersection points (which they will have for sure)

import cv2
import numpy as np

img = cv2.imread('image6.jpg')
grad_x = cv2.Sobel(img, cv2.CV_64F, 1, 0, 3)
grad_y = cv2.Sobel(img, cv2.CV_64F, 0, 1, 3)
grad = np.sqrt(grad_x**2 + grad_y**2)
grad_norm = (grad * 255 / grad.max()).astype(np.uint8)
grad_norm = cv2.blur(grad_norm,(3,3))
cv2.imshow('Blur', grad_norm)
cv2.waitKey(0)
# Canny Edge Detection
edges = cv2.Canny(image=grad_norm, threshold1=100, threshold2=200) # Canny Edge Detection
# Display Canny Edge Detection Image
cv2.imshow('Canny Edge Detection', edges)
cv2.waitKey(0)

Original End result The red points shown above are the ones that I am looking to get to at

Any suggestions?

thanks and Happy New Year to all


Solution

    1. First of all, you need to remove noise:
    1. Then you can fit an ellipse as done in this answer.

    2. you can divide the set of points into two halves (above the longest axis, under the longest axis), then you fit each of the two sets into a parabola, the algebraic solution of the two equations will be the intersection points.

    here is an example with one of your images using basic thresholding and cropping, and ellipse fitting, the challenge in your case lies in better filtering noise to keep only the ellipse/curves.

    enter image description here

    #!/usr/bin/python3
    
    import os
    import numpy as np
    import cv2
    from skimage.measure import EllipseModel
    from matplotlib.patches import Ellipse
    import matplotlib.pyplot as plt
    
    im_id = 1
    im_path = f"eliipse_fitting/{im_id}.jpg"
     
    original = cv2.imread(im_path, cv2.IMREAD_GRAYSCALE)
    img = original.copy()
    width, height = img.shape
    
    ## Cropping to remove large objects
    img = img[width//3:2*width//3, height//3: 2*height//3]
    
    ## Thresholding
    th = 50
    img[img>=th] = 255
    img[img<th] = 0
    
    ## TODO: Add Noise Filtering, and Large Objects Removal
    nonzero_indices = np.transpose(np.nonzero(img))
    points = [(index[1], index[0]) for index in nonzero_indices]
    
    a_points = np.array(points)
    x = a_points[:, 0]
    y = a_points[:, 1]
    
    ell = EllipseModel()
    ell.estimate(a_points)
    
    xc, yc, a, b, theta = ell.params
    
    print("center = ",  (xc, yc))
    print("angle of rotation = ",  theta)
    print("axes = ", (a,b))
    
    fig = plt.figure() 
    ax = fig.add_subplot(1, 1, 1) 
    
    plt.imshow(original, cmap='gray')
    
    x0 = xc+height//3
    y0 = yc+width//3
    
    plt.scatter(x0, y0, color='green', s=10)
    ell_patch = Ellipse((x0, y0), 2*a, 2*b, theta*180/np.pi, edgecolor='green', facecolor='none')
    
    ax.add_patch(ell_patch)
    plt.show()