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?
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.