持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第9天,点击查看活动详情
由于工作中一个小需求,需要对轮廓进行查找、标记、轮廓中的周长、面积、像素数进行统计,最开始使用的是cv2.findContours()函数,该函数配合cv2.drawContours()可以实现对轮廓的标记,但是在处理轮廓的过程中,发现了连通域是一个很好的检测特征,并且顺藤摸瓜找到了opencv中的cv2.connectedComponentsWithStats,该函数的功能非常的强,其作用我已经在代码中详细的说明,下面先上代码。
import cv2
# 读取图像
image = cv2.imread("rice.png")
# 转换为灰度图
img = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
# 图像二值化
ret, dst = cv2.threshold(img, 50, 255, cv2.THRESH_BINARY)
'''
使用connectedComponentsWithStats函数
retval : 返回值是连通区域的数量。
labels : labels是一个与image一样大小的矩形(labels.shape = image.shape),其中每一个连通区域会有一个唯一标识,标识从0开始。
stats :stats会包含5个参数分别为x,y,h,w,s。分别对应每一个连通区域的外接矩形的起始坐标x,y;外接矩形的wide,height;s其实不是外接矩形的面积,实践证明是labels对应的连通区域的像素个数。
centroids : 返回的是连通区域的质心。
'''
retval, labels, stats, centroids = cv2.connectedComponentsWithStats(dst, connectivity=8, ltype=cv2.CV_16U)
count = 0
for istat in stats:
cv2.rectangle(image, tuple(istat[0:2]), tuple(istat[0:2] + istat[2:4]), (0, 0, 255), thickness=1)
cv2.putText(image, str(count), (int(centroids[count][0]), int(centroids[count][1])), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 0), 1)
count += 1
cv2.imshow('rice_result',image)
cv2.waitKey(0)
代码非常简单,二十来行,其功能还是比较完善的,主要得益于opencv所作的贡献,其内部早已经帮我们封装好了现成的函数,为了节省了许多代码逻辑,上述代码的效果如下: 其中图像处理前的原图如下所示
这张图像还是很出名的,许多使用Lena图像的都应该使用过这张图像,下面是对该图像处理得到的结果图
上面一共25粒大米,但是上述代码实际上计算时发现了27个轮廓,其中一个轮廓为整幅图像,另一个轮廓为标号3,我费了很大劲也没看出标号3位置有像素值存在,除了这两处之外,其他的检测和编号全为正常。
不能说检测函数有什么问题,在实际应用时我们可以根据面积过滤掉排行第一的轮廓从而排除包含整幅图像的轮廓0,至于轮廓3位置,我有点不甘心,首先我是用matplot显示图像,用鼠标滑动方式获取了该大致区域的坐标轮廓,然后我将 该区域的图像像素值进行了累加,最后的结果居然不是0,说明该区域确实有像素值存在,也就可以说明为什么会在此处检测出单独的轮廓。
最后,希望经常使用findContours函数的下次可以使用connectedComponentsWithStats看一下效果,说不定可以节省很多的时间。