none
CreateDIBSectionについて RRS feed

  • 質問

  • いつもお世話になっております。

    先日
        http://social.msdn.microsoft.com/Forums/ja-JP/vcgeneralja/thread/a80d3b41-6101-4549-b982-3c0e6ca619d4

    でキャプチャ画像のトリミングについて相談させていただいたものです。

    あのあと、トリミング後のビット配列を、DIBに変換して利用することになりました。
     http://wisdom.sakura.ne.jp/system/winapi/win32/win121.html
    ↑を参考にしてトリミングをする前に

    //DirectShow関連
    VIDEOINFOHEADER *g_pVideoInfoHeader = NULL;

    //トリミング画像用(グローバルで宣言)
    HBITMAP g_hPCImg;
    BITMAPINFO g_bmiTrim;
    BYTE *g_TrimImg = NULL;
    long g_TrimImgSize;

    //以下トリミングを行っている関数内で実施
    RECT TrimRegion;

    //キャプチャしたりトリミングする座標を取得したり・・・(省略します)

    //トリミング
     g_TrimImgSize = (TrimRegion.bottom - TrimRegion.top) * ((((TrimRegion.right - TrimRegion.left) *  g_TrimImg = (BYTE *)VirtualAlloc(NULL, g_TrimImgSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
     if(g_TrimImg == NULL)
     {
          MessageBox(NULL, L"トリミング領域のバッファ確保に失敗", L"エラー", MB_OK);
          return FALSE;
     }
      g_pVideoInfoHeader->bmiHeader.biBitCount + 31) >> 3) & ~3);
     ZeroMemory(&g_bmiTrim, sizeof(BITMAPINFO));
     CopyMemory(&g_bmiTrim.bmiHeader, &g_pVideoInfoHeader->bmiHeader, sizeof(BITMAPINFOHEADER));
     g_bmiTrim.bmiHeader.biWidth = TrimRegion.right - TrimRegion.left;
     g_bmiTrim.bmiHeader.biHeight = TrimRegion.bottom - TrimRegion.top;
     g_bmiTrim.bmiHeader.biSize = sizeof(BITMAPINFO) + g_TrimImgSize;
     g_bmiTrim.bmiHeader.biSizeImage = g_TrimImgSize;
     g_hPCImg = CreateDIBSection(NULL, &g_bmiTrim, DIB_RGB_COLORS, (void **)&g_TrimImg, NULL, 0);
     if(g_hPCImg == NULL)
     {
          MessageBox(NULL, L"トリミング領域のDIBセクションの生成に失敗", L"エラー", MB_OK);
          return FALSE;
     }

    //トリミング実行!!
     for(l_lLoop1 = (g_lOriginalWidth * (g_lOriginalHeight - TrimRegion.bottom) + TrimRegion.left) * l_SizeByPixcel; l_lLoop1 < (g_lOriginalWidth * (g_lOriginalHeight - TrimRegion.top)) * l_SizeByPixcel; l_lLoop1 += g_lOriginalWidth * l_SizeByPixcel)
     {
      CopyMemory(&g_TrimImg[l_lLoop2],&l_pBuffer[l_lLoop1], (TrimRegion.right - TrimRegion.left) * l_SizeByPixcel);
      l_lLoop2 += (((TrimRegion.right - TrimRegion.left) * g_pVideoInfoHeader->bmiHeader.biBitCount + 31) >> 3) & ~3;
     }

    と、こんな感じにしてみたのですが、どうもCreateDIBSectionで失敗しているらしく・・・
    GetLastErrorで確認しても「正常に終了しました」と・・・
    (MSDNをみると、この関数は2000までしかGetLastErrorが使えないっぽいですね・・・)

    原因を切り分ける方法などありましたらご教授いただけませんでしょうか?
    たびたび申し訳ありません・・・

    ちなみに、トリミングした部分に対してこのあと行う予定の処理は
        ・ダイアログ上のピクチャコントロールにトリミングした画像を表示
        ・購入したOCR SDK(Simple OCR)を使ってテキストを取得(ここでDIBを使います)
    です。

    2010年11月4日 3:57

回答

  • 一部内容が重複しますが、気になった点を書きます。

      1.BITMAPINFOHEADERのCopy 
       
        >ここに関しては、オリジナルの画像情報がVIDEOINFOHEADERクラスから取得されているため・・・ 

        AM_MEDIA_TYPE.subtypeが記載されていないため推測ですが、16bpp/32bppではBitFieldsを指定する場合があります。

        この場合、biCompressionの値がBI_BITFIELDSになっており、bmiHeaderの直後にDWORD * 3のColor Maskがつきます。 BI_RGBならColor Maskは不要です。

        Color Maskがつく場合は、Color Mask付きでDIBSectionを作成するか、Color Maskなしで作成しColorの配列に注意しながらCopyする必要があります。前者のが楽です。

     

      2.CreateDIBSection時には、バッファ領域もこの関数で確保してくれるのでしたっけ?
        FileMappingを利用しない場合は、そうです。今回のCaseが該当します。

        void**で取得したBufferに直接書き込んでください。

     

      3.CreateDIBSectionの失敗時の詳細を取得する方法

        Parameterの間違いが主な失敗の理由になりますが、具体的どのParamterが間違っているか知る方法はないですね。

     

      4.CreateDIBSectionのhDC

        MSDNにはCan be NULLとは書かれていません。

        GetDC(NULL)でScreen DCが取得できますのでこれを指定するか、Picture BoxのDCを指定しましょう。

        後片付けのReleaseDCも忘れずに行いましょう。

     

      5.24bpp BITMAPINFOHEADERのSample

        biSize              40 
        biWidth            100 
        biHeight           100 
        biPlanes            1 
        biBitCount         24 
        biCompression   0 
        biSizeImage      0 
        biXPelsPerMeter 0 
        biYPelsPerMeter 0 
        biClrUsed          0 
        biClrImportant   0

        HDCはScreen DCを指定しています。Bitmapの幅と高さは100*100です。

    • 回答としてマーク どらちん 2010年11月5日 0:00
    2010年11月4日 14:04
  • こんにちわ。

     

    BITMAPINFO 構造体のポインタは BITMAPINFOHEADER 構造体とイメージそのものが必要とする画素分のメモリが確保されたポインタでなくてはなりません ( パレットを使うならその分も必要ですが、DIB_RGB_COLORS なので関係ありませんか・・・ ) 。

    また、既にご存知かもしれませんが、このイメージそのものが必要とするメモリは DWORD 境界の幅を持たせる必要があります。

     

    なので、この部分は

    g_pVideoInfoHeader->bmiHeader.biBitCount + 31) >> 3) & ~3);

    イメージの幅を計算する時に使うべきでは?
    biBitCount は DIB のカラービット数を設定する場所なので、フルカラーであれば 24 になると思います。

     

    後、CreateDIBSection は HDC を必要としたはずですので、NULL ではマズいかと思います。

     

     # CreateDIBSection 時にイメージそのものを設定出来たかは記憶があやふやなのですが、

        作成後、void** を使って、そこにコピーした覚えがあります。

        DIB 作成時にイメージも設定できましたっけ?

    • 回答としてマーク どらちん 2010年11月5日 0:01
    2010年11月4日 7:38

