SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Advanced/W-ZERO3 [es]で作ろう

Advanced/W-ZERO3 [es]でカメラアプリを作ろう Part3

Windows Mobile用DirectShowフィルタの実装例

  • X ポスト
  • このエントリーをはてなブックマークに追加

ダウンロード サンプルソース (23.7 KB)

フィルタの実装

プロジェクトの作成

 Visual Studioを起動し、これまで作成してきたソリューションファイル「WM_Camera.sln」を読み込んでください。次に「mono_filter」という名前でプロジェクトを新規作成し、ソリューションへ追加してください。このときプロジェクトの種類を「Win32スマートデバイスプロジェクト」、アプリケーションの種類を「DLL」とします。

フィルタ情報を定義する

 作成するフィルタ情報を「mono_filter.h」へ定義していきます。フィルタクラスのクラスIDをCLSID_MonoFilterとして定義します。

mono_filter.h
#pragma once

#define TEMPLATE_NAME    (L"Mono_Filter")
#define FILTER_NAME        (TEMPLATE_NAME)

// {A2A38415-10AC-4bdc-8403-5FA8E59D8A84}
const GUID CLSID_MonoFilter=
{0xa2a38415, 0x10ac, 0x4bdc, 0x84, 0x3, 
 0x5f, 0xa8, 0xe5, 0x9d, 0x8a, 0x84};

 フィルタのピン情報を定義します。トランスフォームフィルタでは、入力ピンと出力ピンの2種類のピン情報を定義する必要があります。

mono_filter.h
// メディアタイプの定義
const AMOVIESETUP_MEDIATYPE sudPinTypes = {
    &MEDIATYPE_Video, &MEDIASUBTYPE_RGB565
};

// 入力、出力ピンの情報
const AMOVIESETUP_PIN sudPins[] =
{
    {
        L"",
        FALSE,
        FALSE, // 入力ピンなので FALSE とすること
        FALSE,
        FALSE,
        &CLSID_NULL,
        0,
        1,
        &sudPinTypes
    },
    {
        L"",
        FALSE,
        TRUE, // 出力ピンなので TRUE とすること
        FALSE,
        FALSE,
        &CLSID_NULL,
        0,
        1,
        &sudPinTypes
    },
};

 AMOVIESETUP_FILTER構造体を使ってフィルタ情報を定義します。入力ピンと出力ピン、合わせて2つのピンがあるので、ピン数を2とすることに注意してください。

mono_filter.h
// フィルタ情報
const AMOVIESETUP_FILTER afFilterInfo=
{
    &CLSID_MonoFilter,// フィルタのCLSID
    FILTER_NAME,      // フィルタ名
    MERIT_DO_NOT_USE, // メリット値
    2,                // ピン数
    sudPins           // ピン情報
};

フィルタクラスの作成

 トランスフォームフィルタを作成するには、DirectShowクラスライブラリのCTransformFilterクラスを使うと便利です。今回は、それを派生させてCMonoFilterクラスを作成します。クラス図を図1に示します。

図1.CMonoFilterクラス図
図1.CMonoFilterクラス図

 CTransformFilterで定義されている純粋仮想メソッドをCMonoFilterに実装していきます。加えて、ピンの接続完了時に呼び出されるCompleteConnectを実装します。

 メンバ変数としては、クリティカルセクションと画像フォーマットを記憶するためのVIDEOINFOHEADER構造体を宣言します。

mono_filter.h
class CMonoFilter : public CTransformFilter {
    VIDEOINFOHEADER    m_Vih;
public:
    CMonoFilter(LPUNKNOWN pUnk,HRESULT *phr);
    virtual    ~CMonoFilter();
    static CUnknown * WINAPI CreateInstance(LPUNKNOWN pUnk, 
                                            HRESULT *phr);
    // CTransformFilter
    HRESULT CheckInputType(const CMediaType *mtIn);
    HRESULT GetMediaType(int iPosition,CMediaType *pMediaType);
    HRESULT CheckTransform(const CMediaType *mtIn,
                           const CMediaType *mtOut);
    HRESULT CompleteConnect(PIN_DIRECTION direction,IPin *pReceivePin);
    HRESULT DecideBufferSize(IMemAllocator *pAlloc,
                             ALLOCATOR_PROPERTIES *ppropInputRequest);
    HRESULT Transform(IMediaSample *pIn,IMediaSample *pOut);
};

 コンストラクタ、デストラクタ、CreateInstanceの実装については本記事の添付ファイルを参照して頂くとして、CheckInputTypeから見ていきます。

 CheckInputTypeは、上位フィルタが入力ピンに接続しようとしたときに、メディアタイプを判別して許可するかどうかを判定します。

 本フィルタではメディアタイプがMEDIATYPE_Video、サブメディアタイプがMEDIASUBTYPE_RGB565のみ許可します。

