python-背景差分法
·
一、实验原理
背景差分法是一种对静止场景进行运动分割的通用方法,它将当前获取的图像帧与背景图像做差分运算,得到目标运动区域的灰度图,对灰度图进行阈值化提取运动区域,而且为避免环境光照变化影响,背景图像根据当前获取图像帧进行更新。在稳定的监控场景中,背景一般保持不变或变化缓慢,而前景目标(如行人、车辆等)则表现为明显的移动或变化。通过设置一个背景模型,实时更新并存储当前场景的静态部分,然后将新进帧与背景模型进行逐像素比较,从而计算出差分图像。该差分图像清晰地显示了前景目标的位置和形状,这就是背景差分法的理论基础。
二、实验代码
import numpy as np
import cv2
import os
def initialize_background_model(video_path, num_frames=50):
"""
初始化背景模型(使用中值法)
参数:
video_path: 视频文件路径
num_frames: 用于初始化的帧数
返回:
background_model: 背景模型
"""
# 打开视频
cap = cv2.VideoCapture(video_path)
# 读取前N帧
frames = []
frame_count = 0
while frame_count < num_frames:
ret, frame = cap.read()
if not ret:
break
frames.append(frame)
frame_count += 1
if frame_count % 10 == 0:
print(f"已采集 {frame_count}/{num_frames} 帧")
cap.release()
print(f"成功采集 {len(frames)} 帧用于背景建模")
# 转换为numpy数组
frames_array = np.array(frames)
# 使用中值法计算背景模型
print("使用中值法建立背景模型...")
background_model = np.median(frames_array, axis=0).astype(np.uint8)
return background_model
def background_subtraction(current_frame, background_model, threshold=30):
"""
背景差分法检测前景
参数:
current_frame: 当前帧
background_model: 背景模型
threshold: 二值化阈值
返回:
foreground_mask: 前景掩码
result_frame: 标记结果的帧
"""
# 计算绝对差
diff = cv2.absdiff(current_frame, background_model)
# 转换为灰度图
gray_diff = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY)
# 二值化
_, foreground_mask = cv2.threshold(gray_diff, threshold, 255, cv2.THRESH_BINARY)
# 在原图上标记前景(红色)
result_frame = current_frame.copy()
result_frame[foreground_mask == 255] = [0, 0, 255] # BGR格式,红色
return foreground_mask, result_frame
def main():
# 参数设置
video_path = 'video1.mp4' # 视频文件路径
num_frames = 50 # 用于初始化的帧数
threshold = 30 # 背景差分阈值
# 初始化背景模型(只使用中值法)
print("正在使用中值法初始化背景模型...")
background_model = initialize_background_model(video_path, num_frames)
# 保存背景模型(中值法)
cv2.imwrite('background_model.jpg', background_model)
# 创建显示窗口
cv2.namedWindow('frame1', cv2.WINDOW_NORMAL)
cv2.resizeWindow('frame1', 1200, 400)
print("开始实时显示背景差分结果...")
print("按 'q' 键退出")
print("按 'r' 键重新开始播放")
print("按 '+' 键增加阈值")
print("按 '-' 键减少阈值")
# 循环播放视频
while True:
# 打开视频
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
print("错误:无法打开视频文件!")
break
frame_count = 0
while True:
ret, frame = cap.read()
if not ret:
break # 视频结束,重新开始
# 背景差分
foreground_mask, result_frame = background_subtraction(frame, background_model, threshold)
# 调整掩码为彩色用于显示
mask_display = cv2.cvtColor(foreground_mask, cv2.COLOR_GRAY2BGR)
# 水平拼接三个画面
combined_frame = np.hstack([frame, result_frame, mask_display])
# 添加文字说明
cv2.putText(combined_frame, f"original (threshold: {threshold})", (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
cv2.putText(combined_frame, "back", (frame.shape[1] + 10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
cv2.putText(combined_frame, "yanma", (2 * frame.shape[1] + 10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
# 显示帧编号
cv2.putText(combined_frame, f"帧: {frame_count}", (10, combined_frame.shape[0] - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2)
# 显示结果
cv2.imshow('frame1', combined_frame)
# 键盘控制
key = cv2.waitKey(25) & 0xFF
if key == ord('q'): # 退出
cap.release()
cv2.destroyAllWindows()
return
elif key == ord('r'): # 重新开始
break
elif key == ord('+'): # 增加阈值
threshold = min(255, threshold + 5)
print(f"阈值增加到: {threshold}")
elif key == ord('-'): # 减少阈值
threshold = max(0, threshold - 5)
print(f"阈值减少到: {threshold}")
elif key == ord('p'): # 暂停/继续
print("暂停,按任意键继续...")
cv2.waitKey(0)
frame_count += 1
cap.release()
if __name__ == "__main__":
main()
三、实验结果


本实验中采用中值法建立背景模型实现背景差分,可观察到背景模型比较干净,二值化图像有轻微误差。阈值是用来决定像素差异是否足够大以被认为是前景的关键参数,本实验中可根据视频情况改变阈值,更好的分离背景和前景,检测出运动目标。
四、问题解答
如何得到背景模型?
使用算法训练得到背景模型,本实验中采用中值法,通过计算视频序列中每个像素位置的中值来建立模型。读取多帧视频帧作为训练样本,对于图像中的每个像素位置 (x, y),从所有训练帧中收集该位置的像素值,然后计算中值得到背景模型。
参考博客
原文链接:https://blog.csdn.net/ajianyingxiaoqinghan/article/details/72628402
原文链接:https://blog.csdn.net/weixin_42607969/article/details/147806313
更多推荐



所有评论(0)