findContours是连通域处理中最常用的函数之一,顾名思义,是实现图像中连通域的查找定位。结合cv2.drawContours/cv2.contourArea/cv2.boundingRect/cv2.minAreaRect等函数可以实现连通域轮廓的描绘,面积计算,外接四边形和最小外接四边形的求取。
opencv4中函数原型:
contours,hierarchy=cv2.findContours(img, mode, method[, contours[, hierarchy[, offset ]]])
输出参数:
contours:查找到的轮廓的列表
hierarchy:保存每个连通域对应的属性
输入参数:
img: 输入的图像,可以为灰度图和二值图,常用的为二值图,背景为黑色,前景为白色
mode:表示轮廓的检索模式.常用的有cv2.RETR_EXTERNAL和cv2.RETR_TREEcv2.RETR_EXTERNAL 只检测外轮廓cv2.RETR_TREE 内外轮廓都检测此外还有,具体功能待探索:cv2.RETR_LISTcv2.RETR_CCOMP
method:轮廓的近似办法cv2.CHAIN_APPROX_NONE 存储所有的轮廓点cv2.CHAIN_APPROX_SIMPLE 压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息
函数测试如下:
def imshow(img, height, width, title, flagbatch):cv2.namedWindow(title, 0) # 参数必须为0cv2.resizeWindow(title, width, height)cv2.moveWindow(title, 600, 300) # 窗口出现的位置cv2.imshow(title, img)if flagbatch:cv2.waitKey(0)
def TestContour():font = cv2.FONT_HERSHEY_SIMPLEXimg = cv2.imread('./datasets/logo1.jpg')gray = 255 - cv2.imread('./datasets/logo1.jpg',0) # 保证前景为白色_,binary = cv2.threshold(gray,100,255,cv2.THRESH_BINARY)H,W = binary.shapemode = [cv2.RETR_EXTERNAL,cv2.RETR_TREE]method = [cv2.CHAIN_APPROX_NONE,cv2.CHAIN_APPROX_SIMPLE]plt.figure()for mode_id in range(2):for met_id in range(2):pltimg = img.copy()title = str(mode_id) + '_' + str(met_id)contours, hierarchy = cv2.findContours(binary,mode[mode_id],method[met_id])contours = sorted(contours, key=lambda contour: cv2.contourArea(contour),reverse=True) # 按连通域面积从大到小排序Num = len(contours)for i in range(Num):area = cv2.contourArea(contours[i])(x, y, w, h) = cv2.boundingRect(contours[i])cv2.rectangle(pltimg, (x, y), (x + w, y + h), (0, 0, 255), 2, cv2.LINE_AA)cv2.putText(pltimg, "No.{},area:{}".format(i+1,area), (x-5, y - 5), font, 0.8, (255, 0, 0), 2)# imshow(img,H,W,'img',True)plt.subplot(2,2,2*mode_id + met_id + 1)plt.imshow(pltimg[:,:,[2,1,0]]) # plt 和 opencv的通道顺序相反,前者为rgb, 后者为bgrplt.title(title)plt.show()
- 无内轮廓图像
测试效果如下:
可以看到对于只有外轮廓的连通域,四种组合参数的效果一致。 - 有内外轮廓图像
测试效果如下:
可以看到对于有内外轮廓的连通域,external只检测外轮廓,tree则同时检测了内外轮廓。