Разрыв кривой везде, где наблюдается значительное изменение кривой

Везде, где происходит значительное изменение кривой, мне нужно разбить кривую с толщиной разрыва всего в 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

Пока я не получаю того, что хочу. Он ломается в нескольких точках, а не только там, где я намеревался. Есть ли какая-нибудь библиотека или код для этого?

предоставление неизмененного изображения может помочь в вашем деле (без красных точек). Подумайте о том, чтобы пойти статистическим путем. Здесь может пригодиться некоторая неконтролируемая кластеризация с двумя классами (горизонтальными или вертикальными линиями).

Tino D 04.07.2024 08:51

Вы спрашиваете, как найти кривые?

Markus 04.07.2024 09:11

Нет. Я просто пытаюсь сломать кривую там, где есть существенное изменение угла. Я реализую идею @TinoD и, похоже, на правильном пути. Я открыт и для других идей

AHFromHogwarts 04.07.2024 09:19

нужен контекст. все это слишком абстрактно, чтобы понять истинную проблему. быть менее абстрактным, более конкретным. не удваивайте ставки на расплывчатых условиях. очевидно, что эти изображения представляют собой нарисованные от руки иллюстрации. Я не думаю, что это реальные данные. покажите нам реальные данные. опишите приложение. опишите цель, а не выбранный вами подход. этот контекст ценен.

Christoph Rackwitz 04.07.2024 09:19

Не существует приложения или библиотеки, способной читать ваши мысли. Слева от первой красной точки слева есть три резких изменения градиента. Ни один достойный алгоритм поиска значительных изменений на кривой не будет игнорировать их и просто найдет те, которые вам нужны.

Martin Brown 04.07.2024 11:20

opencv помечен, поэтому я скажу approxPolyDP

Christoph Rackwitz 04.07.2024 11:32

Ваши линии всегда практически выровнены по осям или, например, они могут быть диагональными?

Mark Setchell 04.07.2024 12:43

@MarkSetchell Это может быть любой формы. Диагональ тоже одна из возможностей, да.

AHFromHogwarts 04.07.2024 13:05

Прежде всего, вы не можете оценить кривизну (calculateAngle() вычисляет изменение угла, то есть кривизну) на таких коротких расстояниях, потому что вы измеряете шум и эффекты сетки. Используйте больший участок кривой для измерения кривизны. Во-вторых, np.column_stack(np.where(binary > 0)) не отображает пиксели в том порядке, в котором они расположены на линии, и поэтому не позволяет измерить кривизну вдоль линии. Вам нужно «пройти по линии», начать с первой точки, найти ее соседа и т. д. cv.findContours() может быть началом.

Cris Luengo 04.07.2024 15:45

идти по линии, чтобы оценить кривизну? да, это будет шумно, когда вы приближаетесь к шагам размером в пиксель. в другом вопросе я прототипировал кое-что из этого. комментарий содержит изображение: stackoverflow.com/questions/78653196/…

Christoph Rackwitz 04.07.2024 16:08

Попробуйте функцию convexityDefects. Возможно, в большинстве случаев этого будет достаточно.

Micka 04.07.2024 19:30
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
3
11
103
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий
  1. Преобразование Хафа https://en.wikipedia.org/wiki/Hough_transform

  1. K-означает итерации с разными K - найдите, сколько групп линий

  1. К-значит опять с правильным К

  1. Кластеризация пикселей по минимальному расстоянию до линий

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()

Он не идеален, есть еще некоторые проблемы, но суть вы поняли.

Другие вопросы по теме