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

推薦閱讀:

相關文章