目录大纲
理论架构
基础梗概
1.混合高斯模型
2. L-K光流估计方法的基本原理。
代码实践
高斯混合模型
背景分割,图像标记
l-k光流估计
理论架构
基础知识汇总篇:
https://blog.csdn.net/weixin_42237113/article/details/104500993
API详解:
https://blog.csdn.net/weixin_42237113/article/details/104488809
基础梗概
1.混合高斯模型
混合高斯模型
是一种参数化的概率统计方法,该模型将背景、背景内的小幅度运动以及阴影等的混合信号表示成混合高斯概率统计模型,为不同的状态建立不同的高斯模型,采用最大似然概率来实现背景建模,并利用学习因子实时地更新背景高斯模型,适用于动态背景下的运动检测。
背景建模是通过帧差法对运动目标进行检测,即,利用前后时刻两帧的差值来判断是否是背景或者前景(目标)。适用于摄像头和背景不变,目标运动的检测。图像的帧差法的结果,可认为是用多种混合高斯函数统计的。并且
I(x,y,t) - u > 3sigma,即特定位置的前后图像的变化过大,则为前景;
否则,则为背景。
通过混合高斯模型相关理论,可以进行背景建模,进行目标运动侦测。
相关混合高斯背景建模步骤如下:
模型初始化 将采到的第一帧图像的每个象素的灰度值作为 均值,再赋以较大的方差。初值 Q =1, w=1.0。 l
模型学习 将当前帧的对应点象素的灰度值与已有的 Q 个高 斯模型作比较,若满足 |x - u|<2.5sigma ,则按上页方式调 整第q个高斯模型的参数和权重;否则转入(3):
增加/替换高斯分量 若不满足条件,且 q < Q ,则增加一个 新分量;若q=Q,则替换
判断背景 B = argmini(sum 1_b(w) > T)
判断前景
2. L-K光流估计方法的基本原理。
光流(Optical flow or optic flow)是关于视域中的物体运动检测中的概念。用来描述相对于观察者的运动所造成的观测目标、表面或边缘的运动。光流法在样型识别、计算机视觉以及其他影像处理领域中非常有用,可用于运动检测、物件切割、碰撞时间与物体膨胀的计算、运动补偿编码,或者通过物体表面与边缘进行立体的测量等等。
光流估计有3个前提假设:
有三个前提假设条件:
(1) 亮度恒定:一个像素点随着时间的变化,其亮度值(像素灰度值)是恒定不变的。这是光流法的基本设定。所有光流法都必须满足。(也就是说,一个物体当前的像素灰度值是恒定的,不改变的,光照不改变的情况下来进行)
(2) 小运动: 时间的变化不会引起位置的剧烈变化。这样才能利用相邻帧之间的位置变化引起的灰度值变化,去求取灰度对位置的偏导数。所有光流法必须满足。
(3) 空间一致:即前一帧中相邻像素点在后一帧中也是相邻的。这是LK光流法独有的假定。因为为了求取x,y方向的速度,需要建立多个方程联立求解。而空间一致假设就可以利用邻域n个像素点来建立n个方程。
L-K光流估计基本原理
在亮度恒定,位移变化小,空间一致的相邻两帧图像中,找出位移后的像素值,主要是通过当前位置亮度在x和y方向,以及在相邻两帧中的灰度变化值来估计x和y变化了多少y,主要的方法是将图像进行缩小,只有缩小到足够小,才可以用泰勒展开,对缩小后的图片第一次LK得到位移值,然后按照一定的倍数放大当前采集到的位移值,再进行LK,重复此操作,直到达到原图的像素值时停止分层,得到最终的位移像素。
这个算法是最常见,最流行的。它计算两帧在时间t 到t + δt之间每个每个像素点位置的移动。 由于它是基于图像信号的泰勒级数,这种方法称为差分,这就是对于空间和时间坐标使用偏导数。
代码实践
高斯混合模型
在测试视频上,使用基于混合高斯模型的背景提取算法,提取前景并显示(显示二值化图像,前景为白色)。
程序中的视频素材在此下载
链接: https://pan.baidu.com/s/1DIkyYTq7lE3Wou17oHZDnw 提取码: jk4j
# Q1 提取前景并显示
import cv2
cap = cv2.VideoCapture("./vtest.avi")
if not cap.isOpened():
print("can't open camera")
# 创建MOG2背景提取模型
mog2 = cv2.createBackgroundSubtractorMOG2(history=20, varThreshold=50,detectShadows=True)
threshold = 200
while True:
# 读取视频图像
status, img = cap.read()
if not status:
print("can't read video")
break
# 初步获取前景图像
mask = mog2.apply(img)
# 使用二值法,去掉噪声点
_, fg_mask = cv2.threshold(mask, 30, 255, cv2.THRESH_BINARY)
cv2.imshow("img", img)
cv2.imshow("background", bg_img)
cv2.imshow("frontground", fg_mask)
key = cv2.waitKey(30)
if key == 27:
break
cap.release()
cv2.destroyAllWindows()
背景分割,图像标记
在1基础上,将前景目标进行分割,进一步使用不同颜色矩形框标记,并在命令行窗口中输出每个矩形框的位置和大小。
#Q2将目标标注并显示
# 创建视频类对象,读取本地视频
cap = cv2.VideoCapture("./vtest.avi")
# 检测视频是否读取正常
if not cap.isOpened():
print("can't open camera")
# 创建MOG2背景提取模型
mog2 = cv2.createBackgroundSubtractorMOG2(history=20, varThreshold=30,detectShadows=True)
threshold = 200
# 目标标记函数
def labelTargets(img, mask, threshold):
seg = mask.copy()
# 勾画运动目标轮廓,只返回外部轮廓,只存储最简单像素轮廓点
# 注意:findContours(image, mode, method[, contours[, hierarchy[, offset]]]) -> image, contours, hierarchy
# 第二个返回值才是轮廓点的集合
cnts = cv2.findContours(seg, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
count = 0
for i in cnts[1]:
area = cv2.contourArea(i)
if area < threshold:
continue
count += 1
rect = cv2.boundingRect(i)
print("矩形:X:{} Y:{} 宽:{} 高:{}".format(rect[0], rect[1], rect[2], rect[3]))
cv2.drawContours(img, [i], -1, (255, 0, 0), 1)
cv2.rectangle(img, (rect[0], rect[1]), (rect[0] + rect[2], rect[1] + rect[3]), (0, 0, 255), 1)
cv2.putText(img, str(count), (rect[0], rect[1]), cv2.FONT_HERSHEY_PLAIN, 0.5, (0, 255, 0))
return count
while True:
# 读取视频图像
status, img = cap.read()
if not status:
print("can't read video")
break
# 初步获取前景图像
mask = mog2.apply(img)
# 使用二值法,去掉噪声点
_, fg_mask = cv2.threshold(mask, 30, 255, cv2.THRESH_BINARY)
# 获取背景图片
bg_img = mog2.getBackgroundImage()
tar_num = labelTargets(img, fg_mask, threshold)
print("检测目标个数:",tar_num,"\n")
cv2.imshow('source', img)
cv2.imshow('background', bg_img)
cv2.imshow('foreground', fg_mask)
key = cv2.waitKey(0)
if key == 27:
break
cap.release()
cv2.destroyAllWindows()
l-k光流估计
使用光流估计方法,在前述测试视频上计算特征点,进一步进行特征点光流估计。
# Q4使用光流估计
import numpy as np
import cv2
"""
code2:
光流估计法,提取特征值并且检测运动距离
:param
无
:return
无
"""
# 加载视频
cap = cv2.VideoCapture()
cap.open(r'./vtest.avi')
if not cap.isOpened():
print("can't open video \n")
# 方便调参,使用dict字典
# lucas kanade光流法参数设定
lk_params = dict(winSize=(15, 15),
maxLevel=2,
criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))
# winSize:每个金字塔级别的搜索窗口大小。
# maxLevel:从0开始的最大金字塔等级数字;如果设置为0,则不使用金字塔(单个级别);如果设置为1,则使用两个级别。
# criteria:参数,指定迭代搜索算法的终止条件
# (在指定的最大迭代次数之后 criteria.maxCount 或当搜索窗口移动小于时 criteria.epsilon)。
# 角点检测参数设定
feature_params = dict(maxCorners=500,
qualityLevel=0.3,
minDistance=7,
blockSize=7)
# maxCorners : 设置最多返回的关键点数量。
# qualityLevel : 角点的品质因子,决定能不能成为角点。
# minDistance : 关键点之间的最少像素点,此邻域范围内如果存在更强角点,则删除此角点。
# blockSize : 计算一个角点是否为关键点时所取的区域大小。
# 读取第一帧视频图像
status, prev_img = cap.read()
if not status:
print("can't read first video picture")
prev_gray = cv2.cvtColor(prev_img, cv2.COLOR_BGR2GRAY)
# 提取第一帧的特征点
p0 = cv2.goodFeaturesToTrack(prev_gray, mask=None, **feature_params)
while True:
# 读取当前帧视频图像
status, img = cap.read()
if not status:
print("can't read video \n")
break
cur_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#计算光流
p1, st, err = cv2.calcOpticalFlowPyrLK(prev_gray,cur_gray, p0, None,
**lk_params)
# 选取好的跟踪点
good_points = p1[st==1]
good_prv_points = p0[st==1]
# 在结果图像中叠加画出特征点和计算出来的光流向量
res = img.copy()
draw_line = (0, 0, 255)
draw_circle = (0, 255, 0)
# draw_color = np.random.randint(0, 255, (100,3))
# zip将good, good_prv合成一个元祖
for i, (cur, prev) in enumerate(zip(good_points,good_prv_points)):
x0, y0 = cur.ravel()#将多维度转化为一维
x1, y1 = prev.ravel()
# 标注上特征点和运动轨迹
# cv2.line(res, (x0,y0), (x1,y1), draw_color[i].tolist())
# cv2.circle(res, (x0, y0), 3, draw_color[i].tolist())
cv2.line(res, (x0,y0), (x1,y1), draw_line)
cv2.circle(res, (x0, y0), 3, draw_circle)
#更新上一帧
prev_gray = cur_gray.copy()
p0 = good_points.reshape(-1, 1, 2)
# 显示计算结果图像
cv2.imshow("check result pic", res)
# 等待esc退出
key = cv2.waitKey(0)
if key == 27:
break
cap.release()
cv2.destroyAllWindows()