Question: Wherever there's a significant change in the curve, I need to break the curve with a break thickness of just 1 pixel (I just want to break the curves into multiple parts). I have attached an image for reference. So after i read the image, i am thinning the curve and wherever there are red dots, i need to split it around that area.    The first image is the input image and red dots indicate where I want the cut (the image will not actually have the red dot)/ The second image is the current output that I am getting. The third image is the unaltered image for reference. I have tried implementing the following codes: import cv2 import numpy as np from matplotlib import pyplot as plt image_path = rf'C:\Users\User\Desktop\output.png' img = cv2.imread(image_path, 0) ret, binary = cv2.threshold(img, 128, 255, cv2.THRESH_BINARY) binary = cv2.ximgproc.thinning(binary, thinningType=cv2.ximgproc.THINNING_GUOHALL) coords = np.column_stack(np.where(binary > 0)) def calculateAngle(p1, p2, p3): v1 = np.array(p2) - np.array(p1) v2 = np.array(p3) - np.array(p2) angle = np.arctan2(v2[1], v2[0]) - np.arctan2(v1[1], v1[0]) angle = np.degrees(angle) if angle < 0: angle += 360 return angle startBlackImg = np.zeros((binary.shape[0], binary.shape[1], 1), np.uint8) i = 1 while i < (len(coords) - 1): p1 = coords[i - 1] p2 = coords[i] p3 = coords[i + 1] i += 1 angle = calculateAngle(p1, p2, p3) if angle < 45 or angle > 315: startBlackImg[p2[0], p2[1]] = 255 else: startBlackImg[p2[0], p2[1]] = 0 cv2.namedWindow('Check', 0) cv2.imshow('Check', startBlackImg) cv2.waitKey(0) cv2.destroyAllWindows()  and the other logic is while kk < len(cutContour) - 10: xCdte = cutContour[kk][0][0] yCdte = cutContour[kk][0][1] xNextCdte = cutContour[kk + 10][0][0] yNextCdte = cutContour[kk + 10][0][1] kk += 1 if totalDistance <= 0.3048: startBlackImg[yCdte, xCdte] = np.array([255, 255, 255]) startBlackImg[yNextCdte, xNextCdte] = np.array([255, 255, 255]) else: if (abs(xCdte - xNextCdte) < 10 and abs(yCdte - yNextCdte) >= 10) or (abs(xCdte - xNextCdte) >= 10 and abs(yCdte - yNextCdte) < 10): startBlackImg[yCdte, xCdte] = np.array([255, 255, 255]) startBlackImg[yNextCdte, xNextCdte] = np.array([255, 255, 255]) else: startBlackImg[yCdte, xCdte] = np.array([0, 0, 0]) kk += 10  So far I am not getting what I want. Its breaking at multiple points and not just where I intend it to. Is there any library or and code to do this?

Answer:  Hough Transform https://en.wikipedia.org/wiki/Hough_transform    K-means iterations with different K's - find how many line groups    K-means again with the right K     Pixel clustering by min distance to lines   import math import cv2 as cv import numpy as np import matplotlib.pyplot as plt orig_im = cv.imread("/home/ophir/temp/stackoverflow2.png",cv.IMREAD_GRAYSCALE) # use Hough Transform to get lots of straight lines lines = cv.HoughLines(orig_im, 1, np.pi/180, 30); im = cv.cvtColor(orig_im, cv.COLOR_GRAY2BGR) im2 = im.copy() im3 = im.copy() plt.figure() plt.imshow(im) # draw all straight lines on image rho_vals = [] theta_vals = [] for line in lines: for rho,theta in line: rho_vals.append(rho) theta_vals.append(theta) a = np.cos(theta) b = np.sin(theta) x0 = a*rho y0 = b*rho x1 = int(x0 + 1000*(-b)) y1 = int(y0 + 1000*(a)) x2 = int(x0 - 1000*(-b)) y2 = int(y0 - 1000*(a)) cv.line(im2,(x1,y1),(x2,y2),(0,0,255),2) rho_vals = np.array(rho_vals) rho_vals = np.expand_dims(rho_vals, axis=0) theta_vals = np.array(theta_vals) theta_vals = np.expand_dims(theta_vals, axis=0) Z = np.vstack((rho_vals,theta_vals)).T Z = np.float32(Z) # use K-means to cluster all straight lines to groups # I don't know how many groups, so I check several K's criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 10, 1.0) compactness = [] for i in range(2, 9): ret,label,center=cv.kmeans(Z,i + 1,None,criteria,10,cv.KMEANS_RANDOM_CENTERS) compactness.append(ret) compactness = np.array(compactness) # choose the right k, where the compactness isn't getting any better derivative = compactness[1:] - compactness[:-1] amax = np.argmax(derivative > -800) + 2 # do K-means again, this time with the right K ret,label,center=cv.kmeans(Z, amax,None,criteria,10,cv.KMEANS_RANDOM_CENTERS) # draw the centers of thr clusters on the lines parameters graph lines = [] for rho, theta in center: a = np.cos(theta) b = np.sin(theta) x0 = a*rho y0 = b*rho x1 = int(x0 + 1000*(-b)) y1 = int(y0 + 1000*(a)) x2 = int(x0 - 1000*(-b)) y2 = int(y0 - 1000*(a)) lines.append((x1,y1,x2,y2)) cv.line(im3,(x1,y1),(x2,y2),(0,0,255),2) # cluster the pixels in the original image by the minimum distance to a line pixels = cv.findNonZero(orig_im) labels = [] for pixel in pixels: x0, y0 = pixel[0] distances = [] for line in lines: x1, y1, x2, y2 = line # https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line dist = abs((y2 - y1) * x0 - (x2 - x1) * y0 + x2 * y1 - y2 * x1) / math.sqrt((y2 - y1)**2 + (x2 - x1)**2) distances.append(dist) labels.append(np.argmin(np.array(distances))) im4 = np.zeros(im2.shape) colors = [[0, 0, 255], [0, 255, 0], [255, 0, 0], [255, 255, 0], [255, 0, 255], [0, 255, 255]] # assign different color to each pixel by the label of the clustering for pixel, label in zip(pixels, labels): x, y = pixel[0] color = colors[label] im4[y,x, 0] = color[0] im4[y,x, 1] = color[1] im4[y,x, 2] = color[2] plt.figure() plt.plot(list(range(2,9)), compactness) plt.scatter(amax, compactness[amax - 2], c = 'r') plt.ylabel("compactness") plt.xlabel("K") plt.title("K-means compactness") plt.figure() plt.imshow(im2) plt.figure() plt.imshow(im3) plt.figure() plt.imshow(im4) plt.figure() plt.scatter(rho_vals, theta_vals) plt.scatter(center[:,0],center[:,1],s = 80,c = 'y', marker = 's') plt.xlabel("rho") plt.ylabel("theta") plt.title("lines parameters") plt.show()  It's not perfect, there are still some issues, but you get the idea.