边缘检测虽然能够检测出边缘,但边缘是不连续的,检测到的边缘并不是一个整体。图像轮廓是指将边缘连接起来形成的一个整体,用于后续的计算。
图像轮廓是图像中非常重要的一个特征信息,通过对图像轮廓的操作,我们能够获取目标图像的大小、位置、方向等信息。
案例来源于傅老师。
寻找轮廓的操作一般用于二值化图,所以通常会使用阈值分割或Canny边缘检测先得到二值图。
函数cv2.findContours()的语法格式为:
contours,hierarchy=cv2.findContours(image,mode,method)
contours:返回的轮廓。例如contours[i]表示第i个轮廓。
hierarchy:图像的拓扑信息(轮廓层次)。
image:输入的图像。
mode:轮廓搜索模式:决定了轮廓的提取方式。
method:轮廓近似方法:决定了如何表达轮廓。
import cv2
import numpy as np
img = cv2.imread('shape.jpg') #读取图像
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) #转为灰度值图
ret, binary = cv2.threshold(gray,220,255,cv2.THRESH_BINARY) #转为二值图
contours, hierarchy = cv2.findContours(binary,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE) #寻找轮廓
n=len(contours) #轮廓个数
print(n)
print(len(contours[0])) #轮廓0像素数目
print(len(contours[1])) #轮廓1像素数目
print(len(contours[2])) #轮廓2像素数目
print(len(contours[3])) #轮廓3像素数目
函数cv2.drawContours的语法格式为:
image=cv2.drawContours(image, contours, contourIdx, color, thickness=None, lineType=None, hierarchy=None, maxLevel=None, offset=None)
cv2.imshow("img",img) #显示原图像
img2 = cv2.drawContours(img,contours,1,(0,165,255),-1) #绘制轮廓,1表示绘制第几个轮廓
cv2.imshow("contours",img2) #显示轮廓
cv2.waitKey()
cv2.destroyAllWindows()
比较两个轮廓最简单的方法是比较二者的轮廓矩。轮廓矩代表了一个轮廓、一幅图像、一组点集的全局特征。矩信息包含了对应对象不同类型的几何特征,例如大小、位置、角度、形状等。矩特征被广泛地应用在模式识别、图像识别等方面。
函数cv2.moments()的语法格式为:
retval=cv2.moments(array[,binaryImage])
array:可以是点集,也可以是灰度图像或者二值图像。
binaryImage:当为True时,array内所有的非零值都会被处理为1。
import cv2
import numpy as np
img = cv2.imread('shape.jpg') #读取图像
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) #转为灰度值图
ret, binary = cv2.threshold(gray,220,255,cv2.THRESH_BINARY) #转为二值图
contours, hierarchy = cv2.findContours(binary,cv2.RETR_TREE,\
cv2.CHAIN_APPROX_NONE) #寻找轮廓
n=len(contours) #轮廓个数
contoursImg=[]
for i in range(n):
temp=np.zeros(img.shape,np.uint8) #生成黑背景
contoursImg.append(temp)
contoursImg[i]=cv2.drawContours(contoursImg[i],contours,i,(255,255,255), 3) #绘制轮廓
cv2.imshow("contours[" + str(i)+"]",contoursImg[i]) #显示轮廓
print("计算图像的矩特征:")
for i in range(n):
moment=cv2.moments(contours[i])
print(f"轮廓{i}的矩:\n{moment}")
cv2.waitKey()
cv2.destroyAllWindows()
for i in range(n):
area=cv2.moments(contours[i])["m00"]
print(f"轮廓{i}的面积:\n{area}")
函数cv2.contourArea()的语法格式为:
area=cv2.contourArea(contour [, oriented])
contour:轮廓。
oriented:布尔值,当为True时,表示顺时针或逆时针计算。
import cv2
import numpy as np
img = cv2.imread('shape.jpg') #读取图像
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) #转为灰度值图
ret, binary = cv2.threshold(gray,220,255,cv2.THRESH_BINARY) #转为二值图
contours, hierarchy = cv2.findContours(binary,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE) #寻找轮廓
n=len(contours) #轮廓个数
contoursImg=[]
for i in range(n):
area = cv2.contourArea(contours[i])
print(f"轮廓{i}的面积:\n{area}")
函数cv2.arcLength()的语法格式为:
length=cv2.arcLength(curve,closed)
curve:轮廓。
closed:布尔值,当为True时,表示闭合。
n=len(contours) #轮廓个数
contoursImg=[]
for i in range(n):
length = cv2.arcLength(contours[i], True) #获取轮廓长度
print(f"轮廓{i}的长度:\n{length}")
Hu矩是归一化中心矩的线性组合,Hu矩再图像旋转,缩放,平移等操作后,仍能保持矩的不变性,经常使用 Hu 矩来识别图像的特征。
通过Hu 矩可以来判断两个对象的一致性。但是结果比较抽象,OpenCV 提供了 cv2.matchShapes() 对两个对象的Hu矩进行比较。
函数cv2.matchShapes()的语法格式为:
retval = cv2.matchShapes(contour1, contour2, method, 0.0)
retval:矩形边界左上角顶点的坐标值及矩形边界的宽度和高度。
contour1:第一个轮廓或灰度图像。
contour2:第二个灰度或轮廓图像。
method:比较两个对象的Hu 矩的方法,写1吧。
import cv2
o1 = cv2.imread('m1.png')
o2 = cv2.imread('m2.png')
o3 = cv2.imread('m3.png')
gray1 = cv2.cvtColor(o1,cv2.COLOR_BGR2GRAY)
gray2 = cv2.cvtColor(o2,cv2.COLOR_BGR2GRAY)
gray3 = cv2.cvtColor(o3,cv2.COLOR_BGR2GRAY)
ret, binary1 = cv2.threshold(gray1,127,255,cv2.THRESH_BINARY)
ret, binary2 = cv2.threshold(gray2,127,255,cv2.THRESH_BINARY)
ret, binary3 = cv2.threshold(gray3,127,255,cv2.THRESH_BINARY)
contours1, hierarchy = cv2.findContours(binary1,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
contours2, hierarchy = cv2.findContours(binary2,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
contours3, hierarchy = cv2.findContours(binary3,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
cnt1 = contours1[0]
cnt2 = contours2[0]
cnt3 = contours3[0]
ret0 = cv2.matchShapes(cnt1,cnt1,1,0.0)
ret1 = cv2.matchShapes(cnt1,cnt2,1,0.0)
ret2 = cv2.matchShapes(cnt1,cnt3,1,0.0)
print("相同图像的 matchShape=",ret0)
print("相似图像的 matchShape=",ret1)
print("不相似图像的 matchShape=",ret2)
函数cv2.minAreaRect()的语法格式为:
retval =cv2.minAreaRect( points )
retval:返回的矩阵特征信息,结构是(最小外接矩形的中心(x,y),(宽度,高度),旋转角度)。
points:轮廓。
import cv2
o=cv2.imread('shape.jpg')
cv2.imshow("original",o)
gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
rect = cv2.minAreaRect(contours[1])
print("返回值 rect:\n",rect)
points = cv2.boxPoints(rect)
print("\n 转换后的 points:\n",points)
points = np.int64(points) #取整,np.int64=np.int0
image=cv2.drawContours(o,[points],0,(0,0,0),2)
cv2.imshow("result",o)
cv2.waitKey()
cv2.destroyAllWindows()
函数cv2.fitEllipse()的语法格式为:
retval = cv2.fitEllipse( points )
import cv2
o=cv2.imread('shape.jpg')
cv2.imshow("original",o)
gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
ellipse = cv2.fitEllipse(contours[1])
print("ellipse=",ellipse)
cv2.ellipse(o,ellipse,(0,255,0),3)
cv2.imshow("result",o)
cv2.waitKey()
cv2.destroyAllWindows()
函数cv2.fitLine()的语法格式为:
line = cv2.fitLine( points, distType, param, reps, aeps )
import cv2
o=cv2.imread('shape.jpg')
cv2.imshow("original",o)
gray = cv2.cvtColor(o,cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
rows,cols = o.shape[:2]
[vx,vy,x,y] = cv2.fitLine(contours[1], cv2.DIST_L2,0,0.01,0.01) # 返回值是共线的归一化向量,和线上一点
lefty = int((-x*vy/vx) + y) # 说白了就是一个方向和一个点,点斜式嘛,还啥vec4f,,讲究
righty = int(((cols-x)*vy/vx)+y) # 计算两个点,代值计算就行
cv2.line(o,(cols-1,righty),(0,lefty),(0,255,0),2)
cv2.imshow("result",o)
cv2.waitKey()
cv2.destroyAllWindows()
函数cv2.approxPolyDP()用来构造指定精度的逼近多边形曲线,语法格式为:
approx = cv2.approxPolyDP(contour[0],epsilon,True)
import cv2
import numpy as np
# 多边形逼近
# 1.先找到轮廓
img = cv2.imread('contours3.png')
cv2.imshow("original",img)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(binary,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
cnt = contours[0]
# 2.进行多边形逼近,得到多边形的角点
approx1 = cv2.approxPolyDP(cnt, 3, True)
approx2 = cv2.approxPolyDP(cnt, 15, True)
approx3 = cv2.approxPolyDP(cnt, 75, True)
# 3.画出多边形
adp=img.copy()
img1=cv2.polylines(adp, [approx1], True, (255, 0, 0), 2)
cv2.imshow('approxPloyDP1', img1)
####
adp=img.copy()
img2=cv2.polylines(adp, [approx2], True, (0, 255, 0), 2)
cv2.imshow('approxPloyDP2', img2)
#####
adp=img.copy()
img3=cv2.polylines(adp, [approx3], True, (0, 0, 255), 2)
cv2.imshow('approxPloyDP3', img3)
print(len(approx1),len(approx2),len(approx3)) # 角点的个数
cv2.waitKey(0)
cv2.destroyAllWindows()