利用粒子滤波算法对摄像头输入的实时视频进行目标跟踪(opencv-python)

一、概述

目标跟踪是指对输入视频文件或实时摄像头图像中的运动物体进行跟踪,常见的目标跟踪算法有Kalman滤波、粒子滤波、GoturnUnicorn Traker 以及KCF等。本实验基于粒子滤波算法(Particle Filter)对通过摄像头输入的实时视频进行目标跟踪。

基于PF的目标跟踪,每次通过当前的跟踪结果重采样粒子的分布,然后根据粒子的分布对粒子进行扩散,再通过扩散的结果来重新观察目标的状态,最后归一化更新目标的状态。此算法的特点是跟踪速度特别快,而且能解决目标的部分遮挡问题,在实际工程应用过程中越来越多的被使用。

其主要步骤如下:

二、效果展示

首先框选目标

选中目标后,移动目标: 

      可见,随着目标移动,蓝框和绿框时钟框住目标并跟随其移动。其中绿色框为跟踪过程中粒子所在位置的矩形框,是对目标区域的预测。蓝色框为最终得到的跟踪目标区域。 

代码如下:

#####################################################################

# Example : Condensation based cam shift object track processing
# from a video file specified on the command line (e.g. python FILE.py video_file)
# or from an attached web camera

# N.B. use mouse to select region

# Author : Toby Breckon, toby.breckon@durham.ac.uk

# Copyright (c) 2017 Toby Breckon
#                    Durham University, UK
# License : LGPL - http://www.gnu.org/licenses/lgpl.html

#####################################################################

import cv2
import math
import numpy as np
import Condensation as cons

#####################################################################

keep_processing = True;
selection_in_progress = False; # support interactive region selection
fullscreen = False; # run in fullscreen mode

# select a region using the mouse

boxes = [];
current_mouse_position = np.ones(2, dtype=np.int32);
def on_mouse(event, x, y, flags, params):

    global boxes;
    global selection_in_progress;

    current_mouse_position[0] = x;
    current_mouse_position[1] = y;

    if event == cv2.EVENT_LBUTTONDOWN:
        boxes = [];
        sbox = [x, y];
        selection_in_progress = True;
        boxes.append(sbox);

    elif event == cv2.EVENT_LBUTTONUP:
        ebox = [x, y];
        selection_in_progress = False;
        boxes.append(ebox);

# return centre of a set of points representing a rectangle

def center(points):
    x = np.float32((points[0][0] + points[1][0] + points[2][0] + points[3][0]) / 4.0)
    y = np.float32((points[0][1] + points[1][1] + points[2][1] + points[3][1]) / 4.0)
    return np.array([np.float32(x), np.float32(y)], np.float32)



def nothing(x):
    pass

# draw a cross on the specified image at location, colour, size (d)
# specified

def drawCross(img, center, color, d):
    #On error change cv2.CV_AA for cv2.LINE_AA
    cv2.line(img, (center[0] - d, center[1] - d), \
             (center[0] + d, center[1] + d), color, 2, cv2.LINE_AA, 0)
    cv2.line(img, (center[0] + d, center[1] - d), \
             (center[0]- d, center[1] + d), color, 2, cv2.LINE_AA, 0)

# define video capture object

cap = cv2.VideoCapture(0,cv2.CAP_DSHOW)  #or ("filename.avi")
if cap.isOpened():
    print("video is OK!")
else:
    print("video fail!")
 

# define display window name

windowName = "Condensation Tracking"; # window name
#windowName2 = "Hue histogram back projection"; # window name
#windowNameSelection = "initial selected region";

# init Condensation object

dimensions = 2          # number of parameters for tracking
nParticles = 100        # number of particles to use
xRange = 640.0          # image width
yRange = 480.0          # image hieght
LB = [0.0, 0.0]         # lower bounds on sampling
UB = [xRange, yRange]   # upper bounds on sampling

tracker = cons.Condensation(dimensions, dimensions, nParticles)
tracker.cvConDensInitSampleSet(LB, UB)
tracker.DynamMatr = [[1.0, 0.0],[0.0, 1.0]]

