目录
光流
目标跟踪
一、光流
使用OpenCV光流分析,跟踪蚂蚁的轨迹:

代码实现:
import numpy as np
import cv2
if __name__ == '__main__':
    cap = cv2.VideoCapture('ant.mp4')
    # ShiTomasi 角点检测参数
    feature_params = dict(
        maxCorners=100,
        qualityLevel=0.5,
        minDistance=30,
        blockSize=10
    )
    # Lucas Kanada 光流检测参数
    lk_params = dict(
        winSize=(15, 15),
        maxLevel=2,
        criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03)
    )
    # 获取第一帧并发现角点
    ret, last_current_frame = cap.read()
    last_gray = cv2.cvtColor(last_current_frame, cv2.COLOR_BGR2GRAY)
    p0 = cv2.goodFeaturesToTrack(last_gray, mask=None, **feature_params)
    mask = np.zeros_like(last_current_frame)
    color = np.random.randint(0, 255, (100, 3))
    while (1):
        ret, current_frame = cap.read()
        if not ret:
            break
        current_gray = cv2.cvtColor(current_frame, cv2.COLOR_BGR2GRAY)
        # TODO(You): 请在此编写光流跟踪和绘制代码
        p1, st, err = cv2.calcOpticalFlowPyrLK(
        last_gray, current_gray, p0, None, **lk_params)
        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()
            a, b = int(a), int(b)
            c, d = int(c), int(d)
            mask = cv2.line(mask, (a, b), (c, d), color[i].tolist(), 2)
            current_frame = cv2.circle(
                current_frame, (a, b), 5, color[i].tolist(), -1)
        current_img = cv2.add(current_frame, mask)
        cv2.imshow('current_img', current_img)
        k = cv2.waitKey(30) & 0xff
        if k == 27:
            break
        last_gray = current_gray.copy()
        p0 = good_new.reshape(-1, 1, 2)
    cv2.destroyAllWindows()
    cap.release() 
二、目标跟踪

在视频分析(或视频结构化)应用开发中,多目标跟踪是非常重要的一个环节。它能有效弥补上一个目标检测环节中算法的不足,如检测算法输出坐标不稳定、漏检等。与此同时,跟踪算法输出的目标轨迹(track-id)对于应用下阶段的行为分析环节也有着至关重要的作用。下面是常见视频分析类应用系统结构:
 
目标检测算法输出单帧检测结果,目标跟踪算法负责将前后2帧中的目标关联起来、给予唯一标识track-id。假设t帧中检测到了M个目标,t+1帧中检测到了N个目标,跟踪算法本质上是M->N的匹配关联过程。
 
匹配过程中,目标可以分为以下三大类:
matched_tracks,t帧目标出现,t+1帧该目标仍然出现,算法匹配上。unmatched_tracks,t帧目标出现,t+1帧该目标消失,算法未匹配上。unmatched_detections,t帧目标不存在,t+1帧该目标出现,新增检测目标。
其中,对于2和3来说,跟踪算法需要考虑:
t帧目标出现,t+1帧目标其实仍然存在,但是检测算法出现短暂漏检,误认为其消失。此时的解决方案是: 某帧未被匹配到的tracks不要立即清除,而是做若干帧的缓存,等待若干帧后检测算法恢复检测
t帧目标不存在,t+1帧该目标仍然不存在,但是检测算法出现短暂误检,误认为其出现。此时的解决方案是:新增的检测目标不要立即生效,而是做若干帧的缓存,等检测算法连续检测超过若干帧、并且都能匹配关联上后再生效
之所以要考虑以上2点,主要原因是对于连续视频帧而言,大部分检测算法基本无法做到100%连续、稳定检测,出现短暂的误检、漏检非常正常。
代码实现如下:
# 定义跟踪算法类
class Tracker(object):
  # 初始化参数
  def __init__(self, max_age=1, min_hits=3, iou_threshold=0.3):
    self.max_age = max_age
    self.min_hits = min_hits
    self.iou_threshold = iou_threshold
    self.trackers = []
    self.frame_count = 0
  # 跟踪函数,每帧检测结果返回后,调用一次update
  def update(self, dets=np.empty((0, 5))):
    self.frame_count += 1
    
    trks = np.zeros((len(self.trackers), 5))
    to_del = []
    ret = []
    for t, trk in enumerate(trks):
      pos = self.trackers[t].predict()[0]
      trk[:] = [pos[0], pos[1], pos[2], pos[3], 0]
      if np.any(np.isnan(pos)):
        to_del.append(t)
    
    trks = np.ma.compress_rows(np.ma.masked_invalid(trks))
    for t in reversed(to_del):
      self.trackers.pop(t)
    
    # 匹配关联
    matched, unmatched_dets, unmatched_trks = associate_detections_to_trackers(dets, trks, self.iou_threshold)
    # 后处理逻辑
    # TO-DO your code...
    # 更新matched_tracks
    for m in matched:
      self.trackers[m[1]].update(dets[m[0], :])
    # 初始化unmatched_detections,假设是当前帧新出现的检测目标
    for i in unmatched_dets:
        trk = KalmanBoxTracker(dets[i,:])
        self.trackers.append(trk)
    i = len(self.trackers)
    for trk in reversed(self.trackers):
        d = trk.get_state()[0]
        # 输出满足条件的tracks
        if (trk.time_since_update <= self.max_age) and (trk.hit_streak >= self.min_hits or self.frame_count <= self.min_hits):
          ret.append(np.concatenate((d,[trk.id+1])).reshape(1,-1))
        
        i -= 1
        # 移除超过self.max_age次的漏检目标
        if(trk.time_since_update > self.max_age):
          self.trackers.pop(i)
    # 返回跟踪结果 [[left, top, right, bottom, track-id]...]
    if(len(ret) > 0):
      return np.concatenate(ret)
    return np.empty((0, 5)) 
代码相关说明:
self.max_age代表跟踪算法允许出现的最大漏检帧数self.min_hints代表跟踪算法要求的最低连续匹配帧数self.trackers代表跟踪算法维持的目标集合(已生成track-id)update(self, dets)代表跟踪函数,其中参数dets代表t+1帧中目标检测结果list[[left, top, right, bottom, score]...],即t+1帧中待匹配的detectionsassociate_detections_to_trackers(...)代表IOU+卡尔曼滤波匹配算法,返回上面提到的matched_tracks,unmatched_tracks,unmatched_detections三个值time_since_update代表目标当前漏检帧数hit_streak代表目标当前连续匹配帧数



















