文章目录

一、MeanShift算法介绍

Meanshift算法是Fukunaga于1975年提出的,其基本思想是利用概率密度的梯度爬升来寻找局部最优。到了1995年,YizongCheng针对离x越近的采样点对x周围的统计特性越有效,定义了一族核函数,并根据所有样本点的重要性不同,设定了一个权重系数,扩大了MeanShift的使用范围。

简单的示例图:

OpenCV + CPP 系列(圩三)视频分析(CAMshift)_opencv

算法原理

给定 OpenCV + CPP 系列(圩三)视频分析(CAMshift)_opencv_02 维空间中的 OpenCV + CPP 系列(圩三)视频分析(CAMshift)_搜索_03 个样本点 OpenCV + CPP 系列(圩三)视频分析(CAMshift)_ide_04,在x点的MeanShift向量的基本形式定义为:
OpenCV + CPP 系列(圩三)视频分析(CAMshift)_直方图_05

其中,OpenCV + CPP 系列(圩三)视频分析(CAMshift)_ide_06 是一个半径为 h 的高维球区域,k 表示 n 个样本点中有 k个点落入区域 OpenCV + CPP 系列(圩三)视频分析(CAMshift)_ide_06

在扩展的MeanShift中,引入了核函数和权重的概念,相当于给不同的点赋予不同的质量,求解这些不同质量分布的点的质心位置。MeanShift的拓展形式,并对其进行求导,可得:
OpenCV + CPP 系列(圩三)视频分析(CAMshift)_直方图_08

OpenCV + CPP 系列(圩三)视频分析(CAMshift)_opencv_09

meanShift算法总结

meanShift算法用于视频目标跟踪时,其实就是采用目标的颜色直方图作为搜索特征,通过不断迭代meanShift向量使得算法收敛于目标的真实位置,从而达到跟踪的目的。


(1)算法计算量不大,在目标区域已知的情况下完全可以做到实时跟踪;

(2)采用核函数直方图模型,对边缘遮挡、目标旋转、变形和背景运动不敏感。

缺点: (1)缺乏必要的模板更新;

(2)跟踪过程中由于窗口宽度大小保持不变,当目标尺度有所变化时,跟踪就会失败;


(3)当目标速度较快时,跟踪效果不好;


(4)直方图特征在目标颜色特征描述方面略显匮乏,缺少空间信息;

工程化改进和调整如下: (1)引入一定的目标位置变化的预测机制,从而更进一步减少meanShift跟踪的搜索时间,降低计算量;

(2)可以采用一定的方式来增加用于目标匹配的“特征”;


(3)将传统meanShift算法中的核函数固定带宽改为动态变化的带宽;


(4)采用一定的方式对整体模板进行学习和更新;


二、CAMShift跟踪介绍

CamShift算法,全称是 Continuously AdaptiveMeanShift,它是对Mean Shift 算法的改进,能够自动调节搜索窗口大小来适应目标的大小,可以跟踪视频中尺寸变化的目标。它也是一种半自动跟踪算法,需要手动标定跟踪目标。

CamShift基本思想是以视频图像中运动物体的颜色信息作为特征,对输入图像的每一帧分别作 Mean-Shift 运算,并将上一帧的目标中心和搜索窗口大小(核函数带宽)作为下一帧 Mean shift 算法的中心和搜索窗口大小的初始值,如此迭代下去,就可以实现对目标的跟踪。

因为在每次搜索前将搜索窗口的位置和大小设置为运动目标当前中心的位置和大小,而运动目标通常在这区域附近,缩短了搜索时间;另外,在目标运动过程中,颜色变化不大,故该算法具有良好的鲁棒性。已被广泛应用到运动人体跟踪,人脸跟踪等领域。

连续自适应的MeanShift跟踪算法

  • 窗口尺寸自动变化
  • 适合变形目标检测

算法流程:

  1. 获取ROI区域
  2. HSV颜色空间H通道直方图
  3. 直方图反向映射
  4. CAMShift位置跟踪
  5. 绘制位置并显示

