什么是图像轮廓?
具有相同颜色(在彩色图片中)或强度(灰度图像要转变为二值化图像)的连续点的曲线
图像轮廓的作用:
1.可以用于图形分析
2.物体的识别与检测
以下这张图中被红色线描出部分都可以称之为轮廓
注意点:
1.为了检测的准确性,需要先对图像进行二值化或Canny操作
2.画轮廓是会修改输入图像,所以为了不避免破坏原来图像可以先进行深拷贝
轮廓查找api :
findContours (img, mode, ApproximationMode…),两个返回值 :contours(查找到所有轮廓的列表,不会绘制出来,只是以列表的形式存储所有轮廓的点)和 hierarchy(层级。表示查找到的所有轮廓有无层级或前后关系)。
mode : 对查找到的轮廓进行如何的组织.可以分级,也可以把所有的轮廓放在一个列表里,或者将轮廓按照树形结构进行存储。
RETR_EXTERNAL = 0, 表示只检测外轮廓(只检测最大的外圈轮廓,一般用于只想检测大物体)
RETR_LIST = 1,检测的轮廓不建立等级关系,所有轮廓放在一个列表中。
RETR_CCOMP = 2,有层级,每层最多两级
RETR_TREE = 3, 按照树形存储轮廓,从右到左一层一层检测(常用)
ApproximationMode:近似查找轮廓。
CHAIN_APPROX_NONE,保存所有轮廓上的点
CHAIN_APPROX_SIMPLE,只保存角点(常用)
绘制轮廓api :
drawContours(img, contours, contoursIdx, color, thickness,…)
img:在哪张图片上进行展示
contours :轮廓坐标点,也就是findContours第一个返回值。
contoursIdx:轮廓顺序号,拿到的所有轮廓坐标点会按照储存方式有个顺序,从0开始【-1表示绘制所有轮廓】
color :轮廓的颜色,书写为(0,0,255)
thickness :显卡,1为细线,2为粗线,-1表示对整个轮廓进行填充
以下是实例代码
import cv2
import numpy as np
#读文件
img = cv2.imread('d:\\STUDY_OPENCV\\img\\rct.png')
#转变成单通道
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#二值化,第一个返回值是执行的结果和状态是否成功,第二个返回值才是真正的图片结果
ret,binary = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY)
#轮廓查找,第一个返回值是轮廓,第二个是层级
contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
#绘制轮廓
cv2.drawContours(img, contours, -1, (0,0,255), 1)#改变的是img这张图
print(contours)
cv2.imshow('img',img)
cv2.imshow('binary',binary)
cv2.waitKey(0)
根据轮廓的面积,太碎小就去除
轮廓面积api:
countourArea(contour),contour指列表中具体的某一个轮廓,而且算出的面积是图像中的面积,不是实际的。
轮廓的周长api :
arcLength( curve , closed )
curve :计算的轮廓
closed :轮廓是否闭合,若闭合设为True,否则设为False
#计算面积
area = cv2.contourArea(contours[0])
#计算周长
len = cv2.arcLength(contours[0],True)
print("len = ",len)
print("area=%d"%(area))
多边形逼近:如下图左边把手的一些特征点标出来使其画出手的大致形状,而不是沿着手边缘把所有像素点保存下来。这就叫多边形逼近,此类方法可以减少保存大量数据,并且根具实际情况调整精度(即特征点的个数)。
凸包 :如下图右边所示描绘物体的一个整个突出的轮廓。
多边形逼近api:
apporxPolyDP(curve, epsilon, closed)
curve : 轮廓查找中具体一个轮廓
epsilon :精度,数字越小代表精度越低,表示和图形轮廓越吻合
closed :是否是闭合的轮廓
凸包 api :
convexHull(points, clockwise, …)
points : 点,某一个具体的轮廓
clockwise :True为顺时针绘制,False为逆时针绘制
【以上两个api返回值都是数组,所以需要自己写一个函数把返回的点描绘出来】
import cv2
import numpy as np
def drawShape(src, points):
i = 0
while i < len(points):
if(i == len(points)-1):
x,y = points[i][0]
x1,y1 = points[0][0]
cv2.line(src, (x,y), (x1,y1), (0,255,0), 3)
else:
x,y = points[i][0]
x1,y1 = points[i+1][0]
cv2.line(src, (x,y), (x1,y1), (0,255,0), 3)
i = i+1
#读文件
img = cv2.imread('d:\\STUDY_OPENCV\\img\\hand.png')
#转变成单通道
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#二值化,第一个返回值是执行的结果和状态是否成功,第二个返回值才是真正的图片结果
ret,binary = cv2.threshold(gray, 100, 255, cv2.THRESH_BINARY)
#轮廓查找,第一个返回值是轮廓,第二个是层级
contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
#绘制轮廓
cv2.drawContours(img, contours, 0, (0,0,255), 1)#改变的是img这张图
#多边形逼近,20代表精度,数字越大代表精度越低,approx返回的是点的数组
approx = cv2.approxPolyDP(contours[0], 5, True)
drawShape(img, approx)
print(approx)
print(len(approx))
#凸包
hull = cv2.convexHull(contours[0])
drawShape(img, hull)
cv2.imshow('img',img)
cv2.imshow('binary',binary)
cv2.waitKey(0)
最小外接矩形:按照物体的角度以最小的面积进行衔接,可以知道物体有没有旋转(红色框)
最大外接矩形:把物体扩在里面但是面积最大(绿色框)。
最小外接矩形api:
minAreaRect( points )
points : 物体的轮廓
返回值RotatedRect:x,y,width,height,angle。(起始点,宽,高,角度)
最大外接矩形api:
boundingRect(array)
array:轮廓
返回值Rect :x,y,width,height(起始点,宽,高)
以下是实例
import cv2
import numpy as np
#读文件
img = cv2.imread('d:\\STUDY_OPENCV\\img\\hello.png')
#转变成单通道
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#二值化,第一个返回值是执行的结果和状态是否成功,第二个返回值才是真正的图片结果
ret,binary = cv2.threshold(gray, 100, 255, cv2.THRESH_BINARY)
#轮廓查找,第一个返回值是轮廓,第二个是层级
contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
#最小外接矩形,根据绘制轮廓api测试可以知道包起字母的正方形轮廓是contours[1]
r = cv2.minAreaRect(contours[1])
#由于返回的r还带角度,所以利用boxPoints把最小外接矩形的四个顶点坐标提取出来
box = cv2.boxPoints(r) #由于返回的box是浮点型,所以需要对box进行一次强制转换
box = np.int0(box)
cv2.drawContours(img, [box], 0, (0,0,255), 2)#box是一个二维列表,需要转换为三维列表
#最大外接矩形
x,y,w,h = cv2.boundingRect(contours[1])
cv2.rectangle(img, (x,y),(x+w, y+h),(255,0,0),2)
print(x+w,y+h)
cv2.imshow('img',img)
cv2.imshow('binary',binary)
cv2.waitKey(0)