萌新的opencv入門2--meanshift物體追蹤


1 Meanshift原理

  meanshift算法,其本質還是一種梯度下降法求最值方法。我認為可以這樣表述,我們在取一個點(比如區域的某個角)作為區域的代表,將區域與目標相似程度數值化(或者機器學習中,將此點一定大小范圍內匹配點的數目),作為這點的值,這樣在圖像上就可以形成坐標的xy的標量場,這樣再利用梯度沿着相似程度上升的方向移動,這大概是就是算法原理。

  在目標追蹤中描述這個算法,我在代碼實現中選擇的是HSV空間中的H分量直方圖分布作為目標的的特征,將追蹤到的區域的直方圖與目標直方圖進行比較,數值化兩個直方圖的相似程度。然后在追蹤區域的附近區域計算相似程度,一旦發現鄰域中某區域(A)的相似程度有提升,就將追蹤區域更新為A,並在A鄰域重復上述操作。直到追蹤區域與目標的相似程度達到局部最大值停止迭代。

2 出現的問題

  1 容易陷入局部最優值。

  梯度下降法的通病,表現為追蹤到顏色相近的物體。

  想到的解決方法:設置相似度的閾值,假如某鄰域低於某個閾值將從此鄰域跳出。(未在代碼中體現)

  2 梯度下降收斂速度過慢。

  假如在區域鄰域計算相似度的過程花費時間,物體移動速度又較快,很容易就會失去對物體的跟隨。

  想到的優化:考慮到物體移動應該不是無規律的,那么梯度上升的方向一定程度上代表了物體的移動方向,物體保留原來速度的可能性比較大,那么在新區域也優先計算上一區域相似度上升方向的的相似度,這樣一定程度上加快了收斂速率。

3 實現代碼。

  

// meanshifttet.cpp: 定義控制台應用程序的入口點。

#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 << "攝像頭未能正常開啟\n";
        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;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM