OpenCV/(旧バージョン)DirectShowでキャプチャした画像を表示する

Last-modified: 2006-12-25 (月) 10:24:44

注意

  • 使用するカメラによってはエラーが発生するバグが確認されています。
    参考のためソースは残しますが、動作を保障するものではありません。
    新バージョンはこちらです→OpenCV/DirectShowでキャプチャした画像を表示する

説明

  • OpenCVのカメラキャプチャの性能が若干不安定だったので、DirectShowからカメラを取得して、キャプチャしたバッファだけを貰ってきてOpenCVで動かす。
    • (2006.12.21変更) IplImage* を返すようにした。
  • 処理速度の検証のため、ループを一周するのにかかった時間を画面内に表示している。
    • 結果として、キャプチャ速度に大幅な改善が見られた。原因は不明。
    • 処理速度比較に関する第一段階 : 作業ログ/2006-12-17

スクリーンショット

20061219.PNG


コードに関するコメント

  • DirectShowに関する処理は、DShowクラスで一括して管理する。
  • 最小限の機能しか持たせていない。エラー処理もしていない。
  • コンストラクタでキャプチャの幅と高さを指定し、その後 GetCaptureIplImage() 関数を用いてキャプチャ画像yを取得する。
  • ときどきによって、キャプチャ画像が上下逆に取れたり、そのまま取れたりする。何の違いで起きている現象なのかわからないので、とりあえずどちらにも対応できるようにしておいた。(#ifdef REVERSE_IMAGEあたり)
  • 実装にあたり、以下ページのソースを一部拝借しましたので、この場を借りてお礼申し上げます。

VisualStudioプロジェクトファイル




filedshow.h
#ifndef _DSHOW_H
#define _DSHOW_H


//for DirectShow
#include <dshow.h>
#include <qedit.h>
#pragma comment(lib,"strmiids.lib")
//for OpenCV
#include <cv.h>
#include <highgui.h>
#pragma comment(lib,"cv.lib")
#pragma comment(lib,"cxcore.lib")
#pragma comment(lib,"cvaux.lib")
#pragma comment(lib,"highgui.lib")

// REVERSE_IMAGE ???`?????????A
// ?L???v?`???????o?b?t?@????????t????
#define REVERSE_IMAGE

//=============================================================
// DShow?N???X
//-------------------------------------------------------------
// DirectShow??J?????f?o?C?X??J??A
// ?L???v?`???o?b?t?@?????????N???X
//=============================================================
class DShow{
public:
    //?R???X?g???N?^
    DShow(int width, int height) {
        //?p?????[?^??Z?b?g
        capture_size_x = width;
        capture_size_y = height;
        //???
        Init();
    };
    //?f?X?g???N?^
    ~DShow() {
        //?J?????
        Release();
    };

    //?O???????????
    void GetCaptureBuffer(unsigned char *buffer);
    IplImage *GetCaptureIplImage();

private:
    //?v???C?x?[?g????
    int Init();
    void Release();

    //?v???C?x?[?g????
    //?L???v?`??????????
    int capture_size_x;
    int capture_size_y;

    long buffer_size;       //?L???v?`???o?b?t?@??T?C?Y
    unsigned char *buffer;  //?L???v?`????o?b?t?@??

    //DirectShow??A
    IGraphBuilder           *pGraph;
    IMediaControl           *pMC;
    ICaptureGraphBuilder2   *pCapture;
    IBaseFilter             *pF;
    ISampleGrabber          *pGrab;
    ICreateDevEnum          *pDevEnum;


};

#endif //_DSHOW_H


filedshow.cpp

// ============================================================
// DirectShow??J?????f?o?C?X??J??A
// ?L???v?`???o?b?t?@?????????N???X
// Init - ???[?v???apture - Release ??????
// ?z??????g?p?@
// ============================================================

#include "dshow.h"

//-------------------------------------------------------------
// Name     : DShow::Init
// Function : 
// Argument : int width  : ?L???v?`?????
//          : int height : ?L???v?`???????
// Return   : ??????
// Comment  : 
//-------------------------------------------------------------
int DShow::Init()
{
    AM_MEDIA_TYPE amt;

    // COM??????
    CoInitialize(NULL);

    // ---- ?L???v?`???t?B???^?????----
    // ?L???v?`???f?o?C?X??T??
    IBaseFilter  *pbf = NULL;
    IMoniker * pMoniker = NULL;
    ULONG cFetched;
    // ?f?o?C?X????????
    CoCreateInstance( CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,
        IID_ICreateDevEnum, (void ** ) &pDevEnum);
    // ?r?f?I?L???v?`???f?o?C?X????????
    IEnumMoniker * pClassEnum = NULL;
    pDevEnum->CreateClassEnumerator(
        CLSID_VideoInputDeviceCategory, &pClassEnum, 0);
    if (pClassEnum == NULL){
        printf("?r?f?I?L???v?`???f?o?C?X????????n");
        pDevEnum->Release();
        CoUninitialize();
        return -1;
    }

    // ??????????r?f?I?L???v?`???f?o?C?X??I?u?W?F?N?g??
    // ?C???^?t?F?[?X????
    pClassEnum->Next(1, &pMoniker, &cFetched);
    pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&pbf);

    // ---- ?t?B???^?O???t?????----
    // ?t?B???^?O???t????A?C???^?[?t?F?[?X????
    CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC,
        IID_IGraphBuilder, (void **) &pGraph);
    pGraph->QueryInterface(IID_IMediaControl, (LPVOID *) &pMC);
    // ?L???v?`???t?B???^??t?B???^?O???t????
    pGraph->AddFilter(pbf, L"Video Capture");
    // ????s?????L???v?`???t?B???^??Q??????[?X
    pbf->Release();

    // ---- ?O???o?t?B???^?????----
    // ?O???o?t?B???^????
    CoCreateInstance( CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, 
        IID_IBaseFilter, (LPVOID *)&pF);
    pF->QueryInterface( IID_ISampleGrabber, (void **)&pGrab );
    // ?O???o?t?B???^??}??????????????
    ZeroMemory(&amt, sizeof(AM_MEDIA_TYPE));
    amt.majortype  = MEDIATYPE_Video;
    amt.subtype    = MEDIASUBTYPE_RGB24;
    amt.formattype = FORMAT_VideoInfo; 
    pGrab->SetMediaType( &amt );
    // ?O???o?t?B???^??t?B???^?O???t????
    pGraph->AddFilter(pF, L"SamGra");

    // ---- ?L???v?`???O???t?????----
    // ?L???v?`???O???t????  
    CoCreateInstance( CLSID_CaptureGraphBuilder2 , NULL, CLSCTX_INPROC,
        IID_ICaptureGraphBuilder2, (void **) &pCapture );
    // ?t?B???^?O???t??L???v?`???O???t??g?????
    pCapture->SetFiltergraph( pGraph );
    // ?L???v?`???O???t????A?O???o??????_?????O?????
    pCapture->RenderStream (&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video,
        pbf, NULL, pF);

    // ?r?b?g?}?b?v?????
    pGrab->GetConnectedMediaType( &amt ); 
    // ?r?f?I ?w?b?_?[???|?C???^??l????
    VIDEOINFOHEADER *pVideoHeader = (VIDEOINFOHEADER*)amt.pbFormat;
    // ?r?f?I ?w?b?_?[???C?r?b?g?}?b?v???????
    // ?r?b?g?}?b?v???BITMAPINFO ?\????R?s?[???
    BITMAPINFO BitmapInfo;
    ZeroMemory( &BitmapInfo, sizeof(BitmapInfo) );
    CopyMemory( &BitmapInfo.bmiHeader, &(pVideoHeader->bmiHeader), 
        sizeof(BITMAPINFOHEADER));
    //?L???v?`???o?b?t?@??T?C?Y????
    buffer_size = BitmapInfo.bmiHeader.biSizeImage;
    //?L???v?`???o?b?t?@????????????m??
    buffer = new unsigned char[buffer_size];

    // ---- ?L???v?`???J? ----
    pGrab->SetBufferSamples(TRUE);	// ?O???u?J?
    pMC->Run();						// ?????_?????O?J?

    return 0;
}


