使用cv2.connectedComponents函数并消除像素数量较少的元素。

3
我想使用cv2.connectedComponents函数在二进制图像上连接组件,就像以下示例一样...

enter image description here

我已经在cv2.connectedComponents中添加了一个功能,用于消除像素数量较少的元素。
不幸的是,由于扩展,对于大图像,该算法非常缓慢。是否有一种重写扩展以加速算法的方法?
import cv2
import numpy as np

def zerolistmaker(n):
    listofzeros = [0] * n
    return listofzeros


img = cv2.imread('files/motorway/gabor/eGaIy.jpg', 0)

img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)[1]  # ensure binary
retval, labels = cv2.connectedComponents(img)

##################################################
# ENLARGEMENT
##################################################
sorted_labels = labels.ravel()
sorted_labels = np.sort(sorted_labels)


maxPixel = 50  # eliminate elements with less than maxPixel

# detect how often an element occurs
i=0
counter=0
counterlist = zerolistmaker(retval)

while i < len(sorted_labels):
    if sorted_labels[i] == counter:
        counterlist[counter] = counterlist[counter] + 1
    else:
        counter = counter + 1
        i = i - 1

    i = i + 1


# delete small pixel values
i=0
while i < len(counterlist):
    if counterlist[i] < maxPixel:
        counterlist[i] = 0
    i = i + 1

i=0
counterlisthelper = []
while i < len(counterlist):
    if counterlist[i] == 0:
        counterlisthelper.append(i)
    i = i + 1

i=0
j=0
k=0
while k < len(counterlisthelper):
    while i < labels.shape[0]:
        while j < labels.shape[1]:
            if labels[i,j] == counterlisthelper[k]:
                labels[i,j] = 0
            else:
                labels[i,j] = labels[i,j]
            j = j + 1
        j = 0
        i = i + 1
    i = 0
    j = 0
    k = k + 1

##################################################
##################################################

# Map component labels to hue val
label_hue = np.uint8(179*labels/np.max(labels))
blank_ch = 255*np.ones_like(label_hue)
labeled_img = cv2.merge([label_hue, blank_ch, blank_ch])

# cvt to BGR for display
labeled_img = cv2.cvtColor(labeled_img, cv2.COLOR_HSV2BGR)

# set bg label to black
labeled_img[label_hue==0] = 0

cv2.imshow('labeled.png', labeled_img)
cv2.waitKey()

3层嵌套循环对于Python来说是一件可怕的事情...尝试使用NumPy替换值...类似于labels[labels == 3]= 0。3是要删除的一个标签,根据需要进行替换。 - api55
2个回答

2
在Python中,应避免使用深层循环。建议使用numpy而不是python-loop
##################################################
ts = time.time()
num = labels.max()

N = 50

## If the count of pixels less than a threshold, then set pixels to `0`.
for i in range(1, num+1):
    pts =  np.where(labels == i)
    if len(pts[0]) < N:
        labels[pts] = 0

print("Time passed: {:.3f} ms".format(1000*(time.time()-ts)))
# Time passed: 4.607 ms

##################################################

结果:

在这里输入图片描述 在这里输入图片描述


整个代码:
#!/usr/bin/python3
# 2018.01.17 22:36:20 CST
import cv2
import numpy as np
import time

img = cv2.imread('test.jpg', 0)
img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)[1]  # ensure binary
retval, labels = cv2.connectedComponents(img)

##################################################
ts = time.time()
num = labels.max()

N = 50
for i in range(1, num+1):
    pts =  np.where(labels == i)
    if len(pts[0]) < N:
        labels[pts] = 0

print("Time passed: {:.3f} ms".format(1000*(time.time()-ts)))
# Time passed: 4.607 ms

##################################################

# Map component labels to hue val
label_hue = np.uint8(179*labels/np.max(labels))
blank_ch = 255*np.ones_like(label_hue)
labeled_img = cv2.merge([label_hue, blank_ch, blank_ch])

# cvt to BGR for display
labeled_img = cv2.cvtColor(labeled_img, cv2.COLOR_HSV2BGR)

# set bg label to black
labeled_img[label_hue==0] = 0

cv2.imshow('labeled.png', labeled_img)
cv2.imwrite("labeled.png", labeled_img)
cv2.waitKey()

对于每个标签都遍历整个图像是非常耗费资源的,特别是当标签数量增加时。 - Cris Luengo

1
我知道我来得很晚,但是我找到了一个比Knight金的解决方案快两倍以上的解决方案。这个解决方案在我的电脑上运行时间大约为350毫秒,而另一个解决方案则需要大约850��秒。
功能:

def remove_small_groups(image: np.ndarray, minimum: int) -> np.ndarray:
    """
    Removes all the groups of connected components smaller than the minimum
    from a binary image

    :param image: The binary image to process
    :param minimum: The minimum size of groups of connected components
    """

    labels, vals = cv2.connectedComponentsWithStats(image)[1:3]
    num = labels.max()

    vals = vals[1:, cv2.CC_STAT_AREA]

    new_img = np.zeros_like(labels)
    for i in filter(lambda v: vals[v] >= minimum, range(0, num)):
        pts = np.where(labels == i + 1)
        new_img[pts] = 255

    return new_img

支持代码:

import numpy as np
import time
import cv2

image = cv2.imread('test.jpg', 0)
image = cv2.threshold(img, 50, 255, cv2.THRESH_BINARY)[1]

avg_time = 0
for i in range(25):
    ts = time.time()
    vals = x(image, 50)
    avg_time += time.time() - ts

print("Time passed: {:.3f} ms".format(1000 * (avg_time / 25)))

是的,获取统计数据是很好的。你可以通过创建一个大小为num+1的映射数组map来使这个过程更快,其中如果第i个连接组件足够大,则map[i]=i,否则为0。然后,map[labels]将给出去除小区域的标记图像。 - Cris Luengo
Cris Luengo,你能详细说明一下吗? - 357865
类似于 map = ((vals >= minimum) * 255).astype(np.uint8) 然后 new_img = map[labels] - Cris Luengo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接