使用opencv检测图像中折线的顶点

时间:2018-08-25 12:02:30

标签: python opencv

我有一些灰度图像,如下所示:

https://xqueryfiddle.liberty-development.net/pPgCcon/1

它们是具有不同标记样式的灰度线形图(可以是正方形,圆形,星形,点形。列表会继续显示)。

我想获取标记的坐标。我计划将数据线读取为折线,并使用角点检测算法检测顶点,但是从我一直以来的角度来看,角点检测更适合3D空间。

我的问题是:是否可以将折线作为折线读取并提取顶点的坐标?有没有办法使用霍夫变换通过opencv检测折线?

预先感谢

编辑

原始图像如下:

grayscale chart

1 个答案:

答案 0 :(得分:2)

怎么样?

提取行

import numpy as np
import cv2

img = cv2.imread('origin.png')

## 1. choose HSV
# rough hsv values in this image.
# grid(gray)=(0, 0, 174)
# border(black)=(0, 0, 15)
# back ground(white)=(0, 0, 254)

# hsv value including border and back ground
hsv_min = (0, 0, 0) # Lower end of the HSV range
hsv_max = (10, 10, 255) # Upper end of the HSV range

# Transform image to HSV color space
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# Threshold based on HSV values
color_thresh = cv2.inRange(hsv, hsv_min, hsv_max)

# Invert the image
invert = cv2.bitwise_not(color_thresh) 

## If you need perform skeletonization, use skimage.
#import skimage
#from skimage.morphology import skeletonize
#color_thresh = skeletonize(skimage.img_as_float(color_thresh))
#color_thresh = color_thresh.astype('uint8') * 255

cv2.imwrite("lines.png", invert)

lines.png
enter image description here

## 2. cv2.HoughLinesP
# Actually, parameter tuning will be necessary 

minLineLength = 100
maxLineGap = 100
lines = cv2.HoughLinesP(color_thresh, 2, np.pi/180,70,minLineLength,maxLineGap)
# lines = [[[319 321 431 188]], ... ,[[ 83 283 195 399]]]

lines = [x.flatten() for x in lines]
# lines = [[319, 321, 431, 188], ... ,[ 83, 283, 195, 399]]

# sort by first element
lines = sorted(lines, key=lambda x : x[0])

# drow lines
for line in lines:
    x1,y1,x2,y2 = line
    cv2.line(img,(x1,y1),(x2,y2),(0,255,0),2)


## 3. Find the intersection of two lines      
def cross_point(segment_p1p2, segment_p3p4):
    '''
    Intersection point of two lines
    p1p2:{p1(a,b)、p2(c,d)}
    p3p4:{p3(e,f)、p4(g,h)}
    '''
    a = segment_p1p2[0]
    b = segment_p1p2[1]
    c = segment_p1p2[2]
    d = segment_p1p2[3]
    e = segment_p3p4[0]
    f = segment_p3p4[1]
    g = segment_p3p4[2]
    h = segment_p3p4[3]

    dev = (d-b)*(g-e)-(c-a)*(h-f)
    d1 = f*g-e*h
    d2 = b*c-a*d

    xp = (d1*(c-a)-d2*(g-e))/dev
    yp = (d1*(d-b)-d2*(h-f))/dev       
    return (xp, yp)

# draw circles
for i in range(len(lines)-1):
    x, y = cross_point(lines[i], lines[i+1])
    cv2.circle(img,(int(x),int(y)), 5, (255,0,0), 2)

cv2.imwrite("out.png",img)

out.png
enter image description here

[excursus]
HoughLinesP可能会返回多条重叠的线。
在这种情况下,最好进行以下处理。

# [81, 281, 201, 406] and [81, 279, 198, 400] are overlapping
# [435, 184, 557, 214] and [451, 187, 558, 213] are overlapping
lines = [[81, 281, 201, 406],
         [81, 279, 198, 400], 
         [193, 405, 313, 327], 
         [312, 329, 437, 180], 
         [435, 184, 557, 214], 
         [451, 187, 558, 213]]

import math

def is_close(data1, data2, threshold=10):
    '''
    e.g.  data1=[x, y] , data1=[x, y, z]
    `threshold` is euclidean distance.
    calculate the distance between two points, and determine if they are in the neighborhood.
    '''
    return math.sqrt(sum((d1-d2)**2 for d1, d2 in zip(data1, data2))) < threshold

# sort by first element
lines = sorted(lines, key=lambda x : x[0])

chained_lines=[lines[0]]
index=0
for i in range(len(lines)):
    if i < index:
        continue
    end_of_line = lines[i][-2:]
    # generator which return the index of the chained elements
    y = (i for i, v in enumerate(lines) if is_close(end_of_line, v[:2]))
    # get the index of the first element
    chained_index = next(y, None) 
    if chained_index != None and chained_index > index:
        index = chained_index
        chained_lines.append(lines[index])

print(chained_lines)

chained_lines
[[81,281,201,406],[193,405,313,327],[312,329,437,180],[435,184,557,214]]

提取边界

import numpy as np
import cv2

img = cv2.imread('origin.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

ret, bin_img = cv2.threshold(gray, 1, 255, cv2.THRESH_BINARY_INV)

minLineLength = 100
maxLineGap = 100
lines = cv2.HoughLinesP(bin_img, 2, np.pi/180,70,minLineLength,maxLineGap)

lines = [x.flatten() for x in lines]

# drow lines
for line in lines:
    x1,y1,x2,y2 = line
    cv2.line(img,(x1,y1),(x2,y2),(0,255,0),2)

cv2.imwrite("out2.png",img)

out2.png
enter image description here