1. 程式人生 > >OpenCV目標跟蹤(四)-運動模板

OpenCV目標跟蹤(四)-運動模板

OpenCV中運動與跟蹤這一章節中,在前面的介紹中,主要給出了LK光流法,以及基於概率統計,視窗搜尋的meanshif演算法以及meanshif演算法的改進版-camshift演算法,這兩天主要在看運動模板的跟蹤運動方法,下面就簡要的介紹下。
運動模板的方法是美國的MIT實驗室提出來的,是一種有效的跟蹤普通運動的方法,尤其可應用在姿態識別中。運動模板的方法首先需要的是知道物體的輪廓,而輪廓的獲取可以有很多的方法,關於輪廓的獲取的方法在此不做多餘的贅述。
運動模板的方法程式的大致程式設計思路應該是是這樣的:獲得當前幀與上一幀的差,然後對差影象進行二值化;更新運動歷史影象;然後計算運動歷史影象的梯度方向,並將整個運動分割為獨立的運動部分,在用一個結構序列標記每一個運動分割,最後計算出選擇區域的全域性運動方向,從而獲得運動目標的質心位置與運動方向。
關於上面的思路,給出幾個主要的函式,對其引數進行介紹。

void cvUpdateMotionHistory(
    const CvArr* silhouette,//非0畫素代表前景物體的最新的分割輪廓
    CvArr* mhi,//運動模板,也即運動歷史影象
    double timestamp,//當前系統時間
    double duration//運動跟蹤的最大持續時間
);

一旦運動模板記錄了不同時間的物體輪廓,就快成用計算mhi影象的梯度來獲取全域性運動資訊。即有函式:

