OpenCV:09车辆统计项目

车辆统计项目

  • 涉及到的内容
  • 大体流程
  • 知识补充
    • 背景减除
  • 具体流程
    • 视频加载
    • 去除背景
    • 形态学识别车辆
    • 对车辆进行统计
    • 显示车辆统计信息

效果图:
OpenCV:09车辆统计项目_第1张图片


涉及到的内容

  • 窗口的展示
  • 图像/视频的加载
  • 基本图像的绘制
  • *车辆识别
    • 基本图像运算与处理
    • 形态学
    • 轮廓查找

大体流程

1. 加载视频
2. 通过形态学识别车辆
3. 对车辆进行统计
4. 显示车辆统计信息


知识补充

背景减除

背景减除(Background Subtraction 是许多基于计算机视觉的任务中主要的预处理步骤。如果我们有完整的静止的背景帧,那么我们可以通过帧差法来计算像素差从而获取到前景对象。

但是在大多数情况下,我们可能没有这样的图像,所以我们需要从我们拥有的任何图像中提取背景。当运动物体有阴影时,由于阴影也在移动,情况会变得更加复杂,为此引入背景减除算法

通过这一方法我们能够从视频中分离出运动的物体前景,从而达到目标检测的目的

  • BackgroundSubtractorMOG
    • 这是一个以混合高斯模型为基础的前景/背景分割算法
    • 它使用K(K = 3 或 5)个高斯分布混合对背景图像进行建模。使用这些颜色(在整个视频中)存在时间的长短作为混合的权重,背景的颜色一般持续时间最长,而且更加静止
    • 在编写代码时,我们需要使用函数cv2.createBackgroundSubtractorMOG2()创建一个背景对象。这个函数有些可选参数,比如要进行建模场景的时间长度,高斯混合成分的数量,阈值等。将它们全部设置为默认值,然后在整个视频中我们是需要使用backgroundsubtractor.apply()就可以得到前景的掩膜
    • 移动的物体会被标记为白色,背景会被标记为黑色的

具体流程

视频加载

关键API:打开摄像头cap = cv2.VideoCapture('./video.mp4');读取数据 ret,frame = cap.read()

# 先进行视频加载 ——> 将视频中的每一帧加载进来
import cv2
import numpy as np

# cap = cv2.VideoCapture(0) # 0表示调用摄像头
cap = cv2.VideoCapture('./video.mp4')

# 循环读取视频帧
while True :
    ret,frame = cap.read()
    
    #如果成功读取到一帧数据
    if ret == True:
        cv2.imshow('video',frame) # 展示这一帧数据到窗口
        
    # 退出
    key = cv2.waitKey(1) # 每隔1ms接受用户按下的按键
    # 用户按‘ESC’即可退出
    if key == 27:
        break
        
# 最后别忘了释放资源
cap.release()
cv2.destroyAllWindows()

去除背景

OpenCV会把不会动的东西(蓝天白云…)分辨出背景;把会动的东西(车辆、行人…)叫做前景

由于我们要检测会动的车辆(前景),所以要把不会动的背景去除掉,用到了背景减除(Background Subtraction ——>效果是: 动的东西(前景)会变成白色 ;不动的东西(背景)会变成黑色

关键API:cv2.createBackgroundSubtractorMOG2() # 直接用 不用写参数

# 去背景 ——> opencv把不会动的东西(蓝天白云...)叫做背景,把会动的(车辆,行人...)叫做前景
# 示例

import cv2
import numpy as np

# cap = cv2.VideoCapture(0) # 0表示调用摄像头
cap = cv2.VideoCapture('./video.mp4') # 调用视频


# 创建一个MOG对象
mog = cv2.createBackgroundSubtractorMOG2() # 直接用 不用写参数

while True:
    ret,frame = cap.read()
    
    if ret == True :
        fgmask = mog.apply(frame) # 将前景掩码 "fgmask" 运用到这一帧图像上
        cv2.imshow('video',fgmask) # 显示识别到的前景fgmask(不是写frame!)
           
    # 退出
    key = cv2.waitKey(100) # 每隔1ms接受用户按下的按键
    # 用户按‘ESC’即可退出
    if key == 27:
        break
        
# 最后别忘了释放资源
cap.release()
cv2.destroyAllWindows()

# 结果:动的东西(前景)会变成白色 ;不动的东西(背景)会变成黑色

结果:
OpenCV:09车辆统计项目_第2张图片


形态学识别车辆

从去背景例程中,我们可以发现有很多白点(噪声),因此我们需要降噪

我们可以发现噪声比较均匀,不适合用中值滤波去除,而是用高斯滤波

关键API:cv2.GaussianBlur(gray,(7,7),5)

降噪:

# 从去背景例程中,我们可以发现有很多白点(噪声),因此我们需要降噪

import cv2
import numpy as np

# cap = cv2.VideoCapture(0) # 0表示调用摄像头
# cap = cv2.VideoCapture('./video.mp4') # 调用视频
cap = cv2.VideoCapture('./car.mp4') # 调用视频


# 创建一个MOG对象
mog = cv2.createBackgroundSubtractorMOG2() # 直接用 不用写参数

# 使用opencv获取卷积核
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))

