zoukankan      html  css  js  c++  java
  • 在DirectShow的视频图像上叠加线条和文字 分类: VC++ DirectX 2013-11-13 09:23 706人阅读 评论(0) 收藏

    在DirectShow的视频图像上叠加线条和文字

    最近一直在从事工业测量方面的开发工作,难免会用到各种各样的相机,其中支持DX的USB相机开发起来比较方便,由于工作需要经常要在视频图像上叠加线条和文字,图1便是我最近一段时间写的一套工业检测系统,图像是从USB相机中实时获取的。看到网上有些帖子也在讨论这个问题,现在给出我的一个非常简单的思路并附上源代码(vc++6.0编译通过,需要连接USB相机,可用普通摄像头来代替。地址:http://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就好。

    编译,完成!看看最后效果吧:

     

    版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 相关阅读:
    混合式应用开发之AngularJS ng-repeat数组有重复值的解决方法
    混合式应用开发之串口通讯(2)
    混合式应用开发之串口通讯(1)
    第一篇博客
    win10出现"本地计算机上的MySQL57服务启动后停止"
    彻底区分html的attribute与dom的property
    Angularv4入门篇1
    node开发后将本地mysql数据导入到服务器mysql
    weex入门
    Color.js 方便修改颜色值
  • 原文地址:https://www.cnblogs.com/mao0504/p/4706725.html
Copyright © 2011-2022 走看看