图像基础22 运动侦测

来源:互联网 发布:金庸 知乎 编辑:程序博客网 时间:2024/05/17 23:14

本文学习资源来自《机器学习实践指南》

定义

运动侦测,英文翻译为“Motion detection technology”,一般也叫移动检测,常用于无人值守监控录像和自动报警。通过摄像头按照不同帧率采集得到的图像会被CPU按照一定算法进行计算和比较,当画面有变化时,如有人走过,镜头被移动,计算比较结果得出的数字会超过阈值并指示系统能自动作出相应的处理。
—–百度百科

视频采集

示例中首先设置捕获设备,然后每15毫秒更新一次画面显示,并检测是否退出。

# -*- coding: utf-8 -*-import cv2mycap = cv2.VideoCapture(0)id=0while True:    ret , im=mycap.read()    cv2.imshow('myvideo',im)    # 每15毫秒采集1次    key = cv2.waitKey(15)    # 空格键退出    if key ==32:        break    elif key == ord('c'):        cv2.imwrite('vd_'+str(id)+'.png',im)        id += 1

每次按c键后,采集连接图像。

图像序列数组

可按帧生成视频图像序列数组。

# -*- coding: utf-8 -*-import cv2import numpy as npmycap = cv2.VideoCapture(0)myframes = []while True:    ret, im=mycap.read()    cv2.imshow('myvideo',im)    # 采集    myframes.append(im)    # 每15ms采集1次    key = cv2.waitKey(15)    # 空格键退出    if key ==32:        break# 生成视频帧数组myframes = np.array(myframes)print(myframes)

读取视频文件

mycapture = cv2.VideoCapture("视频文件")

差分算法

差分检测根据当前图像与参考图像的差别分析来判断序列图像中是否有运动的物体。在环境亮度变化不大的情况下,如果对应像素灰度值的差异小于某个阈值,则认为画面静止无运动变化,如果图像区域某处的灰度变化大于某个阈值,则认为这是由于图像中运动 的物体所引起的,然后求出运动目标在图像中的位置。

  1. 基于相邻帧差的算法:
    将前后两帧图像对应像素点的灰度值相减;
  2. 基于背景图像与当前帧差的算法:
    将当前帧和背景帧对应像素的灰度值相减;

基于相邻帧差的算法

帧差法的特点是实现简单,运算速度快,对于动态环境自适应性是很强的,对光线的变化不是十分的敏感。但是在运动体内易产生空洞.特别是目标运动速度较快时,影响目标区域准确提取。我们以年辆检测为例,车辆检测除了要检测出运动车辆.同时还要检测出暂时停止的车辆,在这个方面,此类方法无能为力。而且如果车辆的体积较大,那么车辆在前后帧中根容易产生重叠部分,尤其是大货车,这使得帧问差分的结果主要为车头和车尾。车辆中间部分的差分值相对报小.形成空洞,不利于检测。

算法过程:
1. 将当前帧的灰度图像与前一帧的灰度图像相减,得到差分值;当差分值大于某个阈值时,将差分矩阵的相应位置设置为1,否则设置为0.
差分矩阵的计算公式:
D(i,j)=

其中,fk(i,j) 表示第 k 个图像矩阵(i,j) 处的灰度值,D(i,j) 表示差分矩阵(i,j) 处的值。
2. 如果差分矩阵不全为0,则表示检测到运动 。如果有必要,可按一定的运动检测次数对运动画面进行分组,并融合叠加形成多组运动轨迹图。
3. 取下一帧图像,转到(1),直到所有帧图像全部处理退出。

输入图片示例:
这里写图片描述

程序示例

# -*- coding: utf-8 -*-import cv2import numpy as npfn=[]for i in range(7):    fn.append("vd_"+str(i) + ".png")img = []    # 灰度图像数组colorimg =[]  # 彩色图像数组myimg =[]   # 临时变量for i in range(len(fn) -1):    tmpimg = (cv2.imread(fn[i]))    colorimg.append(tmpimg)    myimg = cv2.cvtColor(tmpimg,cv2.COLOR_BGR2GRAY)    img.append(myimg)# 差分计算myimg1 = colorimg[0].copy()myimg2 = myimg1.copy()w = myimg1.shape[1]h = myimg1.shape[0]moveimg1 = np.zeros((h, w),np.uint8)moveimg2 = np.zeros((h,w,3),np.uint8)for ii in range(len(fn)-2):    print (u'开始分析第' + str(ii+2) + u'个运动图像')    myd = img[ii+1] -img[ii]    # 生成差分矩阵    THRESHOLD = int(np.median(abs(myd))) # 取中位数做为阈值    mymove = np.ones([h,w],np.uint8)    for i in range(h):        for j in range(w):            if abs(myd[i,j]) < THRESHOLD or myd[i,j]==0:                mymove[i,j]=0    # 如果有物体运动    if np.sum(mymove)>0:        print(u"第"+str(ii+2)+u"个运动图像发生了变化 ")        moveimg1 = img[ii+1]*(1-mymove)*0.16+img[ii]*(1-mymove)*0.16+moveimg1        moveimg2 = colorimg[ii+1]*0.16 + colorimg[ii]*0.16+moveimg2        moveimg1 = np.array(moveimg1,np.uint8)        moveimg2 = np.array(moveimg2,np.uint8)# 显示移动部分moveimg1 = moveimg1-img[0]# 灰度图二值化以更好地显示运动的效果retval, showimg1 = cv2.threshold(moveimg1, 140,255,cv2.THRESH_BINARY)showimg1=moveimg1showimg2=moveimg2cv2.imshow("move1", showimg1)cv2.imshow("move2", showimg2)cv2.waitKey()cv2.destroyAllWindows()

