本文中我们将用Python3.8结合OpenCV库实现车辆检测和计数系统。最终的效果及原理实现可以参考我的上一篇文章:车辆检测(非监督学习)-1. 原理篇
在运行本文中的代码前,需要先安装如下包:
sk-video==1.1.10
opencv-python==4.5.2.52
视频转换为帧
本文中使用的算法是"帧间差分法",所以第一步就是要把视频转换为帧的集合。
import cv2
def getFrame(sec):
vidcap.set(cv2.CAP_PROP_POS_MSEC, sec*1000)
hasFrames, image = vidcap.read()
if hasFrames:
# 会把帧保存为jpg图片
cv2.imwrite("video/车流_frames/image"+str(count)+".jpg", image)
return hasFrames
vidcap = cv2.VideoCapture('video/车流_1080p30fps.mp4')
sec = 0
frameRate = 0.05 # 每隔0.05s截一次图片
count = 1
success = getFrame(sec)
while success:
count = count + 1
sec = sec + frameRate
sec = round(sec, 2)
success = getFrame(sec)
处理帧
接下来就要按照如下步骤处理图片:
- 读取图片
- 相邻图片进行差分
- 卡像素的阈值,把图片转换为二进制图像
- 利用卷积合并相邻的区域
- 限定检测区域
- 限定区域面积,进一步过滤掉零碎区域
- 统计车辆
- 把处理后的图像恢复为RGB图片并进行保存
- 把保存的图片转换为视频
具体代码实现如下
# 导入需要的库
import os
import re
import cv2 # opencv library
import numpy as np
from os.path import isfile, join
import matplotlib.pyplot as plt
# 读取图片
col_frames = os.listdir('video/车流_frames/')
col_frames.sort(key=lambda f: int(re.sub('\D', '', f)))
col_images = []
for i in col_frames:
img = cv2.imread('video/车流_frames/'+i)
col_images.append(img)
# 定义卷积核
kernel = np.ones((64, 64), np.uint8)
# 字体
font = cv2.FONT_HERSHEY_SIMPLEX
# 输出的图像路径
pathIn = "output/车流_frames/"
for i in range(len(col_images)-1):
# 把RGB图像转换为Gray图像
grayA = cv2.cvtColor(col_images[i], cv2.COLOR_BGR2GRAY)
grayB = cv2.cvtColor(col_images[i+1], cv2.COLOR_BGR2GRAY)
# 相邻图像进行差分
diff_image = cv2.absdiff(grayB, grayA)
# 卡像素的阈值,把图片转换为二进制图像
ret, thresh = cv2.threshold(diff_image, 128, 255, cv2.THRESH_BINARY)
# 利用卷积合并相邻的区域
dilated = cv2.dilate(thresh, kernel, iterations=1)
# 得到轮廓
contours, hierarchy = cv2.findContours(
dilated.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
# 划定检测区域,并过滤碎片化的轮廓
valid_cntrs = []
for cntr in contours:
x, y, w, h = cv2.boundingRect(cntr)
if (x <= 820) & (y >= 750) & (cv2.contourArea(cntr) >= 150):
if (y >= 900) & (cv2.contourArea(cntr) < 200):
break
valid_cntrs.append(cntr)
# 把轮廓添加到图像中
dmy = col_images[i].copy()
cv2.drawContours(dmy, valid_cntrs, -1, (127, 200, 0), 2)
cv2.putText(dmy, "vehicles detected: " + str(len(valid_cntrs)),
(55, 100), font, 1.5, (0, 180, 0), 5)
cv2.line(dmy, (0, 750), (1919, 750),
color=(255, 0, 0), thickness=8)
cv2.imwrite(pathIn+str(i)+'.png', dmy)
pathOut = 'output/车流counter.mp4' # 输出的视频路径
fps = 20.0 # 每秒播放多少帧
frame_array = []
files = [f for f in os.listdir(pathIn) if isfile(join(pathIn, f))]
files.sort(key=lambda f: int(re.sub('\D', '', f)))
for i in range(len(files)):
filename = pathIn + files[i]
img = cv2.imread(filename)
height, width, layers = img.shape
size = (width, height)
frame_array.append(img)
# 把输出的图片转换为视频
out = cv2.VideoWriter(pathOut, cv2.VideoWriter_fourcc(*'DIVX'), fps, size)
for i in range(len(frame_array)):
out.write(frame_array[i])
out.release()