使用opencv对类似物理顺序的轮廓进行排序

时间:2017-10-24 09:45:28

标签: python opencv image-processing

我想在图像中对一组轮廓进行排序。订单应该像我们正在堆叠那些轮廓。成像我们正在堆叠纸张,然后从顶部或底部开始逐个检索。

在下图中,我描述了所需的顺序(如果顺序是自下而上或上下不重要):

Image with contours

我使用不同模式的cv2.findCountours函数检索了这些轮廓,但是没有它们实现了这个顺序。这是我用来标记轮廓的代码:

img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)
ret, contours, hierarchy = cv2.findContours(img, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
dst_img =img.copy()
for idx, cnt in enumerate(contours):
    x, y = tuple(cnt[cnt[:, :, 0].argmin()][0])
    cv2.putText(dst_img, str(idx), (x + 10, y), cv2.FONT_HERSHEY_SIMPLEX, 0.6, 150, 2)

我怎么能得到这个特定的订单?在这种情况下,在其他问题中提出的用于排序上下+左右的简单排序方法是不够的,因为它可以从图像中推断出来。

1 个答案:

答案 0 :(得分:2)

我想到了以下算法:

构建一个依赖树(或者更确切地说"直接被"树阻塞),然后删除叶子直到你到达根目录。如果在两个B占据的某些列中,轮廓B直接阻碍轮廓A,则它们之间没有其他轮廓。我们还需要添加一些启发式选项,以便在有超过一个候选者时首先选择哪个叶子。

更详细:

  1. 查找轮廓,枚举它们,并填充依赖关系树。

  2. 创建标签图片。没有轮廓的区域包含-1,具有轮廓的区域包含等于轮廓索引的值。

  3. 查找依赖项,一次处理标签图像的一列:

    一个。如果轮廓B位于轮廓A的正上方(即它们之间没有轮廓),那么A取决于B。

  4. 通过从依赖关系树中删除叶子并将它们附加到结果列表来排序。重复直到依赖关系树为空:

    一个。查找当前所有叶子。这些是候选人。

    湾按深度排序候选项(轮廓占用的最小列索引)。

    ℃。从树中删除第一个候选项(具有最小深度的候选项),并将其附加到结果列表中。

  5. 结果列表现已排序。

  6. 下图说明了堆叠顺序。

    Illustration of the sorting order

    示例脚本:

    import cv2
    import numpy as np
    
    # ============================================================================
    
    class ContourInfo(object):
        def __init__(self, n, points):
            self.index = n
            self.color = np.random.rand(3) * 255
            self.points = points
            self.dependencies = set()
    
        def add_dependency(self, n):
            self.dependencies.add(n)
    
        def remove_dependency(self, n):
            self.dependencies.discard(n)
    
        @property
        def is_leaf(self):
            return not bool(self.dependencies)
    
        @property
        def depth(self):
            return self.points[:,:,1].min()
    
        def __repr__(self):
            return "{n=%d, dependencies=%s, leaf=%s, depth=%d}" % (self.index
                , self.dependencies
                , self.is_leaf
                , self.depth)
    
    # ============================================================================
    
    img = cv2.imread('papers.png', cv2.IMREAD_GRAYSCALE)
    _, contours, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    
    # ----------------------------------------------------------------------------
    # Create a label image and populate dependency tree
    
    NO_CONTOUR = -1
    
    labels = np.full_like(img, NO_CONTOUR, dtype=np.int32)
    dependency_tree = {}
    
    for n,contour in enumerate(contours):
        cv2.drawContours(labels, [contour], -1, n, -1)
        dependency_tree[n] = ContourInfo(n, contour)
    
    # ----------------------------------------------------------------------------
    # Find dependencies, processing each column from the bottom up
    
    rows, cols = img.shape[:2]
    for c in range(cols):
        last_contour = NO_CONTOUR
        for r in range(rows - 1, -1, -1):
            current_contour = labels[r,c]
            if current_contour != NO_CONTOUR:
                if (last_contour != current_contour) and (last_contour != NO_CONTOUR):
                    dependency_tree[last_contour].add_dependency(current_contour)
                last_contour = current_contour
    
    # ----------------------------------------------------------------------------
    # Sort by removing one leaf at a time
    
    sorted_contours = []
    
    while bool(dependency_tree):
        candidates = []
        for node in dependency_tree.values():
            if node.is_leaf:
                candidates.append(node.index)
    
        if not bool(candidates):
            raise RuntimeError("Cycle found, cannot sort.")
    
        candidates = sorted(candidates, key=lambda n: dependency_tree[n].depth)
    
        sorted_contours.append(dependency_tree.pop(candidates[0]))
        for node in dependency_tree.values():
            node.remove_dependency(candidates[0])
    
    # ============================================================================
    # Done, create an output to illustrate the sort order
    
    result_images = []
    for n in range(len(sorted_contours)):
        tmp = np.zeros((rows, cols, 3), dtype=np.uint8)
        for c in sorted_contours[:n+1]:
            cv2.drawContours(tmp, [c.points], -1, c.color, -1)
        result_images.append(tmp)
    
    combined_result = np.hstack(result_images)
    cv2.imwrite("papers_out.png", combined_result)
    

    为方便起见,clean unlabeled input image