Opencv轮廓线层次

问题描述 投票:3回答:1

我正在使用opencvs findContour查找点来描述由线(而非多边形)组成的图像,如下所示:cv::findContours(src, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);

但是,由于findContour给出了图像的边界,因此返回的轮廓实际上描述了图像“两次”。它包含从行的开始到行的末尾再到行的点。

尽管我RETR_EXTERNAL会解决这个问题,但是也许我并不完全了解轮廓层次。

我只想指出从直线的一端到另一端的点,而不是同时指向和返回的点

enter image description here

enter image description here

enter image description here

c++ opencv hierarchy contour
1个回答
2
投票

如果我理解正确,则"cv2.connectedComponents"方法会提供您所需要的。它为图像中的每个点分配一个标签,如果连接了点,则标签是相同的。通过执行此分配,不会发生重复。因此,如果您的线宽为一个像素(例如,边缘检测器或细化运算符的输出),则每个位置都会得到一个点。

编辑:

根据OP的要求,线条应为1像素宽。为了实现这一点,在找到连接的组件之前先进行细化操作。步骤图像也已添加。

请注意,每个连接的组成点均按y线的升序排列。

img_path = "D:/_temp/fig.png"
output_dir = 'D:/_temp/'

img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)

_, img = cv2.threshold(img, 128, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY_INV)

total_white_pixels = cv2.countNonZero(img)
print ("Total White Pixels Before Thinning = ", total_white_pixels)

cv2.imwrite(output_dir + '1-thresholded.png', img)

#apply thinning -> each line is one-pixel wide
img = cv2.ximgproc.thinning(img)
cv2.imwrite(output_dir + '2-thinned.png', img)

total_white_pixels = cv2.countNonZero(img)
print ("Total White Pixels After Thinning = ", total_white_pixels)

no_ccs, labels = cv2.connectedComponents(img)

label_pnts_dic = {}

colored = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)

i = 1 # skip label 0 as it corresponds to the backgground points
sum_of_cc_points = 0 
while i < no_ccs:
    label_pnts_dic[i] = np.where(labels == i) #where return tuple(list of x cords, list of y cords)
    colored[label_pnts_dic[i]] = (random.randint(100, 255), random.randint(100, 255), random.randint(100, 255))
    i +=1

cv2.imwrite(output_dir + '3-colored.png', colored)    


print ("First ten points of label-1 cc: ")
for i in range(10):
    print ("x: ", label_pnts_dic[1][1][i], "y: ", label_pnts_dic[1][0][i])

输出:

Total White Pixels Before Thinning =  6814
Total White Pixels After Thinning =  2065
First ten points of label-1 cc: 
x:  312 y:  104
x:  313 y:  104
x:  314 y:  104
x:  315 y:  104
x:  316 y:  104
x:  317 y:  104
x:  318 y:  104
x:  319 y:  104
x:  320 y:  104
x:  321 y:  104

图像:

1。阈值

enter image description here

  1. 变薄

enter image description here

  1. 彩色组件

enter image description here

Edit2

与OP讨论后,我了解仅列出(分散的)点是不够的。点应排序以便可以追踪。为了实现这一点,应在对图像进行细化之后引入新的逻辑。

  1. 查找极端点(具有单个8连接邻居的点)
  2. 查找连接器点(具有三向连接的点)
  3. 查找简单点(所有其他点)
  4. 从一个端点开始跟踪,直到到达另一个端点或连接器为止。
  5. 提取行进的路径。
  6. 检查连接器点是否已变成极限点
  7. 重复

极限/连接器/简单点分类的代码

def filter_neighbors(ns):    
    i = 0
    while i < len(ns):
        j = 0
        while j < len(ns):
            if i != j:
                if (ns[i][0] == ns[j][0] and abs(ns[i][1] - ns[j][1]) <= 1) or (ns[i][1] == ns[j][1] and abs(ns[i][0] - ns[j][0]) <= 1):
                    del ns[j]
                    j -= 1                    
            j += 1
        i += 1    

def sort_points_types(pnts):
    extremes = []
    connections = []
    simple = []

    for i in range(pnts.shape[0]):
        neighbors = []
        for j in range (pnts.shape[0]):
            if i == j: continue
            if abs(pnts[i, 0] - pnts[j, 0]) <= 1 and abs(pnts[i, 1] - pnts[j, 1]) <= 1:#8-connectivity check
                neighbors.append(pnts[j])
        filter_neighbors(neighbors)
        if len(neighbors) == 1:
            extremes.append(pnts[i])
        elif len(neighbors) == 2:
            simple.append(pnts[i])
        elif len(neighbors) > 2:
            connections.append(pnts[i])
    return extremes, connections, simple


img_path = "D:/_temp/fig.png"
output_dir = 'D:/_temp/'

img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)

_, img = cv2.threshold(img, 128, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY_INV)
img = cv2.ximgproc.thinning(img)

pnts = cv2.findNonZero(img)
pnts = np.squeeze(pnts)


ext, conn, simple = sort_points_types(pnts)

for p in conn:
    cv2.circle(img, (p[0], p[1]), 5, 128)

for p in ext:
    cv2.circle(img, (p[0], p[1]), 5, 128)

cv2.imwrite(output_dir + "6-both.png", img)

print (len(ext), len(conn), len(simple))

可视化端点和连接器点的图像:

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.