这里写图片描述

基于背景图像与当前帧差的算法

# -*- coding: utf-8 -*-import cv2import numpy as npfn=[]for i in range(7):    fn.append("vd_"+str(i) + ".png")img = []    # 灰度图像数组colorimg =[]  # 彩色图像数组myimg =[]   # 临时变量for i in range(len(fn) -1):    tmpimg = (cv2.imread(fn[i]))    colorimg.append(tmpimg)    myimg = cv2.cvtColor(tmpimg,cv2.COLOR_BGR2GRAY)    img.append(myimg)# 差分计算myimg = colorimg[0].copy()w = myimg.shape[1]h = myimg.shape[0]moveimg = np.zeros((h, w, 3),np.uint8)for ii in range(len(fn)-2):    print (u'开始分析第' + str(ii+2) + u'个运动图像')    myd = img[ii+1] -img[0]    # 生成差分矩阵    THRESHOLD = int(np.median(abs(myd))) # 取中位数做为阈值    mymove = np.ones([h,w],np.uint8)    for i in range(h):        for j in range(w):            if abs(myd[i,j]) < THRESHOLD or myd[i,j]==0:                mymove[i,j]=0    # 如果有物体运动    if np.sum(mymove)>0:        print(u"第"+str(ii+2)+u"个运动图像发生了变化 ")        moveimg = colorimg[ii+1]*0.16 + colorimg[ii] * 0.16 + moveimg        moveimg = np.array(moveimg, np.uint8)# 显示移动部分showimg = moveimgcv2.imshow("move", showimg)cv2.waitKey()cv2.destroyAllWindows()

运行结果:
这里写图片描述

 光流法

光流的概念是Gibson在1950年首先提出来的。它是空间运动物体在观察成像平面上的像素运动的瞬时速度,是利用图像序列中像素在时间域上的变化以及相邻帧之间的相关性来找到上一帧跟当前帧之间存在的对应关系,从而计算出相邻帧之间物体的运动信息的一种方法。一般而言,光流是由于场景中前景目标本身的移动、相机的运动,或者两者的共同运动所产生的。其计算方法可以分为三类:
(1)基于区域或者基于特征的匹配方法;
(2)基于频域的方法;
(3)基于梯度的方法;
简单来说,光流是空间运动物体在观测成像平面上的像素运动的“瞬时速度”。光流的研究是利用图像序列中的像素强度数据的时域变化和相关性来确定各自像素位置的“运动”。研究光流场的目的就是为了从图片序列中近似得到不能直接得到的运动场。
光流法的前提假设:
(1)相邻帧之间的亮度恒定;
(2)相邻视频帧的取帧时间连续,或者,相邻帧之间物体的运动比较“微小”;
(3)保持空间一致性;即,同一子图像的像素点具有相同的运动
这里有两个概念需要解释:
运动场,其实就是物体在三维真实世界中的运动;
光流场,是运动场在二维图像平面上的投影。

示例代码:

# -*- coding: utf-8 -*-import cv2from numpy import *def draw_flow(im,flow,step=16):    h,w = im.shape[:2]    y,x = mgrid[step/2:h:step,step/2:w:step].reshape(2,-1)    fx,fy = flow[y,x].T    # create line endpoints    lines = vstack([x,y,x+fx,y+fy]).T.reshape(-1,2,2)    lines = int32(lines)    # create image and draw    vis = cv2.cvtColor(im,cv2.COLOR_GRAY2BGR)    for (x1,y1),(x2,y2) in lines:        cv2.line(vis,(x1,y1),(x2,y2),(0,255,0),1)        cv2.circle(vis,(x1,y1),1,(0,255,0), -1)    return vis# 设置需要采集视频的设备ID为0,从第0个摄像头采集cap = cv2.VideoCapture(0)# 前一个图像ret,im = cap.read()prev_gray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)while True:    # get grayscale image    ret, im = cap.read()    gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)    # compute flow    # flow = cv2.calcOpticalFlowFarneback(prev_gray,gray,None,0.5,3,15,3,5,1.2,0)    flow = cv2.calcOpticalFlowFarneback(prev_gray, gray, float(0), float(0), 3, 15, 3, 5, float(1), 0)    prev_gray = gray    # plot the flow vectors    cv2.imshow('Optical flow', draw_flow(gray, flow))    if cv2.waitKey(10) == 32:        break

代码在python3下未执行成功。
参考:
http://blog.csdn.net/carson2005/article/details/7581642
https://baike.baidu.com/item/%E5%85%89%E6%B5%81%E6%B3%95/180688?fr=aladdin
http://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_video/py_lucas_kanade/py_lucas_kanade.html