# 取阈值,当图形轮廓小于阈值时排除掉(认为它不是车) ——> 只有长宽均大于阈值,才认为是一部车
min_w = 90
min_h = 90


while True:
    ret,frame = cap.read()
    
    if ret == True :
        # 把原始帧进行灰度化,然后去噪
        gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
        # 去噪(我们可以发现噪声比较均匀,不适合用中值滤波去除,而是用高斯滤波)
        blur = cv2.GaussianBlur(gray,(7,7),5)
#         blur = cv2.blur(gray,(3,3))
        
       
        # 去除背景
        fgmask = mog.apply(blur) # 将前景掩码 "fgmask" 运用到这一帧图像上
        
        # 腐蚀做进一步去噪
        erode = cv2.erode(fgmask,kernel)
        
        # 经过腐蚀后车也有些被腐蚀了!再膨胀回来
        dilate = cv2.dilate(erode,kernel,iterations = 2) # 做两次迭代,膨胀效果更明显(因为我们要求去噪,并不需要和原图一模一样,因此迭代次数可以不一样)
        
        # 我们发现有很多内部的小方块 ——> 怎么消除?
        # 闭运算
        close = cv2.morphologyEx(dilate,cv2.MORPH_CLOSE,kernel)
        

        # 展示
        cv2.imshow('video',close) # 显示识别到的前景fgmask(不是写frame!)
        
            
    # 退出
    key = cv2.waitKey(1) # 每隔1ms接受用户按下的按键
    # 用户按‘ESC’即可退出
    if key == 27:
        break
        
# 最后别忘了释放资源
cap.release()
cv2.destroyAllWindows()

# 结果:动的东西(前景)会变成白色 ;不动的东西(背景)会变成黑色

结果:
OpenCV:09车辆统计项目_第3张图片

识别车辆轮廓:

关键API: cv2.findContours(close,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)

我们设置一个阈值,如果某一像素面积大于这个阈值,那么就说明这是

降噪后的黑白图中得到轮廓的对应(x,y,w,h)坐标,再用cv2.rectangle(frame,(int(x),int(y)),(int(x+w),int(y+h)),(0,255,255),2)原图中画出

# 从去背景例程中,我们可以发现有很多白点(噪声),因此我们需要降噪

import cv2
import numpy as np

# cap = cv2.VideoCapture(0) # 0表示调用摄像头
# cap = cv2.VideoCapture('./video.mp4') # 调用视频
cap = cv2.VideoCapture('./car.mp4') # 调用视频


# 创建一个MOG对象
mog = cv2.createBackgroundSubtractorMOG2() # 直接用 不用写参数

# 使用opencv获取卷积核
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))

# 取阈值,当图形轮廓小于阈值时排除掉(认为它不是车) ——> 只有长宽均大于阈值,才认为是一部车
min_w = 90
min_h = 90


while True:
    ret,frame = cap.read()
    
    if ret == True :
        # 把原始帧进行灰度化,然后去噪
        gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
        # 去噪(我们可以发现噪声比较均匀,不适合用中值滤波去除,而是用高斯滤波)
        blur = cv2.GaussianBlur(gray,(7,7),5)
