由于在使用c ++和opencv时难以改变相机的分辨率,我不得不更改为我的eyetracking软件的directshow。
Directshow对我来说是新手,很难理解一切。但我发现这个很好的例子非常适合捕获和放大查看网络摄像头。
http://www.codeproject.com/Articles/12869/Real-time-video-image-processing-frame-grabber-usi
我使用的是不需要directShow SDK的版本。 (但它仍然是示例中使用的directshow,对吧?)
#include <windows.h>
#include <dshow.h>
#pragma comment(lib,"Strmiids.lib")
#define DsHook(a,b,c) if (!c##_) { INT_PTR* p=b+*(INT_PTR**)a; VirtualProtect(&c##_,4,PAGE_EXECUTE_READWRITE,&no);\
*(INT_PTR*)&c##_=*p; VirtualProtect(p, 4,PAGE_EXECUTE_READWRITE,&no); *p=(INT_PTR)c; }
// Here you get image video data in buf / len. Process it before calling Receive_ because renderer dealocates it.
HRESULT ( __stdcall * Receive_ ) ( void* inst, IMediaSample *smp ) ;
HRESULT __stdcall Receive ( void* inst, IMediaSample *smp ) {
BYTE* buf; smp->GetPointer(&buf); DWORD len = smp->GetActualDataLength();
HRESULT ret = Receive_ ( inst, smp );
return ret;
}
int WINAPI WinMain(HINSTANCE inst,HINSTANCE prev,LPSTR cmd,int show){
HRESULT hr = CoInitialize(0); MSG msg={0}; DWORD no;
IGraphBuilder* graph= 0; hr = CoCreateInstance( CLSID_FilterGraph, 0, CLSCTX_INPROC,IID_IGraphBuilder, (void **)&graph );
IMediaControl* ctrl = 0; hr = graph->QueryInterface( IID_IMediaControl, (void **)&ctrl );
ICreateDevEnum* devs = 0; hr = CoCreateInstance (CLSID_SystemDeviceEnum, 0, CLSCTX_INPROC, IID_ICreateDevEnum, (void **) &devs);
IEnumMoniker* cams = 0; hr = devs?devs->CreateClassEnumerator (CLSID_VideoInputDeviceCategory, &cams, 0):0;
IMoniker* mon = 0; hr = cams->Next (1,&mon,0); // get first found capture device (webcam?)
IBaseFilter* cam = 0; hr = mon->BindToObject(0,0,IID_IBaseFilter, (void**)&cam);
hr = graph->AddFilter(cam, L"Capture Source"); // add web cam to graph as source
IEnumPins* pins = 0; hr = cam?cam->EnumPins(&pins):0; // we need output pin to autogenerate rest of the graph
IPin* pin = 0; hr = pins?pins->Next(1,&pin, 0):0; // via graph->Render
hr = graph->Render(pin); // graph builder now builds whole filter chain including MJPG decompression on some webcams
IEnumFilters* fil = 0; hr = graph->EnumFilters(&fil); // from all newly added filters
IBaseFilter* rnd = 0; hr = fil->Next(1,&rnd,0); // we find last one (renderer)
hr = rnd->EnumPins(&pins); // because data we are intersted in are pumped to renderers input pin
hr = pins->Next(1,&pin, 0); // via Receive member of IMemInputPin interface
IMemInputPin* mem = 0; hr = pin->QueryInterface(IID_IMemInputPin,(void**)&mem);
DsHook(mem,6,Receive); // so we redirect it to our own proc to grab image data
hr = ctrl->Run();
while ( GetMessage( &msg, 0, 0, 0 ) ) {
TranslateMessage( &msg );
DispatchMessage( &msg );
}
};
为凸轮中的每个新帧调用方法HRESULT Receive。评论说buf包含数据。但我有3个问题/问题。
我不能包含opencv lib。我在visual studio中创建了一个新项目,并添加了我总是包含的相同属性表。与早期项目的唯一区别是我现在创建一个完全空的项目,之前我创建了一个win32应用程序。 如何将opencv添加到directshow项目中?
上面的例子。从buf。这是一个指向数据的指针。我怎么把它用于opencv calc的iplImage / Mat?
有没有办法不显示网络摄像头的图像(我只需要在帧上执行一些算法,我想用结果删除窗口可能会给分析算法更多的功能?!)
谢谢!
答案 0 :(得分:4)
使用DirectShow,您通常会创建一个管道,即图表并向其添加过滤器,如下所示:
相机 - &gt; [可能是一些额外的东西] - &gt;样品采集器 - &gt;空渲染器
相机,Sample Grabber,Null Renderer都是干净的Windows附带的标准组件。 Sample Grabber可以设置为通过ISampleGrabberCB::SampleCB
给您回电,并为您捕获的每个视频帧提供数据。 Null Renderer是管道的终止,不在监视器上显示视频(只是视频捕获)。
SampleCB
是为您提供所需示例代码的关键字。通过此调用接收数据,您可以按照@ praks411的建议将其转换/包装到IPL / OpenCV类中。
如此简单,您不需要DirectShow BaseClasses,代码只是常规的ATL / MFC代码和项目。确保使用CComPtr
包装类来处理COM接口,以免丢失引用和泄漏对象。最新的Windows SDK中可能缺少某些声明,因此您需要使用Windows SDK 6.x或仅复制缺少的部分。
另见:
答案 1 :(得分:1)
我认为你可以在现有的包含opencv。我已经为控制台应用程序做了这个。您需要在当前项目的属性页中包含opencv头的路径和opencv lib的路径。
转到项目属性: 1.添加标题 C / C ++ -----&gt;附加包含目录---&gt;这里添加opencv include目录(您可能希望包含多个目录)
从buf创建IplImage。一旦获得图像的高度和宽度,就可以使用以下内容。
IplImage *m_img_show;
CvSize cv_img_size = cvSize(m_mediaInfo.m_width, m_mediaInfo.m_height);
m_img_show = cvCreateImageHeader(cv_img_size, IPL_DEPTH_8U,3);
cvSetData(m_img_show, m_pBuffer, m_mediaInfo.m_width*3);
我认为图像预览非常有用。上面的过滤器似乎从渲染器中获取数据。如果您确实需要,可能需要更改渲染器并在无窗口模式下使用它。其他选择可能是使用样本抓取过滤器。