"彩虹"
- 1. 图形轮廓(contours)
- 1.1 查找轮廓
- 1.2 绘制轮廓
- 1.3 轮廓的面积和周长
- 2. 多边形逼近
- 3. 多边形凸包
- 4. 外接矩形
希望有能力的朋友还是拿C++做。
本节讨论查找、绘制图像轮廓,轮廓的面积,周长,多边形逼近,多边形凸包,轮廓的外接矩形
1. 图形轮廓(contours)
具有相同颜色或灰度的连续点的曲线,轮廓是形状分析和物体的检测和识别中很有用
- 图形分析
- 物体的识别和检测
- 注意:
- 为了检测的准确性,首先要二值化或canny操作
- 画轮廓时会修改输入的图像,如果之后想继续使用原始图像,应该先将原始图像储存到其他变量
1.1 查找轮廓
- cv2.findContours(image, mode, method…)
- mode:查找轮廓模式
- RETR_EXTERNAL=0 只检测外围轮廓
- RETR_LIST=1 检测的轮廓不建立等级关系,检测所有轮廓
- RETR_CCOMP=2 每层最多两级,从小到大,从里到外
- RETR_TREE = 3 按照树形储存轮廓,从大到小,从右到左
- method轮廓近似方法
- CHAIN_APPROX_NONE 保存轮廓上的所有点
- CHAIN_APPROX_SIMPLE 只保存焦点,比如四边形只存储四个角,常用
- 返回值:contour和hierachy即轮廓和等级
import cv2
import numpy as np
cat = cv2.imread('cat.jpeg')
img = cv2.cvtColor(cat, cv2.COLOR_BGR2GRAY)
#二值化,有两个返回值,阈值,结果
ret, binary = cv2.threshold(img, 150, 255, cv2.THRESH_BINARY)
cv2.imshow('img', img)
# cv2.imshow('img2', binary)
contours, hierarchy = cv2.findContours(binary, mode = cv2.RETR_TREE, method=cv2.CHAIN_APPROX_SIMPLE)
print(contours)
cv2.waitKey(0)
cv2.destroyAllWindows()
1.2 绘制轮廓
- drawContours(image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset]]]]])
- image 要绘制的轮廓图像
- contours: 轮廓点
- contouridx 要绘制的轮廓编号 -1表示绘制所有轮廓
- color 轮廓的颜色 (0,0,255)表示红色
- thickness 线宽 -1 表示全部填充
import cv2
import numpy as np
cat = cv2.imread('cat.jpeg')
img = cv2.cvtColor(cat, cv2.COLOR_BGR2GRAY)
#二值化,有两个返回值,阈值,结果
ret, binary = cv2.threshold(img, 150, 255, cv2.THRESH_BINARY)
cat_cp = cat.copy()
contours, hierarchy = cv2.findContours(binary, mode = cv2.RETR_TREE, method=cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(cat_cp, contours, -1, (0, 0, 255), 2)
cv2.imshow('cat', cat)
cv2.imshow('contours', cat_cp)
cv2.waitKey(0)
cv2.destroyAllWindows()
1.3 轮廓的面积和周长
单位为像素,再找到轮廓后,会有很多细小的轮廓,可以通过轮廓面积进行过滤
- 轮廓面积 contourArea(contour)
- 轮廓周长 arcLengh(curve, closed)
- curve即轮廓
- closed是否是闭合的轮廓
import cv2
import numpy as np
cat = cv2.imread('cat.jpeg')
img = cv2.cvtColor(cat, cv2.COLOR_BGR2GRAY)
#二值化,有两个返回值,阈值,结果
ret, binary = cv2.threshold(img, 150, 255, cv2.THRESH_BINARY)
cat_cp = cat.copy()
contours, hierarchby = cv2.findContours(binary, mode = cv2.RETR_TREE, method=cv2.CHAIN_APPROX_SIMPLE)
#一个轮廓的面积
area = cv2.contourArea(contours[0])
print(area)
#一个轮廓周长
perimeter = cv2.arcLength(contours[1], closed=True)
print(perimeter)
2. 多边形逼近
findContours后的轮廓信息contours可能过于复杂不平花,可以用approxPolyDP函数对该多边形曲线近似,这就是轮廓的多边形逼近。
- approxPolyDP就是以多边形去逼近轮廓,采用的是Douglas-Peucker算法(DP)
- DP算法简单,就是不断找多边形最远的点加入形成新的多边形,指导最短距离小于指定精度。
- approxPolyDP(curbe, epsilon, closed[])
- curve就是要近似逼近的轮廓
- epsilon 即DP算法使用的阈值,偏差值
closed轮廓是否闭合
import cv2
import numpy as np
hand = cv2.imread('hand.jpg')
img = cv2.cvtColor(hand, cv2.COLOR_BGR2GRAY)
#二值化,有两个返回值,阈值,结果
ret, binary = cv2.threshold(img, 150, 255, cv2.THRESH_BINARY)
#hand_cp = img.copy()
hand_cp2 = img.copy()
contours, hierarchy = cv2.findContours(binary, mode = cv2.RETR_TREE, method=cv2.CHAIN_APPROX_SIMPLE)
#cv2.drawContours(hand_cp, contours, -1, (0, 0, 255), 2)
#cv2.imshow('cat', hand)
#cv2.imshow('contours', hand_cp)
print(len(contours))
#使用多边形逼近,近似模拟手的轮廓
#阈值给20,阈值越大,逼近效果越粗糙
approx = cv2.approxPolyDP(contours[0], 20, closed=True)
#本质还是一个轮廓数据
cv2.drawContours(hand_cp2, [approx], 0, (0, 0, 255), 2)
cv2.imshow('moni', hand_cp)
cv2.waitKey(0)
cv2.destroyAllWindows()
3. 多边形凸包
多边形逼近是高度近似,凸包和多边形逼近很像,只不过它是物体最外层的凸多边形,凸包是指完全包含所有轮廓,并且仅由轮廓上的点构成的多边形。
- 完全包含所有轮廓
- 凸包的每一处都是凸的,在凸包内,任意三个连续点的内角小于180°
- convexHull(points[, hull[, clockwise[r, eturnPoints[]]]])
- points 轮廓
- clockwise 顺时针绘制
import cv2
import numpy as np
hand = cv2.imread('hand.jpg')
img = cv2.cvtColor(hand, cv2.COLOR_BGR2GRAY)
#二值化
ret, binary = cv2.threshold(img, 150, 255, cv2.THRESH_BINARY)
hand_cp2 = img.copy()
contours, hierarchy = cv2.findContours(binary, mode = cv2.RETR_TREE, method=cv2.CHAIN_APPROX_SIMPLE)
#计算凸包
hull = cv2.convexHull(contours[0])
cv2.namedWindow('moni', cv2.WINDOW_NORMAL)
cv2.resizeWindow('moni', 640, 480)
cv2.drawContours(hand_cp2, [hull], 0, (0, 0, 255), 2)
cv2.imshow('moni', hand_cp2)
cv2.waitKey(0)
cv2.destroyAllWindows()
4. 外接矩形
- 最小外接矩形 minAreaRect(points):有可能会有旋转
- 最大外接矩形 boundingRect(points):没有旋转,方方正正,返回4点坐标
import cv2
import numpy as np
cv2.namedWindow('img', cv2.WINDOW_NORMAL)
cv2.resizeWindow('img', 640, 480)
hand = cv2.imread('hand.jpg')
img = cv2.cvtColor(hand, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(img, 150, 255, cv2.THRESH_BINARY)
hand_cp2 = img.copy()
contours, hierarchy = cv2.findContours(binary, mode = cv2.RETR_TREE, method=cv2.CHAIN_APPROX_SIMPLE)
# rect是一个rotated 的矩形,包含起始坐标(x,y),长宽,旋转角度
# 最小外接矩形
rect = cv2.minAreaRect(contours[540])
print(rect)
# 最大外接矩形,方方正正,返回四个点x,y w,h
x,y,w,h = cv2.boundingRect(contours[540])
cv2.rectangle(hand_cp2, (x,y), (x+w, y+h), (0,0,255), 2)
#画出矩形,画旋转矩形有用的
#帮我们计算出旋转矩形的4个坐标点
#坐标为整数
box = cv2.boxPoints(rect)
#四舍五入,再取整
box = np.round(box).astype('int')
cv2.drawContours(hand_cp2, [box], 0, (255,0,0), 2)
cv2.imshow('img', hand_cp2)
cv2.waitKey(0)
cv2.destroyAllWindows()