图像轮廓 Contours
- cv2.findContours(img,mode,method)
- img: 二值图像
- mode: 轮廓检索模式
- RETR_EXTERNAL :只检索最外面的轮廓;
- RETR_LIST:检索所有的轮廓,并将其保存到一条链表当中;
- RETR_CCOMP:检索所有的轮廓,并将他们组织为两层:顶层是各部分的外部边界,第二层是空洞的边界;
- RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次; (通常选这个)
method:轮廓逼近方法
- CHAIN_APPROX_NONE:以Freeman链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列)。
-
CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分。
返回值
- contours 是一个list,list中每个元素都是图像中的一个轮廓,用numpy中的ndarray表示
- hierarchy 是一个ndarray,其中的元素个数和轮廓个数相同,每个轮廓contours[i]对应4个hierarchy元素hierarchy[i][0] ~hierarchy[i][3],分别表示后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号,如果没有对应项,则该值为负数
如果报错
binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
ValueError: not enough values to unpack (expected 3, got 2)
要想返回三个参数:
把OpenCV 降级成3.4.3.18 就可以了,在终端输入pip install opencv-python==3.4.3.18
OpenCV 新版返回两个参数:
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
.
查看opencv版本:print(cv2.version)
我的opecv版本为: 4.2.0
hierarchy [ˈhaɪərɑːrki] 层级;等级制度
绘制轮廓
- cv2.drawContours
参数如下:传入绘制图像,轮廓,轮廓索引,颜色模式,线条厚度
img = cv2.imread('contours.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# cv_show(thresh,'thresh')
# 计算轮廓
binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE) #binary上面处理完的结果 contours轮廓信息 hierarchy层级
# 绘制轮廓
draw_img1 = img.copy() # 需进行copy,因drawContours会在原图上绘制,改变原图
draw_img2 = img.copy()
draw_img3 = img.copy()
#传入绘制图像,轮廓,轮廓索引,颜色模式,线条厚度
all_contours = cv2.drawContours(draw_img1, contours, -1, (0, 0, 255), 2) # -1是指所有的轮廓,(0,0,255) BGR , 2 线条的宽度
contours_0 = cv2.drawContours(draw_img2,contours,0,(0,0,255),2) # 检测到的第1个轮廓
contours_1 = cv2.drawContours(draw_img3,contours,1,(0,0,255),2) # 检测到的第2个轮廓
cv_show('res',np.hstack((all_contours,contours_0,contours_1)))
轮廓特征
cnt = contours[0] #第几个轮廓
#面积
cv2.contourArea(cnt)
#周长,True表示闭合的
cv2.arcLength(cnt,True)
轮廓近似
approx [əˈprɑːks] :大约,近似
cv2.approxPolyDP(contour,epsilon,True) 把一条平滑的曲线曲折化
参数
epsilon:表示的是精度,越小精度越高,因为表示的意思是是原始曲线与近似曲线之间的最大距离
closed:表示输出的多边形是否封闭;true表示封闭,false表示不封闭。
算法步骤 :
连接曲线首尾两点A、B形成一条直线AB; 计算曲线上离该直线段距离最大的点C,计算其与AB的距离d;
比较该距离与预先给定的阈值threshold的大小,如果小于threshold,则以该直线作为曲线的近似,该段曲线处理完毕。
如果距离大于阈值,则用点C将曲线分为两段AC和BC,并分别对两段曲线进行步骤[1~3]的处理。
当所有曲线都处理完毕后,依次连接各个分割
img = cv2.imread('contours2.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
binary, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
cnt = contours[0]
epsilon1 = 0.05*cv2.arcLength(cnt,True)
epsilon2 = 0.1*cv2.arcLength(cnt,True)
epsilon3 = 0.15*cv2.arcLength(cnt,True)
approx1 = cv2.approxPolyDP(cnt,epsilon1,True)
approx2 = cv2.approxPolyDP(cnt,epsilon2,True)
approx3 = cv2.approxPolyDP(cnt,epsilon3,True)
draw_img1 = img.copy()
draw_img2 = img.copy()
draw_img3 = img.copy()
approx1_res = cv2.drawContours(draw_img1, [approx1], -1, (0, 0, 255), 2)
approx2_res = cv2.drawContours(draw_img2, [approx2], -1, (0, 0, 255), 2)
approx3_res = cv2.drawContours(draw_img3, [approx3], -1, (0, 0, 255), 2)
cv_show('res',np.hstack((approx1_res,approx2_res,approx3_res)))
# cv_show(res,'res')
边界矩形
cv2.boundingRect:矩形边框(Bounding Rectangle)是用一个最小的矩形,把找到的形状包起来
返回四个值,分别是x,y,w,h;
x,y是矩阵左上点的坐标,w,h是矩阵的宽和高
cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 2)画出矩行
第一个参数:img是原图
第二个参数:(x,y)是矩阵的左上点坐标
第三个参数:(x+w,y+h)是矩阵的右下点坐标
第四个参数:(0,255,0)是画线对应的rgb颜色
第五个参数:2是所画的线的宽度
cv2.minAreaRect():得到包覆轮廓的最小斜矩形,
cv2.minEnclosingCircle():得到包覆此轮廓的最小圆形
返回一个二元组,第一个元素为圆心坐标组成的元组,第二个元素为圆的半径值。
cv2.circle(img, center, radius, color, thickness, lineType, shift) 根据给定的圆心和半径等画圆
参数说明
img:输入的图片data
center:圆心位置
radius:圆的半径
color:圆的颜色
thickness:圆形轮廓的粗细(如果为正)。负厚度表示要绘制实心圆。
lineType: 圆边界的类型。
shift:中心坐标和半径值中的小数位数。
# 边界矩形
img = cv2.imread('contours.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnt_2 = contours[2]
x,y,w,h = cv2.boundingRect(cnt_2)
img_rec = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
cv_show('img_rec',img_rec)
# 轮廓面积与边界矩形比
area = cv2.contourArea(cnt)
rect_area = w * h
extent = float(area) / rect_area
# print ('轮廓面积与边界矩形比',extent)
外接圆
# 外接圆
(x,y),radius = cv2.minEnclosingCircle(cnt)
center = (int(x),int(y))
radius = int(radius)
img_circle = cv2.circle(img,center,radius,(0,255,0),2)
cv_show('circle',img_circle)