Везде, где происходит значительное изменение кривой, мне нужно разбить кривую с толщиной разрыва всего в 1 пиксель (я просто хочу разбить кривые на несколько частей). Я приложил изображение для справки. Итак, после того, как я прочитал изображение, я утончил кривую, и там, где есть красные точки, мне нужно разделить ее вокруг этой области.
Первое изображение — это входное изображение, а красные точки указывают, где я хочу разрезать (на изображении на самом деле не будет красной точки). Второе изображение — это текущий результат, который я получаю. Третье изображение представляет собой неизмененное изображение для справки.
Я попробовал реализовать следующие коды:
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()
и другая логика
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
Пока я не получаю того, что хочу. Он ломается в нескольких точках, а не только там, где я намеревался. Есть ли какая-нибудь библиотека или код для этого?
Вы спрашиваете, как найти кривые?
Нет. Я просто пытаюсь сломать кривую там, где есть существенное изменение угла. Я реализую идею @TinoD и, похоже, на правильном пути. Я открыт и для других идей
нужен контекст. все это слишком абстрактно, чтобы понять истинную проблему. быть менее абстрактным, более конкретным. не удваивайте ставки на расплывчатых условиях. очевидно, что эти изображения представляют собой нарисованные от руки иллюстрации. Я не думаю, что это реальные данные. покажите нам реальные данные. опишите приложение. опишите цель, а не выбранный вами подход. этот контекст ценен.
Не существует приложения или библиотеки, способной читать ваши мысли. Слева от первой красной точки слева есть три резких изменения градиента. Ни один достойный алгоритм поиска значительных изменений на кривой не будет игнорировать их и просто найдет те, которые вам нужны.
opencv помечен, поэтому я скажу approxPolyDP
Ваши линии всегда практически выровнены по осям или, например, они могут быть диагональными?
@MarkSetchell Это может быть любой формы. Диагональ тоже одна из возможностей, да.
Прежде всего, вы не можете оценить кривизну (calculateAngle()
вычисляет изменение угла, то есть кривизну) на таких коротких расстояниях, потому что вы измеряете шум и эффекты сетки. Используйте больший участок кривой для измерения кривизны. Во-вторых, np.column_stack(np.where(binary > 0))
не отображает пиксели в том порядке, в котором они расположены на линии, и поэтому не позволяет измерить кривизну вдоль линии. Вам нужно «пройти по линии», начать с первой точки, найти ее соседа и т. д. cv.findContours()
может быть началом.
идти по линии, чтобы оценить кривизну? да, это будет шумно, когда вы приближаетесь к шагам размером в пиксель. в другом вопросе я прототипировал кое-что из этого. комментарий содержит изображение: stackoverflow.com/questions/78653196/…
Попробуйте функцию convexityDefects. Возможно, в большинстве случаев этого будет достаточно.
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()
Он не идеален, есть еще некоторые проблемы, но суть вы поняли.
предоставление неизмененного изображения может помочь в вашем деле (без красных точек). Подумайте о том, чтобы пойти статистическим путем. Здесь может пригодиться некоторая неконтролируемая кластеризация с двумя классами (горизонтальными или вертикальными линиями).