mono_filter.cpp
HRESULT CMonoFilter::CheckInputType(const CMediaType *mtIn) {
    OutputDebugString(TEXT("CMonoFilter::CheckInputType\n"));
    if(*mtIn->FormatType()!=FORMAT_VideoInfo) {
        return VFW_E_TYPE_NOT_ACCEPTED;
    }
    if(*mtIn->Type()!=MEDIATYPE_Video ||
       *mtIn->Subtype()!=MEDIASUBTYPE_RGB565)
    {
        return VFW_E_TYPE_NOT_ACCEPTED;
    }
    return S_OK;
}
2008/08/26 追記
不要なロック処理がありましたので削除しました。

 次にGetMediaTypeを実装します。このメソッドは、出力ピンがサポートするメディアタイプを1つ返します。引数にインデックス値が指定されるので、それに対応するメディアタイプを返します。

 本フィルタでは、入力ピンが受けつけたメディアタイプだけを返すように実装します。つまり出力ピンから送出されるメディアタイプが入力ピンと同じになります。またインデックス値は0のみ許容します。

mono_filter.cpp
HRESULT CMonoFilter::GetMediaType(
    int iPosition,CMediaType *pMediaType) 
{
    OutputDebugString(TEXT("CMonoFilter::GetMediaType\n"));
    if(iPosition<0) {
        return E_INVALIDARG;
    }else if(iPosition>0) {
        return VFW_S_NO_MORE_ITEMS;
    }
    *pMediaType=m_pInput->CurrentMediaType();
    return S_OK;
}
フィルタクラスからピンへのアクセス
 入力ピンは親クラスのCTransformFilter::m_pInputというメンバ変数で表されます。
 出力ピンはCTransformFilter::m_pOutputとなります。
 

 次にCheckTransformを実装します。このメソッドは、引数に入力メディアタイプと出力メディアタイプを渡されるので、その変換をフィルタがサポートするか判定し、結果を返します。

 本フィルタでは、入力メディアタイプと出力メディアタイプが等しい場合のみサポートします。

mono_filter.cpp
HRESULT CMonoFilter::CheckTransform(
    const CMediaType *mtIn,const CMediaType *mtOut) 
{
    OutputDebugString(TEXT("CMonoFilter::CheckTransform\n"));
    if(*mtIn->FormatType()!=FORMAT_VideoInfo) {
        return VFW_E_TYPE_NOT_ACCEPTED;
    }
    if(*mtIn->Type()!=MEDIATYPE_Video || 
       *mtIn->Subtype()!=MEDIASUBTYPE_RGB565) {
        return VFW_E_TYPE_NOT_ACCEPTED;
    }
    if(*mtIn!=*mtOut) {
        return VFW_E_TYPE_NOT_ACCEPTED;
    }
    return S_OK;
}

 次にCompleteConnectを実装します。このメソッドは、ピンの接続が完了したときに呼ばれます。

 本フィルタでは、入力ピンの接続が完了したとき、入力ピンのメディアタイプを取得し、画像フォーマットが格納されているVIDEOINFOHEADER構造体をメンバ変数m_Vihにコピーします。

