3.使用DShow进行摄像头预览并拍照

来源:互联网 发布:怎么备份游戏数据 编辑:程序博客网 时间:2024/05/15 08:13

上一篇讲了怎么采集摄像头图像并预览,本篇主要讲预览的同时怎么拍照。

拍照就需要抓取图像,这里要用到一个不太一样的Filter,叫SampleGrabber Filter,通过这个Filter可以获取到ISampleGrabber接口,通过这个接口就可以设置抓取什么样的视频。对于这个接口获取采集到的每一帧的信息,我们可以对其进行处理,可以拿来显示,也可以用来生成图片。下面来一步一步做做看。

首先,我新定义了一个结构,用来存采集设备支持的所有分辨率,ASCamResolutionInfoArray m_arrCamResolutionArr; 它的结构定义如下:

struct CamResolutionInfo{int nWidth;//分辨率宽int nHeight;//分辨率高int nResolutionIndex;//分辨率序号CamResolutionInfo(){nWidth = 640;nHeight = 480;nResolutionIndex = -1;};CamResolutionInfo(const CamResolutionInfo &other){*this = other;};CamResolutionInfo& operator = (const CamResolutionInfo& other){nWidth = other.nWidth;nHeight = other.nHeight;nResolutionIndex = other.nResolutionIndex;return *this;};};typedef CArray <CamResolutionInfo, CamResolutionInfo&> ASCamResolutionInfoArray;
然后获取采集设备支持的所有分辨率,代码如下:

void CGetDeviceInfoDlg::GetVideoResolution(){if (m_pCapture){m_arrCamResolutionArr.RemoveAll();m_cbxResolutionCtrl.ResetContent();IAMStreamConfig *pConfig = NULL;  //&MEDIATYPE_Video,如果包括其他媒体类型,第二个参数设置为0HRESULT hr = m_pCapture->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, m_pVideoFilter, IID_IAMStreamConfig, (void **)&pConfig);int iCount = 0, iSize = 0;hr = pConfig->GetNumberOfCapabilities(&iCount, &iSize);// Check the size to make sure we pass in the correct structure.if (iSize == sizeof(VIDEO_STREAM_CONFIG_CAPS)){// Use the video capabilities structure.for (int iFormat = 0; iFormat < iCount; iFormat++){VIDEO_STREAM_CONFIG_CAPS scc;AM_MEDIA_TYPE *pmtConfig = NULL;hr = pConfig->GetStreamCaps(iFormat, &pmtConfig, (BYTE*)&scc);if (SUCCEEDED(hr)){//(pmtConfig->subtype == MEDIASUBTYPE_RGB24) &&if ((pmtConfig->majortype == MEDIATYPE_Video) &&(pmtConfig->formattype == FORMAT_VideoInfo) &&(pmtConfig->cbFormat >= sizeof (VIDEOINFOHEADER)) &&(pmtConfig->pbFormat != NULL)){VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)pmtConfig->pbFormat;// pVih contains the detailed format information.LONG lWidth = pVih->bmiHeader.biWidth;LONG lHeight = pVih->bmiHeader.biHeight;BOOL bFind = FALSE;//是否已经存在这个分辨率,不存在就加入arrayfor (int n=0; n < m_arrCamResolutionArr.GetSize(); n++){CamResolutionInfo sInfo = m_arrCamResolutionArr.GetAt(n);if (sInfo.nWidth == lWidth && sInfo.nHeight == lHeight){bFind = TRUE;break;}}if (!bFind){CamResolutionInfo camInfo;camInfo.nResolutionIndex = iFormat;camInfo.nWidth = lWidth;camInfo.nHeight = lHeight;m_arrCamResolutionArr.Add(camInfo);CString strFormat = _T("");strFormat.Format(_T("%d * %d"), lWidth, lHeight);m_cbxResolutionCtrl.AddString(strFormat);}}// Delete the media type when you are done.FreeMediaType(pmtConfig);}}}if (m_cbxResolutionCtrl.GetCount() > 0){m_cbxResolutionCtrl.SetCurSel(0);}}}void CGetDeviceInfoDlg::FreeMediaType(AM_MEDIA_TYPE *pmt){if (pmt == NULL){return;}    if (pmt->cbFormat != 0)     {        CoTaskMemFree((PVOID)pmt->pbFormat);        // Strictly unnecessary but tidier        pmt->cbFormat = 0;        pmt->pbFormat = NULL;    }        if (pmt->pUnk != NULL)     {        pmt->pUnk->Release();        pmt->pUnk = NULL;    }} 
获取到所有分辨率后,选择一个,其实就得到选择的分辨率序号,在后面的代码中对其进行设置。代码如下:

//设置视频分辨率、格式IAMStreamConfig *pConfig = NULL;  m_pCapture->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, m_pVideoFilter, IID_IAMStreamConfig, (void **) &pConfig);AM_MEDIA_TYPE *pmt = NULL; VIDEO_STREAM_CONFIG_CAPS scc;pConfig->GetStreamCaps(nResolutionIndex, &pmt, (BYTE*)&scc);  //nResolutionIndex就是选择的分辨率序号pConfig->SetFormat(pmt);

这些都做好后,就开始创建SampleGrabber Filter,代码如下:

//创建视频捕捉实例 HRESULT hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&m_pGrabberFilter); if (m_pGrabberFilter == NULL){MessageBox(_T("获取m_pGrabberFilter失败"), _T("提示"));return;}
//将视频捕捉过滤器加入图表hr = m_pGraphBuilder->AddFilter(m_pGrabberFilter, L"Grabber");if(S_OK != hr){MessageBox(_T("Fail to put sample grabber in graph"));return;}
再获取ISampleGrabber接口,并设置要抓取的视频

m_pGrabberFilter->QueryInterface(IID_ISampleGrabber, (void **)&m_pGrabber);pmt->majortype = MEDIATYPE_Video;pmt->subtype = MEDIASUBTYPE_RGB24;  //抓取24位位图HRESULT hr = m_pGrabber->SetMediaType(pmt);if(FAILED(hr)){AfxMessageBox(_T("Fail to set media type!"));return;}
接下来一步非常重要,设置是否缓存,如果设置了缓存,则可以用接口获取到缓存数据,并生成图片,如果不设置缓存,则需要设置回调函数,在回调函数中获取到帧的数据,并生成图片,两种方法都可以,根据实际需要来选择。我这里就用缓存的方式,设置方法如下:

//是否缓存数据,缓存的话,可以给后面做其他处理,不缓存的话,图像处理就放在回调中
m_pGrabber->SetBufferSamples( TRUE ); 

如果用设置回调的方法,请调用m_pGrabber->SetCallback(...,...),第一个参数就是回调函数的名称,是一个结构的实例,这个结构里有两个虚函数:SampleCB和BufferCB,所以你需要写一个类继承这个结构,然后重载这两个函数,看两个函数的参数,就知道怎么处理 了。

接下来就是链接Filter,代码如下:

m_pCapture->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, m_pVideoFilter, m_pGrabberFilter, NULL);

后面就是设置预览窗口,然后拍照,拍照代码如下:

void CGetDeviceInfoDlg::OnBnClickedBtnTakepic(){//获取抓取buffer大小long nBufferSize = 0; HRESULT hr = m_pGrabber->GetCurrentBuffer(&nBufferSize, NULL); if(FAILED(hr)){AfxMessageBox(_T("Get BufferSize failed"));return;}//获取抓取的data数据BYTE *pBuffer = new BYTE[nBufferSize]; hr = m_pGrabber-> GetCurrentBuffer(&nBufferSize, (long*)pBuffer); if(FAILED(hr)){AfxMessageBox(_T("Get BufferData failed"));}AM_MEDIA_TYPE mt;    hr = m_pGrabber->GetConnectedMediaType(&mt);    VIDEOINFOHEADER * vih = (VIDEOINFOHEADER*) mt.pbFormat;    int nWidth  = vih->bmiHeader.biWidth;    int nHeight = vih->bmiHeader.biHeight;    FreeMediaType(&mt);//生成图片CTime cTime = CTime::GetCurrentTime();CString strTime = cTime.Format(_T("%Y%m%d_%H%M%S.bmp"));CString strFullPath = _T("");strFullPath.Format(_T("D:\\JYSoft\\temp\\%s"), strTime);    //文件头    BITMAPFILEHEADER bfh;    memset(&bfh, 0, sizeof(bfh));    bfh.bfType = 'MB';    bfh.bfSize = sizeof(bfh) + nBufferSize + sizeof(BITMAPINFOHEADER);    bfh.bfOffBits = sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER);    //格式信息    BITMAPINFOHEADER bih;    memset(&bih, 0, sizeof(bih));    bih.biSize = sizeof(bih);    bih.biWidth = nWidth;    bih.biHeight = nHeight;    bih.biPlanes = 1;    bih.biBitCount = 24;bih.biCompression = BI_RGB;   //非压缩  CFile file;if(file.Open(strFullPath, CFile::modeWrite | CFile::modeCreate))  {//写入文件      file.Write((LPSTR)&bfh,sizeof(BITMAPFILEHEADER));  file.Write((LPSTR)&bih,sizeof(BITMAPINFOHEADER));  file.Write(pBuffer, nBufferSize);  file.Close();  } //用完释放delete[] pBuffer;}

拍照生成bmp图片,最后的界面如下:



具体工程代码,请到这里下载:工程代码下载
如有什么问题,请多指教。




0 0
原创粉丝点击