1.背景消除
1.帧差法
对于视频,不同影像所在的位置不同,那么相邻两帧的图像进行差分运算,如果所得区域灰度差值的绝对值超过一定的阈值时,那么判定为运动的目标,否则为背景。
为什么要用阈值?因为背景中可能也会有些物品动,比如风吹过的树。
因为图像中容易有噪音点和空洞问题,该方法基本不会使用。
2.混合高斯模型
(1)若第一帧图像像素值为100,xigema值为5(假设,默认);
(2)取t帧数据图像(要足够大!!200up)进行模型的训练;
(3)接下来像素值为105,比较,若当前的105与前面的像素值进行差值比较,结果为5,若大于5,说明差距大,说明此分布不太合适,需要新建一个分布;若没有大于,所以比较合适,则把他们两个放一起,是同一个分布,更新xigema和像素值。
(4)。。。循环往复,建立多个分布,最好是3-5个。
测试,实战
(1)逐帧读取视频。
(2)mask掩码操作,前景置为白色,背景置为黑色。
(3)形态学变换,去掉一些噪音点,让得到的结果更好。
(4)检测每个人的轮廓(这里用的是opencv的方法而不是深度学习,深度学习的方法是现在的主流方法)。计算轮廓的周长,若周长比较大就代表是个人。绘制轮廓。
import numpy as np
import cv2
#经典的测试视频
cap = cv2.VideoCapture('test.avi')
#形态学操作需要使用
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(3,3))
#创建混合高斯模型用于背景建模
fgbg = cv2.createBackgroundSubtractorMOG2()
while(True):
ret, frame = cap.read()
fgmask = fgbg.apply(frame)
#形态学开运算去噪点
fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel)
#寻找视频中的轮廓
im, contours, hierarchy = cv2.findContours(fgmask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for c in contours:
#计算各轮廓的周长
perimeter = cv2.arcLength(c,True)
if perimeter > 188:
#找到一个直矩形(不会旋转)
x,y,w,h = cv2.boundingRect(c)
#画出这个矩形
cv2.rectangle(frame,(x,y),(x+w,y+h),(0,255,0),2)
cv2.imshow('frame',frame)
cv2.imshow('fgmask', fgmask)
k = cv2.waitKey(150) & 0xff
if k == 27:
break
cap.release()
cv2.destroyAllWindows()
2.光流估计
像素运动的瞬时速度,也就是速度的矢量特征。,可以对图像进行动态分析,例如目标追踪。
有三个前提:亮度恒定、小运动(帧与帧之间)(不是夸张的剧烈运动)、空间一致(邻近像素点变化总的来说一致)。
1.Lucas-kanada算法
泰勒级数展开函数(第二个等号处):
对xy的偏导,之前在sobel算子中有进行求导,所以我们可以求得;对t的偏导由前一帧减去后一帧(导数就是变化率嘛)。
移项,想要求得u,v两个未知数,至少要两个像素点。
Ab区域,5*5,一共有25个点,若得到的uv没办法满足所有的xy,那就尽可能多地满足。
矩阵什么时候可逆?入1入2,在角点检测时,当两个值都大时,他就是一个角点,通常情况下,角点是可逆的。
### cv2.calcOpticalFlowPyrLK():
参数:
- prevImage 前一帧图像
- nextImage 当前帧图像
- prevPts 待跟踪的特征点向量
- winSize 搜索窗口的大小
- maxLevel 最大的金字塔层数
返回:
- nextPts 输出跟踪特征点向量(这里的特征点从数学的意义上看就是角点)
- status 特征点是否找到角点,找到的状态为1,未找到的状态为0
实战:
(1)初始化参数
(2)预处理
(3)返回所有检测点:
(4)创建mask:
(5)循环处理
import numpy as np
import cv2
#输入视频
cap = cv2.VideoCapture('test.avi')
# 角点检测所需参数
#角点最大数量(效率),品质因子,最小距离:该角点最小距离内的其他角点都可以筛选
feature_params = dict( maxCorners = 100,
qualityLevel = 0.3,
minDistance = 7)
# lucas kanade参数
lk_params = dict( winSize = (15,15),
maxLevel = 2)
# 随机颜色条
color = np.random.randint(0,255,(100,3))
# 拿到第一帧图像
ret, old_frame = cap.read()
old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY)
# 返回所有检测特征点,需要输入图像
# 距离相当于这区间有比这个角点强的,就不要这个弱的了
p0 = cv2.goodFeaturesToTrack(old_gray, mask = None, **feature_params)
# 创建一个mask
mask = np.zeros_like(old_frame)
while(True):
ret,frame = cap.read()
frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 光流估计:需要传入前一帧和当前图像以及前一帧检测到的角点。
p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)
# st=1表示,st==1就传入
good_new = p1[st==1]
good_old = p0[st==1]
# 绘制轨迹
for i,(new,old) in enumerate(zip(good_new,good_old)):
a,b = new.ravel()
c,d = old.ravel()
#绘制新旧两点之间的线
mask = cv2.line(mask, (int(a),int(b)),(int(c),int(d)), color[i].tolist(), 2)
#绘制新点的线
frame = cv2.circle(frame,(int(a),int(b)),5,color[i].tolist(),-1)
img = cv2.add(frame,mask)
cv2.imshow('frame',img)
k = cv2.waitKey(150) & 0xff
if k == 27:
break
# 更新,将当前帧赋值给旧帧
old_gray = frame_gray.copy()
p0 = good_new.reshape(-1,1,2)
cv2.destroyAllWindows()
cap.release()
总结:目标追踪的方法:
深度学习的算法;光流估计。