Как сегментировать изогнутый стержень для расчета угла?

Я пытаюсь использовать OpenCV для сегментирования изогнутого стержня из его фона, затем найти изгибы в нем и рассчитать угол между каждым изгибом.

Первая часть к счастью тривиальна с достаточным контрастом между передним и задним фонами. Немного эрозии/дилатации заботится о отражениях/бликах при сегментировании.

Вторая часть - это то, где я не уверен, как подойти к ней.

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

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

Вот мое входное изображение (bend.png)

bend.png

И вот что я пробовал до сих пор:

#!/usr/bin/env python
import numpy as np
import cv2

threshold = 229
# erosion/dilation kernel
kernel = np.ones((5,5),np.uint8)
# contour simplification
epsilon = 0

# slider callbacks
def onThreshold(x):
    global threshold
    print "threshold = ",x
    threshold = x
def onEpsilon(x):
    global epsilon
    epsilon = x * 0.01
    print "epsilon = ",epsilon

# make a window to add sliders/preview to
cv2.namedWindow('processed')
#make some sliders
cv2.createTrackbar('threshold','processed',60,255,onThreshold)
cv2.createTrackbar('epsilon','processed',1,1000,onEpsilon)
# load image
img = cv2.imread('bend.png',0)
# continuously process for quick feedback
while 1:
    # exit on ESC key
    k = cv2.waitKey(1) & 0xFF
    if k == 27:
        break

    # Threshold
    ret,processed = cv2.threshold(img,threshold,255,0)
    # Invert
    processed = (255-processed)
    # Dilate
    processed = cv2.dilate(processed,kernel)
    processed = cv2.erode(processed,kernel)
    # Canny
    processed = cv2.Canny(processed,100,200)

    contours, hierarchy = cv2.findContours(processed,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
    if len(contours) > 0:
        approx = cv2.approxPolyDP(contours[0],epsilon,True)
        # print len(approx)
        cv2.drawContours(processed, [approx], -1, (255,255,255), 3)
        demo = img.copy()
        cv2.drawContours(demo, [approx], -1, (192,0,0), 3)
    # show result
    cv2.imshow('processed ',processed)
    cv2.imshow('demo ',demo)


# exit
cv2.destroyAllWindows()

Вот что я получил до сих пор, но я не уверен, что это лучший подход:

поиск контуров

упрощенный контур

Я попытался понять это визуально, и на что я нацелился - это что-то в этом роде:

прямые и изгибы сегментированы

Поскольку конечной целью является вычисление угла между изогнутыми частями, что-то вроде этого кажется более простым:

установка линии

Мое предположение о том, что подходящие линии и вычислять углы между парами пересекающихся линий могут работать:

углы от пересечений линий

Я быстро проверил, используя HoughLines OpenCV Python tutorial, но независимо от переданных параметров я не получил отличных результатов:

#!/usr/bin/env python
import numpy as np
import cv2

threshold = 229
minLineLength = 30
maxLineGap = 10
houghThresh = 15

# erosion/dilation kernel
kernel = np.ones((5,5),np.uint8)

# slider callbacks
def onMinLineLength(x):
    global minLineLength
    minLineLength = x
    print "minLineLength = ",x

def onMaxLineGap(x):
    global maxLineGap
    maxLineGap = x
    print "maxLineGap = ",x

def onHoughThresh(x):
    global houghThresh
    houghThresh = x
    print "houghThresh = ",x

# make a window to add sliders/preview to
cv2.namedWindow('processed')
#make some sliders
cv2.createTrackbar('minLineLength','processed',1,50,onMinLineLength)
cv2.createTrackbar('maxLineGap','processed',5,30,onMaxLineGap)
cv2.createTrackbar('houghThresh','processed',15,50,onHoughThresh)
# load image
img = cv2.imread('bend.png',0)
# continuously process for quick feedback
while 1:
    # exit on ESC key
    k = cv2.waitKey(1) & 0xFF
    if k == 27:
        break

    # Threshold
    ret,processed = cv2.threshold(img,threshold,255,0)
    # Invert
    processed = (255-processed)
    # Dilate
    processed = cv2.dilate(processed,kernel)
    processed = cv2.erode(processed,kernel)
    # Canny
    processed = cv2.Canny(processed,100,200)

    lineBottom = np.zeros(img.shape,np.uint8)

    contours, hierarchy = cv2.findContours(processed,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
    if len(contours) > 0:
        cv2.drawContours(lineBottom, contours, 0, (255,255,255), 1)

    # HoughLinesP
    houghResult = img.copy()
    lines = cv2.HoughLinesP(lineBottom,1,np.pi/180,houghThresh,minLineLength,maxLineGap)
    try:
        for x in range(0, len(lines)):
            for x1,y1,x2,y2 in lines[x]:
                cv2.line(houghResult,(x1,y1),(x2,y2),(0,255,0),2)
    except Exception as e:
        print e

    # show result
    cv2.imshow('lineBottom',lineBottom)
    cv2.imshow('houghResult ',houghResult)


# exit
cv2.destroyAllWindows()

Результат HoughLinesP

Это приемлемый подход? Если да, то какой правильный способ сделать установку линии в OpenCV Python?

В противном случае это лучший способ решить эту проблему?

Обновление. Следуя рекомендациям Miki, я пробовал OpenCV 3 LSD и получил более приятные результаты, чем с HoughLinesP, но похоже, что требуется еще какая-то настройка, хотя она не выглядит иначе, чем cv2.createLineSegmentDetector Есть не так много вариантов для игры:

Результат LSD

Ответ 1

Для нахождения отрезков линии может быть удобно использовать кривизну. Здесь пример контура расщепления по минимальным точкам кривизны, может быть лучше использовать максимальные точки кривизны в вашем случае. B Вы можете разделить свою кривую на части, затем каждая часть приближается к сегменту линии с использованием метода RANSAC.

Ответ 2

Как только у вас есть контур, вы можете проанализировать его, используя метод, подобный предложенному в этой статье: https://link.springer.com/article/10.1007/s10032-011-0175-3

В принципе, контур отслеживается, вычисляя кривизну в каждой точке. Затем вы можете использовать порог кривизны для сегментации контура в прямые и изогнутые секции.