mono_filter.cpp
HRESULT CMonoFilter::CompleteConnect(
    PIN_DIRECTION direction,IPin *pReceivePin) 
{
    OutputDebugString(TEXT("CMonoFilter::CompleteConnect\n"));
    if(direction==PINDIR_INPUT) {
        CMediaType &type=m_pInput->CurrentMediaType();
        CopyMemory(&m_Vih,type.Format(),sizeof(VIDEOINFOHEADER));
    }
    return S_OK;
}

 次にDecideBufferSizeを実装します。このメソッドは、出力ピンから下位フィルタに送る画像データ用のバッファを確保します。バッファの数とサイズをALLOCATOR_PROPERTIES構造体で指定し、IMemAllocator::SetPropertiesを呼び出すことによりバッファの確保が行われます。

 本フィルタではバッファ数を1、サイズを入力メディアタイプのサンプルサイズとしています。すなわち、画像1フレーム分のバッファです。

mono_filter.cpp
HRESULT CMonoFilter::DecideBufferSize(
    IMemAllocator *pAlloc,ALLOCATOR_PROPERTIES *ppropInputRequest)
{
    OutputDebugString(TEXT("CMonoFilter::DecideBufferSize\n"));
    if(m_pInput!=NULL && m_pInput->IsConnected()==FALSE) {
        return E_FAIL;
    }
    ppropInputRequest->cBuffers=1;
    ppropInputRequest->cbBuffer
     =m_pInput->CurrentMediaType().GetSampleSize();
    ALLOCATOR_PROPERTIES Actual;
    HRESULT hr=pAlloc->SetProperties(ppropInputRequest,&Actual);
    if(FAILED(hr)) {
        return hr;
    }
    // 確保されたかチェック
    if((ppropInputRequest->cBuffers>Actual.cBuffers) || 
       (ppropInputRequest->cbBuffer>Actual.cbBuffer))
    {
        return E_FAIL;
    }
    return S_OK;
}

 最後にTransformを実装します。このメソッドは、入力サンプルを受け取り、何らかの処理をし、出力サンプルを書き込む処理を行います。

 つまり、このフィルタの肝となるモノトーン加工をここで行います。また入力サンプルのタイム情報を出力サンプルにコピーします。

mono_filter.cpp
HRESULT CMonoFilter::Transform(
    IMediaSample *pIn,IMediaSample *pOut)
{
    LPWORD pSrc,pDest;
    pIn->GetPointer((BYTE**)&pSrc);
    pOut->GetPointer((BYTE**)&pDest);
    const long acSize=pIn->GetActualDataLength();
    for(LONG y=0;y<m_Vih.bmiHeader.biHeight;y++) {
        for(LONG x=0;x<m_Vih.bmiHeader.biWidth;x++) {
            // 各要素を6ビットとして取り出す
            WORD r=(*pSrc & 0xf800) >> 10;
            WORD g=(*pSrc & 0x7e0) >> 5;
            WORD b=(*pSrc & 0x1f) << 1;
            // 明度を求める
            WORD Yg =(r*299+g*587+b*114) >> 10;
            WORD Yrb=Yg>>1;
            // 書き込む
            *pDest=(Yrb<<11)+(Yg<<5)+(Yrb);
            pSrc++;
            pDest++;
        }
    }
    pOut->SetActualDataLength(acSize);
    pOut->SetSyncPoint(TRUE);
    // 入力サンプルのタイムを出力サンプルへコピー
    REFERENCE_TIME start, end;
    pIn->GetTime(&start, &end);
    pOut->SetTime(&start, &end);
    pIn->GetMediaTime(&start, &end);
    pOut->SetMediaTime(&start, &end);
    return S_OK;
}
モノトーン加工について
 ここでは以下の式を元にモノトーン加工を行っています。
 Y = 0.299*R + 0.587*G + 0.114*B
 (Yは明度。R、G、B、Yの各範囲は0以上1以下)。
 本フィルタでは、RとBを2倍してGとビット数を揃えた後、次式を適用します。
 Y = 299*R + 587*G + 114*B
 このとき、Yの最大値は62000、16進数では0xF230です。
 RとBに対しては上位5ビット残すため11ビット、Gに対しては上位6ビットを残すため10ビット右シフトしたYを、出力画像のR、G、Bそれぞれの要素へ書き込むことにより、モノトーン加工された画像が得られます。
 

次のページ
フィルタグラフの変更

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
Advanced/W-ZERO3 [es]で作ろう連載記事一覧

もっと読む

この記事の著者

syu5()

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/2470 2008/09/17 01:01

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング