以光學為基礎,融光電子學、電腦技術、雷射技術、影像處理技術等現代科學技術為一體的映像測量技術在測量領域中形成了新的測量技術,基於數位影像處理技術的映像測量系統目前已廣泛應用於幾何量的測量、航空等遙感測量、精密複雜零件的微尺寸測量和外觀測量,以及光波幹涉圖、應力應變場狀態分布等和映像有關的技術領域中。在基於數位影像處理技術的映像測量系統中,必須解決的問題就是映像採集,即映像資料的擷取,採集的映像資料用於後期的影像處理。
視頻映像捕獲一般來講有兩種方法,一種是利用視頻捕獲卡所附帶的SDK開發工具,這種捕獲方法的實現是與裝置有關的,依賴於視頻捕獲卡與網路攝影機的類型,不利於靈活應用;另外一種捕獲方法是Microsoft的Visual C++自從4.0版就開始支援Video for Windows(簡稱VFW),這給視頻捕獲編程帶來了很大的方便,利用VFW技術的可以提高視頻捕獲的靈活性,減少了對視頻裝置的依賴。在VC++6.0中,含有MCIAVI、DRAWDIB、AVIFILE和AVICAP等組件。通過它們之間的協調工作,可以完成播放、編輯、檔案管理和視頻捕獲等功能,為視頻影像處理和分析帶來非常大的便利,本文就利用VFW進行視頻資料的即時採集中的碰到的幾個實際問題進行探討。
VFW庫函數簡介
視頻資料的即時採集主要是通過調用AVICap32.dll建立AVICap視窗類別 ,由AVICap視窗類別中的訊息、宏函數、結構以及回呼函數來完成。 AVICap在捕獲視頻方面具有明顯的優勢,它能直接存取視頻緩衝區,不需要產生中間檔案,即時性很高,它也可將數位視訊儲存到事先建好的檔案中。實際應用表明,通過這種方法,提高了視頻採集的效果和程式啟動並執行效率,同時也減少了對硬體的依賴性,提高了程式的相容性和移植性。
VFW的視頻採集功能主要包括捕獲視頻流至AVI檔案(capCaptureSequence)、捕獲視頻流至緩衝(capCaptureSequenceNofile)、捕獲視頻流至AVI檔案(capCaptureSingleFrame)、本地預覽(capPreview/capOverlay)和捕獲單幀預覽(capGrabFrame/capGrabFrameNoStop)等。VFW還提供了回呼函數 ,允許應用程式精確控制視頻流的捕獲、檢測錯誤、監控狀態變化 ,以及在捕獲兩幀資料的空隙和每捕獲新幀時對即時資料進行處理。
幾個實際問題的探討
1、回呼函數處理的問題
回呼函數是至今為止最有用的編程機制之一。在Windows中,回呼函數更是視窗過程、鉤子過程、非同步程序呼叫所必需的,在整個回調過程中自始至終地使用回調方法。人們可以註冊回調方法以獲得載入/卸載通知,未處理異常通知,資料庫/視窗狀態修改通知,檔案系統修改通知,功能表項目選擇,完成的非同步作業通知,過濾一組條目等等。在VFW中有幾條這樣的宏函數,如用於設定在發生某事件後能作出反應的回呼函數的宏函數,它和中斷服務機制很相似,條件一滿足,程式會自動進入相應的回呼函數體中,該函數究竟要做些什麼,全由開發人員藉助其參數自行編製程式來確定。利用VFW擷取即時視頻資料通常可以運用視頻處理的回調機制(call-backmechanism) 獲得即時資料緩衝區的首址和長度並對映像資料進行處理,同時也可以進行視頻資料的直接傳輸,在這一方面很多文章都作了具體的介紹。但是按照大多數文章的介紹,在具體的應用過程中,對回呼函數作如下定義時,程式總是無法通過編譯:
{ LRESULT CALLBACK FrameCallbackProc(HWND ghWnd,LPVIDEOHDR lpVData) unsigned char *data; data=lpVData->lpData;//獲得視頻資料首地址,並將資料存入data數組中以便處理 } |
通過研究,發現根本原因是回呼函數是基於C編程的Windows SDK的技術,不是針對C++的,可以將一個C函數直接作為回呼函數,但是如果試圖直接使用C++的成員函數作為回呼函數將發生錯誤,甚至編譯就不能通過。其錯誤是普通的C++成員函數都隱含了一個傳遞函數作為參數,亦即“this”指標,C++通過傳遞一個指向自身的指標給其成員函數從而實現程式函數可以訪問C++的資料成員。這也可以理解為什麼C++類的多個執行個體可以共用成員函數但是確有不同的資料成員。由於this指標的作用,使得將一個CALLBACK型的成員函數作為回呼函數安裝時就會因為隱含的this指標使得函數參數個數不匹配,從而導致回呼函數安裝失敗。要解決這一問題的關鍵就是不讓this指標起作用,通過採用以下兩種典型技術可以解決在C++中使用回呼函數所遇到的問題。這種方法具有通用性,適合於任何C++。
(1) 不使用成員函數,直接使用普通C函數,為了實現在C函數中可以訪問類的成員變數,可以使用友元操作符(friend),在C++中將該C函數說明為類的友元即可。這種處理機制與普通的C編程中使用回呼函數一樣。
(2) 使用靜態成員函數,靜態成員函數不使用this指標作為隱含參數,這樣就可以作為回呼函數了。靜態成員函數具有兩大特點:其一,可以在沒有類執行個體的情況下使用;其二,只能訪問靜態成員變數和靜態成員函數,不能訪問非靜態成員變數和非靜態成員函數。由於在C++中使用類成員函數作為回呼函數的目的就是為了訪問所有的成員變數和成員函數,如果作不到這一點將不具有實際意義。解決的辦法也很簡單,就是使用一個靜態類指標作為類成員,通過在類建立時初始化該靜態指標,如pThis=this,然後在回呼函數中通過該靜態指標就可以訪問所有成員變數和成員函數了。這種處理辦法適用於只有一個類執行個體的情況,因為多個類執行個體將共用靜態類成員和靜態成員函數,這就導致靜態指標指向最後建立的類執行個體。為了避免這種情況,可以使用回呼函數的一個參數來傳遞this指標,從而實現資料成員共用。這種方法稍稍麻煩,這裡就不再贅述。
因此,可以對回呼函數作如下定義:
static LRESULT CALLBACK FrameCallbackProc(HWND ghWnd,LPVIDEOHDR lpVData) { unsigned char *data; data=lpVData->lpData;//獲得視頻資料首地址,並將資料存入data數組中以便處理 } |
2、映像採集中的問題
視頻資料的採集是整個應用的關鍵,根據應用的不同可以將視訊框架採集到的檔案或採集的緩衝直接加以處理。利用VFW擷取即時視頻資料通常可以運用視頻處理的回調機制(call-backmechanism) 獲得即時資料緩衝區的首址和長度並對映像資料進行即時處理,但是在這個過程中影像處理程式不能太長,否則視頻顯示不流暢。另外,在實際採集過程中,有些映像採集卡驅動程式對通過回調機制所擷取的資料進行了壓縮,比如DC10,而在映像測量系統高精度的要求,壓縮後的映像資料直接影響以後的影像處理工作。通過採用以下兩種典型技術可以實現將沒有壓縮的視訊框架的映像資料的採集。
(1) 通過研究發現,儘管映像採集卡驅動程式對通過回調機制所擷取的資料進行了壓縮,但是利用VFW中的capEditCopy( )宏函數將幀映像緩衝區中的映像資料拷貝到剪貼簿上時,並沒有壓縮映像資料,因此可以不採用回調機制而直接利用capGrabFrameNoStop()捕獲一幀映像,然後將資料拷貝拷貝到剪貼簿上,再通過DIB(Device Independent Bitmap)操作擷取記憶體中映像資料首地址,進行後續的映像資料處理。具體的程式碼片段如下:
//獲得capEditCopy( )拷貝到剪貼簿中的映像資料控制代碼,通過CF_DIB參數指定資料 HANDLE hData; ::GlobalFree ((HGLOBAL)hData); hData=(HANDLE)CopyHandle(::GetClipboardData(CF_DIB)); |
(2) 利用capFileSaveDIB將緩衝區中的映像資料轉化為DIB位元影像直接儲存為檔案,需要處理時,再讀取位元影像中的映像資料到記憶體進行後續的處理。這種方式因為有一個檔案儲存體和讀取的延遲,對於即時的影像處理來說,響應速度比前者要稍慢,經過多次實驗證明,只要影像處理演算法的計算量不是很大,仍然可以保證比較好的即時性。
3、映像採集視窗建立的問題
在視頻捕獲之前需要建立一個捕獲窗,所有的捕獲操作及其設定都以它為基礎。一個AVICap視視窗控制代碼描述了聲頻與視頻流的細節,這樣就使程式員的應用程式從AVI檔案格式,聲頻視頻緩衝管理,低層聲頻視頻驅動訪問等解脫出來。AVICap即是預定義的Windows視窗類別,利用該視窗類別建立的子視窗可以與視頻採集裝置的驅動程式相聯絡,該子視窗的客戶區用來顯示採集裝置捕獲的即時視頻映像。
但是在實際應用過程中,應用程式可能會基於單文檔(SDI)、多文檔或者是基於對話方塊的介面,由於三種類型的不同,捕獲窗的具體建立應根據具體要求而有所區別。不管是採用哪種類型,根據即時視頻的顯示的具體要求,關鍵是如何擷取捕獲視窗的父視窗控制代碼。通常捕獲視窗的建立可以如下兩種方式:
(1)擷取動態建立的父視窗的控制代碼,動態建立捕獲視窗。具體的程式碼片段如下:
CFrameWnd m_wndSource; if(!m_wndSource.CreateEx(WS_EX_TOPMOST,NULL,"source",WS_CAPTION,CRect(100,100,150,180),NULL,0)) return -1; m_wndSource.ShowWindow(SW_HIDE); m_WndCap=capCreateCaptureWindow((LPSTR)" 視頻捕捉測試程式",WS_CHILD|WS_VISIBLE,0,0,300,240,m_wndSource.m_hWnd ,0); |
(2)擷取顯示的即時視頻視窗的控制代碼,靜態建立捕獲視窗。具體的程式碼片段如下:
m_hCapWnd = capCreateCaptureWindow((LPSTR)TEXT("視頻捕捉測試程式"),WS_CHILD|WS_VISIBLE, 0,0,768,576,this->m_hWnd,0); |
結束語
利用VFW技術實現視頻資料即時擷取,提高了視頻採集的效果和程式啟動並執行效率,同時也減少了對硬體的依賴性,提高了程式的相容性和移植性。在很多基於數位影像處理技術的映像測量系統中都使用了這種方法。本文就具體應用中所碰到的實際問題進行了詳細的討論,並給出了具體的解決辦法。