图像轮廓
图像轮廓概述
-
图像轮廓是具有相同颜色或强度的连续点的曲线
-
作用:
- 可以用于图形分析
- 物体的识别与检测
-
注意:
- 为了检测的准确性,需要先对图像进行二值化或Canny操作
- 画轮廓时会修改输入的图像
-
轮廓查找API:
-
findContours(img,mode,ApproximationMode,…)
- mode:模式,对查找到的轮廓如何处置
- RETR_EXTERNAL = 0, 表示只检测外轮廓
- RETR_LIST = 1,检测的轮廓不建立等级关系
- RETR_CCOMP = 2,每层最多两级
- RETR_TREE = 3,按树形存储轮廓
- ApproximationMode:近似模式
- CHAIN_APPROX_NODE,保存所有轮廓上的点
- CHAIN_APPROX_SIMPLE,只保存角点
- mode:模式,对查找到的轮廓如何处置
-
有两个返回值:contours(所有轮廓)和hierarchy(层级关系)
查找轮廓
#转变成单通道
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)
绘制轮廓
- drawContours(img,contours,contourIdx,color,thichness…)
- contourIdx轮廓顺序号,-1表示绘制所有轮廓
- color轮廓颜色
- thichness线宽,-1是全部填充
#绘制轮廓
cv2.drawContours(img,contours,-1,(0,0,255),1)
轮廓的面积和周长
- 轮廓的面积contourArea(contour)
- 轮廓的周长arcLength(curve,closed)
- curve轮廓曲线
- closed是否是闭合的轮廓
#计算面积
area = cv2.contourArea(contours[0])
print("area = %d"%(area))
#计算周长
len = cv2.arcLength(contours[0],true)
多边形逼近与凸包
- 多边形逼近,可凸可凹,更接近真实物体
- 多边形逼近作用:减少了存储的数据量,同时将特征点描述出来
- 特征点可多可少,精度调高,逼近更真实;反之则数据量更少
- approxPolyDP(curve,epsilon,closed)
- curve 曲线、轮廓
- epsilon 精度
- closed 是否是闭合曲线
e = 20 #设置精度
approx = cv2.approxPolyDP(contours[0],e,True)
#画线函数
def drawShape(src,points):
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,0,255),3)
else:
x,y = points[i][0]
x1,y1 = points[i+1][0]
cv2.line(src,(x,y),(x1,y1),(0,0,255),3)
i = i + 1
#绘制
drawShape(img,approx)
- 凸包:只能凸出,不会下凹,只画出大致轮廓
- convexHull(points,clockwise,…)
- points 轮廓
- clockwise 顺时针绘制
hull = cv2.convexHull(contours[0])
drawShape(img,hull)
外接矩阵
- 最小外接矩阵 红框 知道物体有没有旋转,以及旋转角度
- minAreaRect(points)
- points 轮廓
- 返回值:RotatedRect
- x,y 起始点
- width,height
- angle 旋转角度
- 最大外接矩阵 绿框
- boundingRect(array)
- array 轮廓
- 返回值:Rect
- x,y 起始点
- width,height
r = minAreaRect(contours[1])
box = cv2.boxPoints(r) #拿到起始点和宽高
box = np.int0(box) #将浮点型box转换为int型
cv2.drawContours(img,[box],0,(0,0,255),2)
x,y,w,h = cv2.boundingRect(contours[1])
cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
[实战]车辆统计
项目总览
涉及到的内容
- 窗口的展示
- 图像/视频的加载
- 基本图像的绘制
- 车辆识别
- 基本图像的运算与处理
- 形态学
- 轮廓查找
涉及到的知识点
- 加载视频
- 通过形态学识别车辆
- 对车辆进行统计
- 显示车辆的统计信息
加载视频
import cv2
import numpy as np
#加载视频
cap = cv2.VideoCapture('video.mp4')
#视频由很多帧组成 循环读取
while True:
ret, frame = cap.read() #ret读取视频帧是否成功,frame读取视频帧
if(ret == True):
cv2.imshow('video',frame)
key = cv2.waitKey(1)
if(key == 27): #27为ESC键
break
#释放资源
cap.release()
cv2.destroyAllWindows()
去除背景
- createBackgroundSubtractorMOG(…)
- history = 200
bgsubmog = cv2.createBackgroundSubtractorMOG()
形态学处理
#腐蚀,去除图中小斑块
erode = cv2.eroed(mask,kernel)
#膨胀,还原放大
dilate = cv2.dilate(erode,kernel,iterations = 3)
#闭操作,去除物体内部的小块
close = cv2.morphologyEx(dilate,cv2.MORPH_CLOSE,kernel)
close = cv2.morphologyEx(close,cv2.MORPH_CLOSE,kernel)
#绘制轮廓
cnts, h = cv2.findContours(close,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
for(i,c) in enumerate(cnts):
(x,y,w,h) = cv2.boundingRect(c)
cv2.rectangle(frame,(x,y),(x+w,y+h),(0,0,255),2)
逻辑处理 车辆统计
#对车辆的宽高进行判断
#以验证是否是有效的车辆
isValid = (w>=min_w) and (h>=min_h)
if(not isValid):
continue
#到这里都是有效的车
cv2.rectangle(frame,(x,y),(x+w,y+h),(0,0,255),2)
#取出车的中心点
cpoint = center(x,y,w,h)
cars.append(cpoint)
#中心点落在线内,统计
for (x,y) in cars:
#要有一条线,有范围,从数组中减去
if((y > line_high - offset) and (y < line_high + offset)):
carno += 1
cars.remove((x,y))
print(carno)
显示车辆统计信息
cv2.putText(frame,"Cars Count:" + str(carno),(500,60),cv2.FONT_HERSHEY_SIMPLEX,2,(255,0,0),5)
- 同时进行线高、偏移等参数,使其检测尽可能准确
- 后期完善:进行深度学习的数据训练
完整代码
import cv2
import numpy as np
min_w = 90
min_h = 90
#检测线的高度
line_high = 550
#线的偏移
offset = 7
#统计车的数量
carno = 0
#存放有效车辆的数组
cars = []
#获取中心点
def center(x,y,w,h):
x1 = int(w/2)
y1 = int(h/2)
cx = x + x1
cy = y + y1
return cx,cy
#加载视频
cap = cv2.VideoCapture('video.mp4')
bgsubmog = cv2.createBackgroundSubtractorMOG()
#形态学kernel
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))
#视频由很多帧组成 循环读取
while True:
ret, frame = cap.read() #ret读取视频帧是否成功,frame读取视频帧
if(ret == True):
#灰度化
cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
#高斯去噪点
blur = cv2.GuassianBlur(frame,(3,3),5)
#去背景
mask = bgsubmog.apply(blur)
#腐蚀,去除图中小斑块
erode = cv2.eroed(mask,kernel)
#膨胀,还原放大
dilate = cv2.dilate(erode,kernel,iterations = 3)
#闭操作,去除物体内部的小块
close = cv2.morphologyEx(dilate,cv2.MORPH_CLOSE,kernel)
close = cv2.morphologyEx(close,cv2.MORPH_CLOSE,kernel)
#绘制轮廓
cnts, h = cv2.findContours(close,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
cv2.line(frame,(10,line_high),(1200,line_hjgh),(255,255,0),3)
for(i,c) in enumerate(cnts):
(x,y,w,h) = cv2.boundingRect(c)
#对车辆的宽高进行判断
#以验证是否是有效的车辆
isValid = (w>=min_w) and (h>=min_h)
if(not isValid):
continue
#到这里都是有效的车
cv2.rectangle(frame,(x,y),(x+w,y+h),(0,0,255),2)
#取出车的中心点
cpoint = center(x,y,w,h)
cars.append(cpoint)
#中心点落在线内,统计
for (x,y) in cars:
#要有一条线,有范围,从数组中减去
if((y > line_high - offset) and (y < line_high + offset)):
carno += 1
cars.remove((x,y))
print(carno)
cv2.putText(frame,"Cars Count:" + str(carno),(500,60),cv2.FONT_HERSHEY_SIMPLEX,2,(255,0,0),5)
cv2.imshow('video',framw)
key = cv2.waitKey(1)
if(key == 27): #27为ESC键
break
#释放资源
cap.release()
cv2.destroyAllWindows()