基于连通域标记的灰度图的前景背景分割——opency-python的实现!
一、connectedComponentsWithStats()函数:
def connectedComponentsWithStats(image, labels=None, stats=None, centroids=None, connectivity=None, ltype=None): # real signature unknown; restored from __doc__
"""
connectedComponentsWithStats(image[, labels[, stats[, centroids[, connectivity[, ltype]]]]]) -> retval, labels, stats, centroids
. @overload
. @param image the 8-bit single-channel image to be labeled
. @param labels destination labeled image
. @param stats statistics output for each label, including the background label, see below for
. available statistics. Statistics are accessed via stats(label, COLUMN) where COLUMN is one of
. #ConnectedComponentsTypes. The data type is CV_32S.
. @param centroids centroid output for each label, including the background label. Centroids are
. accessed via centroids(label, 0) for x and centroids(label, 1) for y. The data type CV_64F.
. @param connectivity 8 or 4 for 8-way or 4-way connectivity respectively
. @param ltype output image label type. Currently CV_32S and CV_16U are supported.
"""
这个函数的作用是对一幅图像进行连通域提取,并返回找到的连通域的信息:retval、labels、stats、centroids
参数介绍如下:
- image:也就是输入图像,必须是二值图,即8位单通道图像。(因此输入图像必须先进行二值化处理才能被这个函数接受)
- labels:指向被标记的图像
- stats:每一个标记的统计信息输出,包括背景。可以通过打印stas(label, column)来查看每一个标记的信息。
- centroids:每一个标记的中心位置。
- connectivity:可选值为4或8,也就是使用4连通还是8连通。
- ltype:输出图像标记的类型,目前支持CV_32S 和 CV_16U。
返回值:
- retval:所有标记类型的数目
- labels:图像上每一像素的标记,用数字1、2、3......表示
- stats:每一个标记的统计信息,是一个5列的矩阵,每一行对应各个轮廓的x、y、width、height和面积,示例如下:
0 0 720 720 291805
92 0 628 720 226595
- centroids:连通域的中心点
二、如何提取连通域
我的思路是这样的:首先对图像进行阈值分割,并输出二值化图像,二值化后可能存在一些孤立点区域,可以通过形态学闭运算适当的消除。然后对图像进行连通域标记,将连通域中面积最大的那一部分(背景)的像素值赋值为0,其余部分的像素值赋值为255。代码如下,大家适当参考:
import cv2
import numpy as np
#read the image
img = cv2.imread("home/dulingwen/Pictures/pointcloud_road/146Road_5_0.png")
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv2.namedWindow('original',cv2.WINDOW_AUTOSIZE)
cv2.imshow('original',gray)
#take the threshold and morphology thransform
ret, binary = cv2.threshold(gray, 30, 255, cv2.THRESH_BINARY)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(2,2))
bin_clo = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel, iterations = 2)
#obtain th label of the connection areas
num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(bin_clo,connectivity = 8)
"""
#查看各个返回值
print('num_labels = ',num_labels)
print('stats = ',stats)
print('centroids = ',centroids)
print('labels = ',labels)
"""
label_area = stats[:,-1]
max_index = np.argmax(label_area)
#label the backgroud and foreground
height = labels.shape[0]
width = labels.shape[1]
for row in range(height):
for col in range(width):
if labels[row,col] == max_index:
gray[row,col] = 0
else:
gray[row,col] = 255
#if stats[labels[row,col],4] < 100:
#gray[row,col] = 255
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(2,2))
conne = cv2.morphologyEx(gray, cv2.MORPH_OPEN, kernel, iterations = 2)
cv2.namedWindow('results',cv2.WINDOW_AUTOSIZE)
cv2.imshow('results',conne)
cv2.waitKey(0)
三、原图及效果图如下:
四、优缺点分析
优点:很明显可以看到,函数connectedComponentsWithStats()在标记连通域的时候能够判断哪些连通域实际上是属于同一类的,否则上图中应当具有三个连通域才对,最大的部分应当是中间的道路区域,此时这部分应该置为黑色(我用print(stats)输出过连通域的信息,发现只输出了两个连通域的统计信息)。
缺点:由于在分割图像的过程中总是将连通域中面积最大的(也就是背景)置为黑色,因此若遇到图像的背景区域面积小于前景区域的时候就会导致错误的分割。如果想始终正确分割前景和背景,必须应用机器学习的方法,故而接下来我将考虑使用KNN、SVM等方法进行前背景分割。