【OpenCV学习】(九)目标识别之车辆检测与计数 | 您所在的位置:网站首页 › 输入车辆 › 【OpenCV学习】(九)目标识别之车辆检测与计数 |
【OpenCV学习】(九)目标识别之车辆检测及计数
背景
本篇将具体介绍一个实际应用项目——车辆检测及计数,在交通安全中是很重要的一项计数;当然,本次完全采用OpenCV进行实现,和目前落地的采用深度学习的算法并不相同,但原理是一致的;本篇将从基础开始介绍,一步步完成车辆检测计数的项目; 一、图像轮廓本质:具有相同颜色或强度的连续点的曲线; 作用: 1、可用于图形分析; 2、应用于物体的识别与检测; 注意点: 1、为了检测的准确性,需要先对图像进行二值化或Canny操作; 2、画轮廓的时候回修改输入的图像,需要先深拷贝原图; 轮廓查找的函数原型: findContours(img,mode,ApproximationMode…) mode RETR_EXTERNAL=0,表示只检测外轮廓; RETR_LIST=1,检测的轮廓不建立等级关系;(常用) RETR_CCOMP=2,每层最多两级; RETR_TREE=3,按树形结构存储轮廓,从右到左,从大到小;(常用) ApproximationMode CHAIN_APPROX_BOBE:保存轮廓上所有的点; CHAIN_APPROX_SIMPLE:只保存轮廓的角点; 代码实战: img = cv2.imread('./contours1.jpeg') # 转变成单通道 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 二值化 ret, binary = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY) # 轮廓查找 contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)contours输出结果: (array([[[ 0, 0]], [[ 0, 435]], [[345, 435]], [[345, 0]]], dtype=int32),)可以看出,我们找最外层轮廓,找出了一个矩形轮廓的四个点; 当然,我们不需要通过画形状来绘制轮廓,可以通过一个内置函数来绘制轮廓; 绘制轮廓函数原型: drawContours(img,contours,contoursIdx,color,thickness,…) contours:表示保存轮廓的数组;contoursIdx:表示绘制第几个轮廓,-1表示所有轮廓;代码案例: img = cv2.imread('./contours1.jpeg') img2 = img.copy() # 转变成单通道 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) cv2.drawContours(img2, contours, -1, (0, 0, 255), -1) cv2.imshow('org', img) cv2.imshow('org2', img2) cv2.waitKey(0)如上图所示,左图是线宽设置为1,右图为线宽设置为-1,也就是填充的效果; 当然,OpenCV还提供了计算轮廓周长和面积的方法; 轮廓面积函数原型: contourArea(contour) 轮廓周长函数原型: arcLength(curve,closed) curve:表示轮廓;closed:是否是闭合的轮廓;上述两个函数比较简单,在这就不做代码演示了; 二、多边形逼近与凸包多边形逼近函数原型: approxPolyDP(curve,epsilon,closed) epslion:精度;凸包的函数原型: convexHull(points,clockwise,…) points:轮廓;clockwise:绘制方向,顺时针或逆时针;(不重要)首先我们看一下基于轮廓查找输出的轮廓形状: 可以看出轮廓点十分密集,接下来看一下基于多变形逼近和凸包的效果: 代码案例: img = cv2.imread('./hand.png') img2 = img.copy() # 转变成单通道 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 二值化 ret, binary = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY) # 轮廓查找 contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # cv2.drawContours(img, contours, -1, (0, 0, 255), 1) e = 20 approx = cv2.approxPolyDP(contours[0], e, True) # 多边形逼近 approx = (approx, ) cv2.drawContours(img, approx, 0, (0, 0, 255), 3) hull = cv2.convexHull(contours[0]) hull = (hull, ) cv2.drawContours(img2, hull, 0, (0, 0, 255), 3) # 凸包 cv2.imshow('org', img) cv2.imshow('org2', img2) cv2.waitKey(0)这里需要注意一点,绘制轮廓的函数对于轮廓的传入需要为元组,需要将得到的数组放到一个元组中! 当然,多边形逼近这里设置的精度为20,所以比较粗糙,设置小一些可以达到更好的效果; 三、外接矩形外接矩阵分为最大外接矩阵和最小外接矩阵,如下图所示: 最小外接矩阵还有一个功能,就是计算旋转角度,从上图的绿框应该可以很明显看出; 最小外接矩阵函数原型: minAreaRect(points) 返回值:起始点(x,y)、宽高(w,h)、角度(angle) 最大外接矩形函数原型: boundingRect(array) 返回值:起始点(x,y)、宽高(w,h) 代码案例: img = cv2.imread('./hello.jpeg') img2 = img.copy() # 转变成单通道 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) # 获取最小外接矩形 r = cv2.minAreaRect(contours[1]) box = cv2.boxPoints(r) # 提取其中的点 box = np.int0(box) # 将浮点型转换为整型 cv2.drawContours(img, (box, ), 0, (0, 0, 255), 2) # 获取最大外接矩形 x, y, w, h = cv2.boundingRect(contours[1]) cv2.rectangle(img2, (x, y), (x+w, y+h), (0, 0, 255), 2) cv2.imshow('org', img) cv2.imshow('org2', img2) cv2.waitKey(0)涉及的知识点: 窗口展示图像、视频的加载基本图形的绘制基本图像运算与处理形态学轮廓查找实现流程: 加载视频 —— 通过形态学识别车辆 —— 对车辆进行统计 —— 显示统计信息 1、加载视频这里就是一个简单加载视频的实现: cap = cv2.VideoCapture('video.mp4') while True: ret, frame = cap.read() if(ret == True): cv2.imshow('video', frame) key = cv2.waitKey(1) if(key == 27): # Esc退出 break cap.release() cv2.destroyAllWindows() 2、去除背景函数原型: createBackgroundSubtractorMOG() history:缓冲,表示多少毫秒,可不指定参数,用默认的即可;具体实现原理比较复杂,用到了一些视频序列关联信息,把像素值不变的认为是背景; 注意:在opencv中已经不支持该函数,而是用createBackgroundSubtractorMOG2()替代;如果需要使用可以安装opencv_contrib模块,在其中的bgsegm中保留了该函数; 代码实现: cap = cv2.VideoCapture('video.mp4') bgsubmog = cv2.bgsegm.createBackgroundSubtractorMOG() while True: ret, frame = cap.read() if(ret == True): # 灰度处理 cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 高斯去噪 blur = cv2.GaussianBlur(frame, (3, 3), 5) mask = bgsubmog.apply(blur) cv2.imshow('video', mask) key = cv2.waitKey(1) if(key == 27): # Esc退出 break cap.release() cv2.destroyAllWindows()这里尽量采用旧版的MOG函数,新版的MOG2函数比较精细,会将树叶等信息输出,去除效果没那么好; 3、形态处理这里主要是为了处理一些小的噪声点以及目标中的黑色块; 代码实现: cap = cv2.VideoCapture('video.mp4') bgsubmog = cv2.bgsegm.createBackgroundSubtractorMOG() # 形态学kernel kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) while True: ret, frame = cap.read() if(ret == True): # 灰度处理 cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 高斯去噪 blur = cv2.GaussianBlur(frame, (3, 3), 5) mask = bgsubmog.apply(blur) # 腐蚀 erode = cv2.erode(mask, kernel) # 膨胀 dilate = cv2.dilate(erode, kernel, 3) # 闭操作 close = cv2.morphologyEx(dilate, cv2.MORPH_CLOSE, kernel) close = cv2.morphologyEx(close, cv2.MORPH_CLOSE, kernel) contours, h = cv2.findContours(close, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE,) for (i, c) in enumerate(contours): (x, y, w, h) = cv2.boundingRect(c) cv2.rectangle(frame, (x, y), (x+w, y+h), (0,0,255), 2) cv2.imshow('video', frame) key = cv2.waitKey(1) if(key == 27): # Esc退出 break cap.release() cv2.destroyAllWindows()从图中效果来看,还是会有很多小的检测框,接下来就是处理重合检测框以及去掉一些多余的检测框,类似于NMS去重,当然原理还不太一样; 4、车辆统计首先需要过滤一些小的矩形,已经检测框的长和宽,设定一些阈值即可; 代码实现: cap = cv2.VideoCapture('video.mp4') bgsubmog = cv2.bgsegm.createBackgroundSubtractorMOG() # 保存车辆中心点信息 cars = [] # 统计车的数量 car_n = 0 # 形态学kernel kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) while True: ret, frame = cap.read() if(ret == True): # 灰度处理 cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 高斯去噪 blur = cv2.GaussianBlur(frame, (3, 3), 5) mask = bgsubmog.apply(blur) # 腐蚀 erode = cv2.erode(mask, kernel) # 膨胀 dilate = cv2.dilate(erode, kernel, 3) # 闭操作 close = cv2.morphologyEx(dilate, cv2.MORPH_CLOSE, kernel) close = cv2.morphologyEx(close, cv2.MORPH_CLOSE, kernel) contours, h = cv2.findContours(close, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE,) # 画一条线 cv2.line(frame, (10, 550), (1200, 550), (0, 255, 255), 3) for (i, c) in enumerate(contours): (x, y, w, h) = cv2.boundingRect(c) # 过滤小的检测框 isshow = (w >= 90) and (h >= 90) if(not isshow): continue # 保存中心点信息 cv2.rectangle(frame, (x, y), (x+w, y+h), (0,0,255), 2) centre_p = (x + int(w/2), y + int(h/2)) cars.append(centre_p) cv2.circle(frame, (centre_p), 5, (0,0,255), -1) for (x, y) in cars: if(593 |
CopyRight 2018-2019 实验室设备网 版权所有 |