#         blur = cv2.blur(gray,(3,3))
        
       
        
        # 去除背景
        fgmask = mog.apply(blur) # 将前景掩码 "fgmask" 运用到这一帧图像上
        
        # 腐蚀做进一步去噪
        erode = cv2.erode(fgmask,kernel)
        
        # 经过腐蚀后车也有些被腐蚀了!再膨胀回来
        dilate = cv2.dilate(erode,kernel,iterations = 2) # 做两次迭代,膨胀效果更明显(因为我们要求去噪,并不需要和原图一模一样,因此迭代次数可以不一样)
        
        # 我们发现有很多内部的小方块 ——> 怎么消除?
        # 闭运算
        close = cv2.morphologyEx(dilate,cv2.MORPH_CLOSE,kernel)
        
        # 识别车辆(对上次形态学操作的结果进行查找轮廓)
        # 查找轮廓
        contours,h  = cv2.findContours(close,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
        
        # 画出所有检测到的轮廓
        for contour in contours: # contours是返回的一个列表,列表中放的是一个个ndarray,也就是轮廓的数据
            # 画最大外界矩形 (要求坐标点都是整数)
            (x,y,w,h) = cv2.boundingRect(contour) # 返回一个值,这一个值包含了最大外接矩形的x,y,w,h四个变量
            # 通过外接矩形的宽高大小来过滤掉小的矩形
#             if w * h < 5000: # 用面积的话误差较大
            if (w < min_w) and (h < min_h) :
                continue
            cv2.rectangle(frame,(int(x),int(y)),(int(x+w),int(y+h)),(0,255,255),2)
        
        # 展示
#         cv2.imshow('video',close) # 显示识别到的前景fgmask(不是写frame!)
        cv2.imshow('frame',frame) # 显示识别到的前景fgmask(不是写frame!)
        
            
    # 退出
    key = cv2.waitKey(1) # 每隔1ms接受用户按下的按键
    # 用户按‘ESC’即可退出
    if key == 27:
        break
        
# 最后别忘了释放资源
cap.release()
cv2.destroyAllWindows()

# 结果:动的东西(前景)会变成白色 ;不动的东西(背景)会变成黑色

对车辆进行统计

我们不可能对整幅图进行统计,那样太乱了,也有很多噪声—— 我们应该画一条线,对经过这条线的车辆进行统计

这就有两个难点了:

1.如何画线,在哪画线

  • 关键API: 画线 cv2.line(frame,(10,line_high),(1200,line_high),(255,255,0),3) # 参数:画在那、起始点、结束点、颜色

2.如何统计

  • 把车抽象为一个点 ——> 即外接矩形的中心点,记录下当前的点,并且用中心点的y坐标和检测线加上偏移量进行对比,如果坐标在区间内,则检测到车辆,否则不算
  • 设置一个空列表cars[ ],以追加cars.append()的方式把每一个点都追加到列表中,后用for循环遍历这些点,对于满足的点剔除掉 cars.remove((x,y)),避免重复
# 我们不可能对整幅图进行统计,那样太乱了,也有很多噪声—— 我们应该画一条线
# 这就有两个难点了:1.如何画线,在哪画线  2.如何统计

import cv2
import numpy as np

# cap = cv2.VideoCapture(0) # 0表示调用摄像头
# cap = cv2.VideoCapture('./video.mp4') # 调用视频
cap = cv2.VideoCapture('./car.mp4') # 调用视频


# 创建一个MOG对象
mog = cv2.createBackgroundSubtractorMOG2() # 直接用 不用写参数

# 使用opencv获取卷积核
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))

# 取阈值,当图形轮廓小于阈值时排除掉(认为它不是车) ——> 只有长宽均大于阈值,才认为是一部车
min_w = 90
min_h = 90

# 车辆检测线的高度
line_high = 450

# 车辆检测线的偏移量
offset = 6 # 车辆检测线比检测线高offset个像素点或低offset个像素点——都算是被检测到

# 设置一个空列表,用于保存汽车外接矩形的中心点
cars = []

# 当前检测到的车辆数
car_num = 0

# 计算外接矩形的中心点
def center(x,y,w,h):
    x1 = int(w / 2)
    y1 = int(h / 2)
    cx = int(x) + x1
    cy = int(y) + y1
    return cx,cy

while True:
    ret,frame = cap.read()
    
    if ret == True :
        # 把原始帧进行灰度化,然后去噪
        gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
        # 去噪(我们可以发现噪声比较均匀,不适合用中值滤波去除,而是用高斯滤波)
        blur = cv2.GaussianBlur(gray,(7,7),5)