すべての返信

  • すみません、環境を記載忘れていました。

    開発環境はXP Pro SP3(32bit) + Visual Studio Team System 2008 Developer Edition です。

    2010年11月4日 7:23
  • こんにちわ。

     

    BITMAPINFO 構造体のポインタは BITMAPINFOHEADER 構造体とイメージそのものが必要とする画素分のメモリが確保されたポインタでなくてはなりません ( パレットを使うならその分も必要ですが、DIB_RGB_COLORS なので関係ありませんか・・・ ) 。

    また、既にご存知かもしれませんが、このイメージそのものが必要とするメモリは DWORD 境界の幅を持たせる必要があります。

     

    なので、この部分は

    g_pVideoInfoHeader->bmiHeader.biBitCount + 31) >> 3) & ~3);

    イメージの幅を計算する時に使うべきでは?
    biBitCount は DIB のカラービット数を設定する場所なので、フルカラーであれば 24 になると思います。

     

    後、CreateDIBSection は HDC を必要としたはずですので、NULL ではマズいかと思います。

     

     # CreateDIBSection 時にイメージそのものを設定出来たかは記憶があやふやなのですが、

        作成後、void** を使って、そこにコピーした覚えがあります。

        DIB 作成時にイメージも設定できましたっけ?

    • 回答としてマーク どらちん 2010年11月5日 0:01
    2010年11月4日 7:38
  • すいません、もう一点見逃していました。

    g_bmiTrim.bmiHeader.biSize = sizeof(BITMAPINFO) + g_TrimImgSize;

    この部分ですが、BITMAPINFOHEADER 構造体のサイズを必要とされているので、sizeof(BITMAPINFOHEADER) にしないとおかしくなると思います。

    BITMAPV4HEADER とか他のヘッダーと区別する為でしょうから。

    2010年11月4日 7:56
  • ミッヒーさん

    レスありがとうございます。

    g_pVideoInfoHeader->bmiHeader.biBitCount + 31) >> 3) & ~3);

    > イメージの幅を計算する時に使うべきでは?

    あ、おっしゃるとおりです。
    私のミスです^^;
    (前回のスレで散々教えていただいたことを完全に失念しているとは・・・我ながら情けなや>_<)

    >biBitCount は DIB のカラービット数を設定する場所なので、フルカラーであれば 24 になると思います。

    ここに関しては、オリジナルの画像情報がVIDEOINFOHEADERクラスから取得されているため、(トリミングすることで変更になる、縦・横・サイズ関連)以外のものはすべてVIDEOINFOHEADER::bmiHeader をそのまま用いています。

    CopyMemory(&g_bmiTrim.bmiHeader, &g_pVideoInfoHeader->bmiHeader, sizeof(BITMAPINFOHEADER));
    ・・・・

    が、それに該当します。

    >後、CreateDIBSection は HDC を必要としたはずですので、NULL ではマズいかと思います。

    ここなんですが、投稿時に記載した参考URLに「この引数は iUsage に DIB_PAL_COLOS を指定した時のみ使用されます。iUsage が DIB_RGB_COLORS の場合は無視されるので NULL でもかまいません」とあったのでNULLにしてみたのですが、まずかったんですかね・・・

    ちなみに、

    HDC hDC;
    hDC = CreateCompatibleDC(NULL);
    ・・・
    g_hPCImg = CreateDIBSection(hDC , &g_bmiTrim, DIB_RGB_COLORS, (void **)&g_TrimImg, NULL, 0);

    としても、やっぱり戻り値がNULLで、失敗しているようです・・・

    >DIB 作成時にイメージも設定できましたっけ

    そうか、CreateDIBSection時には、バッファ領域もこの関数で確保してくれるのでしたっけ?
    早速、VirtualAllocしているところをコメントアウトしてみましたが・・・やはりだめでした・・・。

    CreateDIBSectionの失敗時の詳細を取得する方法って・・・XP以上ではないのでしょうか・・・^^;

    2010年11月4日 8:20
  • 拡張エラーについてしか記載がないですね・・・。

    申し訳ないのですが、更にもう一点。

    g_TrimImgSize = (TrimRegion.bottom - TrimRegion.top) * ((((TrimRegion.right - TrimRegion.left) *  g_TrimImg = (BYTE *)VirtualAlloc(NULL, g_TrimImgSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    メモリのアドレスと掛け算になっていますが、コピペの際のミスでしょうか?
    2010年11月4日 8:30
  • ミッヒーさん

    レスありがとうございます。

    >メモリのアドレスと掛け算になっていますが、コピペの際のミスでしょうか?

    おっしゃるとおり、コピぺミスです^^;

     g_TrimImgSize = (TrimRegion.bottom - TrimRegion.top) * ((((TrimRegion.right - TrimRegion.left) * g_pVideoInfoHeader->bmiHeader.biBitCount + 31) >> 3) & ~3);

    が、正しい記載です。
    大変失礼しました>_<
    2010年11月4日 9:00
  • たびたびすいません・・・。説明下手だと周りから言われまくっています・・・。

    g_TrimImgSize = (TrimRegion.bottom - TrimRegion.top) * ((TrimRegion.right - TrimRegion.left + 31) ~3) * (g_pVideoInfoHeader->bmiHeader.biBitCount / 8);

    こうではありませんか?

    なお、HDC が NULL でいいかどうかは確認していませんが、DIB_PAL_COLORS の場合にのみ必要、という説明は何となく納得がいきます。DIB_PAL_COLORS という事はシステムが 256 色のパレットモードで、論理パレットが HDC に選択されているという事なのでしょうから。

    とは言え、HDC が NULL でいいと MSDN には記載されていませんねぇ。

    2010年11月4日 9:18
  • ミッヒーさん
    何度もありがとうございます^^

    > g_TrimImgSize = (TrimRegion.bottom - TrimRegion.top) * ((TrimRegion.right - TrimRegion.left + 31) ~3) * (g_pVideoInfoHeader->bmiHeader.biBitCount / 8);

    > こうではありませんか

    前回本プログラムの別件で問い合わせた際(投稿時のURL)、

    > ビットマップの場合、横のサイズは必ず4の倍数にしないと駄目なので、バッファサイズは以下のような計算になるかと思います。

    > const int pitch = ((横 * ColorBit + 31) >> 3) & ~3;
    > const int size = pitch * 縦
    とご教授いただきました。
    そこで、
        横 = TrimRegion.right - TrimRegion.left
        縦 = TrimRegion.bottom - TrimRegion.top
    を代入して算出しています。
    これ自体が間違っているのでしょうか?
    実際に計算してみると、横のバッファサイズは4の倍数にちゃんとなっているみたいですが・・・。
    >とは言え、HDC が NULL でいいと MSDN には記載されていませんねぇ。
    確かに、MSDNには載っていませんでした。
    すでにHDCを利用する形にしているのですが、それでもだめです・・・。
    2010年11月4日 10:08
  • ごめんなさい、合ってましたね・・・。

    混乱させる書き込みをしてしまい、すいませんでした。

     

    そうなると何故 CreateDIBSection が成功しないかですが・・・。

    以前の書き込みの

    g_bmiTrim.bmiHeader.biSize = sizeof(BITMAPINFO) + g_TrimImgSize;

    部分を

    g_bmiTrim.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);

    にする事と、ようやく気付いたのですが、

    g_bmiTrim.bmiHeader.biPlanes = 1;

    が無い ( ZeroMemory で 0 になっている ) 為ではないかと思います。

     

    改めて、混乱を招く書き込みをしてすいませんでした。

    • 編集済み ミッヒー 2010年11月4日 12:09 誤記修正
    2010年11月4日 11:04
  • 一部内容が重複しますが、気になった点を書きます。

      1.BITMAPINFOHEADERのCopy 
       
        >ここに関しては、オリジナルの画像情報がVIDEOINFOHEADERクラスから取得されているため・・・ 

        AM_MEDIA_TYPE.subtypeが記載されていないため推測ですが、16bpp/32bppではBitFieldsを指定する場合があります。

        この場合、biCompressionの値がBI_BITFIELDSになっており、bmiHeaderの直後にDWORD * 3のColor Maskがつきます。 BI_RGBならColor Maskは不要です。

        Color Maskがつく場合は、Color Mask付きでDIBSectionを作成するか、Color Maskなしで作成しColorの配列に注意しながらCopyする必要があります。前者のが楽です。

     

      2.CreateDIBSection時には、バッファ領域もこの関数で確保してくれるのでしたっけ?
        FileMappingを利用しない場合は、そうです。今回のCaseが該当します。

        void**で取得したBufferに直接書き込んでください。

     

      3.CreateDIBSectionの失敗時の詳細を取得する方法

        Parameterの間違いが主な失敗の理由になりますが、具体的どのParamterが間違っているか知る方法はないですね。

     

      4.CreateDIBSectionのhDC

        MSDNにはCan be NULLとは書かれていません。

        GetDC(NULL)でScreen DCが取得できますのでこれを指定するか、Picture BoxのDCを指定しましょう。

        後片付けのReleaseDCも忘れずに行いましょう。

     

      5.24bpp BITMAPINFOHEADERのSample

        biSize              40 
        biWidth            100 
        biHeight           100 
        biPlanes            1 
        biBitCount         24 
        biCompression   0 
        biSizeImage      0 
        biXPelsPerMeter 0 
        biYPelsPerMeter 0 
        biClrUsed          0 
        biClrImportant   0

        HDCはScreen DCを指定しています。Bitmapの幅と高さは100*100です。

    • 回答としてマーク どらちん 2010年11月5日 0:00
    2010年11月4日 14:04
  • ミッヒーさん、kozzさん

    レスありがとうございました。
    原因が分かりました。

    自分でもなんでこうしたのか???な状態だったのですが・・・

    g_bmiTrim.bmiHeader.biSize = sizeof(BITMAPINFOHEADER) + g_TrimImgSize;

    なんてことになっていました^^;
    kozzさんのアドバイスをみて、そう言えばBITMAPINFOHEADERの値を一つ一つ確認していなかったなぁ・・・と思いみてみたらすごい値になっていて^^;
    我ながらあっふぉ~なミスを>_<

    無事、関数の実行は成功し、中に値を入れることができました☆
    お騒がせしました^^;

    ちなみに、CreateDIBSectionの第一引数、NULLにしても問題なく動いちゃいますね^^;
    MSDNにNULLでよいとは書いていないので、怖いからやめておこうと思いますが、NULLにすると後で何がおきるんですかねぇ(笑)。

    本当にありがとうございました!!

    2010年11月5日 0:00