1 Meanshift原理

meanshift演算法

,其本质还是一种梯度下降法求最值方法。我认为可以这样表述,我们在取一个点(比如区域的某个角)作为区域的代表,将区域与目标相似程度数值化(或者机器学习中,将此点一定大小范围内匹配点的数目),作为这点的值,这样在图像上就可以形成坐标的xy的标量场,这样再利用梯度沿著相似程度上升的方向移动,这大概是就是演算法原理。

  在目标追踪中描述这个演算法,我在代码实现中选择的是HSV空间中的H分量直方图分布作为目标的的特征,将追踪到的区域的直方图与目标直方图进行比较,数值化两个直方图的相似程度。然后在追踪区域的附近区域计算相似程度,一旦发现邻域中某区域(A)的相似程度有提升,就将追踪区域更新为A,并在A邻域重复上述操作。直到追踪区域与目标的相似程度达到局部最大值停止迭代。

2 出现的问题

  1 容易陷入局部最优值。

  梯度下降法的通病,表现为追踪到颜色相近的物体。

  想到的解决方法:设置相似度的阈值,假如某邻域低于某个阈值将从此邻域跳出。(未在代码中体现)

  2 梯度下降收敛速度过慢。

 假如在区域邻域计算相似度的过程花费时间,物体移动速度又较快,很容易就会失去对物体的跟随。

  想到的优化:考虑到物体移动应该不是无规律的,那么梯度上升的方向一定程度上代表了物体的移动方向,物体保留原来速度的可能性比较大,那么在新区域也优先计算上一区域相似度上升方向的的相似度,这样一定程度上加快了收敛速率。

3 实现代码。

#include "stdafx.h"
#include<opencv2/opencv.hpp>
#include<math.h>
using namespace cv;
using namespace std;
Point origin;
Rect selection;
Point track[36];
Mat frame, trackobject, trackhsv;
int flag = 0;
MatND dstlist;
static void onMouse(int event, int x, int y, int, void*)//滑鼠事件,框选要追踪的物体,并计算目标物体HSV空间中H的直方图
{
switch (event)
{
case EVENT_LBUTTONDOWN:
origin = Point(x, y);
selection = Rect(x, y, 0, 0);
break;
case EVENT_LBUTTONUP:
selection.x = MIN(x, origin.x);
selection.y = MIN(y, origin.y);
selection.width = std::abs(x - origin.x);
selection.height = std::abs(y - origin.y);
trackobject = frame(selection).clone();
cvtColor(trackobject, trackhsv, CV_RGB2HSV);
int hsvnum = 30;
float hrange[] = { 0,179 };
const float *range[] = { hrange };
int channels = 0;
int size = 256; //?
calcHist(&trackhsv, 1, &channels, Mat(), dstlist, 1, &size, range);
flag = 1;
break;
}
}
int drawrect(Mat &img, Rect t) { //追踪框
rectangle(img, t.tl(), t.br(), Scalar(0));
return 1;
}
double calcmp(Rect a) { //计算与目标直方图的距离
Mat select = frame(a);
Mat newtrack = select.clone();
Mat newtrackhsv;
MatND newlist;
cvtColor(newtrack, newtrackhsv, CV_RGB2HSV);
int hsvnum = 30;
float hrange[] = { 0,179 };
const float *range[] = { hrange };
int channels = 0;
int size = 256;
calcHist(&newtrackhsv, 1, &channels, Mat(), newlist, 1, &size, range);
return compareHist(dstlist, newlist, CV_COMP_CORREL);
}

int main()
{
Rect newtrack;
VideoCapture cap;
namedWindow("meanshift", CV_WINDOW_NORMAL);
cap.open(0);
double step = 20; //追踪的步长,假如运动速度较快,就设置的大一点
for (int i = 0; i < 4; i++) { //邻域方向,为计算速率的提升,仅仅选取了上下左右四个方向
track[i].x = cos(i*1.0 / 2 * 3 .14 )*step;
track[i].y = sin(i*1.0 / 2 * 3.14)*step;
}
if (!cap.isOpened())
{
cout << "摄像头未能正常开启
";
return -1;
}
while (1) {
cap >> frame;
cvSetMouseCallback("meanshift", onMouse);
drawrect(frame, selection);
imshow("meanshift", frame);

if (flag == 1) {
double last = calcmp(selection);
for (int i = 0; i < 4; i++) {
if ((selection.x + track[i].x) > 0&&(selection.y + track[i].y)>0&& (selection.br().y + track[i].y)<479&& (selection.br().x + track[i].x)<639) { //防止追踪框越界
newtrack = selection + track[i];
}
while (last < calcmp(newtrack)) {
if ((selection.x + track[i].x) > 0 && (selection.y + track[i].y)>0 && (selection.br().y + track[i].y)<479 && (selection.br().x + track[i].x)<639) {//防止追踪框越界
selection = selection + track[i]; //优先计算原梯度上升方向。
}
break;
}

}
}
waitKey(30);
}

return 0;
}

推荐阅读:

相关文章