在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
上一篇:
directdraw顯示yuv420(YV12)
下一篇:
directdraw的多畫麵顯示rgb
探秘:微軟的免費殺毒軟件Morro到底有何本領?
從iOS應用被拒看蘋果的審核。
Go在百萬億級搜索引擎中的應用
MySQL · 引擎特性 · InnoDB Fulltext簡介
MySQL準實時同步到PostgreSQL, Greenplum的方案之一 - rds_dbsync
《Adobe Flash CS4中文版經典教程》——1.12 檢查更新
secrets of the javascript Ninja( with(){} 的用法)(javascript忍者的秘密)
2017雲棲大會·杭州峰會:《在線用戶行為分析:基於流式計算的數據處理及應用》之《流數據采集:海量流式視頻日誌收集》篇
如何從管理IT服務提供商獲得最大收益
悉尼利物浦醫院使用RFID技術跟蹤血製品