MHI(运动历史图)
MHI最初是由Bobick 和 Davis提出的,在此之前,Bobick 和 Davis 首先提出了二值的运动能量图(Motion Energy Image,MEI),通过描述物体如何移动和运动在空间中发生的位置,来进行基于运动的物体识别。运动能量图显示了运动的轮廓和能量的空间分布。在运动能量图的基础上产生了运动历史图(Motion History Image,MHI)。运动历史图是一种基于视觉的模板方法,通过计算时间段内同一位置的像素变化,将目标运动情况以图像亮度的形式表现出来。它是这样的一种图像:其每个像素的灰度值表示了在一组视频序列中该位置像素的最近的运动情况。最后运动的时刻越接近当前帧,该像素的灰度值越高。因此,MHI图像可以表征人体在一个动作过程中最近的动作情况,这使得MHI被广泛应用于动作识别领域。
公式
设H为运动历史图像素的强度值,
H
τ
(
x
,
y
,
t
)
H_\tau (x,y,t)
Hτ(x,y,t)可以由更新函数计算得出:
H
τ
(
x
,
y
,
t
)
=
{
τ
i
f
Ψ
(
x
,
y
,
t
)
=
1
m
a
x
(
0
,
H
τ
(
x
,
y
,
t
−
1
)
−
δ
)
o
t
h
e
r
w
i
s
e
H_\tau (x,y,t)= \begin{cases} \tau & if\ \Psi (x,y,t)=1\\ max(0,H_\tau(x,y,t-1)-\delta )& otherwise \end{cases}
Hτ(x,y,t)={τmax(0,Hτ(x,y,t−1)−δ)if Ψ(x,y,t)=1otherwise
式中,(x, y)和t为像素点的位置及时间;
τ
\tau
τ为持续时间,从帧数的角度决定了运动的时间范围;
δ
\delta
δ为衰退参数。
Ψ
(
x
,
y
,
t
)
\Psi(x, y, t)
Ψ(x,y,t)为更新函数,可由帧间差、图像差分或光流等多种方法定义,其中帧间差法最为常用:
Ψ
(
x
,
y
,
t
)
=
{
1
i
f
D
(
x
,
y
,
t
)
>
ξ
0
o
t
h
e
r
w
i
s
e
\Psi (x,y,t)= \begin{cases} 1&if\ D(x,y,t)>\xi \\ 0& otherwise \end{cases}
Ψ(x,y,t)={10if D(x,y,t)>ξotherwise
其中:
D
(
x
,
y
,
t
)
=
∣
I
(
x
,
y
,
t
)
−
I
(
x
,
y
,
t
±
Δ
)
∣
D (x,y,t)=\left | I(x,y,t)-I(x,y,t\pm\Delta) \right |
D(x,y,t)=∣I(x,y,t)−I(x,y,t±Δ)∣
式中,
I
(
x
,
y
,
t
)
I (x, y, t)
I(x,y,t)为视频图像序列第t帧坐标(x, y)像素点的强度值,
Δ
\Delta
Δ为帧间距离,
ξ
\xi
ξ 为人为给定的差异阈值,随着视频场景的变化而调整。
实现代码:
import numpy as np
import cv2
from time import sleep
import queue
class MHI:
def __init__(self,cap,tau,delta,xi,t):
self.tau=tau
self.delta=delta
self.xi=xi
self.t=t
self.cap=cap
self.data = queue.Queue()
ret,frame=cap.read()
if ret:
for i in range(t):
self.data.put(frame)
self.H = np.zeros(frame.shape)
def getimag(self):
ret,frame=cap.read()
if not ret:
return ret,frame
self.data.put(frame)
old_frame=self.data.get()
a=cv2.addWeighted(old_frame.astype(float),1, frame.astype(float), -1, 0)
D= np.fabs(a)
Psi= D >=self.xi
c=self.H-self.delta
H=np.maximum(0,c)
H[Psi]=self.tau
self.H=H
return ret, H.astype("uint8")
cap = cv2.VideoCapture(0) #
a=MHI(cap,tau=200,xi=20,delta=10,t=1)
while cap.isOpened():
_,frame=a.getimag()
cv2.imshow("out_win", frame)
sleep(0.04)
if cv2.waitKey(1) == ord('q'):
break
cap.release()
cv2.destroyAllWindows()