2005-12-27

[遊戲] 取得並判斷不同區域之猜拳機

【程式整個重寫】:
因為為了跟「楊清驛」同學的最終目標「猜拳機」做結合
所以界面整個重新寫過
也順便去除掉不必要的功能
【拳猜原理】:
視窗截取畫面後,除之做之前做過的『移動差異畫面的偵測』之外
我們還加上了... 很簡單的選擇「剪刀」、「石頭」、「布」的方法
也就是在不同區塊判斷裡面有差異值之比例是否過半
並且「連續」維持了幾個畫面
即可得到「很正確」的猜拳值

「剪刀」、「石頭」、「布」的3區域之分佈圖:


另外提供了類似「格鬥」畫面
血條』、『打羸』、『打輸』、、、的畫面
與戰鬥中「玩家」與「電腦」雙方之氣勢與表情圖案!

情表圖案取自無名網誌 - 《彎彎

初始分數為『100分』

計分方式為:
  猜羸者 » 不加分 & 不扣分
  猜輸者 » 扣 10 分
誰先沒血 (0分) 就誰死,而也就是對方就羸了
【程式預覽圖】:
新界面 - 第 1 頁 (遊戲開始)


新界面 - 第 2 頁 (玩遊戲ing...)


新界面 - 第 2 頁 (遊戲結果出爐...)


新界面 - 第 3 頁 (遊戲結束)

2005-12-14

[視訊] 移動偵測機制

【目標】:
把視訊中移動的物體取出來

好讓之前遊戲要能夠判斷玩家的手勢、選取的區塊…等等。
【做法】:
把之前做過的視訊串流 (Video Stream) 轉 Bitmap 加上這一道機制

A. 首先要在視訊連線後,設定背景畫面
  (之後移動拍攝地點也要重設)


B. 把後來收的畫面,一個 Pixel 一個 Pixel 地“減”去背景畫面,
  因為不管值為正負,只管跟原背景色的【差距】
  所以減完後,還要取【絕對值】(abs),才能得出差距畫面。
   公式 1.
   Diff[Row][Col] = abs(Current[Row][Col] - BackGround[Row][Col]) ;

C. 但經過我的測試後,發現 WebCam 的雜訊很高
  所以要再經過超過【門檻值(Threshold)之判斷的處理
  讓低於門檻值的差距畫面就放棄不要嚕
   公式 2.
Diff[Row][Col] = abs(Current[Row][Col] - BackGround[Row][Col]) - Threshold;

D. 發現背景光線的明暗程度也會隨有侵入物之移動而有所變化
  造成整個畫面都被偵測出【移動差距】
  所以我建議在視訊來源之設定對話方塊 (Video Source Dialog)
  把所有有關【自動調整】的部份,通通關掉
  之後果然不會整個畫面都被偵測出是移動中的狀態。
  《設定圖解》:
  

E. 再經過我個人的測試後,【差距畫面】的雜訊還是很高
  所以我想起大二上過的影像處理
  有教過去雜訊的技術
  所以引進了【3*3 Order-Statistics Filter ~ Median】演算法
  其最後的移動取樣的效果非常好 :D
【完成圖】:
視訊開始播放,未設定背景畫面進程式記憶中



設定背景畫面後



以手移入來當移動偵測


[視訊] 嵌入 Windows Media Player

【目標】:
可以在本程式中,開啟已錄好的 AVI 檔
且播放出來
【做法】:
1. BCB 要先安裝 Windows Media Player 這個 ActiveX 元件
 《步驟》:
   a. 打開 BCB,按選單列的【Components\Import ActiveX Control...
   b. 拉到最下面選【Windows Media Player (Version 1.0)
     再按下面的【Install...
   c. 重開 BCB,即可在元件欄→『ActiveX』那頁找到剛新增的
     『WindowsMediaPlayer』這個元件

2. 除了拉各WindowsMediaPlayer』元件之外,
 再各拉一個 Button 跟 OpenDialog 元件做【開檔】用
 也可再多拉幾個 Button 分別做
 【播放(Play)】,【暫停(Pause)】,【停止(Stop)

3. 常用函式如下:
  // 開啟舊檔 (格式為 AnsiString 即可)
  if (OpenAVIDlg->Execute()) {
    wmp->URL = OpenAVIDlg->FileName ;
 }
  // 播放
  wmp->controls->play() ;
  // 暫停
  wmp->controls->pause() ;
  // 停止
  wmp->controls->stop() ;
【完成圖】:

2005-12-08

[視訊] 透明度 & 負片效果

【做法】
  把之前 Video Stream 轉 Bitmap 程式碼
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++;
 }
}
  修改如下
for (Row = 288-1 ; Row >= 0 ; Row--) {
 for (Col = 0 ; Col < 352 ; Col++) {
  ColorBufOrgn[Row][Col][0] =
   Form1->pmiImgInverse->Checked? (255 - *pData++ * nTrp/100) : (*pData++ * nTrp/100) ;

  ColorBufOrgn[Row][Col][1] =
   Form1->pmiImgInverse->Checked? (255 - *pData++ * nTrp/100) : (*pData++ * nTrp/100) ;

  ColorBufOrgn[Row][Col][2] =
   Form1->pmiImgInverse->Checked? (255 - *pData++ * nTrp/100) : (*pData++ * nTrp/100) ;

 }
}
  紫色部份為使用 CheckBox 元件來切換是否使用負片模式 (橘色)
  而
