在現代圖像處理系統中,視訊擷取與即時處理堪稱核心關鍵技術。能否精準擷取指定視訊影像,進而實現精確的資料分析與處理,直接關係到整個系統的成敗。筆者在開發「公路安全線軋壓檢測系統」時深有體會:該系統旨在監測關鍵路段車輛是否瞬間軋壓黃色安全線,透過即時拍攝、抓取瞬間影像,經系統分析處理後及時檢測車輛行駛狀況,進而驅動控制設備做出相應處理。顯然,即時擷取視訊影像是系統的命脈所在。
為解決即時視訊擷取需求,筆者採用 Microsoft 推出的 Video for Windows (VFW) 技術。VFW 讓應用程式能透過數位化設備從傳統類比視訊源取得數位視訊片段,其核心優在於播放時不需專用硬體。針對數位視訊資料量龐大的問題,VFW 引入 AVI 檔案標準,規範視訊與音訊在硬碟及 AVI 檔案中的交替儲存方式。VFW 更提供標準介面,讓程式設計師透過發送訊息或設定屬性即可擷取、播放與編輯視訊片段。安裝 VFW 時會自動配置所需組件(裝置驅動程式、視訊壓縮程式等),主要包含六大功能模組:
| 模組 | 功能 |
|---|---|
| AVICAP.DLL | 包含視訊擷取函數,為 AVI 檔案 I/O 處理及視訊/音訊裝置驅動提供高階介面 |
| MSVIDEO.DLL | 含特殊 DrawDib 函數,處理螢幕視訊操作 |
| MCIAVI.DRV | VFW MCI 命令解譯器驅動程式 |
| AVIFILE.DLL | 提供標準多媒體 I/O (mmio) 更高層命令,存取 .AVI 檔案 |
| ICM | 壓縮管理器,管理視訊壓縮/解壓縮編碼器 (Codec) |
| ACM | 音訊壓縮管理器,提供類似 ICM 服務,適用波形音訊 |
本文採用 AVICap 視窗類別開發視訊擷取程式。AVICap 支援即時視訊串流擷取與單幀擷取,並提供視訊源控制。雖然常用的 MCI 控制項也提供數位視訊服務,但其命令主要基於檔案操作,無法滿足即時從視訊緩衝區提取資料的需求。對於無視訊疊加能力捕獲卡的 PC,MCI 更無法擷取視訊串流。AVICap 優勢在於能直接存取視訊緩衝區,無需產生中間檔案,即時性強、效率高,亦可將數位視訊擷取至檔案中。
capCreateCaptureWindow 函數建立,視窗樣式設為 WS_CHILD 和 WS_VISIBLE。擷取視窗功能涵蓋:擷取視訊/音訊串流至 AVI 檔案、動態連接/斷開輸入裝置、Overlay 或 Preview 模式即時顯示、指定檔案名與複製擷取內容、設定擷取速率、顯示控制對話框、建立/儲存/載入調色盤、複製影像至剪貼簿、儲存單幀影像為 DIB 格式等。capDriverConnect 函數與裝置驅動程式關聯,才能取得視訊訊號。TCaptureParms 結構成員變數,控制取樣頻率、中斷取樣按鍵、狀態列行為。設定完成後用 capCaptureSetSetup 生效,再以 capPreviewScale、capPreviewRate 設定預覽比例與速度,或採用裝置預設值。capOverlay 選擇是否採用疊加模式預覽(減少系統資源佔用、加快顯示速度),再以 capPreview 啟動預覽,即可在螢幕看見攝影機影像。若需自行處理擷取的視訊資料,則須使用擷取視窗回呼函數,如逐幀取得視訊資料或串流方式取得資料。
針對系統存取與處理速度的特殊需求,筆者選用 Delphi 為開發工具。以下以逐幀從視訊裝置擷取視訊資料的程式為例,說明各函數作用與具體開發過程。範例功能:螢幕顯示擷取視訊,並可取得每幀影像資料。
AVICAP32.PAS 加入 uses 子句。TPanel(Name 設為 gCapVideoArea,用於顯示視訊),及兩個 TButton(OpenVideo、CloseVideo)。ghCapWnd: THandle; // 擷取視窗控制代碼
VideoStr: LPVIDEOHDR; // 取得視訊資料指標的結構,用於回呼函數
CapParms: TCaptureParms; // 設定裝置屬性的結構變數
OpenVideo 按鈕 OnClick 事件程式碼:
procedure TForm1.OpenVideoClick(Sender: TObject);
begin
// 使用 TPanel 建立擷取視窗
ghCapWnd := capCreateCaptureWindow(PChar('KruwoSoft'),
WS_CHILD or WS_VISIBLE, 0, 0,
gCapVideoArea.Width, gCapVideoArea.Handle, 0);
// 設定回呼函數
capSetCallbackOnVideoStream(ghCapWnd, @VideoStreamCallBack); // 擷取視訊串流
capSetCallbackOnError(ghCapWnd, @ErrorCallBack); // 取得裝置錯誤
capSetCallbackOnStatus(ghCapWnd, @StatusCallBack); // 取得裝置狀態
capSetCallbackOnFrame(ghCapWnd, @FrameCallBack); // 幀擷取回呼
// 關聯擷取視窗與裝置驅動 (參數 0 為第一個裝置)
capDriverConnect(ghCapWnd, 0);
// 設定擷取參數
CapParms.dwRequestMicroSecPerFrame := 40000; // 25 fps
CapParms.fLimitEnabled := FALSE;
CapParms.fCaptureAudio := FALSE; // 無音訊
CapParms.fMCIControl := FALSE;
CapParms.fYield := TRUE;
CapParms.vKeyAbort := VK_ESCAPE;
CapParms.fAbortLeftMouse := FALSE;
CapParms.fAbortRightMouse := FALSE;
capCaptureSetSetup(ghCapWnd, @CapParms, SizeOf(TCaptureParms));
capPreviewScale(ghCapWnd, 1);
capPreviewRate(ghCapWnd, 66);
// 不產生 AVI 檔案,直接擷取串流
capCaptureSequenceNoFile(ghCapWnd);
// 啟用疊加模式並開始預覽
capOverlay(ghCapWnd, 1);
capPreview(ghCapWnd, 1);
end;
CloseVideo 按鈕 OnClick 事件:
procedure TForm1.CloseVideoClick(Sender: TObject);
begin
capCaptureAbort(ghCapWnd); // 停止擷取
capDriverDisconnect(ghCapWnd); // 斷開擷取視窗與驅動
end;
function FrameCallBack(hWnd: HWND; lpVHdr: LongInt): LongInt; stdcall;
var
DataPoint: ^Byte;
DibLen, RectWidth, RectHeight: Integer;
begin
VideoStr := LPVIDEOHDR(lpVHdr);
DibLen := VideoStr^.dwBufferLength;
GetMem(DataPoint, 64000);
// 複製幀資料到記憶體 (DataPoint 需預先配置空間)
CopyMemory(DataPoint, VideoStr^.lpData, DibLen);
// 此處可進一步處理影像資料...
Result := 1;
end;
靈活運用 AVICap 視窗類別的回呼函數可滿足多樣化需求,但須注意:從視訊卡擷取的視訊資料格式與影像長寬需參考視訊卡參數。部分視訊卡支援多種格式與解析度,還原影像時務必依據實際使用的視訊卡參數設定,以確保影像正確顯示與處理。