void cvCalcMotionGradient(
    const CvArrs* mhi,//運動歷史影象
    CvArr* mask,//非0值代表此點處的梯度有效
CvArr* orientation,//每一點梯度方向的角度 double delta1, double delta2,//delta1,2分別是允許的最小和最大梯度值 int aperture_size=3//設定梯度運算元的寬和高 );

梯度方向求出後,即可以計算全域性運動方向,給出如下函式:

double cvCalcGlobalOrientation(
    const CvArr* orientation,
    const CvArr* mask,
    const CvArr* mhi,//前三個引數都是根據上面兩個函式得出來的
    double
timestamp, double duration );

關於運動模板後面的數學原理,我其實還沒找到相關的文獻閱讀,有些數學原理地方不是特別明白。下面給出完整的運動模板示例程式。


#include "opencv2/video/tracking.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc_c.h"
#include <time.h>
#include <stdio.h>
#include <ctype.h>
//不同的跟蹤引數  
const double MHI_DURATION = 0.5;  
const double MAX_TIME_DELTA = 0.5;  
const double MIN_TIME_DELTA = 0.05;  
// 用於運動檢測的迴圈幀數,與機器速度及FPS設定有關  
const int N = 2;  
IplImage **buf = 0;  
int last = 0;  
// 臨時影象  
IplImage *mhi = 0; // MHI: 運動歷史影象  
IplImage *orient = 0; // 方向  
IplImage *mask = 0; // 有效的運動掩碼  
IplImage *segmask = 0; // 運動分割對映  
CvMemStorage* storage = 0; // 臨時儲存區  // parameters:  
//  img - input video frame  
//  dst - resultant motion picture  
//  args - optional parameters  
void  update_mhi( IplImage* img, IplImage* dst, int diff_threshold ){   
    double timestamp = (double)clock()/CLOCKS_PER_SEC; // 獲取當前時間,以秒為單位  
    CvSize size = cvSize(img->width,img->height); // 獲取當前幀尺寸  
    int i, idx1 = last, idx2;  
    IplImage* silh;  
    CvSeq* seq;  
    CvRect comp_rect;  
    double count;  
    double angle;  
    CvPoint center;  
    double magnitude;  
    CvScalar color;  

    // 開始時為影象分配記憶體 or 幀尺寸改變時重新分配記憶體  
    if( !mhi || mhi->width != size.width || mhi->height != size.height ){    
        if( buf == 0 ){  

            buf = (IplImage**)malloc(N*sizeof(buf[0]));  
            memset( buf, 0, N*sizeof(buf[0]));  
        }  

        for( i = 0; i < N; i++ ){  

            cvReleaseImage( &buf[i] );  
            buf[i] = cvCreateImage( size, IPL_DEPTH_8U, 1 );  
            cvZero( buf[i] );  
        }  
        cvReleaseImage( &mhi );  
        cvReleaseImage( &orient );  
        cvReleaseImage( &segmask );  
        cvReleaseImage( &mask );  

        mhi = cvCreateImage( size, IPL_DEPTH_32F, 1 );  
        cvZero( mhi ); // clear MHI at the beginning  
        orient = cvCreateImage( size, IPL_DEPTH_32F, 1 );  
        segmask = cvCreateImage( size, IPL_DEPTH_32F, 1 );  
        mask = cvCreateImage( size, IPL_DEPTH_8U, 1 );  
    }  

    cvCvtColor( img, buf[last], CV_BGR2GRAY ); //RGB幀影象格式轉換為gray  

    idx2 = (last + 1) % N; // index of (last - (N-1))th frame  
    last = idx2;  

    silh = buf[idx2];  
    // 相鄰兩幀的差  
    cvAbsDiff( buf[idx1], buf[idx2], silh );  

    cvThreshold( silh, silh, diff_threshold, 1, CV_THRESH_BINARY ); // 對差影象做二值化  
    cvUpdateMotionHistory( silh, mhi, timestamp, MHI_DURATION ); // 更新運動歷史  

    // convert MHI to blue 8u image  
    // cvCvtScale的第四個引數 shift = (MHI_DURATION - timestamp)*255./MHI_DURATION  
    // 控制幀差的消失速率  
    cvCvtScale( mhi, mask, 255./MHI_DURATION,  
                (MHI_DURATION - timestamp)*255./MHI_DURATION );  

    cvZero( dst );  
    cvMerge( mask, 0, 0, 0, dst );

  // B,G,R,0 convert to BLUE image  

    // 計算運動的梯度方向以及正確的方向掩碼  
    // Filter size = 3  
    cvCalcMotionGradient( mhi, mask, orient,  
                          MAX_TIME_DELTA, MIN_TIME_DELTA, 3 );  

    if( !storage )  
        storage = cvCreateMemStorage(0);  
    else  
        cvClearMemStorage(storage);  

    // 運動分割: 獲得運動部件的連續序列  
    seq = cvSegmentMotion( mhi, segmask, storage, timestamp, MAX_TIME_DELTA );  
    for( i = -1; i < seq->total; i++ ){  

        if( i < 0 ) {        // 對整幅影象操作  
            comp_rect = cvRect( 0, 0, size.width, size.height );  
            color = CV_RGB(255,255,255);  
            magnitude = 100;  // 畫線長度以及圓半徑的大小控制  
        }  
        else {          // 第i個運動元件  
            comp_rect = ((CvConnectedComp*)cvGetSeqElem( seq, i ))->rect;  
            // 去掉小的部分  
            if( comp_rect.width + comp_rect.height < 100 )  
                continue;  
            color = CV_RGB(255,0,0);  
            magnitude = 30;  
            //if(seq->total > 0) MessageBox(NULL,"Motion Detected",NULL,0);  
        }  
        // 選擇元件ROI  
        cvSetImageROI( silh, comp_rect );  
        cvSetImageROI( mhi, comp_rect );  
        cvSetImageROI( orient, comp_rect );  
        cvSetImageROI( mask, comp_rect );  

        // 在選擇的區域內,計算運動方向  
        angle = cvCalcGlobalOrientation( orient, mask, mhi, timestamp,  MHI_DURATION);  
       angle = 360.0 - angle;  // adjust for images with top-left origin  
        // 在輪廓內計算點數  
        // Norm(L1) = 所有畫素值的和  
        count = cvNorm( silh, 0, CV_L1, 0 );  

        cvResetImageROI( mhi );  
        cvResetImageROI( orient );  
        cvResetImageROI( mask );  
        cvResetImageROI( silh );  

        // 檢查小運動的情形  
        if( count < comp_rect.width*comp_rect.height * 0.05 )  //  畫素的5%  
            continue;  

        // 畫一個帶箭頭的記錄以表示方向  
        center = cvPoint( (comp_rect.x+comp_rect.width/2),(comp_rect.y + comp_rect.height/2) );  

        cvCircle( dst, center, cvRound(magnitude*1.2), color, 3, CV_AA, 0 );  
        cvLine( dst, center, cvPoint( cvRound( center.x +magnitude*cos(angle*CV_PI/180)),  
        cvRound( center.y - magnitude*sin(angle*CV_PI/180))),  color, 3, CV_AA, 0 );  
    } 
}  

int main(int argc, char** argv){  

    IplImage* motion = 0;  
    CvCapture* capture = 0;  

    if( argc == 1 || (argc == 2 && strlen(argv[1]) == 1 && isdigit(argv[1][0])))  
        capture = cvCaptureFromCAM( argc == 2 ? argv[1][0] - '0' : 0 );  
    else if( argc == 2 )  
        capture = cvCaptureFromAVI( argv[1] );  

    if( capture ){  

        cvNamedWindow( "Motion", 1 );  
        for(;;){  

           IplImage* image = cvQueryFrame( capture );
            if( !image )
                break;

            if( !motion )
            {
                motion = cvCreateImage( cvSize(image->width,image->height), 8, 3 );
                cvZero( motion );
                motion->origin = image->origin;
            }

            update_mhi( image, motion, 30 );
            cvShowImage( "Motion", motion );

            if( cvWaitKey(10) >= 0 )
                break;
          }
        cvReleaseCapture( &capture );  
        cvDestroyWindow( "Motion" );  
    }  

    return 0;  
}
#ifdef _EiC
main(1,"motempl.c");
#endif

最後的執行結果如圖所示:
這裡寫圖片描述

相關推薦

OpenCV目標跟蹤-運動模板

OpenCV中運動與跟蹤這一章節中,在前面的介紹中,主要給出了LK光流法,以及基於概率統計,視窗搜尋的meanshif演算法以及meanshif演算法的改進版-camshift演算法,這兩天主要在看運動模板的跟蹤運動方法,下面就簡要的介紹下。 運動模板的方法是

運動目標跟蹤--搜尋演算法預測模型之KF,EKF,UKF

這裡先總體介紹下,原文轉自: 任何感測器,鐳射也好,視覺也好,整個SLAM系統也好,要解決的問題只有一個:如何通過資料來估計自身狀態。每種感測器的測量模型不一樣,它們的精度也不一樣。換句話說,狀態估計問題,也就是“如何最好地使用感測器資料”。可以說,SLAM是狀態估計的一

Flask 學習系列---Jinjia2 模板繼承

list size border padding sub -- margin nbsp 中文 1.基模板 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF

Spring Boot入門——使用模板FreeMaker

junit boot.s char pack utf put 常見 節點 簡單的 這周主要學習怎麽在Spring Boot中使用模板引擎FreeMaker,主要從以下幾方面進行學習。 (1) freemarker介紹: FreeMarker是一款模板引擎: 即

tensorflow利用預訓練模型進行目標檢測:檢測中的精度問題以及evaluation

一、tensorflow提供的evaluation Inference and evaluation on the Open Images dataset:https://github.com/tensorflow/models/blob/master/research/object_detection/g

OpenCV(C++) 基礎-- 邊緣檢測與霍夫變換

1. 邊緣檢測 Sobel():靈活調整水平或者垂直邊緣檢測,基於高斯平滑和微分求導 void Sobel(src, dst, depth, dx, dy, ksize=3); // depth: 對應影象型別 // dx, dy: x,y方向的差分階數,控制在x,y軸上

雙攝像頭的實時視訊拼接及目標跟蹤

視訊拼接演算法 視訊拼接思想及總體框圖 影象拼接是視訊拼接的基礎,是將多張影象生成一張全景圖,衡量的指標主要在於全景圖的質量,而對拼接速度的要求並不高。對於視訊拼接而言,視訊流的輸入是源源不斷的,需要將多個攝像頭採集的圖片進行拼接,每個攝像頭的影象幀序列必須一一

雙攝像頭的實時視訊拼接及目標跟蹤

實時視訊拼接 初步視訊拼接虛影問題的探討 經過上述方案處理之後的初步拼接視訊,將會存在目前視訊拼接技術中一個普遍的問題,即重合區域存在運動物體產生虛影。效果圖如下, 產生偽影的影象幀 可以看到,重疊區域內,存在運動物體時,出現了虛影,並且都

雙攝像頭的實時視訊拼接及目標跟蹤

實時視訊拼接速度的優化方案 優化方案,提高拼接速度 根據之前章節的方法,進行初步視訊拼接得到的效果,其速度並不是最快,需要進行一定的優化。通過實驗,得到下面幾種優化方式。 1.程式碼執行的環境是在VS2017上執行的,其中有兩種執行模式,分別為Debug模式和R

Python3+OpenCV學習筆記:影象濾波基礎均值、高斯、中值、雙邊

OpenCV中還可以在影象上進行繪圖操作,由於資料都比較完善,所以附上鍊接,自行參悟。 好了,進入正題。在一張影象,在資料儲存或傳輸的過程中,或多或少都會引入噪聲,常見的影象噪聲如高斯噪聲、瑞利噪聲、椒鹽噪聲等,可參加連結:數字影象噪聲 為了避免噪聲對影象資訊進行干擾或

flaskjinja2模板

from flask import Flask,render_template @app.route("/index/") def  index():       return render_template("",) render_templ

ubuntu下利用Dlib實現目標跟蹤

                        本文將介紹在Ub

Opencv學習手冊--- 影象灰度變換

#include <opencv2/opencv.hpp> using namespace cv; /* 灰度變換函式ImageAdjust 其中,源影象src和目標影象dst均為8位元的灰度影象 預設的引數值有: [low, high]

SLAM筆記運動恢復結構的幾何數學(本徵矩陣、單應矩陣、基礎矩陣)

1. 間接法進行運動恢復的前提假設 對於結構與運動或視覺三維重建中,通常假設已經通過特徵匹配等方法獲取了匹配好的點對。 先求出匹配點對再獲取結構和運動資訊的方法稱作間接法。 間接法最重要的三個假設是: 1.擁有一系列兩幀之間的匹配點對。但同時假設匹配關係不一定精

SpringBoot入門系列整合模板引擎Thymeleaf

前面介紹了Spring Boot的優點,然後介紹瞭如何快速建立Spring Boot 專案。不清楚的朋友可以看看之前的文章:https://www.cnblogs.com/zhangweizhong/category/1657780.html。 今天我們主要來看看 Thymeleaf 在 Spring Boo

運動目標跟蹤--MIL跟蹤

原文: http://blog.csdn.net/ikerpeng/article/details/19235391 文章:Robust object tracking with on-line  multiple instance learning Boris Baben

OpenCV學習總結4- 目標跟蹤

opened 背景 key font sin mic 目標 mil cap 視覺算法原理:背景提取  1. 打開視頻(文件或攝像頭)  2. 從視頻中提取當前幀  3. 計算背景:以前多幀求取平均  4. 根據背景得到運動目標(當前幀 - 背景)  5. 返回2,程序不斷循

ARCore之路-運動跟蹤之3D聲場

  上節我們已經利用共振音訊(Resonance Audio)SDK實現了3D音效,這只是使用了共振音訊(Resonance Audio)最簡單的特性,共振音訊(Resonance Audio)提供了遠比示例中高階的功能技術特性,特別是模擬房間的引數,可以模擬很

Python-OpenCV 處理視訊: 標記運動軌跡 運動檢測 運動方向判斷

0x00. 光流 光流是進行視訊中運動物件軌跡標記的一種很常用的方法,在OpenCV中實現光流也很容易。 CalcOpticalFlowPyrLK 函式計算一個稀疏特徵集的光流,使用金字塔中的迭代 Lucas-Kanade 方法。 簡單的實現流程: 載入一段視訊。

目標檢測的影象特徵提取之OpenCV中BLOB特徵提取與幾何形狀分類

OpenCV中BLOB特徵提取與幾何形狀分類一:方法二值影象幾何形狀提取與分離,是機器視覺中重點之一,在CT影象分析與機器人視覺感知等領域應用廣泛,OpenCV中提供了一個對二值影象幾何特徵描述與分析最有效的工具 - SimpleBlobDetector類,使用它可以實現對二