Motion History Image_演算法流程圖







程式碼精簡化過後

#include "opencv2/video/tracking.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc_c.h"
#include <time.h> // for CLOCKS_PER_SEC 及 clock()
#include <stdio.h>
#include <ctype.h>

// various tracking parameters (in seconds)
const double MHI_DURATION = 3;//1s 為運動跟踪的最大持續時間

// number of cyclic frame buffer used for motion detection
// (should, probably, depend on FPS)
const int N = 5;//frame buffer當中你要存取的frame數

// ring image buffer
IplImage **buf = 0; // 創建 frame buffer , 型態為IplImage * buf 的 Array
int last = 0;

// temporary images
IplImage *mhi = 0; // MHI
IplImage *mask = 0; // valid orientation mask

//用來建立一個指定大小的記憶體區塊,若為0,
//則建立的記憶體區塊大小依照預設值為64k

// parameters:
//  img - input video frame
//  dst - resultant motion picture
//  args - optional parameters
static void  update_mhi(IplImage* img, IplImage* dst, int diff_threshold)
{
    double timestamp = (double)clock() / CLOCKS_PER_SEC; // 得到當前時間(以毫秒為單位)
    //
    CvSize size = cvSize(img->width, img->height); // //獲取目前攝像頭捕捉到的frame size
    int i, idx1, idx2;
    IplImage* silh; //創建一個用來存輪廓(剪影)的圖像空間                 
    if (!mhi || mhi->width != size.width || mhi->height != size.height)
    {
        //如果frame的大小有所改變,就重新分配他們到緩衝區中    
        
        //這段不可省略 若省則 會造成記憶體錯誤
        if (buf == 0) //若尚沒有初始化則分配內存給他
        {
            buf = (IplImage**)malloc(N*sizeof(buf[0]));
            memset(buf, 0, N*sizeof(buf[0]));//用來對一段內存空間全部設置為某個字符
            //把buffer所指內存區域的前count個字節設置成字符c ,返回指向buffer的指針
        }

        for (i = 0; i < N; i++) {
            cvReleaseImage(&buf[i]);
            buf[i] = cvCreateImage(size, IPL_DEPTH_8U, 1);
            cvZero(buf[i]);
        }

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

    cvCvtColor(img, buf[last], CV_BGR2GRAY); // convert frame to grayscale
    //第一張 視訊影像在剛載入時先做了彩色轉灰階的功能
    //並存放在frame buffer的IplImage *的指針(陣列中)
    //buf[last] 即 buf[0] 第一個位置

    //設定frame的索引編號
    idx1 = last;
    //將idx1設為編號0 (最近進來的視訊frame索引編號)
    //執行第一輪 更新index到 0
    //執行第二輪 更新index到 1

    idx2 = (last + 1) % N;
    // index of (last - (N-1))th frame 
    //idx2 代表 第 最新進來的視訊frame編號
    //執行第一輪 更新index到 1
    //執行第二輪 更新index到 2

    last = idx2;//last以更新到index 1 之後
    //執行第一輪 更新index到 1
    //執行第二輪 更新index到 2

    //做幀差(Frame Difference)
    silh = buf[idx2];//silh首先會在此接收到來自緩衝區buf[1]的一張單通灰階影像
    cvAbsDiff(buf[idx1], buf[idx2], silh);// get difference between frames 
    //執行第一輪 buffer 當中 index 0 - index 1 存給silh
    //執行第二輪 buffer 當中 index 1 - index 2 存給silh

    //對輪廓做二值化
    cvThreshold(silh, silh, diff_threshold, 1, CV_THRESH_BINARY); // and threshold it


    cvUpdateMotionHistory(silh, mhi, timestamp, MHI_DURATION); // update MHI
    //第一個參數 silhouette : 由幀間差分(Frame difference)得到的運動輪廓圖像。
    //第二個參數 mhi : motion histoty image的縮寫,表示運動歷史圖像。
    //第三個參數 timestamp : 時間標記。
    //第四個參數 duration : 發生過運動的像素所能保持的最長時間。

    // 使用線性變換轉換數組 _ convert MHI to  8u image
    //cvCvtScale(mhi, mask, 255. / MHI_DURATION, (MHI_DURATION - timestamp));
    cvCvtScale(mhi, mask, 255. / MHI_DURATION, (MHI_DURATION - timestamp)*255. / MHI_DURATION);
    //cvCvtScale( src, dst, scale, shift);
    //src * scale + shift = dst
    //該函數首先對輸入陣列的元素進行比例縮放,
    //然後將shift加到比例縮放後得到的各元素上,
    //即: dst(I)=src(I)*scale + (shift,shift,...),最後可選的類型轉換將結果拷貝到輸出數組。
    //實驗後 : 若第五個參數直接代入0,1,...等任意數字就會形成MEI不會有深淺變化!!!!
    //shift = (MHI_DURATION - timestamp)*255./MHI_DURATION --> 控制framediff的消失速率



    cvZero(dst);//若預設通道合併處填為藍色就會在一開始顯示為藍色
    //若我在此把cvZero(dst);註解掉你就會看到反色
    //以藍色為例你看到的就會是黃背白前景
    //這裡主要是設計用來將輪廓(剪影)以藍色通道來做顏色強弱顯示

    //(哪種通道混和效果由自己取決)
    cvMerge(mask, mask, 0, 0, dst); //合併單通道矩陣為成為多通道的圖形
    //cvMerge(B,G,R,A,dst)    
    //前四個引數為單通道uchar型別的資料結構,
    //第五個引數為輸出IplImage資料結構或CvMat結構圖形    
}


int main(int argc, char** argv)
{
    IplImage* motion = 0;
    CvCapture* capture = 0;
    capture = cvCaptureFromCAM(0);    // 攝影機開啟
    if (capture)
    {
        cvNamedWindow("Motion", 1);
        for (;;)
        {
            IplImage* image = cvQueryFrame(capture);
            if (!image)//判斷使否沒取到影格
                break;

            if (!motion)//如果motion圖像的資料結構為空值
            {
                motion = cvCreateImage(cvSize(image->width, image->height), 8, 3);
                //就先產生3通道,大小跟攝影機捕捉到的影像相同的空間
                //cvZero(motion);//設值為0---->
                //若預設通道合併處填為藍色就會在一開始顯示為藍色
                motion->origin = image->origin;
            }

            update_mhi(image, motion, 30);
            //自定義的update_mhi函數(攝影機捕獲到的影像 , 效果輸出影像 , 門檻值)
            cvShowImage("Motion", motion);

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


當中控制深淺變化的主要是




cvCvtScale( mhi,    // 輸入矩陣 src
                    mask,  //輸出矩陣 dst
                    255. / MHI_DURATION,   //縮放比例參數 scale
                    (MHI_DURATION - timestamp)*255. / MHI_DURATION  //shift加到輸入數組的加數
                    );

其四個參數主要是做了以下運算

首先 輸入矩陣乘上縮放比例因子

如果 scale=1, shift=0 就不會進行比例縮放


shift
該加數被加到輸入數組元素按比例縮放後得到的元素上

dst(I)=src(I)*scale + (shift,shift,...)



透過此運算式可以使其具有明顯能量深淺變換(具有方向性)的表示






留言

這個網誌中的熱門文章

何謂淨重(Net Weight)、皮重(Tare Weight)與毛重(Gross Weight)

Architecture(架構) 和 Framework(框架) 有何不同?_軟體設計前的事前規劃的藍圖概念

經得起原始碼資安弱點掃描的程式設計習慣培養(五)_Missing HSTS Header