nTrp 的值則是使用 TrackBar 元件,讓使用“即時”調整透明度
  並再使用一個 StaticText 元件,即時顯示當下的透明度 % 數

【預覽】

正常



負片效果


透明化效果


2005-12-06

[顯示] 解決 Image 播放畫面會閃爍

【緣由】:
上次把預覽畫面之視訊串流

轉成 Bitmap 並顯示在一個 Image 物件上

發現一個缺點:Image畫面會閃爍

【解法】:『啟動雙倍緩衝區』

    先判斷顯示畫面的物件 (Image) 是在哪個物件上面?
    (在它之下又是最貼接它的)

    假如是直接在主程式 (Form1) 上面放 Image 物件

    那麼就是 Form1

    則在主程式視窗建立事件 (Form1 的 OnCreate Event)

    加入下列程式碼即可,畫面就不會再閃爍了

Form1->DoubleBuffered = true ;


   再例如我的程式:

   的 Image 元件是放在分頁元件 (PageControl)

   其中一頁 (TabSheet) 上面叫 tsPreviewImg

   則我的程式要在 Form1\Event → OnCreate 內加入以下這段

tsPreviewImg->DoubleBuffered = true ;

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

2005-10-26

[BCB] 視訊遊戲程式 - 舊新更內容

[20050921]
1.) 建立基本的視訊截取視窗、取得裝置數量
2.) 界面上可選則裝置 Index,並顯示裝置名稱與版本號碼
3.) 提供截取裝置與視訊視窗之連線 & 離線

[20050923]
1.) 增加「Menu」選單列,可以切換 Display mode 為 Preview 還是 Overlay
2.) 可以預覽了 (是動態的,而不是第一張靜止畫面而已)

[20050924]
1.) 連線時,順便取得裝置之性能資訊 (capDriverCaps){Capabilities}
2.) 可"即時"調整「預覽速率」

[20050925]
1.) 視窗可隨意調整大小,並讓截取視窗跟著自動「即時」調整大小
2.) 增加選單列的項目:開啟截取裝置之
「視訊來源」、「視訊格式」、「視訊顯示」、「視訊壓縮」對話方塊

[20050927]
1.) 增加 Callback on Frame function

[20051001]
1.) 增加狀態列,並顯示 Frame 編號
2.) 調整了視窗縮放時,各 Panel 的尺寸比例,視訊畫面不會再是長長的

[20051004]
1.) 調整了視窗縮放時畫面是否自動縮放(Auto Scale)之判斷,不再會有殘影現像 ^^
2.) 美化了程式區段註解與精簡程式碼。

[20051005]
1.) 取得截取裝置之視訊狀態資訊 (capGetStatus)
2.) 在畫面不自動縮放時也就是正常大小時,把截取視窗(hVideo)依視訊長寬來縮放。
3.) 增加程式結束時,釋放之前所把持的系統資源
(如: DriverDisconnect、capSetCallbackOnFrame)
4.) 把 Connect 跟 Disconnect 鈕做在同一個 Button 改用 Flag 記憶狀態與控制。

[20051011]
1.) 修改狀態列,在狀態列新增 2 個 Panel 欄位,顯示裝置連線狀態跟 Frame 編號
2.) 趕專研文書,近幾天還沒有做出新的功能 :P

[20051012]
1.) 新增截取單一畫面,並複製到剪貼簿的功能

[20051015]
1.) 新增截取單一畫面,並另存成 BMP 圖檔的功能

[20051017]
1.) 上一條功能,增加在存檔時,判斷副檔名是否正確

[20051018]
1.) 取得視訊截取參數(結構)
2.) 新增一個 PageControl 元件,把原來右半部的控制、資訊元件放到第一頁
而上面新增的截取裝置參考則顯示在第二頁.....

[20051022]
1.) 右半部 PageControl 新增三個分頁
第一頁放原來初始、啟動視訊裝置的控制元件
第二頁放取得截取參數按鈕、設定截取參數按鈕,跟兩個子分頁顯示截取參數資訊
第三頁準備放截取視訊用的元件
2.) 左半部的 Frame Panel ,也改為 PageControl,新增兩個分頁
第一頁放原來的預覽影格
第二頁準備來放截取視訊影格、或播放影格
3.) 大幅修改整個主視窗與每個子視窗、分頁、Panel 的 Resize 動作
利用取得系統各字元,框邊,標題列,選單列的單位大小來計算每個元件之定位
4.) 完成了截取視訊連續畫面並存檔的功能,還可以壓縮

[20051025]
1.) 修改了一下顯示 Capture Params 的欄位.

[20051026]
1.) 實現了儲存單一張 bmp 畫面跟 avi 視訊時,強制把副檔名分別設為 bmp 跟 avi
2.) 改進之前截取單張畫面到剪貼簿成為:截取到左欄第二頁裡的 Image 元件上秀出
3.) 修復之前 avi 存檔時會當機的問題