【问题标题】:Get iplImage or Mat from directshow to opencv从directshow获取iplImage或Mat到opencv
【发布时间】:2013-05-10 07:34:25
【问题描述】:

由于在使用 c++ 和 opencv 时难以更改相机的分辨率,我不得不为我的眼动追踪软件更改为 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 ); 
    }
};

对于来自 cam 的每个新帧都会调用 HRESULT Receive 方法。 cmets 说 buf 包含数据。但我有 3 个问题/疑问。

  1. 我不能包含 opencv 库。我在 Visual Studio 中创建了一个新项目,并添加了与我一直包含的相同的属性表。与早期项目的唯一区别是我现在创建了一个完全空的项目,之前我创建了一个 win32 应用程序。 如何将opencv添加到directshow项目中?

  2. 上面的例子。来自 buf。这是一个指向数据的指针。如何将其输入到 iplImage/Mat 中以进行 opencv 计算?

  3. 有没有办法不显示来自网络摄像头的图像(我只需要对帧执行一些算法,我猜删除带有结果的窗口可能会给我更多的分析算法能力?!)

谢谢!

【问题讨论】:

  • 您作为示例的 sn-p 很糟糕:使用原始指针,挂钩而不是普通 API,没有停止/终止代码。你很快就会一事无成。
  • 感谢您的评论。是的,我读过这个例子很糟糕。但问题是,这是我发现我以某种方式理解并且有效的唯一示例。这个 COM 的东西和 directshow 对我来说是全新的,我认为它真的很难理解。如果您知道任何具有更好代码的示例(更容易理解),请给我一个链接。
  • 但我真的需要学习,并学习构建好的代码。如果有人有很好的示例代码,请告诉我!我只在directshow 工作了几天,需要学习。

标签: visual-c++ opencv video com directshow


【解决方案1】:

使用 DirectShow,您通常会创建一个管道,即一个图形,然后向其添加过滤器,如下所示:

Camera -> [可能是一些额外的东西] -> Sample Grabber -> Null Renderer

相机、Sample GrabberNull Renderer 都是随清洁 Windows 一起提供的标准组件。 Sample Grabber 可以设置为通过ISampleGrabberCB::SampleCB 给您回电,并为您提供捕获的每个视频帧的数据。 Null Renderer 是管道的终止,而不在监视器上显示视频(只是视频捕获)。

SampleCB 是为您带来您需要的示例代码的关键字。通过此调用接收到数据后,您可以按照@praks411 的建议将其转换/包装为 IPL/OpenCV 类。

如此简单,您就不需要 DirectShow BaseClasses,代码将只是常规的 ATL/MFC 代码和项目。确保使用 CComPtr 包装类来处理 COM 接口,以免丢失引用和泄漏对象。最新的 Windows SDK 中可能缺少某些声明,因此您需要使用 Windows SDK 6.x 或从那里复制缺少的部分。

另见:

【讨论】:

  • 非常感谢!我查看了“setlifecam...”,也许是你写了那段代码?运行代码时发生了一些错误,在设置分辨率时,但我首先确定了可用的设置,然后选择其中一个。但这是我不明白的一件事。我了解什么是空渲染器(我认为),但我在代码中找不到任何感觉像是空渲染器的东西。你能告诉我在哪里吗?还是:pCaptureGraphBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, NULL, pCurrentOutputPin, NULL, pBaseFilter) ?
  • 这段代码(SetLife...)确实没有使用Null Renderer(我只是给了你足够熟悉的东西,并且随时可用,并且也接近你原来的Q改变分辨率)。您将需要在其他地方查找 Null Renderer sn-p,例如这里Building the Filter Graph - 也有很好的描述。
【解决方案2】:

我认为您可以将 opencv 包含在现有文件中。我已经为控制台应用程序做到了。您需要在当前项目的属性页中包含 opencv 标头的路径和 opencv lib 的路径。

转到项目属性: 1.添加标题 C/C++ -----> Additional Include Directories ---> 这里添加 opencv 包含目录(你可能想包含多个目录)

  1. 添加库 链接器 -----> 附加库目录 ----> 这里添加 opencv 库。

从 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);

我认为图像预览很有帮助。您上面的过滤器似乎从渲染器获取数据。如果您确实需要,您可能想要更改渲染器并在无窗口模式下使用它。其他选项可能是使用采样器过滤器。

【讨论】:

  • 谢谢,我会在白天测试这个。关于项目属性。我使用与其他 opencv 项目相同的属性文件。他们有所有的包含路径等等,但是当我包含 opencv 库时,我仍然得到红色下划线。比如,“cv.h”。
  • 我认为您将不得不重新检查包含的路径。在我的情况下,我在 c/c++ 中包含以下路径,包括 C:\OpenCV-2.4.3.bak\include。所以我在我的主文件中使用以下 #include
  • 我真的不知道出了什么问题,“系统构建路径”与这个项目和更早的项目不同。 (右键单击包含文件 -> 打开文档 -> 错误消息显示路径。)我想必须深入研究一下.. Praks,感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多