2005-11-29

[視訊] CallbackOnFrame 之視訊串流轉 Bitmap

【緣由】:

我認為要把視訊中的資料做出遊戲用的控制,

必需要先把視訊串流 (Video Stream) 轉換成 Bitmap (可秀在 Image 物件上的),

才能再進行影像處理 or 遊戲的控制之判斷
【想法】:

VFW 函式庫提供的 CallbackOnFrame procedure,會給一個指標

這指標指向一個結構: lpVHdr (LPVIDEOHDR :: Video Header)

此結構裡包含一個指標 lpData,指向 Video Stream

也就是我們要的視訊串流資料

我參考了 K-Top 討論區「【BCB】【問題】請問CCD經由panel取出再轉入image」這篇

利用一個一個 RGB Color 的對應原理

成功轉換成 Bitmap 並顯示在一個 Image 元件上
【做法】:
  拉一個 Image 元件 (我設 Name 為 imgPreviewImg )來放
  用小畫家做一張 352×288大小全黑色的 BMP 圖檔
  載入 Image 元件中 (按屬性的 Picture 來 Load 進來)。

  宣告以下廣域變數:

UCHAR *pData ;
UCHAR ColorBufOrgn[288][352][3] ;
UCHAR ColorBufShow[288][352][3] ;
Graphics::TBitmap *Pic ;
  在主程式 (Form1)OnCreate 事件中加入下列程片段

Pic = imgPreviewImg->Picture->Bitmap ;
imgPreviewImg->Canvas->Brush->Style = bsClear ;
imgPreviewImg->Canvas->Pen->Color = clBlack ;

  在 CallbackOnFrame Procedure 中加入以下程式片段

// 宣告區域變數
pData = lpVHdr->lpData ;
static int i, j ;

// 上下顛倒取出 RGB 並存入 RGB Color 陣列
for (i = 288-1 ; i >= 0 ; i--) {
for (j = 0 ; j < 352 ; j++) {
ColorBufOrgn[i][j][0] = *pData++;
ColorBufOrgn[i][j][1] = *pData++;
ColorBufOrgn[i][j][2] = *pData++;
}
}

// 顯示在 Image 上
if (!Pic->Empty) {
Pic->PixelFormat = pf32bit ;
for (int Row = 0; Row < 288 ; Row++) {
RGBQUAD *Pixel = (RGBQUAD *) Pic->ScanLine[Row] ;
for (int Col = 0; Col < 352 ; Col++, Pixel++) {
Pixel->rgbBlue = (BYTE) ColorBufOrgn[Row][Col][0] ;
Pixel->rgbGreen = (BYTE) ColorBufOrgn[Row][Col][1] ;
Pixel->rgbRed = (BYTE) ColorBufOrgn[Row][Col][2] ;
}
}
Form1->imgPreviewImg->Invalidate() ;
}
【成果】:


【缺點】:

目前只能固定視訊大小為 352×288,不能更動。不然會程式當掉。

2005-11-09

[預計] 專題計畫題目 Idea

期末專題

題目:「猜數字」

做法:

先把視訊中,畫製幾個格子,填入一個範圍內的數字

再把視訊中的玩家,從背景中截出,判斷手指點選了畫面中哪一個格式的數字

再跟電腦事先亂數決定的數字比較,來判斷輸贏

並且累計分數....

可能再加上輸贏過關的一些動畫、或音樂

讓玩家玩得盡興!!


2005-11-05

[界面] 取得系統各種單位像素大小

取得系統單位,以利程式設計者方便計算長寬,與調整各個元件之維度

單位大小: Pixel (像素)

以下取用我的程式中,有的部份當範例

// 取得系統字體單位大小
int cxChar = LOWORD (GetDialogBaseUnits ()) ;
int cyChar = HIWORD (GetDialogBaseUnits ()) ;

// 取得系統框邊單位大小
int cxBorder = GetSystemMetrics(SM_CXBORDER) ;
int cyBorder = GetSystemMetrics(SM_CYBORDER) ;

// 取得系統標題單位高度
int cyCaption = GetSystemMetrics(SM_CYCAPTION) ;

// 取得系統單行選單列之單位高度
int cyMenu = GetSystemMetrics(SM_CYMENU) ;


之後,可以再利用 MoveWindow(handle, x, y, w, h, true) 函式,
在各元件的 OnResize 事件
(Event) 中去自動執行調整大小,
則可達到視窗縮放時,保持著各元件之比例

本程式範例圖:

原始大小


視窗拉大


2005-11-02

[界面] 視窗啟動時的顯示位置

之前主程式 Form1 執行時,位置一直記憶在篆寫時的移動位置

每次換台電腦做,解析度不一樣,執行就會偏太旁邊

所以我想要把 Form1 啟動時,對齊在畫面正中央

方法如下圖:

2005-11-01

[用法] fnsplit() - 切割檔名路徑資訊

〔函式〕
int fnsplit(const char *path, char *drive, char *dir, char *name, char *ext);
〔說明〕
  • [輸入]
  1. path :: 檔名路徑
  • [輸出]
  1. drive :: 第幾槽 [含反斜] (例 C:\ )
  2. dir :: 資料夾 [含子資料夾、前後反斜線] (例 \Program Files\Winamp\)
  3. name :: 檔名 (例 Winamp)
  4. ext :: 副檔名 [含句點 "."] (例 .exe)
〔用法〕
開頭要 #include <dir.h>
範例

char szFPath[MAX_FILENAME+4];
char szFDrv[8];
char szFDir[MAX_FILENAME];
char szFName[64];
char szFExt[16];

if (SaveCapFileDlg->Execute()) {
wsprintf(szFPath, "%s", SaveCapFileDlg->FileName.c_str());
fnsplit(szFPath, szFDrv, szFDir, szFName, szFExt);
ShowMessage(
 
 "[Driver]= " + AnsiString(szFDrv) + '\n' +
 
 "[Directory]= " + AnsiString(szFDir) + '\n' +
  "[File name]= " + AnsiString(szFName) + '\n' +
  "[File Extension]= " + AnsiString(szFExt) + '\n' );
}