閱讀516 返回首頁    go 阿裏雲 go 技術社區[雲棲]


在DirectShow的視頻圖像上疊加線條和文字

在DirectShow的視頻圖像上疊加線條和文字

最近一直在從事工業測量方麵的開發工作,難免會用到各種各樣的相機,其中支持DX的USB相機開發起來比較方便,由於工作需要經常要在視頻圖像上疊加線條和文字,圖1便是我最近一段時間寫的一套工業檢測係統,圖像是從USB相機中實時獲取的。看到網上有些帖子也在討論這個問題,現在給出我的一個非常簡單的思路並附上源代碼(vc++6.0編譯通過,需要連接USB相機,可用普通攝像頭來代替。地址:https://xiaolang86.download.csdn.net/)。

圖1

用過DirectShow的就知道,用DirectShow連接相機並繪製視頻圖像十分方便,隻需要把當前窗口的句柄傳給IVideoWindow過濾器,然後設置一下圖像繪製的區域就可以實現了,但是這樣一來要在圖像窗口上疊加一些線條和文字就比較難了。我的思路是,隻用DirectShow來連接相機和獲取數據,繪製則自己來寫,在DC裏麵實現。大家可以參考我上傳的DDShowDemo的程序。具體步驟如下:

1. 設置環境變量:

DShow裏麵的靜態庫很多,如果隻是需要連接相機,隻有幾個庫就可以了。根據我個人的實際我隻用到了Winmm.lib,strmiids.lib,quartz.lib三個庫,第一個好像是windows的庫。頭文件的話我建議把DX裏所有的頭文件都拷過來,大家也可以把我的源代碼目錄下的Include/DX這個文件夾全部拷貝過去。具體在VC6.0裏麵的設置,大家可以去Project->Setting裏麵看一下。

2. 連接相機:

(1)首先介紹一下連接相機的類:

連接相機是利用CCaptureVideo類,這個類是根據DX中的Demo改編過來的,在網上都可以下到,我的程序中做了一些改動,大家可以直接將源程序中的CaptureView.cpp和CaptureView.h文件拷貝到自己的工程中。在CCaptureVideo類中其實還嵌套了一個CSampleGrabberCB的友元類,該友元類裏的BufferCB函數是用來獲取每一幀的圖像的。

在CCaptureVideo的cpp文件中引入了兩個頭文件:

#include "DDShowDemoView.h"

#include "GlobalVar.h"

第一個是要繪製的視圖的頭文件,大家可以改成自己的;第二個是放一些全局變量的頭文件,大家可以去看一下定義,需要介紹的是定義在GlobalVar.h裏麵的一個結構體,個人認為非常有用:

//struct

typedef struct tagCallBackInfo

{

double dblSampleTime;

long lBufferSize;

BYTE *pBuffer;

BITMAPINFOHEADER bih;

} CALLBACKINFO;

這個結構裏麵定義了采樣時間,圖像大小,圖像數據,圖像信息頭。並定義了一個實例:CALLBACKINFO cb. 定義好之後,如果要對圖像進行某些操作,我們關心的就隻是cb.pBuffer就可以了,並且這是一個全局變量在程序的任何地方都可以引用,非常方便。

(2)具體代碼

在App中定義一個相機實例CCaptureVideo m_cap;注意需要把theApp進行extern聲明,這樣可以在其它地方訪問m_cap,當然也可以把m_cap放到上麵說的全局定義文件中。

在View類的InitialUpdate函數中添加如下代碼:黑體字所示

void CDDShowDemoView::OnInitialUpdate()

{

CView::OnInitialUpdate();

//Contact Camera

//zhou's comment:if put this->GetSafeHwnd(),

//DDShow will draw itself, but no vector can be drawed up the buffer,so //give it NULL

HRESULT hr = theApp.m_cap.Init(0,NULL);

//Start Invalidate

SetTimer(0,20,NULL);

}

需要說明的是,如果在Init函數中把當前視圖的句柄傳進去,那麼繪製的工作就可以由DShow來幫你是實現了,這個是我所不希望的,所以我傳NULL進去。大家可以跟進Init函數裏去看一看,其實真正將圖像和當前視圖聯係起來是函數SetupVideoWindow(),在這個函數裏麵又需要把視圖句柄傳進去,如黑體字代碼。這不是矛盾嗎?大家可以試一下,如果在SetupVideoWindow函數裏麵也傳NULL進去,效果是一樣的,但是DX會認為你指定一個視圖繪製數據,它會幫你開啟一個ActiveWindow的窗口。

HRESULT CCaptureVideo::SetupVideoWindow()

{

HRESULT hr;

//can not be NULL, or will active activewindow

hr = m_pVW->put_Owner((OAHWND)g_pView->m_hWnd);

// hr = m_pVW->put_Owner((OAHWND)m_hWnd);

if (FAILED(hr))return hr;

hr = m_pVW->put_WindowStyle(WS_CHILD);

if (FAILED(hr))return hr;

ResizeVideoWindow();

hr = m_pVW->put_Visible(OATRUE);

return hr;

}

連接好相機之後,就需要通知視圖有視頻流數據過來,需要刷新。這裏的刷新通過開啟一個Timer進程來實現。這個是我最近才改動的,之前在做視頻開發的時候,我都是將視圖刷新放到CSampleGrabberCB的BufferCB函數中,效果也還不錯,但是後來換了一種品牌的相機之後,程序也可以獲取數據,但是整個程序占用CPU非常高,達到80%以上,才改成在視圖裏麵開啟一個時間進程,在視圖內部刷新。大家可以看到在OnTimer函數裏麵就一句話:Invalidate(false):

void CDDShowDemoView::OnTimer(UINT nIDEvent)

{

if( nIDEvent == 0 )

Invalidate( false );

CView::OnTimer(nIDEvent);

}

這樣相機就連接成功,現在需要自己動手寫一個繪製函數了。

3. 繪製數據

在視圖類中添加一個DrawImgBuffer函數

void CDDShowDemoView::DrawImgBuffer(CDC *pMemDC, BYTE *pBuffer, BITMAPINFOHEADER *pbih, CRect rtInput, CRect rtOutput, HWND hwnd )

{

// If we haven't yet snapped a still, return

if (!pBuffer)

return;// FALSE;

HDC hdcDraw = pMemDC->GetSafeHdc();//::GetDC( hwnd );

//select into dc

CBitmap *pOldbitmap = (CBitmap*)pMemDC->SelectObject( pBuffer );

PAINTSTRUCT ps;

::BeginPaint(hwnd, &ps);

::SetStretchBltMode(hdcDraw, COLORONCOLOR);

StretchDIBits(

hdcDraw,

rtOutput.left, rtOutput.top, rtOutput.Width(), rtOutput.Height(),

rtInput.top, rtInput.left, rtInput.Width(), rtInput.Height(),

pBuffer,

(BITMAPINFO*) pbih,

DIB_RGB_COLORS,

SRCCOPY );

::EndPaint(hwnd, &ps);

::ReleaseDC( hwnd, hdcDraw );

}

接著在OnDraw函數裏麵加上這個函數就好了:

void CDDShowDemoView::OnDraw(CDC* pDC)

{

CDDShowDemoDoc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

CMemDC *pMemDC = new CMemDC(pDC);

CRect rt(0,0,1280,1024);

if( cb.pBuffer )

{

DrawImgBuffer( pMemDC, cb.pBuffer, &cb.bih, rt,rt, this->GetSafeHwnd() );

//just for test:zhoulm's demo

//you can put your line and text there:

CFont font;

font.CreatePointFont( 120, _T("Arial") );

CFont *pOldFont = pMemDC->SelectObject( &font );

pMemDC->SetBkMode(TRANSPARENT);

pMemDC->SetTextColor(RGB(0,255,0));

pMemDC->TextOut( 100, 100, "zhoulm's Demo! Welcome to:www.whudpcv.cn" );

pMemDC->SelectObject( pOldFont );

}

if( pMemDC )

{

delete pMemDC;

pMemDC = NULL;

}

}

注意這裏一定要用MemDC來繪製,而不能用微軟自帶的DC。MemDC定義在MemDC.h文件中,大家需要把這個文件也加入到工程中來。在這裏繪製的時候,就可以看到CALLBACKINFO結構體是多麼的方便了,直接訪問cb.pBuffer就好了,信息頭也直接使用cb.bih就好。

編譯,完成!看看最後效果吧:

 

最後更新:2017-04-03 14:54:18

  上一篇:go directdraw顯示yuv420(YV12)
  下一篇:go directdraw的多畫麵顯示rgb