Reputation: 319
I want to use the function cv2.connectedComponents to connect components on a binary image, like the following...
I have added the feature to cv2. connectedComponents to eliminate elements with a small number of pixels.
Unfortunately, the algorithm is extremely slow for large images due to the extension. Is there a way to rewrite the extension to speed up the algorithm?
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()
Upvotes: 3
Views: 7653
Reputation: 23
I know I'm very late to this, but I found a solution that is more than twice as fast as the solution by Kinght 金
. This solution tends to run in around 350ms on my computer with a 600×800 Image, compared to around 850ms with the other solution.
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)))
Upvotes: 1
Reputation: 18331
In python, you should avoid deep loop. Prefer to use numpy
other than python-loop
.
Imporved:
##################################################
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
##################################################
Result:
The whole code:
#!/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()
Upvotes: 2