我想在图像中对一组轮廓进行排序。订单应该像我们正在堆叠那些轮廓。成像我们正在堆叠纸张,然后从顶部或底部开始逐个检索。
在下图中,我描述了所需的顺序(如果顺序是自下而上或上下不重要):
我使用不同模式的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)
我怎么能得到这个特定的订单?在这种情况下,在其他问题中提出的用于排序上下+左右的简单排序方法是不够的,因为它可以从图像中推断出来。
答案 0 :(得分:2)
我想到了以下算法:
构建一个依赖树(或者更确切地说"直接被"树阻塞),然后删除叶子直到你到达根目录。如果在两个B占据的某些列中,轮廓B直接阻碍轮廓A,则它们之间没有其他轮廓。我们还需要添加一些启发式选项,以便在有超过一个候选者时首先选择哪个叶子。
更详细:
查找轮廓,枚举它们,并填充依赖关系树。
创建标签图片。没有轮廓的区域包含-1,具有轮廓的区域包含等于轮廓索引的值。
查找依赖项,一次处理标签图像的一列:
一个。如果轮廓B位于轮廓A的正上方(即它们之间没有轮廓),那么A取决于B。
通过从依赖关系树中删除叶子并将它们附加到结果列表来排序。重复直到依赖关系树为空:
一个。查找当前所有叶子。这些是候选人。
湾按深度排序候选项(轮廓占用的最小列索引)。
℃。从树中删除第一个候选项(具有最小深度的候选项),并将其附加到结果列表中。
结果列表现已排序。
下图说明了堆叠顺序。
示例脚本:
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。