//------------------------------------------------------------------------------
// Name     : DShow::GetCaptureBuffer
// Function : ?L???v?`????s??A?o?b?t?@??????i?[???
// Argument : unsigned char *buf : ?o?b?t?@??i?[??
// Return   : ???
// Comment  : ???t??????i??????????????????j
//------------------------------------------------------------------------------
void DShow::GetCaptureBuffer(unsigned char *buf)
{
    // ?O???u
    pGrab->GetCurrentBuffer(&buffer_size, (long *)buffer);

    // REVERSE_IMAGE ???`???????A???????t????
    // ??`?????????????R?s?[
#ifdef REVERSE_IMAGE
    //???t????R?s?[
    int byte = buffer_size/capture_size_y;
    for(int y=0; y<capture_size_y; y++) {
        memcpy(buf+(capture_size_y-1-y)*byte, buffer+y*byte,byte);
    }
#else
    memcpy(buf, buffer, buffer_size);
#endif
}


//------------------------------------------------------------------------------
// Name     : DShow::GetCaptureIplImage
// Function : ?L???v?`????s??A?????plImage????
// Argument : ???
// Return   : IplImage : ?L???v?`?????
// Comment  : ???t??????i??????????????????j
//------------------------------------------------------------------------------
IplImage *DShow::GetCaptureIplImage()
{
    IplImage *image = cvCreateImage(cvSize(capture_size_x, capture_size_y), 8, 3);

    GetCaptureBuffer( (unsigned char *)image->imageData);
    return image;
}

