OpenCV 自适应背景更新 cv2.accumulateWeighted
flyfish
cv2.accumulateWeighted
主要用于实现自适应背景更新。
作用
在视频处理中,背景通常是相对稳定的部分,而前景则是在背景上移动或变化的物体。自适应背景更新的目的是根据视频帧的变化动态地更新背景模型,以适应光照变化、背景缓慢移动等情况。cv2.accumulateWeighted
函数可以帮助我们逐步更新背景模型,使得背景模型能够更好地反映当前的背景状态。
原理
cv2.accumulateWeighted
函数基于加权平均的方法来更新背景模型。对于每一帧视频图像,它会将当前帧与之前存储的背景模型进行加权平均,得到新的背景模型。具体的计算公式如下:
B n e w = α × F + ( 1 − α ) × B o l d B_{new} = \alpha \times F + (1 - \alpha) \times B_{old} Bnew=α×F+(1−α)×Bold
其中:
- B n e w B_{new} Bnew 是更新后的背景模型。
- α \alpha α 是加权系数,取值范围是 0 < α ≤ 1 0 < \alpha \leq 1 0<α≤1。 α \alpha α 越大,当前帧在新背景模型中所占的权重就越大,背景模型更新得就越快;反之, α \alpha α 越小,背景模型更新得就越慢。
- F F F 是当前帧的图像。
- B o l d B_{old} Bold 是之前的背景模型。
使用方法
下面是一个使用 cv2.accumulateWeighted
进行自适应背景更新的示例代码:
python
import cv2
# 打开视频文件
cap = cv2.VideoCapture('input_video.mp4')
# 读取第一帧作为初始背景
ret, frame = cap.read()
if not ret:
print("无法读取视频帧")
exit()
# 将第一帧转换为灰度图像
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 创建一个与帧大小相同的背景模型,初始化为第一帧
background = gray.copy().astype("float")
# 定义加权系数
alpha = 0.01
while True:
# 读取下一帧
ret, frame = cap.read()
if not ret:
break
# 将当前帧转换为灰度图像
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 使用 cv2.accumulateWeighted 更新背景模型
cv2.accumulateWeighted(gray, background, alpha)
# 将背景模型转换为 8 位无符号整数类型
background_vis = cv2.convertScaleAbs(background)
# 计算当前帧与背景模型的差值
diff = cv2.absdiff(gray, background_vis)
# 对差值图像进行阈值处理,得到前景掩码
_, thresh = cv2.threshold(diff, 25, 255, cv2.THRESH_BINARY)
# 显示原始帧、背景模型和前景掩码
cv2.imshow('Original Frame', frame)
cv2.imshow('Background Model', background_vis)
cv2.imshow('Foreground Mask', thresh)
# 按 'q' 键退出循环
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 释放视频捕获对象并关闭所有窗口
cap.release()
cv2.destroyAllWindows()
简单写
py
import cv2
import numpy as np
video_path = 'input_video.mp4'
cap = cv2.VideoCapture(video_path)
# 初始化背景模型
ret, first_frame = cap.read()
background = np.zeros_like(first_frame[:, :, 0], dtype=np.float32) # 初始化为全0或第一帧
alpha = 0.05 # 学习率
while True:
ret, frame = cap.read()
if not ret:
break
frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 转为 float32 类型(与背景模型类型一致)
frame_gray = frame_gray.astype(np.float32)
# 自适应更新背景
cv2.accumulateWeighted(frame_gray, background, alpha)
background_uint8 = background.astype(np.uint8)
# 显示背景模型
cv2.imshow('Background', background_uint8)
if cv2.waitKey(1) == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
1. cv2.accumulateWeighted(frame_gray, background, alpha)
作用
- 动态更新背景模型 :
通过加权平均的方式,将当前帧的灰度图像frame_gray
与历史背景模型background
结合,逐步更新背景,使其适应场景中的缓慢变化(如光线变化、轻微移动的物体等)。
参数说明
frame_gray
:当前帧的灰度图像(单通道,通常为float32
类型)。background
:背景模型(初始化为与frame_gray
相同大小的float32
数组)。alpha
:学习率 (取值范围0 < alpha < 1
),控制新帧与旧背景的权重:alpha
越小:背景更新越慢,对缓慢变化(如光线)更鲁棒,但对快速变化(如新物体进入)反应较慢。alpha
越大:背景更新越快,能更快适应新变化,但可能对噪声更敏感。
数学公式
cv2.accumulateWeighted
的计算公式为:
KaTeX parse error: Expected 'EOF', got '_' at position 47: ...mes \text{frame_̲gray} + (1 - \a...
- 初始化时 :
background
需要先初始化为第一帧的灰度值(或全零)。 - 效果 :
每一帧的背景模型都会逐渐融合新帧的信息,但历史帧的贡献随时间衰减(呈指数衰减)。
2. background_uint8 = background.astype(np.uint8)
作用
- 数据类型转换 :
将背景模型background
(float32
类型)转换为uint8
类型(0-255 的整数),以便后续显示或处理。
cv2.accumulateWeighted
函数是基于指数加权平均(Exponential Moving Average,EMA)的原理来更新背景模型,其核心公式如下:
公式表示
S t = α × X t + ( 1 − α ) × S t − 1 S_{t}=\alpha\times X_{t}+(1 - \alpha)\times S_{t - 1} St=α×Xt+(1−α)×St−1
公式参数解释
- S t S_{t} St :表示第 t t t 时刻更新后的背景模型。在图像处理中,通常对应于经过加权平均后得到的新的背景图像。
- α \alpha α :是加权系数,也被称为学习率,取值范围为 0 < α ≤ 1 0 < \alpha\leq1 0<α≤1。它控制着当前帧图像在更新背景模型时所占的权重。
- 当 α \alpha α 接近 1 时,意味着当前帧图像在新的背景模型中占比很大,背景模型会快速适应图像的变化,能快速跟上场景的动态变化,但也可能会过于敏感,将前景物体误当作背景的一部分进行更新。
- 当 α \alpha α 接近 0 时,当前帧图像在新背景模型中占比很小,背景模型更新缓慢,对场景变化的响应较为迟钝,不过这样可以减少因短期干扰(如快速移动的物体)对背景模型的影响。
- X t X_{t} Xt :代表第 t t t 时刻的当前帧图像。在处理视频时,就是每一帧的图像数据。
- S t − 1 S_{t - 1} St−1 :是第 t − 1 t - 1 t−1 时刻的背景模型,也就是上一次更新得到的背景图像。
cv2.accumulateWeighted(gray, background, alpha)
其中 gray
对应公式里的 X t X_{t} Xt,background
对应 S t − 1 S_{t - 1} St−1 和 S t S_{t} St(更新前后的背景模型),alpha
就是公式中的 α \alpha α。每次调用该函数时,就会按照上述公式更新背景模型。
虽然在原始的递推公式 S t = α × X t + ( 1 − α ) × S t − 1 S_{t}=\alpha\times X_{t}+(1 - \alpha)\times S_{t - 1} St=α×Xt+(1−α)×St−1 里没有直接呈现指数,但通过展开公式就能发现 ( 1 − α ) (1 - \alpha) (1−α) 的指数形式。
指数加权的体现
从该公式出发,若持续迭代展开这个式子,就能清晰看到指数的存在。
对于第 t t t 时刻的背景模型 S t S_{t} St,通过不断代入前一时刻的公式进行展开:
- 当 t = 1 t = 1 t=1 时, S 1 = α × X 1 + ( 1 − α ) × S 0 S_{1}=\alpha\times X_{1}+(1 - \alpha)\times S_{0} S1=α×X1+(1−α)×S0。
- 当 t = 2 t = 2 t=2 时, S 2 = α × X 2 + ( 1 − α ) × S 1 = α × X 2 + ( 1 − α ) ( α × X 1 + ( 1 − α ) × S 0 ) = α × X 2 + α ( 1 − α ) × X 1 + ( 1 − α ) 2 × S 0 S_{2}=\alpha\times X_{2}+(1 - \alpha)\times S_{1}=\alpha\times X_{2}+(1 - \alpha)(\alpha\times X_{1}+(1 - \alpha)\times S_{0})=\alpha\times X_{2}+\alpha(1 - \alpha)\times X_{1}+(1 - \alpha)^2\times S_{0} S2=α×X2+(1−α)×S1=α×X2+(1−α)(α×X1+(1−α)×S0)=α×X2+α(1−α)×X1+(1−α)2×S0。
- 当 t = 3 t = 3 t=3 时, S 3 = α × X 3 + ( 1 − α ) × S 2 = α × X 3 + α ( 1 − α ) × X 2 + α ( 1 − α ) 2 × X 1 + ( 1 − α ) 3 × S 0 S_{3}=\alpha\times X_{3}+(1 - \alpha)\times S_{2}=\alpha\times X_{3}+\alpha(1 - \alpha)\times X_{2}+\alpha(1 - \alpha)^2\times X_{1}+(1 - \alpha)^3\times S_{0} S3=α×X3+(1−α)×S2=α×X3+α(1−α)×X2+α(1−α)2×X1+(1−α)3×S0。
以此类推,对于第 t t t 时刻的 S t S_{t} St,可以得到展开式:
S t = α ∑ i = 0 t − 1 ( 1 − α ) i X t − i + ( 1 − α ) t S 0 S_{t}=\alpha\sum_{i = 0}^{t - 1}(1 - \alpha)^iX_{t - i}+(1 - \alpha)^tS_{0} St=αi=0∑t−1(1−α)iXt−i+(1−α)tS0
指数的体现及意义
- 指数形式 :从展开式中能看出,权重 ( 1 − α ) (1 - \alpha) (1−α) 以指数 i i i 的形式出现,也就是 ( 1 − α ) i (1 - \alpha)^i (1−α)i。
- 意义 :离当前时刻越远的帧(即 i i i 越大),其对应的权重 ( 1 − α ) i (1 - \alpha)^i (1−α)i 就越小,呈指数级衰减。这表明在更新背景模型时,近期的帧对当前背景模型的影响更大,而较早的帧影响则会随着时间推移以指数形式快速减小。这种特性让算法能快速适应背景的变化,同时还能保留一定的历史信息。