星期一, 4月 19, 2010

Week 9: 如何在視訊擷取視窗中添加物件?

1. KOF98 真人版

 

2. 請同學思考上述影片的製作過程

3. 如果想要直接在視訊擷取視窗中直接加入下圖的物件, 該如何做?
 
  

 

4. 範例程式 : VGC-2010S-SP10.txt

 

 抹香鯨和藍鯨不是加上去的, 攝影機拍的,是準備對戰的兩位主角喔!

 

5. 同學可以思考如何修改範例程式, 可以讓計時器開始倒數...

 
 

星期一, 4月 12, 2010

Week 8: What's Callback Function?

1. 什麼是 Callback Function ?

 當我們執行某個應用程式時, 針對 Form 上各個物件所發生的事件, 嚴格來說可以分成兩類:

 a. 由使用者觸發, 如使用者透過滑鼠或鍵盤所觸發的事件。

  這類事件, 在 object-oriented 程式語言中, 是用 event-driven 的設計, 使用者針對個別物件, 撰寫不同事件處理程序來因應。

 b. 非由使用者觸發, 乃系統自行發生的事件。

  如程式發生錯誤情況, 或系統做了特定事件, 如從視訊裝置擷取一個畫面。此類事件, 使用者就可以設定 callback function 來因應這些事件的發生。

2. 函數的呼叫 (call) 與 結束(return)

 在程式的執行過程中, 當主程式呼叫一個函數 (call function) 時, 程式會跳到函數繼續執行, 當遇到 return 指令時, 就會返回主程式的呼叫點繼續執行。 

3. 預覽回呼函數 preview callback function

 當視訊擷取驅動程式接收到來自視訊裝置的畫面(frame), 在 preview 模式之下, 將此畫面顯示所連結的視訊視窗之中。

 我們可以透過 capSetCallbackOnFrame 這個函數來指定當發生 frame preview 之前, 先執行一個特定的 callback function, 即預覽回呼函數。這個預覽回呼函數的名稱可以由我們自由命名, 但是傳入的參數個數與其資料型態則必須符合 VFW 的規定。

4. 實驗: 當程式開啟視訊擷取視窗後, 顯示預覽的畫面總數到 Label 上。

 實驗目的: 了解視訊擷取視窗之預覽回呼函數的設定與使用方法。

 範例程式: VGC-2010S-SP07.txt

5. 如何關閉預覽回呼函數

 範例程式: VGC-2010S-SP08.txt

  
 
6. 預覽回呼函數可以做些什麼?

 好處: 就是我們可以在畫面預覽之前, 先對畫面資料做預先處理, 例如: 加入文字、物件或特效等等。

 

 這段影片就是利用預覽回呼函數, 將龍珠放置到畫面之中, 再與攝影機所拍攝的手進行互動... 

 但, 問題是這些畫面資料到底怎麼存取呢?

7. 如何從記憶體直接擷取視訊畫面?

 透過 capSetCallbackOnFrame 這個函式設定: 當視訊擷取視窗(代碼為 hwndViedo) 取得一個 frame 後, 必須執行我們所定義的 callback function: FrameCallBack,

capSetCallbackOnFrame(hwndVideo, &FrameCallBack);

LRESULT CALLBACK FrameCallBack( HWND hwndCapture, PVIDEOHDR lpvhdr );

 在我們所自行定義的 FrameCallBack callback function 中, 有兩個輸入參數:

 a. HWND hwndCapture,

  指明哪一個視訊擷取視窗, 即該視訊擷取視窗的 handle。

 b. PVIDEOHDR lpvhdr,

  capSetCallbackOnFrame 這個函式會自動把視訊擷取視窗的相關資訊透過 PVIDEOHDR lpvhdr 這個結構變數, 讓 FrameCallBack callback function 來使用。

  有關 PVIEDOHDR 這個結構的相關資訊, 可以在 vfw.h 中, 找到如下的結構定義:

/* video data block header */
typedef struct videohdr_tag {
LPBYTE lpData;      /* pointer to locked data buffer */
DWORD dwBufferLength;   /* Length of data buffer */
DWORD dwBytesUsed;   /* Bytes actually used */
DWORD dwTimeCaptured;  /* Milliseconds from start of stream */
DWORD_PTR dwUser;   /* for client's use */
DWORD dwFlags;      /* assorted flags (see defines) */
DWORD_PTR dwReserved[4];   /* reserved for driver */
} VIDEOHDR, NEAR *PVIDEOHDR, FAR * LPVIDEOHDR;

  透過 結構 videohdr_tag 的宣告, 我們獲知擷取視窗的 data buffer 的指標可以用 lpData 這個變數來取得, 即

  CaptureData = LPVIDEOHDR(lpvhdr);
  ptrCapture = CaptureData->lpData;


  // 全域變數宣告
  LPVIDEOHDR CaptureData;
  BYTE *ptrCapture, *ptrImage;


  說明:
  a. CaptureData 是全域結構變數, 其資料型態亦為 PVIDEOHDR。
  b. *ptrCapture 與 *ptrImage 是兩個指向記憶體位址的全域指標變數。

8. 實驗: 將視訊擷取視窗的內容即時顯示於影像物件之中。

  實驗目的: 了解視訊擷取視窗資料緩衝區的記憶體存取方法。
 
  本實驗說穿了就是把某一塊記憶體(視訊擷取視窗資料緩衝區/data buffer) 複製到另一塊記憶體(影像物件存放色彩資料的記憶體) 之中。因此, 我們只要弄清楚兩個記憶體的起始位址, 自然可以輕鬆完成實驗。

  透過 videohdr_tag 結構中的 lpData, 我們可以得到視訊擷取視窗的資料暫時存放區的起始位址。

  CaptureData = LPVIDEOHDR(lpvhdr);
  ptrCapture = CaptureData->lpData;


  透過 TBitmap::ScanLine 這個 property 我們可以得到影像元件中, 每一列(row) 的影像資料存放的記憶體起始位置。

  ptrImage = (BYTE *)Form1->imCW->Picture->Bitmap->ScanLine[i];

 範例程式: VGC-2010S-SP09.txt