在DirectShow的视频图像上叠加线条和文字
在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就好。
编译,完成!看看最后效果吧:
在DirectShow的视频图像上叠加线条和文字的更多相关文章
- 在YUV图像上根据背景色实现OSD反色
所谓的OSD其实就是在视频图像上叠加一些字符信息,比如时间,地点,通道号等, 在图像上叠加OSD通常有两种方式: 一种是在前端嵌入式设备上,在图像数据上叠加OSD, 这样客户端这边只需解码显示数据即可 ...
- WPF中在摄像头视频上叠加控件的解决方案
一.视频呈现 前段时间,在一个wpf的项目中需要实时显示ip摄像头,对此的解决方案想必大家都应该知道很多.在winform中,我们可以将一个控件(一般用panel或者pictruebox)的句柄丢给摄 ...
- [SimplePlayer] 2. 在屏幕上显示视频图像
我们这里采用SDL(本文所用版本为SDL2.0.5)来进行图像输出,SDL在进行图像渲染时一般采用的会是direct3D或者opengl,SDL对它们进行了封装,不过我们这里只讨论SDL的使用,并不会 ...
- paper 89:视频图像去模糊常用处理方法
随着“平安城市”的广泛建设,各大城市已经建有大量的视频监控系统,虽然监控系统己经广泛地存在于银行.商场.车站和交通路口等公共场所,但是在公安工作中,由于设备或者其他条件的限制,案情发生后的图像回放都存 ...
- matlab中,在灰度解剖图上叠加阈值图,by by DR. Rajeev Raizada
1.参考 reference 1. tutorial主页:http://www.bcs.rochester.edu/people/raizada/fmri-matlab.htm. 2.speech_b ...
- javacpp-opencv图像处理之1:实时视频添加文字水印并截取视频图像保存成图片,实现文字水印的字体、位置、大小、粗度、翻转、平滑等操作
欢迎大家积极开心的加入讨论群 群号:371249677 (点击这里进群) javaCV图像处理系列: javaCV图像处理之1:实时视频添加文字水印并截取视频图像保存成图片,实现文字水印的字体.位置. ...
- 用java实现给图片增加图片水印或者文字水印(也支持视频图像帧添加水印)
javaCV图像处理系列: javaCV图像处理之1:实时视频添加文字水印并截取视频图像保存成图片,实现文字水印的字体.位置.大小.粗度.翻转.平滑等操作 javaCV图像处理之2:实时视频添加图片水 ...
- javaCV开发详解之4:转流器实现(也可作为本地收流器、推流器,新增添加图片及文字水印,视频图像帧保存),实现rtsp/rtmp/本地文件转发到rtmp流媒体服务器(基于javaCV-FFMPEG)
javaCV系列文章: javacv开发详解之1:调用本机摄像头视频 javaCV开发详解之2:推流器实现,推本地摄像头视频到流媒体服务器以及摄像头录制视频功能实现(基于javaCV-FFMPEG.j ...
- 基于Xilinx FPGA的视频图像采集系统
本篇要分享的是基于Xilinx FPGA的视频图像采集系统,使用摄像头采集图像数据,并没有用到SDRAM/DDR.这个工程使用的是OV7670 30w像素摄像头,用双口RAM做存储,显示窗口为320x ...
随机推荐
- Electron应用使用electron-builder配合electron-updater实现自动更新(windows + mac)
发客户端一定要做的就是自动更新模块,否则每次版本升级都是一个头疼的事.下面是Electron应用使用electron-builder配合electron-updater实现自动更新的解决方案. 1.安 ...
- 通过url获取相应的location信息
var properties = ['href', 'origin', 'host', 'hostname', 'port', 'pathname', 'search', 'hash']; var g ...
- PHP可以通过类名调用非静态方法
今日有兄弟遇上一个问题,就是可以通过class名称直接调用该类中的函数,我测试了一下,确实可以,概念中是只有静态方法才可以这样调用的,现在 被刷新了,于是我在方法中加入一行$this相关的操作,再运行 ...
- 基本c功能使用不当导致崩溃
一些基本的c语言操作,使用不当也会有出其不意的问题.比如我最近的一个项目中,用到几句代码: uint8_t * out_pcm = NULL; ....... if (NULL == out_pcm) ...
- POJ Christmas Game [树上删边游戏 Multi-SG]
传送门 题意: 有N 个局部联通的图.Harry 和Sally 轮流从图中删边,删去一条边后,不与根节点相连的部分将被移走.Sally 为先手.图是通过从基础树中加一些边得到的.所有形成的环保证不共用 ...
- POJ 3683 Priest John's Busiest Day[2-SAT 构造解]
题意: $n$对$couple$举行仪式,有两个时间段可以选择,问是否可以不冲突举行完,并求方案 两个时间段选择对应一真一假,对于有时间段冲突冲突的两人按照$2-SAT$的规则连边(把不冲突的时间段连 ...
- 剑指offer试题(PHP篇二)
6.旋转数组的最小数字 题目描述 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素. 例如数组{3,4,5,1,2}为{1 ...
- python如何讲一个文件中的图片分到两个
最近在做一个图像分类的比赛,作为初次接触深度学习的菜鸟,上手了keras.说实话,除了keras教程,中文博客的技术支持太差了.正在头大的学习中...废话不多说,记录一下学习中的一些小细节.在遇到ge ...
- Orleans入门例子
Orleans入门例子 这是Orleans系列文章中的一篇.首篇文章在此 一.铺垫. 虽然是个入门例子,还是需要一些铺垫. Orleans的最小完全体,应该分为2个部分.一个是Orleans客户端, ...
- [Python Study Notes]字符串操作
字符串操作 a.字符串格式化输出 name = "liu" print "i am %s " % name #输出: i am liu PS: 字符 ...