cc(9条消息) 基于MeanShift的目标跟踪算法及实现_taotao1233的博客-CSDN博客_meanshift目标跟踪算法https://blog.csdn.net/jinshengtao/article/details/30258833https://blog.csdn.net/jinshengtao/article/details/30258833(9条消息) MeanShift(均值漂移)MATLAB程序详解_战死为止的博客-CSDN博客_均值漂移matlab代码
https://blog.csdn.net/qq_34122861/article/details/89556503 第一篇博客对meanshift目标追踪介绍的比较详细,但仍有不足之处,我参考了这篇论文(基于均值移位的运动目标跟踪研究 - 中国知网),对目标追踪原理做了一点补充,如果你看得懂英文论文,也可以看这篇(IEEE Xplore Full-Text PDF:)。下面的原理补充的地方主要在第四部分“Meanshift迭代过程”,并且前面省略了meanshift基本原理介绍,可参考第一篇博客。最后面有利用python实现的代码。
1、目标模型描述
通过人工标注的方式在初始帧确定包含跟踪目标的区域。假设其中有n个像素用{zi}-1...n表示其位置,对选中的区域的灰度颜色空间均匀划分,得到由m个相等的区间构成的灰度直方图。目标模型的qu
概率密度(u=1,...m)可表示为:
其中,zi*表示以目标中心为原点的归一化像素位置,为目标中心坐标。K是核函数,常选用Epanechikov核函数,为
b()表示
处像素属于哪个直方图区间,u为直方图的颜色索引。δ[b(
)-u]函数的作用是判断目标区域中像素
处的灰度值是否属于直方图中第u个单元,等于为1,否则为0。C是归一化系数。
2、候选模型描述
在第t帧时,根据第t-1帧的目标中心位置,
作为搜索窗口的中心,得到候选目标的中心位置坐标f,计算当前帧的候选目标区域直方图。该区域的像素用
{}表示,则候选模型的概率密度为:
h为核函数窗口大小,其他参数同目标模型描述。
3、相似性度量
相似性函数用于描述目标模型和候选目标之间的相似程度。本文采用
Bhattacharyya系数作为相似性函数,其定义为:。相似函
数越大则两个模型越相似。将前一帧中目标的中心位置f0作为搜索窗口的中心,寻找使得相似函数最大的候选区域,即是在本帧中目标的位置。
4、Meanshift迭代过程
均值漂移的迭代过程,也就是目标位置搜索的过程。为使相似函数最大,对上式进行泰勒展开,取前两项得到Bhattacharyya系数的近似表达:
式(4.1)中只有第二项与f有关,相似性最大也就是第二项最大,故第二项可看作候选目标的中心位置坐标f的密度估计,对其求梯度得(为方便计算,式子前的常量均看作C):
令,得:
式(4.4)括号中的两项都表示了一定的意义。前一项与用核函数g估计的密度
成正比。后一项称为均值移位向量:
它是用核函数g加权的各样本点的均值与核(窗)中心f的差。因此,式(4.4)变为
式(4.8)指明用核函数g计算得出的点f处的均值移位向量与用核函数K估计出的归一化密度梯度成正比。因此,可用样本的均值移位向量进行归一化密度梯度估计,均值移位向量总是指向密度增长最快的方向。均值移位向量依据局部梯度估计进行调整,它指向了估计密度的“定点”。反复地进行如下两步,就是均值移位过程:
①计算均值移位向量
②用移动窗口
由于,所以g(x)是常量,可以约掉,最终得到
总结来说,Meanshift方法就是从f起向两个模型相比相似性最大的方向不断移动,直到最后两次移动距离小于阈值,即找到当前帧的目标位置,并以此作为下一帧的起始搜索窗口中心,如此重复。
5、总体流程
首先是对目标跟踪的初始化,本文通过手动选取的方式用鼠标选定跟踪目标区域。然后计算核函数加权下的搜索窗口的直方图分布,用同样的方法计算第N帧对应窗口的直方图分布,以两个模板分布的相似性最大为原则,使搜索窗口沿密度增加最大的方向移动,得到目标的真实位置。
MeanShift 算法跟踪步骤如下
①计算目标模板的概率密度{}u=1...m;
②用初始化当前帧的目标位置,计算候选目标模板
}u=1...m
③根据式(4.2)计算当前窗口内各点的权重值;
④计算目标的新位置
本人写程序和open-cv都是新人,看了上面的文章后发现没有python版本的,就按照一摸一样的思路仿写了python的代码,但是是打开笔记本自带摄像头进行MeanShift目标跟踪。希望给萌新一些参考,由于我也是新手,这是我写的第一个比较长的代码,所以会有很多冗余甚至错误,欢迎大家指正。如果看不懂可以参考上面的博客。
import math
import numpy as np
import cv2
def get_tr(img):
# 定义需要返回的参数
mouse_params = {'x': None, 'width': None, 'height': None,
'y': None, 'temp': None}
cv2.namedWindow('image')
# 鼠标框选操作函数
cv2.setMouseCallback('image', on_mouse, mouse_params)
cv2.imshow('image', img)
cv2.waitKey(0)
return [mouse_params['x'], mouse_params['y'], mouse_params['width'],
mouse_params['height']], mouse_params['temp']
def on_mouse(event, x, y, flags, param):
global img, point1
img2 = img.copy()
if event == cv2.EVENT_LBUTTONDOWN: # 左键点击
point1 = (x, y)
cv2.circle(img2, point1, 10, (0, 255, 0), 5)
cv2.imshow('image', img2)
elif event == cv2.EVENT_MOUSEMOVE and (flags & cv2.EVENT_FLAG_LBUTTON): # 按住左键拖曳
cv2.rectangle(img2, point1, (x, y), (255, 0, 0), 5)
cv2.imshow('image', img2)
elif event == cv2.EVENT_LBUTTONUP: # 左键释放
point2 = (x, y)
cv2.rectangle(img2, point1, point2, (0, 0, 255), 5)
cv2.imshow('image', img2)
# 返回框选矩形左上角点的坐标、矩形宽度、高度以及矩形包含的图像
param['x'] = min(point1[0], point2[0])
param['y'] = min(point1[1], point2[1])
param['width'] = abs(point1[0] - point2[0])
param['height'] = abs(point1[1] - point2[1])
param['temp'] = img[param['y']:param['y']+param['height'],
param['x']:param['x']+param['width']]
def main():
global img
cap = cv2.VideoCapture(0)
# 获取视频第一帧
ret, frame = cap.read()
img = frame
# 框选目标并返回相应信息:rect为四个信息,temp为框选出来的图像
rect, temp = get_tr(img)
(a, b, c) = temp.shape
y = [a/2, b/2]
# 计算目标图像的权值矩阵
m_wei = np.zeros((a,b))
for i in range(a):
for j in range(b):
z = (i-y[0])**2 + (j-y[1])**2
m_wei[i, j] = 1 - z/(y[0]**2 + y[1]**2)
# 计算目标权值直方图
C = 1/sum(sum(m_wei))
hist1 = np.zeros(16**3)
for i in range(a):
for j in range(b):
q_b = math.floor(float(temp[i, j, 0]) / 16)
q_g = math.floor(float(temp[i, j, 1]) / 16)
q_r = math.floor(float(temp[i, j, 2]) / 16)
q_temp1 = q_r*256 + q_g*16 + q_b
hist1[int(q_temp1)] = hist1[int(q_temp1)] + m_wei[i, j]
hist1 = hist1*C
# 接着读取视频并进行目标跟踪
while(1):
ret, frame = cap.read()
if ret == True:
Img = frame
num = 0
Y = [1, 1]
# mean shift迭代
while (np.sqrt(Y[0]**2+Y[1]**2) > 0.5) & (num < 20):
num = num+1
# 计算候选区域直方图
temp2 = Img[int(rect[1]):int(rect[1]+rect[3]), int(rect[0]):int(rect[0]+rect[2])]
hist2 = np.zeros(16**3)
q_temp2 = np.zeros((a, b))
for i in range(a):
for j in range(b):
q_b = math.floor(float(temp2[i, j, 0]) / 16)
q_g = math.floor(float(temp2[i, j, 1]) / 16)
q_r = math.floor(float(temp2[i, j, 2]) / 16)
q_temp2[i, j] = q_r * 256 + q_g * 16 + q_b
hist2[int(q_temp2[i, j])] = hist2[int(q_temp2[i, j])] + m_wei[i, j]
hist2 = hist2*C
w = np.zeros(16**3)
for i in range(16**3):
if hist2[i] != 0:
w[i] = math.sqrt(hist1[i]/hist2[i])
else:
w[i] = 0
sum_w = 0
sum_xw = [0, 0]
for i in range(a):
for j in range(b):
sum_w = sum_w + w[int(q_temp2[i, j])]
sum_xw = sum_xw + w[int(q_temp2[i, j])]*np.array([i-y[0], j - y[1]])
Y = sum_xw/sum_w
# 位置更新
rect[0] = rect[0] + Y[1]
rect[1] = rect[1] + Y[0]
v0 = int(rect[0])
v1 = int(rect[1])
v2 = int(rect[2])
v3 = int(rect[3])
pt1 = (v0, v1)
pt2 = (v0+v2, v1+v3)
# 画矩形
IMG = cv2.rectangle(Img, pt1, pt2, (0, 0, 255), 2)
cv2.imshow('IMG', IMG)
k = cv2.waitKey(60) & 0xff
if k == 27:
break
else:
break
if __name__ == '__main__':
main()