近年来,运动检测技术已成为计算机视觉的重要研究领域之一。视频序列上已经发明了许多方法,其中一些方法比其他方法更好。在本文中,我们将解释并在 OpenCV 上实现一些基本方法。
1. 帧差分
帧差分背后的想法非常简单。我们逐像素检查两个视频帧之间的差异。如果有运动,像素值就会发生变化,因此我们将获得运动图。
很简单对吗?但是,由于噪声(例如照明的变化)可能会发生一些像素值变化,为了避免在我们的运动蒙版中捕获噪声,我们应用了一个阈值,该阈值基本上会突出强度方面的大变化并丢弃小的变化。请注意,阈值没有正确的选择,通常是凭经验完成的。
现在我们理解了这个概念,让我们展示一些代码:
frames=[]
MAX_FRAMES = 1000
N = 2
THRESH = 60
ASSIGN_VALUE = 255 #Value to assign the pixel if the threshold is met
cap = cv2.VideoCapture(0) #Capture using Computer's Webcam
for t in range(MAX_FRAMES):
#Capture frame by frame
ret, frame = cap.read()
#Convert frame to grayscale
frame_gray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
#Append to list of frames
frames.append(frame_gray)
if t >= N:
#D(N) = || I(t) - I(t+N) || = || I(t-N) - I(t) ||
diff = cv2.absdiff(frames[t-N], frames[t])
#Mask Thresholding
threshold_method = cv2.THRESH_BINARY
ret, motion_mask = cv2.threshold(diff, THRESH, ASSIGN_VALUE, threshold_method)
#Display the Motion Mask
cv2.imshow('Motion Mask', motion_mask)
这种方法具有计算性能,但是它存在两个主要缺点:前景光圈和由帧速率和物体速度引起的重影。
Kameda 和 Minoh 开发的一种解决方案是双重差分,我们在时间 t 和 t-1 以及 t-1 和 t-2 之间的两个帧之间操作阈值差异,然后将它们与逻辑 AND 结合以确保我们始终检测到一个对象而不是它的重影。
帧差分的另一个问题是,当对象停止移动时,它不再被检测到。这显然取决于我们想要完成的任务,但是假设我们想要继续检测移动物体,即使它停止了一段时间。这个问题的一个答案是背景减法技术。
2. 背景减法
背景减法是一种广泛使用的方法,用于检测静态摄像机帧序列中的移动对象。它需要一个参考图像来播放背景(通常在没有对象的情况下获取)。然后我们计算当前帧和背景帧(参考图像)之间的差异。其主要任务是检测通常代表运动物体的前景。
background = None
MAX_FRAMES = 1000
THRESH = 60
ASSIGN_VALUE = 255
cap = cv2.VideoCapture(0)
for t in range(MAX_FRAMES):
# Capture frame-by-frame
ret, frame = cap.read()
# Convert frame to grayscale
frame_gray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
if t == 0:
# Train background with first frame
background = frame_gray
else:
# Background subtraction
diff = cv2.absdiff(background, frame_gray)
# Mask thresholding
ret, motion_mask = cv2.threshold(diff, THRESH, ASSIGN_VALUE, cv2.THRESH_BINARY)
# Display the motion mask and background
cv2.imshow('Motion mask', motion_mask)
cv2.imshow('Background', background)
背景减法代码
如果对象的颜色与背景框不同,这种方法可以获得很好的效果。然而,像帧差分一样,它也有一些主要的缺点。毋庸置疑,它对光照变化和相机运动高度敏感,它还有一个“waking person"问题,这意味着如果背景物体(属于参考图像的物体)移动,则同时检测到真实物体及其重影.
在这种情况下,我们遇到了与帧差分相反的问题:“如果我们想停止检测前景对象并将其吸收到背景中怎么办?”
3. 自适应背景减法
这种方法基本上结合了之前的两种技术,通过引入学习率 λ 来充分利用两者。在每个时间步,我们对传入图像和先前背景的贡献进行加权以构建新背景。
例如,如果我们设置 λ=0.1,则在更新背景帧之前需要 10 帧(换句话说,前景对象将被吸收到背景中)。而对于 λ=0.5,我们有更快的更新(更新前只需要 2 帧)。请注意,选择 λ 没有规则,它是凭经验完成的,因为它取决于我们正在处理的任务和环境。
background = None
MAX_FRAMES = 1000
THRESH = 60
ASSIGN_VALUE = 255
ALPHA = 0.1
def update_background(current_frame, prev_bg, alpha):
bg = alpha * current_frame + (1 - alpha) * prev_bg
bg = np.uint8(bg)
return bg
cap = cv2.VideoCapture(0)
for t in range(MAX_FRAMES):
# Capture frame-by-frame
ret, frame = cap.read()
# Convert frame to grayscale
frame_gray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
if t == 0:
# Train background with first frame
background = frame_gray
else:
# Background subtraction
diff = cv2.absdiff(background, frame_gray)
# Mask thresholding
ret, motion_mask = cv2.threshold(diff, THRESH, ASSIGN_VALUE, cv2.THRESH_BINARY)
# Update background
background = update_background(frame_gray, background, alpha = ALPHA)
# Display the motion mask and background
cv2.imshow('Motion mask', motion_mask)
cv2.imshow('Background', background)
自适应背景减法代码
4. 高斯混合(MoG)
高斯混合是一种广泛使用的背景建模方法,用于从静态相机中检测运动物体。
简而言之,这种方法首先将每个像素建模为加权高斯的总和,其中权重定义了每个高斯的贡献。拥有多个高斯而不是一个的直觉是一个像素可以代表许多对象(例如雪花和后面的建筑物)。通过使用以前的帧计算颜色直方图,我们可以知道对象可能是背景或前景对象。
例如,当我们得到一个具有大权重和小标准偏差的高斯时,这意味着所描述的对象经常出现并且在帧之间没有变化,因此它可能是背景的一部分。这就是算法的工作原理;每个输入像素都会根据可用模型进行检查。在匹配的情况下,我们更新模型的权重、均值和标准差,如果权重除以标准差很大,我们将像素分类为背景,否则分类为前景。
MAX_FRAMES = 1000
LEARNING_RATE = -1
fgbg = cv2.createBackgroundSubtractorMOG2()
cap = cv2.VideoCapture(0)
for t in range(MAX_FRAMES):
# Capture frame-by-frame
ret, frame = cap.read()
#Apply MOG
motion_mask = fgbg.apply(frame, LEARNING_RATE)
#Get background
background = fgbg.getBackgroundImage()
# Display the motion mask and background
cv2.imshow('background', background)
cv2.imshow('Motion Mask', motion_mask)
混合高斯码
原文标题 : 运动检测技术(OpenCV 上的代码)