Detecting the center of a curved thick line in python using opencv

The images I am working on are like this one

The main goal is to calculate the crease (the blue line like shown in this image)

The idea is that I have to find the center curved line of this image, detect its two extreme points so I can draw the red line, then find the centroid point to draw the blue line I tried the skeleton algorithm:

import cv2
import numpy as np
from matplotlib import pyplot as plt
kernel=np.ones((7,7),np.uint8)
# Read the image as a grayscale image
img = cv2.imread('ressources/ss.jpg', 0)
#Threshold the image
ret,img = cv2.threshold(img,100, 255, 0)
imgGray=cv2.GaussianBlur(img,(7,7),0)
# Step 1: Create an empty skeleton
size = np.size(img)
skel = np.zeros(img.shape, np.uint8)

# Get a Cross Shaped Kernel
element = cv2.getStructuringElement(cv2.MORPH_CROSS, (3,3))

# Repeat steps 2-4
while True:
    #Step 2: Open the image
    open = cv2.morphologyEx(img, cv2.MORPH_OPEN, element)
    #Step 3: Substract open from the original image
    temp = cv2.subtract(img, open)
    #Step 4: Erode the original image and refine the skeleton
    eroded = cv2.erode(img, element)
    skel = cv2.bitwise_or(skel,temp)
    img = eroded.copy()
    # Step 5: If there are no white pixels left ie.. the image has been completely eroded, quit the loop
    if cv2.countNonZero(img)==0:
    break

and this is what I got:
skeleton

Now I don’t have access to that center line that I got and I need it so that I can find these three red points
skeleton explained

What can I do?

starting with your original image,
thinning would collapse it to a single line,
from where you can find the maximum y value:

# some preprocessing
ocv = cv2.blur(ocv,(9,9))
_,ocv = cv2.threshold(ocv, 220, 255, 0)

# collapse to line
ocv = cv2.ximgproc.thinning(ocv,0)

c, _ = cv2.findContours(ocv,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)

z = np.array(c[0])
z = z.reshape(z.shape[0],z.shape[2]) # remove 'middle' dim
m = np.argmax(z,0) # id of max along 1st dim (y)
id = m[1]
x = z[id,0]
y = z[id,1]
# vizualize
p3 = (int(x),int(y))
cv2.circle(ocv,p3,10,(180))

Here is the complete code

def line(p1, p2):
    A = (p1[1] - p2[1])
    B = (p2[0] - p1[0])
    C = (p1[0]*p2[1] - p2[0]*p1[1])
    return A, B, -C

def intersection(L1, L2):
    D  = L1[0] * L2[1] - L1[1] * L2[0]
    Dx = L1[2] * L2[1] - L1[1] * L2[2]
    Dy = L1[0] * L2[2] - L1[2] * L2[0]
    if D != 0:
        x = Dx / D
        y = Dy / D
        return int(x), int(y)

def detect(image):
    # some preprocessing
    thin = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
    thin = cv2.blur(thin, (10, 10))
    _, thin = cv2.threshold(thin, 220, 255, 0)

    # thin image to find clear contours
    thin = cv2.ximgproc.thinning(thin, thinningType=cv2.ximgproc.THINNING_GUOHALL)

    # dind contours
    cnts = cv2.findContours(thin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]
    c = max(cnts, key=cv2.contourArea)

    # obtain outer coordinates
    left = tuple(c[c[:, :, 0].argmin()][0])
    right = tuple(c[c[:, :, 0].argmax()][0])
    top = tuple(c[c[:, :, 1].argmin()][0])
    bottom = tuple(c[c[:, :, 1].argmax()][0])

    # draw connecting line
    cv2.line(image, left, right, (0,0,255), 2)

    # find the point of intersection
    # b/w connecting lines and curve points
    l1 = line(left, right)
    l2 = line((bottom[0], top[1]), bottom)
    inter = intersection(l1, l2)

    # draw center curve intersection line
    cv2.line(image, inter, bottom, (255, 255, 0), 2)

    # draw line contours
    cv2.drawContours(image, [c], -1, (36, 255, 12), 2)

    return image

image = cv2.imread("sample.jpg")
output = detect(image)

cv2_imshow(output)

Output:
output

1 Like