基于CameraDS修改版,增加对DirectShow回调模式的支持

来源:互联网 发布:政府债务数据 编辑:程序博客网 时间:2024/06/06 07:11

        由于工作中要做立体视觉相关开发,使用到摄像头的图像采集,而使用了于老师基于Directshow的CameraDS类, 但遇到使用明美某款摄像头是,QueryFrame()总是抓到同一帧画面,不会更新,细看CameraDS,发现使用的是DS的缓冲区模式,估计是该摄像头对于缓冲区模式支持存在问题。于是,我尝试使用回调模式,由于回调也存在两种小的模式,即SetOneShot(FALSE or TRUE),这两种模式。SetOneShot(FALSE),只用Run()一次即可。如果SetOneShot(TRUE)的话,也可以每请求一帧Run()一次,然后WaitForCompletion,再从回调函数的buffer中取出数据,这种其实和缓冲区 模式一样,只不过缓冲区变成了在回调函数中。

        下面是模式用于回调抓图的类,实现了ISampleGrabberCB的接口

class CSampleGrabberCB : public ISampleGrabberCB {public:long       lWidth ; long       lHeight ; BOOL       bGrabVideo ; // add yeleiBOOL   bReadyGO;double     dblSampleTime;BYTE       *pBuffer;long       lBufferSize;public:CSampleGrabberCB(){ lWidth = 0 ; lHeight = 0 ; bGrabVideo = FALSE ;bReadyGO = FALSE;pBuffer = NULL;}~CSampleGrabberCB(){ if (pBuffer) {delete []pBuffer;pBuffer = NULL;}}STDMETHODIMP_(ULONG) AddRef() { return 2; }STDMETHODIMP_(ULONG) Release() { return 1; }STDMETHODIMP QueryInterface(REFIID riid, void ** ppv) {if( riid == IID_ISampleGrabberCB || riid == IID_IUnknown ){*ppv =  static_cast<void *>( this );return NOERROR;} return E_NOINTERFACE;}STDMETHODIMP SampleCB( double SampleTime, IMediaSample * pSample )  {return 0;}STDMETHODIMP BufferCB( double dblSampleTime, BYTE * pBuffer, long lBufferSize ) {if (!pBuffer) return E_POINTER;if (!this->pBuffer) this->pBuffer = new BYTE[lBufferSize];this->dblSampleTime = dblSampleTime;this->lBufferSize = lBufferSize;if(!bGrabVideo) {bReadyGO = FALSE;  //确保原子操作memcpy(this->pBuffer, pBuffer, lBufferSize);bReadyGO = TRUE;}// 只使用标志位的话,你没法保证一个线程在对标志位做判断和修改// 标志位这两个操作之间不会有其它的线程修改标志位。所以需要有// 一个原子操作去一次性的互斥的完成判断和修改这两个操作。但这// 里只有一个线程会执行该段代码,所以标志位足矣return 0;}};

打开Camera时,设置回调模式

m_pSampleGrabber->SetBufferSamples(FALSE);m_pSampleGrabber->SetOneShot(FALSE);m_pCB->lWidth = m_nWidth = nWidth;m_pCB->lHeight = m_nHeight = nHeight;hr = m_pSampleGrabber->SetCallback(m_pCB, 1);m_pMediaControl->Run();
        最后抓图时

static double lastSampleTime=0;if (lastSampleTime == m_pCB->dblSampleTime)    return NULL; //若时间戳一样,说明和上一帧是同一帧,则返回NULLif (m_pCB->lBufferSize == 0)    return NULL;if ( m_nBufferSize != m_pCB->lBufferSize) {    if (m_pFrame)    cvReleaseImage(&m_pFrame);    m_nBufferSize = m_pCB->lBufferSize;    m_pFrame = cvCreateImage(cvSize(m_nWidth, m_nHeight), IPL_DEPTH_8U, 3);}lastSampleTime = m_pCB->dblSampleTime;m_pCB->bGrabVideo = TRUE;while (!m_pCB->bReadyGO) {    Sleep(0); // 切到下一个时间片,确保复制(原子)操作完成}memcpy(m_pFrame->imageData, (char*)m_pCB->pBuffer, m_nBufferSize);m_pCB->bGrabVideo = FALSE;
  至此,可以使用DS的回调模式来抓图了,操作方式跟缓冲区模式一样,只是为了支持某些不支持缓冲区模式的相机。


0 0
原创粉丝点击