连接组件计数

时间:2011-11-25 03:58:44

标签: c++ python c image-processing graph-algorithm

standard algorithm for connected component counting中,使用名为union-find的不相交集数据结构。

为什么使用这种数据结构?我编写的代码只是线性搜索图像,保留两个线性缓冲区,通过检查四个邻居(E,SE,S,SW)来存储每个连接像素的当前和下一个组件计数,如果是连接,更新连接映射以将较高组件与较低组件连接。 完成后,搜索所有未连接的组件并报告计数。

我无法理解为什么这种方法的效率低于使用union-find。

这是我的代码。输入文件已缩减为01 s。程序输出由0 s。

组成的连接组件的数量
def CompCount(fname):
    fin = open(fname)
    b,l = fin.readline().split()
    b,l = int(b),int(l)+1
    inbuf = '1'*l + fin.read()
    prev = curr = [sys.maxint]*l
    nextComp = 0
    tree = dict()
    for i in xrange(1, b+1):
        curr = [sys.maxint]*l
        for j in xrange(0, l-1):
            curr[j] = sys.maxint
            if inbuf[i*l+j] == '0':
                p = [prev[j+n] for m,n in [(-l+1,1),(-l,0),(-l-1,-1)] if inbuf[i*l + j+m] == '0']
                curr[j] = min([curr[j]] + p + [curr[j-1]])
                if curr[j] == sys.maxint:
                    nextComp += 1
                    curr[j] = nextComp
                    tree[curr[j]] = 0                   
                else:
                    if curr[j] < prev[j+1]: tree[prev[j+1]] = curr[j]
                    if curr[j] < prev[j]:   tree[prev[j]]   = curr[j]
                    if curr[j] < prev[j-1]: tree[prev[j-1]] = curr[j]
                    if curr[j] < curr[j-1]: tree[curr[j-1]] = curr[j]
        prev = curr
    return len([x for x in tree if tree[x]==0])

3 个答案:

答案 0 :(得分:0)

我没有完全理解你的问题,你在写清楚并构建问题时,自己真正获益。

据我所知,您希望使用8邻域在0-1图像中进行连通分量标记。如果这是你的假设,结果邻域图是平面的是错误的。你在&#34;对角线&#34;有过境点。在这样的图像中构造K_ {3,3}或K_ {5}应该是容易的。

答案 1 :(得分:0)

您的算法存在缺陷。考虑这个例子:

11110
01010
10010
11101

你的算法说2个组件,而它只有1个。

为了测试,我使用了这个稍微修改过的代码版本。

import sys

def CompCount(image):
    l = len(image[0])
    b = len(image)
    prev = curr = [sys.maxint]*(l+1)
    nextComp = 0
    tree = dict()
    for i in xrange(b):
        curr = [sys.maxint]*(l+1)
        for j in xrange(l):
            curr[j] = sys.maxint
            if image[i][j] == '0':
                p = [prev[j+n] for m,n in [(1,1),(-1,0),(-1,-1)] if 0<=i+m<b and 0<=j+n<l and image[i+m][j+n] == '0']
                curr[j] = min([curr[j]] + p + [curr[j-1]])
                if curr[j] == sys.maxint:
                    nextComp += 1
                    curr[j] = nextComp
                    tree[curr[j]] = 0                   
                else:
                    if curr[j] < prev[j+1]: tree[prev[j+1]] = curr[j]
                    if curr[j] < prev[j]:   tree[prev[j]]   = curr[j]
                    if curr[j] < prev[j-1]: tree[prev[j-1]] = curr[j]
                    if curr[j] < curr[j-1]: tree[curr[j-1]] = curr[j]
        prev = curr
    return len([x for x in tree if tree[x]==0])

print CompCount(['11110', '01010', '10010', '11101'])

让我尝试用文字解释你的算法(用图形而不是网格)。

  • 将'roots'设为空集。
  • 迭代图中的节点。
    • 对于节点n,查看已处理的所有邻居。称之为A。
    • 如果A为空,则选择一个新值k,将v [node]设置为k,并将k添加到根。
    • 否则,令k为A中节点的v [节点]的最小值。对于A中的每个x,用v [x]!= k从根中删除v [x]。
  • 组件数是根元素的数量。

(您的tree与我的roots相同:请注意,您从不使用tree[]元素的值,只是它们是否为0 ...这只是实现一套)

它就像union-find,除了它假设当你合并两个组件时,具有更高v []值的组件之前从未与另一个组件合并。在反例中,这是被利用的,因为中间列中的两个0已经与它们左边的0合并。

答案 2 :(得分:0)

我的变体:

  1. 将整个图表拆分为边缘。将每个边添加到一个集合中。
  2. 在下一次迭代中,在步骤2中创建的边缘的2个外部节点之间绘制边缘。这意味着将新节点(及其对应的集合)添加到原始边缘所在的集合中。 (基本上设置合并)
  3. 重复2,直到您要查找的2个节点位于同一组中。您还需要在步骤1之后进行检查(以防两个节点相邻)。
  4. 首先,您的节点将分别位于其集合中,

     o-o     o-o    o1-o3  o2   o3-o4
       \     /                    |
       o-o-o-o             o2  o1-o3-o4 
    

    随着算法的进展和合并,它的输入相对减半。

    在示例中,我正在检查某个图表中的组件。在将所有边缘合并到它们的最大可能设置之后,我留下3组,给出3个断开连接的组件。 (组件数是算法完成时能够获得的组数。)

    可能的图表(对于上面的树):

    o-o-o  o4  o2
      |    |
      o    o3
           |
           o1