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

        由于工作中要做立体视觉相关开发,使用到摄像头的图像采集,而使用了于老师基于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 yelei
	BOOL	   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; //若时间戳一样,说明和上一帧是同一帧,则返回NULL
if (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的回调模式来抓图了,操作方式跟缓冲区模式一样,只是为了支持某些不支持缓冲区模式的相机。


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值