#         blur = cv2.blur(gray,(3,3))
        
       
        
        # 去除背景
        fgmask = mog.apply(blur) # 将前景掩码 "fgmask" 运用到这一帧图像上
        
        # 腐蚀做进一步去噪
        erode = cv2.erode(fgmask,kernel)
        
        # 经过腐蚀后车也有些被腐蚀了!再膨胀回来
        dilate = cv2.dilate(erode,kernel,iterations = 2) # 做两次迭代,膨胀效果更明显(因为我们要求去噪,并不需要和原图一模一样,因此迭代次数可以不一样)
        
        # 我们发现有很多内部的小方块 ——> 怎么消除?
        # 闭运算
        close = cv2.morphologyEx(dilate,cv2.MORPH_CLOSE,kernel)
        
        # 识别车辆(对上次形态学操作的结果进行查找轮廓)
        # 查找轮廓
        contours,h  = cv2.findContours(close,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
        
        # 画出车辆检测线(在for循环前就可以去计算了,不然每次循环都要画一次线,内存严重消耗)
        cv2.line(frame,(10,line_high),(1200,line_high),(255,255,0),3) # 参数:画在那、起始点、结束点、颜色
        
        # 画出所有检测到的轮廓
        for contour in contours: # contours是返回的一个列表,列表中放的是一个个ndarray,也就是轮廓的数据
            # 画最大外界矩形 (要求坐标点都是整数)
            (x,y,w,h) = cv2.boundingRect(contour) # 返回一个值,这一个值包含了最大外接矩形的x,y,w,h四个变量
            # 通过外接矩形的宽高大小来过滤掉小的矩形
#             if w * h < 5000: # 用面积的话误差较大
            if (w < min_w) and (h < min_h) :
                continue
            cv2.rectangle(frame,(int(x),int(y)),(int(x+w),int(y+h)),(0,255,255),2)
        
            # 把车抽象为一个点 ——> 即外接矩形的中心点
            # 要通过外界矩形计算矩形中心点 ——> 我们把这段代码独立出来,写成函数
            my_center = center(x,y,w,h)
            cv2.circle(frame,(my_center),5,(0,255,255),-1) # 画出中心点
            
            # 记录下当前的点,并且用中心点的y坐标和检测线加上偏移量进行对比,如果坐标在区间内,则检测到车辆,否则不算
            cars.append(my_center) # 将中心点追加到列表中
            
            # 遍历cars中的点,与检测线的要求进行对比
            for (x , y) in cars:
                if (y > line_high - offset) and (y < line_high + offset): # 落入了有效区间
                    # 计数加1
                    car_num += 1
                    # 统计过的车就remove掉 (如果remove,会出现一部车辆多次计数的情况)
                    cars.remove((x,y))
#                     print(car_num) # 统计结果正常
        
        
        # 展示
#         cv2.imshow('video',close) # 显示识别到的前景fgmask(不是写frame!)
        cv2.imshow('frame',frame) # 显示识别到的前景fgmask(不是写frame!)
        
            
    # 退出
    key = cv2.waitKey(100) # 每隔1ms接受用户按下的按键
    # 用户按‘ESC’即可退出
    if key == 27:
        break
        
# 最后别忘了释放资源
cap.release()
cv2.destroyAllWindows()

结果:
OpenCV:09车辆统计项目_第4张图片

显示车辆统计信息

关键API:cv2.putText(frame,'Vehicle Count:' + str(car_num) ,(200,60),cv2.FONT_HERSHEY_SIMPLEX,2,(0,0,255),3)

总体代码:

# 显示车辆统计信息

import cv2
import numpy as np

# cap = cv2.VideoCapture(0) # 0表示调用摄像头
# cap = cv2.VideoCapture('./video.mp4') # 调用视频
cap = cv2.VideoCapture('./car.mp4') # 调用视频


# 创建一个MOG对象
mog = cv2.createBackgroundSubtractorMOG2() # 直接用 不用写参数

# 使用opencv获取卷积核
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))

# 取阈值,当图形轮廓小于阈值时排除掉(认为它不是车) ——> 只有长宽均大于阈值,才认为是一部车
min_w = 85
min_h = 80

# 车辆检测线的高度
line_high = 465

# 车辆检测线的偏移量
offset = 6 # 车辆检测线比检测线高offset个像素点或低offset个像素点——都算是被检测到

# 设置一个空列表,用于保存汽车外接矩形的中心点
cars = []

# 当前检测到的车辆数
car_num = 0

