meanshift实现视频跟踪

import matplotlib.pyplot as plt
import cv2 as cv
import numpy as np
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正确显示中文

# meanshift算法大体流程
'''
1.首先在图像上选定一个目标区域
2.计算选定区域的直方图分布,一般是HSV色彩空间的直方图
3.对下一帧图像b同样计算直方图
4.计算图像b中与选定区域直方图分布最为相似的区域,使用meanshift算法将选点区域沿着
  最为相似的部分移动,直到找到最相似的区域,便完成了在图像b中的目标追踪
5.重复3到4的过程,就完成了整个视频的目标追踪

通常情况下我们使用直方图反向投影得到的图像和第一帧目标对象的起始位置,当目标对象的
移动会反映到直方图投影中,meanshift算法就把我们的窗口移动到反向投影图像中灰度密度
最大的区域了.


直方图反向投影的流程是:
    假设我们有一张100x100的输入图像,有一张10x10的模板图像,查找过程是这样的:
        1.从输入图像的左上角(0,0)开始,切割一块(0,0)至(10,10)的临时图像
        2.生成临时图像的直方图
        3.用临时图像的直方图和模板图的直方图作比较,对比结果记为c
        4.直方图对比结果c,就是结果图像(0,0)处的像素值
        5.切割输入图像(0,1)至(10,11)的临时图像,对比直方图,并记录到结果图
        6.重复1~5步直到输入图像的右下角,就形成了直方图的反向投影

API:
    cv.meanshift(probImage,window,criteria)
    参数:
        probImage:ROI区域,即目标的直方图的反向投影
        window:初始搜索窗口,就是定义ROI的rect
        criteria:确定搜索通知的准则,主要有迭代次数达到设置的最大值,窗口中心的漂移值
                大于某个设定的限值等

    实现meanshift的主要流程:
        1.读取视频文件
        2.感兴趣的区域设置:获取每一帧图像,并设置目标区域,即感兴趣的区域
        3.计算直方图:计算感兴趣区域的HSV直方图,并进行归一化
        4.目标追踪:设置窗口搜索停止条件,直方图反向投影,进行目标追踪,并在目标位置绘制矩形框
        
'''

# 读取视频
cap = cv.VideoCapture()
cap.open('video/skip.mp4')


#获取视频的宽高
frame_w,frame_h=int(cap.get(3)),int(cap.get(4))
#设置视频编码格式
fourcc=cv.VideoWriter_fourcc('M','J','P','G')
#创建保存视频对象
out=cv.VideoWriter('video/result.mp4',fourcc,25,(frame_w,frame_h))

# 获取第一帧图像,并指定目标位置
ret, frame = cap.read()

# 目标位置(行,高,列,宽)
r, h, c, w = 120, 80, 270, 30

window = (c, r, w, h)
# cv.imshow('frame',frame)
# cv.waitKey()
# 指定目标的感兴趣区域
roi = frame[r:r + h, c:c + w]
cv.imshow('ww', roi)
cv.waitKey()

# 计算直方图
hsv_roi = cv.cvtColor(roi, cv.COLOR_BGR2HSV)  # 转成HSV格式
mask = cv.inRange(hsv_roi, np.array((0., 60., 32.)), np.array((180., 255., 255.)))  # 将低亮度的值忽略掉

roi_hist = cv.calcHist([hsv_roi], [0], mask, [180], [0, 180])

# 归一化
cv.normalize(roi_hist, roi_hist, 0, 255, cv.NORM_MINMAX)

# 目标追踪
# 设置窗口的终止条件:最大迭代次数为10,窗口中心漂移最小值为1
term_crit = (cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 1)

while True:
    # 获取每一帧图像
    ret, frame = cap.read()
    if ret:
        # 计算直方图的反向投影
        hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)  # 转成HSV
        dst = cv.calcBackProject([hsv], [0], roi_hist, [0, 180], 1)

        # 进行meanshift跟踪
        ret, window = cv.meanShift(dst, window, term_crit)

        # 将追踪的位置绘制在视频上,并进行显示
        x, y, w, h = window
        print(f'位置:{window[:2]}')
        cv.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
        cv.imshow('frame', frame)
        out.write(frame)
        cv.waitKey(25)
    else:
        break

# 资源释放
cap.release()
out.release()
cv.destroyAllWindows()

效果如下:


OpenCV实现视频中目标的跟踪