RotatedRect

InputArray probImage,      反向投影的直方图


CV_IN_OUT Rect&

window,  初始化窗口大小(即手动选择的ROI)


TermCriteria

criteria      meanShift()的停止迭代标准


)


头文件 ​​machine_learning_all.h​​:

#pragma once
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;


class Machine_learning{
public:
void video_CAMShift(const char* mp4_path);
void video_calcOpticalFlowFarneback(const char* mp4_path);
};

主函数​​main.cpp​

#include "machine_learning_all.h"


int main(int argc, char** argv) {
const char* mp4_path = "D:\\Desktop\\01.mp4";
Machine_learning ml;
ml.video_CAMShift(mp4_path);
ml.video_calcOpticalFlowFarneback(mp4_path);

waitKey(0);
destroyAllWindows();
return 0;
}

效果展示

void Machine_learning::video_CAMShift(const char* mp4_path) {
VideoCapture capture(mp4_path);
if (!capture.isOpened()) {
cout << "video read failed!" << endl;
}

int s_min = 15;
int v_min = 40;
int v_max = 255;
int bins = 16;

float hrange[] = { 0,180 };
const float *hranges = hrange;

Mat drawImg = Mat::zeros(300, 300, CV_8UC3);

bool first_frame = true;
Rect select_rect(0, 0, 0, 0);
Mat frame, hsv_img, hue_img, mask_img, hist_img, back_project;
while (capture.read(frame)) {
if (first_frame) {
Rect2d first = selectROI("CAMShift", frame);
select_rect.x = first.x;
select_rect.y = first.y;
select_rect.width = first.width;
select_rect.height = first.height;
cout << "ROI=" << select_rect << endl;
}

//BGR2HSV
cvtColor(frame, hsv_img, COLOR_BGR2HSV);
inRange(hsv_img, Scalar(0, s_min, v_min), Scalar(180, v_max, v_max), mask_img);
hue_img = Mat(hsv_img.size(), hsv_img.depth());
int channels[] = { 0,0 };
mixChannels(&hsv_img, 1, &hue_img, 1, channels, 1);

if (first_frame) {
// calculate ROI histogram
Mat roi(hue_img, select_rect);
Mat mask_roi(mask_img, select_rect);
calcHist(&roi, 1, 0, mask_roi, hist_img, 1, &bins, &hranges);
normalize(hist_img, hist_img, 0, 255, NORM_MINMAX);

// show histogram
int bin_w = drawImg.cols / bins;
Mat color_idx = Mat(1, bins, CV_8UC3);
for (int i = 0; i < bins; i++) {
color_idx.at<Vec3b>(0, i) = Vec3b(saturate_cast<uchar>(i * 180 / bins), 255, 255);
}
cvtColor(color_idx, color_idx, COLOR_HSV2BGR);
for (int i = 0; i < bins; i++) {
int value = saturate_cast<int>(hist_img.at<float>(i) * drawImg.rows / 255);
rectangle(
drawImg,
Point(i * bin_w, drawImg.rows),
Point((i + 1) * bin_w, drawImg.rows - value),
Scalar(color_idx.at<Vec3b>(0, i)),
-1, 8, 0
);
}
}

// backproject && CAMShift
calcBackProject(&hue_img, 1, 0, hist_img, back_project, &hranges);
back_project &= mask_img;
RotatedRect trackBox = cv::CamShift(
back_project, select_rect,
TermCriteria(TermCriteria::COUNT | TermCriteria::EPS, 10, 1)
);
ellipse(frame, trackBox, Scalar(0, 0, 255), 2, 8);

// fraw locate on frame
imshow("frame", frame);
imshow("hue_img", hue_img);
imshow("drawImg", drawImg);
first_frame = false;
if (waitKey(50) == 27) {
break;
}
}
}

OpenCV + CPP 系列(圩三)视频分析(CAMshift)_opencv_10