# 计算外接矩形的中心点
def center(x,y,w,h):
    x1 = int(w / 2)
    y1 = int(h / 2)
    cx = int(x) + x1
    cy = int(y) + y1
    return cx,cy

while True:
    ret,frame = cap.read()
    
    if ret == True :
        # 把原始帧进行灰度化,然后去噪
        gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
        # 去噪(我们可以发现噪声比较均匀,不适合用中值滤波去除,而是用高斯滤波)
        blur = cv2.GaussianBlur(gray,(7,7),5)
#         blur = cv2.blur(gray,(3,3))
        
    
##---------------------------------------------------------------------------------------
# 降噪
        # 去除背景
        fgmask = mog.apply(blur) # 将前景掩码 "fgmask" 运用到这一帧图像上
        
        # 腐蚀做进一步去噪
#         erode = cv2.erode(fgmask,kernel)
        erode = cv2.erode(fgmask,kernel,iterations = 2)
        
        # 经过腐蚀后车也有些被腐蚀了!再膨胀回来
        dilate = cv2.dilate(erode,kernel,iterations = 3) # 做两次迭代,膨胀效果更明显(因为我们要求去噪,并不需要和原图一模一样,因此迭代次数可以不一样)
        
        # 我们发现有很多内部的小方块 ——> 怎么消除?
        # 闭运算
        close = cv2.morphologyEx(dilate,cv2.MORPH_CLOSE,kernel)
#      
##---------------------------------------------------------------------------------------
    
        # 识别车辆(对上次形态学操作的结果进行查找轮廓)
        # 查找轮廓
        contours,h  = cv2.findContours(close,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
        
        # 画出车辆检测线(在for循环前就可以去计算了,不然每次循环都要画一次线,内存严重消耗)
        cv2.line(frame,(10,line_high),(1200,line_high),(255,255,0),3) # 参数:画在那、起始点、结束点、颜色
        
        # 画出所有检测到的轮廓
        for contour in contours: # contours是返回的一个列表,列表中放的是一个个ndarray,也就是轮廓的数据
            # 画最大外界矩形 (要求坐标点都是整数)
            (x,y,w,h) = cv2.boundingRect(contour) # 返回一个值,这一个值包含了最大外接矩形的x,y,w,h四个变量
            # 通过外接矩形的宽高大小来过滤掉小的矩形
#             if w * h < 5000: # 用面积的话误差较大
            if (w < min_w) and (h < min_h) :
                continue
            cv2.rectangle(frame,(int(x),int(y)),(int(x+w),int(y+h)),(0,255,255),2)
        
            # 把车抽象为一个点 ——> 即外接矩形的中心点
            # 要通过外界矩形计算矩形中心点 ——> 我们把这段代码独立出来,写成函数
            my_center = center(x,y,w,h)
            cv2.circle(frame,(my_center),5,(0,255,255),-1) # 画出中心点
            
            # 记录下当前的点,并且用中心点的y坐标和检测线加上偏移量进行对比,如果坐标在区间内,则检测到车辆,否则不算
            cars.append(my_center) # 将中心点追加到列表中
            
            # 遍历cars中的点,与检测线的要求进行对比
            for (x , y) in cars:
                if (y >= line_high - offset) and (y <= line_high + offset): # 落入了有效区间
                    # 计数加1
                    car_num += 1
                    # 统计过的车就remove掉 (如果remove,会出现一部车辆多次计数的情况)
                    cars.remove((x,y))
#                     print(car_num) # 统计结果正常
        
        # 显示车辆统计信息
        cv2.putText(frame,'Vehicle Count:' + str(car_num) ,(200,60),cv2.FONT_HERSHEY_SIMPLEX,2,(0,0,255),3)
        
        # 展示
#         cv2.imshow('video',close) # 显示识别到的前景fgmask(不是写frame!)
        cv2.imshow('frame',frame) 
        
            
    # 退出
    key = cv2.waitKey(10) # 每隔1ms接受用户按下的按键
    # 用户按‘ESC’即可退出
    if key == 27:
        break
        
# 最后别忘了释放资源
cap.release()
cv2.destroyAllWindows()

# 左右两车道分别计数效果会好一点:左边线往下一点 右边线往上一点
# 我们需要不停地挑检测线和检测轮廓的参数

结果:
OpenCV:09车辆统计项目_第5张图片

你可能感兴趣的:(opencv,计算机视觉,python)