繁体   English   中英

如何使用Opencv轮廓单向描述线点

[英]How to use Opencv contours to describe line points in a unidirectional way

我正在使用 opencvs findContour来找到描述由线(不是多边形)组成的图像的点,例如: cv::findContours(src, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); .

如果我理解正确, “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.阈值

在此处输入图片说明

  1. 变薄

在此处输入图片说明

  1. 有色组件

在此处输入图片说明

编辑2:

在与 OP 讨论后,我明白只有(分散的)点列表是不够的。 应该对点进行排序,以便可以对其进行跟踪。 为了实现该新逻辑,应在对图像应用细化后引入。

  1. 查找极值点(具有单个 8 连接邻居的点)
  2. 查找连接点(具有 3 路连接的点)
  3. 查找简单点(所有其他点)
  4. 从一个极值点开始跟踪,直到到达另一个极值点或连接器。
  5. 提取行进路径。
  6. 检查连接点是否已变为简单点并更新其状态。
  7. 重复
  8. 检查是否有任何从任何极值点都没有到达的简单点的闭环,将每个闭环提取为一个附加的航路点。

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

def filter_neighbors(ns):    
    i = 0
    while i < len(ns):
        j = i + 1
        while j < len(ns):
            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]
                break                                    
            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))

编辑3:

多亏了eldesgraciado ,通过以类似内核的方式检查邻居,在一次通过中对点进行分类的更有效实现!

注意:在调用此方法之前,应使用一个像素填充图像以避免边界检查或在边界处等效地遮蔽像素。

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

    for p in pnts:
        x = p[0]
        y = p[1]
        n = []
        if img[y - 1,x] > 0: n.append((y-1, x))
        if img[y - 1,x - 1] > 0: n.append((y-1, x - 1))
        if img[y - 1,x + 1] > 0: n.append((y-1, x + 1))
        if img[y,x - 1] > 0: n.append((y, x - 1))
        if img[y,x + 1] > 0: n.append((y, x + 1))
        if img[y + 1,x] > 0: n.append((y+1, x))
        if img[y + 1,x - 1] > 0: n.append((y+1, x - 1))
        if img[y + 1,x + 1] > 0: n.append((y+1, x + 1))
        filter_neighbors(n)
        if len(n) == 1:
            extremes.append(p)
        elif len(n) == 2:
            simple.append(p)
        elif len(n) > 2:
            connections.append(p)
    return extremes, connections, simple

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

在此处输入图片说明

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM