实验目的:进行目标追踪
实验过程:
1、使用HOG特征来表示要追踪的目标
2、根据目标在上一帧中的位置,来选定当前帧的可能位置,已此位置为圆心,step为半径获取一系列的候选目标。利用目标的移动历史数据推测出目标的移动方向,然后再推测树圆心。
3、用HOG特征表示候选目标。计算候选目标与追踪目标的相似度。相似度的计算使用KL散度
4、得到当前帧中追踪目标的位置,更新数据
5、重复2-4步
实验工具:Hog特征,KL散度( Kullback–Leibler divergence)
实验代码:
'''
Created on 2019年5月2日
@author: 17916
'''
import numpy as np
import cv2
import matplotlib.pyplot as pyplot
import random
import math
#视频地址
path = 'C:/Users/17916/Desktop/video/DCTrim3.mp4'
#根据以下4个参数,获取视频第一帧中对应部分,当作追踪目标
width = 145
height = 160
cx = 494
cy = 216
#追踪目标的HOG特征
tarVector = None
start = False
#获取候选目标时的半径半径
step = 20
#追踪目标在视频中的一系列位置
history = []
def makeVideo(path):
global start
global cx,cy,width,height,tarVector,step,history
cap = cv2.VideoCapture(path) #地址
fps = cap.get(cv2.CAP_PROP_FPS)
size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),
int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
videoWriter = cv2.VideoWriter('C:\\Users\\17916\\Desktop\\oto_other.avi', cv2.VideoWriter_fourcc('X', 'V', 'I', 'D'), fps, size)
while(True):
success,frame = cap.read()
if(success):
if(start==False):
history.insert(0, [cx,cy])
#获取追踪目标
srcImg = getImage(frame,cx,cy,width,height)
#获取追踪目标的HOG特征
tarVector = compute_hog(srcImg)
start = True
else:
#根据历史数据预测追踪目标的可能位置
cx,cy = predict(history,5,8)
#获取候选目标与其对应的位置
images,pos = getCandidateImage(frame,cx,cy,width,height,step)
tarPos = reckonMostSimilar(tarVector,images)
#更新目标位置
cx = pos[tarPos][0]
cy = pos[tarPos][1]
history.insert(0, [cx,cy])
labelTarget(frame,cx,cy,width,height)
videoWriter.write(frame)
else:
break;
cap.release()
#计算图像的Hog
def compute_hog(image):
winSize = (32, 32) # winSize = (64,64)
blockSize = (8,8) # blockSize = (16,16)
blockStride = (4,4)
cellSize = (4,4)
nbins = 9
hog = cv2.HOGDescriptor(winSize,blockSize,blockStride,cellSize,nbins)
winStride = (4,4)
padding = (4,4)
locations = ((10,20),(30,30),(50,50),(70,70),(90,90),(110,110),(130,130),(150,150),(170,170),(190,190))
hist = hog.compute(image,winStride,padding,locations)
return hist
#计算KL散度,用来作为图像间的相似度
def compute_KL(arr1,arr2):
if(len(arr1)!=len(arr2)):
print("长度不一,无法计算KL")
res = 0
for i in range(len(arr1)):
res += arr1[i]*abs(np.log(arr1[i]/arr2[i]))
return res
#截取部分图像
def getImage(image,centerX,centerY,w,h):
top = (int)(centerY - h/2)
left = (int)(centerX - w/2)
right = (int)(centerX + w/2)
bottom = (int)(centerY + h/2)
return image[top:bottom,left:right]
#获取候选目标
#image 表示视频中的当前帧
#centerX,centerY 表示在centerX,centerY位置获取一系列候选目标
#step 指示需要在某个半径内获取的目标
def getCandidateImage(image,centerX,centerY,w,h,step):
images = []
pos = []
images.append(getImage(image,centerX,centerY,w,h))
pos.append([centerX,centerY])
#根据随机出来的位置获取候选目标
points = getRandomPos(centerX,centerY,step,25)
for i in range(len(points)):
images.append(getImage(image, points[i][0], points[i][1],w,h))
pos.append([points[i][0],points[i][1]])
return images,pos
#在一系列的帧中,选出最有可能是目标的帧(给的是索引)
#tarVector 开始时标记的目标
#images 一系列的候选目标帧
def reckonMostSimilar(tarVector,images):
score = np.zeros(len(images),dtype = np.float)
for i in range(len(images)):
#计算每个候选目标的hog
temp = compute_hog(images[i])
#计算候选目标与标记的目标的相似度
score[i] = abs(compute_KL(tarVector+1,temp+1))
#选取最相似的候选目标
minScore = min(score)
resIndic = -1
print(minScore,score)
#获取最相似的候选目标的索引
for i in range(len(score)):
if(minScore == score[i]):
resIndic = i
return resIndic
#标记目标
def labelTarget(frame,centerX,centerY,w,h):
top = (int)(centerY - h/2)
left = (int)(centerX - w/2)
right = (int)(centerX + w/2)
bottom = (int)(centerY + h/2)
cv2.rectangle(frame,(left,top),(right,bottom),(0, 255, 0))
#预测目标的走向,下一帧要往那个方向移动,根据预测的目标的中心点px,py,去获得一系列候选图像
#history保存了整个移动过程,目标的中心点
#k表示根据前k帧,来预测目标的移动方向
#step用来推测出下一帧的中心点,step越大会在预测方向上移动越远,来预测目标
def predict(history,k,step):
num = min(k,len(history))
if(num==1):
return history[0][0],history[0][1]
vx = 0
vy = 0
#计算目标在目前时刻与上一时刻之间的向量(用向量和来估计目标的移动方向)
for i in range(num):
for j in range(i+1,num):
if(history[i][0]==history[j][0] or history[i][1]==history[j][1]):
continue
vx += history[i][0] - history[j][0]
vy += history[i][1] - history[j][1]
#根据向量,计算出目标可能出现的地点
if(vx==0 or vy == 0):
return history[0][0],history[0][1]
px =(int)( history[0][0] + step*reckonCos(vx,vy) )
py =(int) ( history[0][1] + step*reckonSin(vx,vy) )
return px,py
def reckonSin(x,y):
return y/math.sqrt(x*x+y*y)
def reckonCos(x,y):
return x/math.sqrt(x*x+y*y)
##在x,y点的周围获取num个候选点,每个点代表一个候选位置(可能包含目标图像)
def getRandomPos(x,y,radius,num):
res = []
for i in range(num):
cx = random.randint(x-radius,x+radius)
cy = random.randint(y-radius,y+radius)
res.append([cx,cy])
return res
makeVideo(path)
实验结果:
https://pan.baidu.com/s/19FprvGfhySjcZ5NSWglZyg
提取码:xwe8