opencv_色彩深度_動態歷史影像(Motion History Image)效果

【運動歷史影像(MHI)】


========================================================

色彩深度(color depth)


色彩深度(color depth) : 用來表示一個像素所佔用的位元數。
視頻幀緩衝區中儲存1像素的顏色所用的位元數目,
它也稱為位元/像素(bpp / bit per pixel)。

色彩深度越高  , 可用的顏色就越多。

==================================================================

運動歷史圖像(MHI): 一種基於視覺的模板方法,通過計算時間段內同一位置像素變化,將目標運動情況以圖像亮度的形式表現出來。

MHI圖像可以表徵人體在一個動作過程中最近的動作情況,這使得MHI被廣泛應用於動作識別領域。

H為運動歷史圖像素的強度值,H (x, y, t) 可以由更新函數計算得出:





τ(tau念法:ʹtau 】: τ(小寫)代表 「一個時間區間」,從幀數的角度決定了​​運動的時間範圍

Ψ(psi) /【念法:ʹpsaɪ】: Ψ(x, y, t)為更新函數,可由幀間差、圖像差分或光流等多種方法定義

δ(delta)  / 念法: ʹdɛltə  : 為衰退參數


先試著讀取一張圖片

這裡我用位於C槽的lena.jpg來做測試





















<第一階段小測試>






另外一些imread()函數內部搭配的參數























CV_LOAD_IMAGE_ANYCOLOR保持不變(原本圖像捨麼顏色就捨麼顏色)
src = imread("C:\\lena.jpg", 1);
src = imread("C:\\lena.jpg");
src = imread("C:\\lena.jpg", CV_LOAD_IMAGE_ANYCOLOR);




CV_LOAD_IMAGE_ANYDEPTH / 輸入圖像格式可以為8位無符號,16位無符號,32位有符號或者32位浮點型




CV_LOAD_IMAGE_COLOR // 彩色























CV_LOAD_IMAGE_GRAYSCALE// 灰階




=======================================================
<第二階段小測試>

void updateMotionHistory(InputArray silhouette, 
                                            InputOutputArray mhi, 
                                            double timestamp, 
                                            double duration  )

Parameters:


  1. silhouette – Silhouette mask that has non-zero pixels where the motion occurs.
  2. mhi – Motion history image that is updated by the function (single-channel, 32-bit floating-point).
  3. timestamp – Current time in milliseconds or other units.
  4. duration – Maximal duration of the motion track in the same units as timestamp .
參考自 

Motion Templates_updateMotionHistory




套用結果






















最後是演算法部分整合效果






程式碼

#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 為運動跟踪的最大持續時間
const double MAX_TIME_DELTA = 0.5;//最大時間增量為0.5s 
const double MIN_TIME_DELTA = 0.05;//最小時間增量0.05s 

// 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 *orient = 0; // orientation
IplImage *mask = 0; // valid orientation mask
IplImage *segmask = NULL; // motion segmentation map
CvMemStorage* storage = NULL; // temporary storage
//用來建立一個指定大小的記憶體區塊,若為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; //創建一個用來存輪廓(剪影)的圖像空間
    CvSeq* seq; // 創建一序列                
        
    
    
    if (!mhi || mhi->width != size.width || mhi->height != size.height) 
    {
        //如果frame的大小有所改變,就重新分配他們到緩衝區中    

        if (buf == 0) //如果IplImage *的陣列 
        {
            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); // 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)*255. / MHI_DURATION);

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

    cvMerge(mask, 0, 0, 0, dst); //合併單通道矩陣為成為多通道的圖形
    //cvMerge(B,G,R,A,dst)    
    //前四個引數為單通道uchar型別的資料結構,
    //第五個引數為輸出IplImage資料結構或CvMat結構圖形

    // calculate motion gradient orientation and valid orientation mask
    //計算運動歷史圖像的梯度方向
    cvCalcMotionGradient(mhi, mask, orient, MAX_TIME_DELTA, MIN_TIME_DELTA, 3);
    //cvCalcMotionGradient(mhi, mask, orient, MIN_TIME_DELTA, MAX_TIME_DELTA, 3);
    //第一個參數 : 運動歷史圖像(單通道)
    //第二個參數 : Mask 圖像;用來標註運動梯度數據正確的點,單通道8bits,為輸出參數
    //第三個參數 : 運動梯度的方向圖像,包含從0 到360 角度
    //第四個參數 : 函數在每個像素點(x,y) 鄰域尋找MHI 的最小值(m(x,y)) 
    //第五個參數 : 函數在每個像素點(x,y) 鄰域尋找MHI 的最大值(m(x,y)) 
    //min(delta1,delta2) <= M(x,y)-m(x,y) <= max(delta1,delta2)
    //第六個參數 : Aperture size of sobel operator
    //索貝爾運算子的光圈大小(函數所用微分算子的開孔尺寸CV_SCHARR, 1, 3, 5 or 7 (見cvSobel))    
    //Both inDelta1 and inDelta2 must be greater than 0.0 in cvCalcMotionGradient.
    //History image must be REAL single-channel in cvCalcMotionGradient.

    if (!storage)
        storage = cvCreateMemStorage(0);//建立的記憶體區塊大小依照預設值為64k
    else
        cvClearMemStorage(storage);

    // segment motion: get sequence of motion components
    // segmask is marked motion components map. It is not used further
    seq = cvSegmentMotion(mhi, segmask, storage, timestamp, MAX_TIME_DELTA);
    //將整個運動分割為獨立的運動部分
    //第一個參數 : 運動歷史圖像(單通道)
    //第二個參數 : 發現應當存儲的mask圖像, 單通道32bits浮點數,用不同的單獨數字(1,2,...)標識它們
    //第三個參數 : 包含運動連通域序列的內存存儲倉區塊
    //第四個參數 :當前時間,以毫秒為單位
    //第五個參數 : 分割閾值,推薦等於或大於運動歷史"每步"之間的間隔    
}


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








留言

這個網誌中的熱門文章

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

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

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