首页>>人工智能->Opencv学习笔记(十一)基于CAMshift算法的物体跟踪实现

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

时间:2023-11-29 本站 点击:0

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;}

效果展示:

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

按下空格键,开始识别跟踪。

原文:https://juejin.cn/post/7095936555520360484


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/AI/1195.html