# if command line arguments are provided try to read video_name
# otherwise default to capture from attached H/W camera
'''
if (((args.video_file) and (cap.open(str(args.video_file))))
    or (cap.open(args.camera_to_use))):
'''
state=1
if(state==1):
   # cap.open(0)
    # create window by name (note flags for resizable or not)

    cv2.namedWindow(windowName, cv2.WINDOW_NORMAL);
    
    # set a mouse callback

    cv2.setMouseCallback(windowName, on_mouse, 0);
    cropped = False;

    print("\nObservation in image: BLUE");
    print("Prediction from Condensation Tracker: GREEN\n");

    # Setup the termination criteria for search, either 10 iteration or
    # move by at least 1 pixel pos. difference
    term_crit = ( cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1 )

    frame_count = 0
    while (keep_processing):
        # replaying video 
        frame_count = frame_count + 1
        if frame_count == int(cap.get(cv2.CAP_PROP_FRAME_COUNT)):
            frame_count = 0
            cap.set(cv2.CAP_PROP_POS_FRAMES, 0)

        if (cap.isOpened):
            ret, frame = cap.read();

        # set parameters from track bars
        
        s_lower = 60
        s_upper = 255
        v_lower = 30
        v_upper = 255
  
        # select region using the mouse and display it

        if (len(boxes) > 1) and (boxes[0][1] < boxes[1][1]) and (boxes[0][0] < boxes[1][0]):
            crop = frame[boxes[0][1]:boxes[1][1],boxes[0][0]:boxes[1][0]].copy()

            h, w, c = crop.shape;   # size of template
            if (h > 0) and (w > 0):
                cropped = True;
                # convert region to HSV
                hsv_crop =  cv2.cvtColor(crop, cv2.COLOR_BGR2HSV);
                # select all Hue and Sat. values (0-> 180) but eliminate values with very low
                # saturation or value (due to lack of useful colour information)
                mask = cv2.inRange(hsv_crop, np.array((0., float(s_lower),float(v_lower))), np.array((180.,float(s_upper),float(v_upper))));
                # construct a histogram of hue and saturation values and normalize it
                crop_hist = cv2.calcHist([hsv_crop],[0, 1],mask,[180, 255],[0,180, 0, 255]);
                cv2.normalize(crop_hist,crop_hist,0,255,cv2.NORM_MINMAX);
                # set intial position of object
                track_window = (boxes[0][0],boxes[0][1],boxes[1][0] - boxes[0][0],boxes[1][1] - boxes[0][1]);
            # reset list of boxes
            boxes = [];
        # interactive display of selection box
        if (selection_in_progress):
            top_left = (boxes[0][0], boxes[0][1]);
            bottom_right = (current_mouse_position[0], current_mouse_position[1]);
            cv2.rectangle(frame,top_left, bottom_right, (0,255,0), 2);
        # if we have a selected region
        if (cropped):
            # convert incoming image to HSV
            img_hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV);
            img_bproject = cv2.calcBackProject([img_hsv],[0,1],crop_hist,[0,180,0,255],1);
            # apply camshift to predict new location (observation)        
            ret, track_window = cv2.CamShift(img_bproject, track_window, term_crit);
            # draw observation on image
            x,y,w,h = track_window;
            frame = cv2.rectangle(frame, (x,y), (x+w,y+h), (255,0,0),2);
            # extract centre of this observation as points
            pts = cv2.boxPoints(ret)
            pts = np.int0(pts)
            pts = center(pts)
            # use to update Condensation tracker
            for hypothesis in range(tracker.SamplesNum):
                # calculate the confidence based on the observations
                diffX = (pts[0] - tracker.flSamples[hypothesis][0]) / xRange;
                diffY = (pts[1] - tracker.flSamples[hypothesis][1]) / yRange;
                tracker.flConfidence[hypothesis] = 1.0/(np.sqrt(np.power(diffX,2) + \
                                                    np.power(diffY,2)))
            tracker.cvConDensUpdateByTime();
            # get new Condensation tracker prediction
            predictionF = tracker.State;
            prediction = [int(s) for s in predictionF]
            # draw predicton on image
            frame = cv2.rectangle(frame, (prediction[0]-int(0.5*w),prediction[1]-int(0.5*h)), (prediction[0]+int(0.5*w),prediction[1]+int(0.5*h)), (0,255,0),2);
            # draw all the tracker hypothesis samples on the image
            for j in range(len(tracker.flSamples)):
                posNew = [int(s) for s in tracker.flSamples[j]]
                drawCross(frame, posNew, (255, 255, 0), 2)

        else:
       
            img_hsv =  cv2.cvtColor(frame, cv2.COLOR_BGR2HSV);
            mask = cv2.inRange(img_hsv, np.array((0., float(s_lower),float(v_lower))), np.array((180.,float(s_upper),float(v_upper))));
        # display image
        cv2.imshow(windowName,frame);
        cv2.setWindowProperty(windowName, cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN & fullscreen);

        #key = cv2.waitKey(max(2, 40 - int(math.ceil(stop_t)))) & 0xFF;
        key = cv2.waitKey(30)
        # It can also be set to detect specific key strokes by recording which key is pressed

        # e.g. if user presses "x" then exit / press "f" for fullscreen display

        if (key == ord('x')):
            keep_processing = False;
        elif (key == ord('f')):
            fullscreen = not(fullscreen);

    # close all windows

    cv2.destroyAllWindows()

else:
    print("No video file specified or camera connected.");

Condensation.py

