Opencv学习笔记(十一)基于CAMshift算法的物体跟踪实现

CAMshift是一种基于对视频序列采用MeanShift算法进行运算,并将上一帧的结果(即搜索窗口的中心位置和窗口大小)作为下一帧MeanShift算法的搜索窗口的初始值的算法。如此迭代下去,我们便可以对视频中移动的物体进行追踪。

MeanShift算法:

首先,我们假设平面空间有这样随机分布的点,如下:
在这里插入图片描述
我们随机以某点为圆心,合适的半径r作圆:
在这里插入图片描述

然后落在圆中的所有点与圆心连接形成向量:

在这里插入图片描述

这样我们不难看出,点分布较密集的区域形成的向量数量较多,于是我们取点最密集的方向作为圆心偏移的方向,所有向量模的平均值为圆心偏移的位移

在这里插入图片描述
这样逐渐进行下去,最终我们的圆将会停留在空间中点的分布最为密集的区域。

这其中的数学公式我们不作详细推导。

而将此算法应用在图像处理中,一般一个图像就是个矩阵,像素点均匀的分布在图像上,就没有点的稠密性。
如果我们就算点x的概率密度,采用的方法如下:以x为圆心,以r为半径。落在球内的点位xi 定义二个模式规则。

(1)x像素点的颜色与xi像素点颜色越相近,我们定义概率密度越高。
(2)离x的位置越近的像素点xi,定义概率密度越高。

所以定义总的概率密度,是二个规则概率密度乘积的结果可以表示为:
在这里插入图片描述

其中:右式第二个因式代表空间位置的信息,离远点越近,其值就越大,第三个表示颜色信息,颜色越相似,其值越大。如图左上角图片,按照(4)计算的概率密度如图右上

因此我们通过计算图像的直方图反向投影,就可以根据上述的算法实现目标追踪。

相关API:

1、calcHist()直方图计算函数

void calcHist(&roi,1,0,maskroi,hist,1,&hist,&phranges)

&roi:输入图像

1:第一个参数中存放了多少张图像

0:需要统计的通道索引

maskroi:输入图像的mask,可选的操作掩码,这里的非零掩码元素用于标记出统计直方图的数组元素数据

hist:,输出的目标直方图

1:需要计算的直方图的维度

&hist:存放每个维度的直方图尺寸的数组

&phranges:每一维数取值范围。

2、calcBackProject()直方图反向投影

void calcBackProject(&hue,1,0,hist,backProj,&phranges)

Hue:输入图像

1:输入图像的个数

0:需要统计的通道索引

Hist:输入的直方图

backProj:目标反向投影阵列,必须为单通道

Phranges:每一个维度数组的边界

3、CamShift()实现目标追踪

void CamShift(backProj,trackWindow,TerCriteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER,10,1))

backProj:输入图像直方图的反向投影图

trackWindow:跟踪目标的初始位置矩形框

TerCriteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER,10,1):算法结束条件

操作步骤:

1、获取跟踪目标的hsv图像,并且分离出其中h部分;

2、根据阈值用inRange函数构建掩膜;

3、计算直方图,并且归一化;

4、直方图反向投影calcBackProject();

5、CamShift算法跟踪,获得跟踪目标的旋转外接矩形RotatedRect;

6、在原视频中绘制矩形锁定跟踪目标。

代码演示:

#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>

using namespace cv;
using namespace std;

int smin = 15;
int vmin = 40;
int vmax = 256;
int bins = 16;
int main(int argc, char** argv) {
 VideoCapture capture(0);
 if (!capture.isOpened()) {
  printf("could not find video data file...\n");
  return -1;
 }
 namedWindow("CAMShift Tracking", WINDOW_AUTOSIZE);
 namedWindow("ROI Histogram", WINDOW_AUTOSIZE);

bool firstRead = true;
 float hrange[] = { 0, 180 };
 const float* hranges = hrange;
 Rect selection;
 Mat frame, hsv, hue, mask, hist, backprojection;
 Mat drawImg = Mat::zeros(300, 300, CV_8UC3);

while (capture.read(frame))
 {
  if (firstRead)
   {
   Rect2d first = selectROI("CAMShift Tracking", frame);
   selection.x = first.x;
   selection.y = first.y;
   selection.width = first.width;
   selection.height = first.height;
   printf("ROI.x= %d, ROI.y= %d, width = %d, height= %d", selection.x, selection.y, selection.width, selection.height);
  }

cvtColor(frame, hsv, COLOR_BGR2HSV);
  inRange(hsv, Scalar(0, smin, vmin), Scalar(180, vmax, vmax), mask);
  hue = Mat(hsv.size(), hsv.depth());
  int channels[] = { 0, 0 };
  mixChannels(&hsv, 1, &hue, 1, channels, 1);

if (firstRead) {
   // ROI 直方图计算
   Mat roi(hue, selection);
   Mat maskroi(mask, selection);
   calcHist(&roi, 1, 0, maskroi, hist, 1, &bins, &hranges);
   normalize(hist, hist, 0, 255, NORM_MINMAX);

int binw = drawImg.cols / bins;
   Mat colorIndex = Mat(1, bins, CV_8UC3);
   for (int i = 0; i < bins; i++) {
    colorIndex.at<Vec3b>(0, i) = Vec3b(saturate_cast<uchar>(i * 180 / bins), 255, 255);
   }
   cvtColor(colorIndex, colorIndex, COLOR_HSV2BGR);
   for (int i = 0; i < bins; i++) {
    int  val = saturate_cast<int>(hist.at<float>(i)*drawImg.rows / 255);
    rectangle(drawImg, Point(i*binw, drawImg.rows), Point((i + 1)*binw, drawImg.rows - val), Scalar(colorIndex.at<Vec3b>(0, i)), -1, 8, 0);
   }
  }

// back projection
  calcBackProject(&hue, 1, 0, hist, backprojection, &hranges);
  // CAMShift tracking
  backprojection &= mask;
  RotatedRect trackBox = CamShift(backprojection, selection, TermCriteria((TermCriteria::COUNT | TermCriteria::EPS), 10, 1));

  Point2f vertices[4];
  trackBox.points(vertices);
  for (size_t i = 0; i < 4; i++)
  line(frame, vertices[i], vertices[(i + 1) % 4], Scalar(0, 0, 255), 3, 8);

capture.release();
 waitKey(0);
 return 0;
}

效果展示:

首先我们选定要识别跟踪的区域:

在这里插入图片描述
按下空格键,开始识别跟踪。

在这里插入图片描述

在这里插入图片描述

希望对读者有所帮助,喜欢的话可以关注一下我的公众号,我会把学习笔记发在上面,大家可以一起共同学习!

在这里插入图片描述
Alt

发布了12 篇原创文章 · 获赞 9 · 访问量 3627

猜你喜欢

转载自blog.csdn.net/Rosen_er/article/details/104213274