電腦視覺應用廣泛,從醫療診斷到工業檢測皆仰賴即時影像處理。本文分享作者使用 VC++ 整合 PCI 影像擷取卡(CA-MPE1000)的實務經驗,涵蓋驅動安裝、調色板設定、影像擷取顯示,並進階實作「畫中畫」技術,解決視訊串流雙畫面閃爍問題。
mpew32.dll/.lib 函式庫VxD.vxd 複製到 Windows\SYSTEM。mpew32.lib,引入標頭檔宣告初始化、擷取等函式。W32_ReadXMS2Buf 讀取原始影像資料,再以 SetDIBitsToDevice 顯示。CTestView::CTestView() {
W32_Init_MPE1000();
W32_Modify_Contrast(50);
W32_Modify_Brightness(45);
W32_Set_HP_Value(945);
wCurrent_Frame = 1;
W32_Set_Input_Source(1);
W32_CACardParam(AD_SETHPFREQ, hpGrabFreq);
W32_Set_PAL_Range(1250, 1024);
W32_Set_VGA_Mode(1);
wGrabWinX1 = wGrabWinY1 = 0;
bGrabMode = FRAME; bZipMode = ZIPPLE;
lpDib = NULL; firstTime = TRUE;
}
CTestView::~CTestView() { W32_Close_MPE1000(); }
void CTestView::OnGraboneframe() {
wCurrent_Frame = 1;
W32_CACardParam(AD_SETGRABDEST, CA_GRABMEM);
if (lpDib) { GlobalUnlock(hglbDIB); GlobalFree(hglbDIB); }
hglbDIB = GlobalAlloc(GHND, (DWORD)wImgWidth * wImgHeight);
lpDib = (BYTE*)GlobalLock(hglbDIB);
hdc = GetDC()->GetSafeHdc();
if (lpDib) {
cxDib = wImgWidth; cyDib = wImgHeight;
SetLogicPal(hdc, cxDib, cyDib, 8);
SetStretchBltMode(hdc, COLORONCOLOR);
bGrabMark = TRUE;
while (bGrabMark) {
if (msg.message == WM_LBUTTONDBLCLK) bGrabMark = FALSE;
W32_ReadXMS2Buf(wCurrent_Frame, lpDib);
SetDIBitsToDevice(hdc, 0,0, cxDib,cyDib, 0,0, 0,cyDib,
(LPSTR)lpDib, bmi, DIB_RGB_COLORS);
}
W32_CAStopCapture();
::ReleaseDC(GetSafeHwnd(), hdc);
}
}
void WINAPI InitLogicPal(HDC hdc, short width, short height, WORD bitCount) {
LOGPALETTE* pLogPal = (LOGPALETTE*)malloc(sizeof(LOGPALETTE)+256*sizeof(PALETTEENTRY));
pLogPal->palVersion = 0x300; pLogPal->palNumEntries = 256;
for (int i=0;i<256;i++) {
pLogPal->palPalEntry[i].peRed = pLogPal->palPalEntry[i].peGreen = pLogPal->palPalEntry[i].peBlue = i;
pLogPal->palPalEntry[i].peFlags = 0;
}
hPal = CreatePalette(pLogPal); free(pLogPal);
SelectPalette(hdc, hPal, 0); RealizePalette(hdc);
bmi = (BITMAPINFO*)malloc(sizeof(BITMAPINFOHEADER)+256*sizeof(RGBQUAD));
bmi->bmiHeader.biSize=40; bmi->bmiHeader.biWidth=width; bmi->bmiHeader.biHeight=height;
bmi->bmiHeader.biPlanes=1; bmi->bmiHeader.biBitCount=bitCount; bmi->bmiHeader.biCompression=0;
for (int i=0;i<256;i++) {
bmi->bmiColors[i].rgbRed = bmi->bmiColors[i].rgbGreen = bmi->bmiColors[i].rgbBlue = i;
bmi->bmiColors[i].rgbReserved = 0;
}
}
傳統做法先畫大圖再畫小圖會造成視訊串流閃爍。改進方法:使用記憶體 DC (CDC MemDC) 先將大圖與小圖合成離屏,再一次 StretchBlt 輸出螢幕,消除閃爍。
void pictureinpicture() {
BYTE* pData1 = new char[biWidth*biHeight*3]; // 大圖緩衝
Read(pData1, biWidth*biHeight*3); // 從擷取卡讀取
BITMAPINFO* bmi1 = CreateBMI(24, biWidth, biHeight); // 24-bit RGB
BYTE* pData2 = new char[biWidth1*biHeight1*3]; // 小圖緩衝 (1/4 或 1/9)
Read(pData2, biWidth1*biHeight1*3);
BITMAPINFO* bmi2 = CreateBMI(24, biWidth1, biHeight1);
CDC MemDC; MemDC.CreateCompatibleDC(&dc);
CBitmap bmp; bmp.CreateCompatibleBitmap(&dc, biWidth, biHeight);
CBitmap* pOld = MemDC.SelectObject(&bmp);
// 1. 大圖畫到記憶體 DC
StretchDIBits(MemDC.m_hDC, 0,0, biWidth,biHeight, 0,0, -biWidth,biHeight,
pData1, bmi1, DIB_RGB_COLORS, SRCCOPY);
// 2. 小圖疊加在右上角 (20,20)
StretchDIBits(MemDC.m_hDC, 20,20, biWidth1,biHeight1, 0,0, biWidth1,biHeight1,
pData2, bmi2, DIB_RGB_COLORS, SRCCOPY);
// 3. 一次性輸出螢幕
StretchBlt(dc.m_hDC, 0,0, biWidth,biHeight, MemDC.m_hDC, 0,0, biWidth,biHeight, SRCCOPY);
MemDC.SelectObject(pOld);
delete[] pData1; delete bmi1; delete[] pData2; delete bmi2;
}
透過廠商 SDK 整合擷取卡、自行管理調色板與記憶體 DC 合成,即可在 VC++ 建立高效能視訊處理應用。畫中畫技術關鍵在於離屏合成,避免直接螢幕繪圖造成閃爍,同樣原理可延伸至多畫面監控、ROI 縮放顯示等進階功能。