【openCV学习】38.车辆检测计数项目

一.总体思路:

(一).图像的预处理

这里把视频的每一帧截取出来当作图像逐帧处理

1.图像变成灰度图之后利用高斯滤波去噪(图像通道的转变以及高斯滤波)

                                                变成灰度图后的视频截图

                                                高斯滤波去噪后的视频截图

2.去噪后消除背景,在计算机视觉中,不动的叫背景,移动的叫前景(这里使用到了一个新的API,是关于去除视频背景的)

                                        进行视频背景去除后的视频截图

3.去掉背景后的视频,不动的地方是黑色的,移动的地方会显现出白色,会发现有黑色的背景有时会出现白色的小方块的噪声,因此采用腐蚀操作去除噪声(腐蚀)

4.因为腐蚀操作会把白色的移动过程中的汽车轮廓给缩小,因此还需要用膨胀操作让汽车变大,更加便于之后的车辆检测,由于车越大更加好判断,因此可以把膨胀操作的iterations设为2,让它膨胀两次,这也是不把3和4合并成一次开运算操作的原因(膨胀)

5.经过观察,白色的移动过程中的车辆中会有黑色的小方块的噪声,因此进行一次闭运算来去除,但是效果不太明显(闭运算)

                                        经过腐蚀膨胀以及闭运算后的视频截图

(二).车辆的查找以及绘制车辆轮廓

6.经过上述的处理后,噪声去除的差不多了,可以直接查找轮廓了(轮廓的查找)

7.把所有的轮廓的最大外接矩形都绘制出来(最大外接矩形的绘制)

8.经过观察,会发现,由于车内有车灯,车牌等物件,可能会在大车的轮廓框内包含一些小的车灯的,车牌的轮廓框,因此我们设置当宽和高大于某一个值的时候,才绘制出显示框,这样就可以把车灯和车牌等干扰项给排除掉

(三).车辆的计数

车辆的计数采取的方法是:找每个车辆的轮廓框的中心点,这个中心点经过一条检测线,就表示有一辆车从这条道路上驶过,车辆数目加一

用人眼做到这一点很容易,但是只用openCV,是无法将视频的这一帧和下一帧联系到一起的,只能单独对一张张的图片处理,因此我们设置一个阈值h,当这一帧检测线上下h的范围内存在上述的中心点,就代表这辆车经过了这条线

因此,设置这个阈值还是很重要的,如果设置小了,可能无法捕捉到中心点出现在那个范围内的一帧,如果设置大了,可能这一帧一辆车的中心点在这个范围内,下一帧同一辆车还没走出这个范围,造成重复计数

9.绘制出车辆轮廓的中心点(简单的数学计算以及圆点的绘制)

10.绘制检测线(直线的绘制)

11.车辆计数

12.在屏幕上显示车辆的数目

                                                        最终效果截图

二.背景消除

上述步骤中只有背景消除是前面没有学到的,因此在此记录一下

这里使用MOG算法进行背景消除,使用方法如下:

#1.创建对象

bgs = cv2.createBackgroundSubtractorMOG2()

#2.调用函数即可,其中fream是要处理的图像,是视频加载器read()函数返回的第二个值,返回值fgmask是背景去除后的图像

fgmask = bgs.apply(blur)

 

三.完整代码

import cv2

import numpy as np


"""创建两个变量,分别为检测为车的最小宽度和高度,为最后的车辆检测而使用的"""

min_w = 100#宽度最小为100像素才能认为是车

min_h = 90#高度最小为90像素才能认为是车

"""检测线的位置"""

line_high = 700#检测线画在y坐标为700的地方

"""偏移量"""

offset = 20#一帧视频,在检测线上线20的范围内出现了车辆,认为该车辆经过了检测线

"""用来存放车辆坐标信息的列表"""

cars = []

"""记录车辆的数量"""

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


"""获取腐蚀的卷积核,方便以后去噪(膨胀,腐蚀,闭运算等形态学操作)使用"""

kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))


"""创建去除背景的对象"""

bgs = cv2.createBackgroundSubtractorMOG2()


"""加载视频"""

cap = cv2.VideoCapture("image/video2.mp4")

while True:

    ret,fream = cap.read()

    if ret ==True:

       

        """图像的预处理,可以先去噪再消除背景也可以反过来"""

        #变成灰度图,然后去噪

        gray = cv2.cvtColor(fream,cv2.COLOR_BGR2GRAY)

        #高斯滤波去噪

        blur = cv2.GaussianBlur(gray,(3,3),5)

        #应用bgs来消除背景,经过处理后,不动的背景变成黑色,动的前景变成白色

        fgmask = bgs.apply(blur)

        #经过高斯滤波消除噪声后,还有一些比较大块的噪声,用腐蚀操作消除

        eroad = cv2.erode(fgmask,kernel)

        #腐蚀之后对车的部分造成了影响,再膨胀回来,并且希望车能够膨胀的更大一点,因此膨胀迭代两次,这也是不直接用开运算的原因

        dilate = cv2.dilate(eroad,kernel,iterations=2)

        #消除车内黑色的小噪声,闭运算

        close = cv2.morphologyEx(dilate,cv2.MORPH_CLOSE,kernel)

       

        """画出检测线,绘制都是在原图上绘制了"""

        cv2.line(fream,(10,line_high),(1880,line_high),(255,255,0),3)

       

        "查找并绘制轮廓"

        contours,h = cv2.findContours(close,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)

        for contour in contours:#因为同一帧有多辆车,有多个轮廓,因此需要用for循环这个列表

            #绘制轮廓的最大外界矩形

            (x,y,w,h) = cv2.boundingRect(contour)

            if (w < min_w)|(h < min_h):#只要有一个小于阈值,就认为不是车,continue掉,去判断下一个轮廓

                continue

            #绘制轮廓,其中点的坐标都是整数

            cv2.rectangle(fream,(int(x),int(y)),(int(x+w),int(y+h)),(0,0,255),2)


            """车辆计数"""

            #找到每个矩形的中心点

            cpoint = center(x,y,w,h)

            #cars是一个列表,用于存储当前每个可以判定为车辆 的矩形的中心点

            cars.append(cpoint)

            cv2.circle(fream,(cpoint),5,(0,0,255),-1)

            #如果该中心点在当前这一帧处于判断线附近,可以认为经过了判断线,这个范围offset需要调试

            for (x,y) in cars:

                if y > (line_high-offset) and y < (line_high + offset):

                    num+=1

                    #print(num)

                    cars.remove((x,y))

                   

        """显示文字"""

        cv2.putText(fream,"Vehicle Count:"+str(num),(300,60),cv2.FONT_HERSHEY_SIMPLEX,2,(0,0,255),5)

       

        """最终显示的还是原图,而不是经过背景去除和去噪之后的图,背景去除和去噪只是为了查找车辆的轮廓位置"""

        cv2.imshow("video",fream)

    key = cv2.waitKey(30)

    if key == 27:#esc键

        break

#最后释放资源

cap.release()

cv2.destroyAllWindows()

 

四.缺点

需要比较清晰并且比较干净的视频才行,因为毕竟是只用opencv实现,当地上车辆的影子经过斑马线的时候,可能斑马线会被误判成移动的物体,造成影响

并且需要车辆不是那么密集的视频,因为离得近会因为车辆影子的覆盖造成两辆车判断成一辆车

其次公路上车辆的速度有快有慢,因此偏移量offset无论怎么设都肯定会有重复计数和漏计的情况,只不过一个好的参数能适当减少这种情况

总之只用opencv来做这个东西效果并不好,真要达到很好的效果还是需要yolo加上一些其他的算法才能实现只能起到复习练手的目的,做这个的目的主要是为了复习之前的学习的知识点和练手

  • 55
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

owooooow

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值