"""

Based on the OpenCV Version of the Condensation Algorithm and the
Android Version available here:
https://android.googlesource.com/platform/external/opencv/+/android-5.1.1_r6/cv/src/cvcondens.cpp

Read before:
http://www.anuncommonlab.com/articles/how-kalman-filters-work/

/*M///
//
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
//  By downloading, copying, installing or using the software you agree to this license.
//  If you do not agree to this license, do not download, install,
//  copy or use the software.
//
//
//                        Intel License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//
//   * The name of Intel Corporation may not be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
"""
import numpy as np

# Condensation Algorithm.
# Python version based on the original OpenCv Version and the
# Android version
# Requires the initialization of DynamMatr
# Example: DynamMatr = [[1.0, 0.0], [0.0, 1.0]]
# The confidence must be defined
# Example:
# a = (PositionX - sample[i][0])/650
# b = (PositionY - sample[i][1])/650
# flConfidence[i]=1.0/(sqrt(a*a + b*b))
class Condensation():

    # Name: __init__
    # Creating variables
    # Parameters:
    #   DP         - dimension of the state vector
    #   MP         - dimension of the measurement vector
    #   SamplesNum - number of samples in sample set used in algorithm
    def __init__(self, dimDP, dimMP, samplesNum):

        if(dimDP < 0 or dimMP < 0 or samplesNum < 0):
            raise ValueError("Parameters out of range")

        self.SamplesNum = samplesNum
        self.DP = dimDP
        self.MP = dimMP
        self.flSamples =[]
        self.flNewSamples = np.empty([self.SamplesNum, self.DP], dtype=float)
        self.flConfidence = []
        self.flCumulative = np.zeros(self.SamplesNum, dtype=float)
        self.DynamMatr = []
        self.State = np.zeros(self.DP, dtype=float)
        self.lowBound=np.empty(self.DP, dtype=float)
        self.uppBound=np.empty(self.DP, dtype=float)


    # Name: cvConDensInitSampleSet
    # Initializing for the Condensation algorithm
    # Parameters:
    #   conDens     - pointer to CvConDensation structure
    #   lowerBound  - vector of lower bounds used to random update
    #                   of sample set
    #   upperBound  - vector of upper bounds used to random update
    #                   of sample set
    #
    def cvConDensInitSampleSet(self, lowerBound, upperBound):
        prob = 1.0/self.SamplesNum

        if((lowerBound is None) or (upperBound is None)):
            raise ValueError("Lower/Upper Bound")

        self.lowBound = lowerBound
        self.uppBound = upperBound

        # Generating the samples
        for j in range(self.SamplesNum):
            valTmp = np.zeros(self.DP, dtype=float)
            for i in range(self.DP):
                valTmp[i] = np.random \
                    .uniform(lowerBound[i],upperBound[i])
            self.flSamples.append(valTmp)
            self.flConfidence.append(prob)

    # Name:    cvConDensUpdateByTime
    # Performing Time Update routine for ConDensation algorithm
    # Parameters:
    def cvConDensUpdateByTime(self):
        valSum  = 0
        #Sets Temp To Zero
        self.Temp = np.zeros(self.DP, dtype=float)

        #Calculating the Mean
        for i in range(self.SamplesNum):
            self.State = np.multiply(self.flSamples[i], self.flConfidence[i])
            self.Temp = np.add(self.Temp, self.State)
            valSum += self.flConfidence[i]
            self.flCumulative[i] = valSum

        #Taking the new vector from transformation of mean by dynamics matrix
        self.Temp = np.multiply(self.Temp, (1.0/valSum))
        for i in range(self.DP):
            self.State[i] = np.sum(np.multiply(self.DynamMatr[i],
                                               self.Temp[i]))

        valSum = valSum / float(self.SamplesNum)

        #Updating the set of random samples
        for i in range(self.SamplesNum):
            j = 0
            while (self.flCumulative[j] <= float(i)*valSum and
                j < self.SamplesNum -1):
                j += 1
            self.flNewSamples[i] = self.flSamples[j]

        lowerBound = np.empty(self.DP, dtype=float)
        upperBound = np.empty(self.DP, dtype=float)
        for j in range(self.DP):
            lowerBound[j] = (self.lowBound[j] - self.uppBound[j])/5.0
            upperBound[j] = (self.uppBound[j] - self.lowBound[j])/5.0

        #Adding the random-generated vector to every vector in sample set

        # which assumes a moiton model of equal likely motion in all directions

        RandomSample = np.empty(self.DP, dtype=float)
        for i in range(self.SamplesNum):
            for j in range(self.DP):
                RandomSample[j] = np.random.uniform(lowerBound[j],
                                                    upperBound[j])
                self.flSamples[i][j] = np.sum(np.multiply(self.DynamMatr[j],
                                               self.flNewSamples[i][j]))

            self.flSamples[i] = np.add(self.flSamples[i], RandomSample)

  • 3
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

via amiko

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值