基于均值迁移的对象移动分析(Meanshift)
✏️ ⛳️
概述
本质:
✔️ Mean Shift均值漂移算法是无参密度估计理论的一种,无参密度估计不需要事先知道对象的任何先验知识,完全依靠训练数据进行估计,并且可以用于任意形状的密度估计,在某一连续点处的密度函数值可由该点邻域中的若干样本点估计得出。
直观的理解:
✔️ 一堆点集,一个圆形的窗口在不断的移动,移动的方向是沿着点的密度最大的区域移动,图示如下:
函数
- 在OpenCV里使用均值平移,首先需要设置目标,找到它的直方图,这样我们可以为了计算均值平移向后投影目标到每一帧上,同时也需要提供窗口的初始位置。
- 对于直方图,只考虑色调(H),要避免低光线带来的错误值,低光线的值使用
cv2.inRange()
函数来丢弃掉。
ret, track_window = cv2.meanShift(probImage, window, criteria )
输入值
- probImage --> 输入图像,是直方图反向投影的结果
- window --> 搜索窗口,ROI对象区域
- criteria --> 均值迁移停止条件
返回值
- ret --> 返回迭代次数
- track_window --> 返回迭代后的窗口
示例代码
import cv2 as cv
cap = cv.VideoCapture('test.mp4')
# 读取第一帧
ret,frame = cap.read()
cv.namedWindow("Demo", cv.WINDOW_AUTOSIZE)
# 可以在图片上选择roi区域
x, y, w, h = cv.selectROI("Demo", frame, True, False)
track_window = (x, y, w, h)
# 获取ROI直方图
roi = frame[y:y+h, x:x+w]
hsv_roi = cv.cvtColor(roi, cv.COLOR_BGR2HSV)
# inRange函数设置亮度阈值
# 去除低亮度的像素点的影响
# 将低于和高于阈值的值设为0
mask = cv.inRange(hsv_roi, (26, 43, 46), (34, 255, 255))
# 计算直方图,参数为 图片(可多),通道数,蒙板区域,直方图长度,范围
roi_hist = cv.calcHist([hsv_roi],[0],mask,[180],[0,180])
# 归一化,像素值区间[0,255]
cv.normalize(roi_hist,roi_hist,0,255,cv.NORM_MINMAX)
# 设置迭代的终止标准,最多十次迭代
term_crit = ( cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 1 )
while True:
ret, frame = cap.read()
if ret is False:
break;
hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
# 直方图反向投影
dst = cv.calcBackProject([hsv],[0],roi_hist,[0,180],1)
# 均值迁移,搜索更新roi区域
ret, track_window = cv.meanShift(dst, track_window, term_crit)
# 绘制窗口
x,y,w,h = track_window
cv.rectangle(frame, (x,y), (x+w,y+h), 255,2)
cv.imshow('Demo',frame)
k = cv.waitKey(60) & 0xff
if k == 27:
break
else:
cv.imwrite(chr(k)+".jpg",frame)
cv.destroyAllWindows()
cap.release()
基于连续自适应均值迁移的对象移动分析(Camshift)
概述
✔️ 由于mean-shift算法的窗口是固定大小的,而我们需要让窗口大小和目标的大小以及旋转相适应,因此提出了Camshift。
✔️ CAM是连续自适应的均值迁移跟踪算法,它跟均值迁移相比较有两个改进 - 会根据跟踪对象大小变化自动调整搜索窗口大小 - 返回位置信息更加完整,包含了位置与角度信息。
✔️ CAM先采用均值平移,当平移覆盖后,更新窗口大小,公式如下:
它也计算最适合的椭圆的方向,然后它在使用新的大小的窗口在之前的位置开始进行均值平移,过程不断继续直到到达指定的准确率。
函数
ret, track_box = cv2.CamShift(
InputArray probImage,
Rect & window,
TermCriteria criteria )
输入
- probImage --> 输入图像,是直方图反向投影的结果
- window --> 搜索窗口,ROI对象区域
- criteria --> 均值迁移停止条件
输出
- ret --> 返回可变角度的最优外接椭圆
- track_box --> 返回新的窗口
示例代码
import cv2 as cv
import numpy as np
cap = cv.VideoCapture('test.mp4')
# 读取第一帧
ret,frame = cap.read()
cv.namedWindow("Demo", cv.WINDOW_AUTOSIZE)
# 选择ROI区域
x, y, w, h = cv.selectROI("Demo", frame, True, False)
track_window = (x, y, w, h)
# 获取ROI直方图
roi = frame[y:y+h, x:x+w]
hsv_roi = cv.cvtColor(roi, cv.COLOR_BGR2HSV)
mask = cv.inRange(hsv_roi, (26, 43, 46), (34, 255, 255))
roi_hist = cv.calcHist([hsv_roi],[0],mask,[180],[0,180])
cv.normalize(roi_hist,roi_hist,0,255,cv.NORM_MINMAX)
# 设置迭代的终止标准,最多十次迭代
term_crit = ( cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 1 )
while True:
ret, frame = cap.read()
if ret is False:
break;
hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
dst = cv.calcBackProject([hsv],[0],roi_hist,[0,180],1)
# 搜索更新roi区域
ret, track_box = cv.CamShift(dst, track_window, term_crit)
# 可变角度的矩形框
pts = cv.boxPoints(ret)
pts = np.int0(pts)
cv.polylines(frame, [pts], True, (0, 255, 0), 2)
# 更新窗口
track_window = track_box
#print(track_box)
# 绘制窗口CAM,目标椭圆图
cv.ellipse(frame, ret, (0, 0, 255), 3, 8)
cv.imshow('Demo',frame)
k = cv.waitKey(50) & 0xff
if k == 27:
break
else:
cv.imwrite(chr(k)+".jpg",frame)
cv.destroyAllWindows()
cap.release()
对象移动轨迹绘制
✔️ 移动对象分析,我们可以绘制对象运行轨迹曲线,这个主要是根据移动对象窗口轮廓,获取中心位置,然后使用中心位置进行绘制即可得到。
✔️ 大致的程序步骤如下:
1. 初始化路径点数组
2. 对每帧的预测轮廓提取中心位置添加到路径数组
3. 绘制路径曲线
代码
✔️ 我们只需要在上一节Camshift的代码中添加一个数组存储中心位置,然后使用cv2.line()
画出即可。
修改部分:
tracking_path = []
while True:
ret, frame = cap.read()
if ret is False:
break;
hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
dst = cv.calcBackProject([hsv],[0],roi_hist,[0,180],1)
# ,搜索更新roi区域, 保持运行轨迹
ret, track_box = cv.CamShift(dst, track_window, term_crit)
track_window = track_box
# 椭圆中心
pt = np.int32(ret[0])
if pt[0] > 0 and pt[1] > 0:
tracking_path.append(pt)
# 绘制跟踪对象位置窗口与对象运行轨迹
#cv.ellipse(frame, ret, (0, 0, 255), 3, 8)
for i in range(1, len(tracking_path)):
cv.line(frame, (tracking_path[i - 1][0], tracking_path[i - 1][1]),
(tracking_path[i][0], tracking_path[i][1]), (0, 255, 0), 2, 6, 0)
cv.imshow('Demo',frame)
k = cv.waitKey(50) & 0xff
if k == 27:
break
else:
cv.imwrite(chr(k)+".jpg",frame)
------------------------------------------可爱の分割线------------------------------------------
更多Opencv教程可以 Follow github的opencv教程,中文&English 欢迎Star❤️❤️❤️
JimmyHHua/opencv_tutorialsgithub.com参考
- OpenCV Tutorial 官网
-[Object Tracking] MeanShift