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,...)
透過此運算式可以使其具有明顯能量深淺變換(具有方向性)的表示
留言
張貼留言