cv2.connectedComponentsWithStats 处理不规则连通区域
- 函数介绍
- 实例-参数详解
- 举个栗子
函数介绍
retval, labels, stats, centroids = cv2.connectedComponentsWithStats(image, connectivity=8)
输入值:
- image : 是要处理的图片,官方文档要求是8位单通道的图像。
- connectivity : 可以选择是4连通还是8连通。
输出值
- retval : 返回值是连通区域的数量。
- labels : labels是一个与image一样大小的矩形(labels.shape = image.shape),其中每一个连通区域会有一个唯一标识,标识从0开始。
- stats :stats会包含5个参数分别为x,y,h,w,s。分别对应每一个连通区域的外接矩形的起始坐标x,y;外接矩形的wide,height;s其实不是外接矩形的面积,实践证明是labels对应的连通区域的像素个数。
- centroids : 返回的是连通区域的质心。
实例-参数详解
举个栗子
1、 我创建了10x10的图片,其中像素值分别有0和100
输入的image就是我们这个10x10的图片
如果我们要分析8连通,就令
connectivity=8
2、用 opencv 将图片读入
image = cv2.imread('test1.tif') # 将上述的10*10图片存为test.tif的图片
img = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) #转灰度
retval, labels, stats, centroids = cv2.connectedComponentsWithStats(img, connectivity=8)
返回值
retval #retval = 3
>>> 3
stats #我们看出有3个连通区域
# x y w h s
>>> array([[ 0, 0, 10, 10, 76], # 这代表整个图片,0值也有连通区域
[ 4, 1, 5, 6, 18], # 这里18代表有18个像素 下面的6同理
[ 2, 2, 3, 2, 6]], dtype=int32)
labels # labdels 将背景0值标为0,值为100的标为1 和 2
>>> array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 2, 2, 2, 0, 1, 0, 0, 0],
[0, 0, 2, 2, 2, 0, 1, 1, 1, 0],
[0, 0, 0, 0, 0, 0, 1, 1, 1, 0],
[0, 0, 0, 0, 1, 1, 1, 1, 1, 0],
[0, 0, 0, 0, 1, 1, 1, 1, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=int32)
centroids
>>> array([[4.17105263, 4.68421053],
[6.38888889, 4.38888889],
[3. , 2.5 ]]
3、我们来详细看一下如何利用stats和labels处理不规则图形
按照 stats返回的外接矩形分别如下两个矩形,其中蓝色的矩形有两个值在红色矩形内,同时可以看到原始数据的连通区域与labels可以相互对应
问题来了: 如果我想要将原始图片红色框内的100变为0,如果我们直接令外接矩形框内全部为0,这样不可避免的会伤及无辜将蓝色框内的两个100值都改为0.这个是不允许的。
所以我们可以利用labels的标识
label = labels[y:y + h, x:x + w] #获取要处理的红色方框外接矩形
lab = label.reshape(-1, ) # 二维转一维
lab = np.unique(lab) # 去掉重复
lab = np.setdiff1d(lab, 0) # 去掉0值,因为0值是背景的标识,我们不用处理0,只要除去1保留2即可
之后又因为stats中的返回值s是像素值的个数,并不是外接矩形内所有100值的个数。
下面两个图可以看出,红色框内100的值是20个,但构成连通区域的之后18个像素,所以stats返回值中是18不是20;这个很重要!!!!
这样在红色矩形框内的labels= 2 的数量只有两个,labels = 1的数量有18个,并且与s一致,可以依据这个条件,获取labels = 1 的坐标,并令这些坐标所在值等于0,这样labels = 2 的值并没有受到影像。
for l in lab:
seeds = np.argwhere(label==l)
seedlist = list(seeds)
print(seedlist)
if len(seedlist) == s:
# print(seedlist)
for point in seedlist:
image[point[0],point[1]] = 0
img[y:y + h, x:x + w] = image
4 、结果:
原始图:
在这之前我尝试了一些别的函数,都不能很好的操作,下期会整理一下。