//------------------------------------------------------------------------------
// Name     : DShow::Release
// Function : ?J?????
// Argument : ???
// Return   : ???
// Comment  : 
//------------------------------------------------------------------------------
void DShow::Release()
{
    pMC->Stop();
    pGrab->SetBufferSamples(0);

    // ---- ????? ---- 
    // ?C???^?[?t?F?[?X??????[?X
    pMC->Release();		
    pDevEnum->Release();
    pGraph->Release();
    pCapture->Release();
    pF->Release();
    pGrab->Release();

    // COM??????[?X
    CoUninitialize();
    delete(buffer);
}


filecv.cpp

#include <stdio.h>
#include <cv.h>
#include <highgui.h>

#include "dshow.h"

// ???C?u??????????
#pragma comment(lib,"cv.lib")
#pragma comment(lib,"cxcore.lib")
#pragma comment(lib,"cvaux.lib")
#pragma comment(lib,"highgui.lib")

// ???
#define CAPTURE_SIZE_X  320
#define CAPTURE_SIZE_Y  240

int main()
{
    //DShow?N???X????
    DShow dshow(CAPTURE_SIZE_X, CAPTURE_SIZE_Y);
    char* window_name = "Capture for DirectShow";
    //????x?\ヲ?p
    CvFont dfont;
    char message[64] = "";

    //?t?H???g??????
    cvInitFont (&dfont, CV_FONT_HERSHEY_SIMPLEX , 1.0f, 1.0f, 0.0f, 2, CV_AA);
 
    //OpenCV?p???????m??
    IplImage *frame = cvCreateImage(cvSize(CAPTURE_SIZE_X, CAPTURE_SIZE_Y),
        8, 3);
    IplImage *image = cvCreateImage(cvSize(CAPTURE_SIZE_X, CAPTURE_SIZE_Y),
        8, 3);
    //?E?C???h?E??J??
    cvNamedWindow(window_name,  CV_WINDOW_AUTOSIZE);

    //?????[?v
    while(1) {
        //??????Z?p
        double t = (double)cvGetTickCount();

        //?L???v?`??
        frame = dshow.GetCaptureIplImage();
        cvCopy(frame, image);

        //?L?[??????
        int c = cvWaitKey(10);
        if( (char)c == 27 ) {   //Ecs?L?[??I??
            break;
        }

        //?O??cvGetTickCount() ???s????????o??????Z?
        t = (double)cvGetTickCount() - t;
        sprintf(message, "%gms", t/((double)cvGetTickFrequency()*1000.) );

        //?e?L?X?g??\ヲ
        cvPutText(image, message, cvPoint(50, 50), &dfont, CV_RGB(255, 255, 255));

        //????\ヲ
        cvShowImage(window_name, image);
    }

    //?????
    cvDestroyWindow("hoge");
    cvReleaseImage(&image);
}