• ベストアンサー

RAW画像高速表示について

画像処理ソフトの研究をc言語で行っています。 GUIの導入を目指してVC++の勉強を始めたのですが、RAW画像をうまく表示できず困っています。 現在練習と言うことで、スクロールバーの値によって画像を二値化して表示する処理を行っています。 表示部分を以下のように書いた(ほとんど本を丸写したので意味もちゃんとわかっていませんが)のですが、動作が遅すぎて困っています。 (IMG:unsigned char型の二次元配列にRAWデータを格納したもの) 高速で表示することはできないのでしょうか? アドバイス等、よろしくお願いします。 void Cimage_binView::writeImg(void) {  CClientDC myDC(this);  CDC *pDC = m_pict.GetDC();  int col,row;  int I;  CRgn myRgn;  RECT rect;  m_pict.GetClientRect(&rect);  myRgn.CreateRectRgn(rect.left, rect.top, rect.right, rect.bottom);  pDC->SelectObject(&myRgn); //画像出力  for(col=0;col<256;col++)   for(row=0;row<256;row++)   {    I=IMG[col][row];        if(I<m_sbar1.GetScrollPos())I=0;//スクロールバーの値より小さければ黒    else I=255;//大きければ白にする }    pDC->SetPixel(row,col,RGB(I,I,I));  } }

質問者が選んだベストアンサー

  • ベストアンサー
回答No.3

 こんばんは。補足いただきました。 >>CONST VOID *lpvBitsにm_arrdwImage >>CONST BITMAPINFO *lpbmiにビットマップヘッダ... >>と言うことでしょうか?  はい。 >>ビットマップのヘッダについても勉強した方が良いのでしょうか?  24ビットと32ビットの二つの使用方法位には慣れた方が良いかも知れません。  24ビットの方がヘッダの準備が簡単なのですが、イメージが3バイトで1ピクセルである為、ループで変換等をする様な場面では面倒です。  そう言った意味では32ビットの方が簡単ですが、ヘッダの準備が面倒になります。  以下は32ビット用のヘッダを準備します、参考程度に。 +----------------------------------------------------------------------------------- //初期化 static void InitHDR32BIT(BITMAPINFOHEADER& bmi, int width, int height) { bmi.biSize = sizeof(BITMAPINFOHEADER); bmi.biWidth = width; bmi.biHeight = height; bmi.biBitCount = 32; bmi.biPlanes = 1; bmi.biCompression = BI_BITFIELDS; bmi.biSizeImage = bmi.biWidth * bmi.biHeight * (bmi.biBitCount / 8); } //32ビット型ビットフィールドの設定 static void InitBitFields32BIT(LPRGBQUAD pRGB) { //RGBQUADとDWORDは同じサイズです。ビットフィールドを指定する場合はLPDWORDにキャストする方が楽です(16ビット等では特に) LPDWORD pBF = reinterpret_cast<LPDWORD>(pRGB); pBF[0] = 0xff0000; pBF[1] = 0x00ff00; pBF[2] = 0x0000ff; } //ヘッダを動的作成する static LPBITMAPINFO CreateHDR32BIT(int width, int height) { //ヘッダのサイズ(RGBQUAD * 3は、ビットフィールド用に必要です) const int nHDRSize = sizeof(BITMAPINFOHEADER) + (sizeof(RGBQUAD) * 3); LPBITMAPINFO pbmi = static_cast<LPBITMAPINFO>(::calloc(nHDRSize, 1)); ::InitHDR32BIT(pbmi->bmiHeader, width, height); ::InitBitFields32BIT(pbmi->bmiColors); return pbmi; } -----------------------------------------------------------------------------------+ //遠くで割り当てておく(メンバに入れておく。当然、不要になった時には::free(m_lpbmi)で消します) LPBITMAPINFO m_lpbmi = ::CreateHDR32BIT(256, 256); //ココで描写する ::SetDIBitsToDevice(pDC, 0, 0, pbmi->bmiHeader.biWidth, pbmi->bmiHeader.biHeight, 0, 0, 0, pbmi->bmiHeader.biWidth, m_arrdwImage, m_lpbmi, DIB_RGB_COLORS);

snake_sato
質問者

お礼

ご丁寧に指導していただき、ありがとうございました。 お陰さまでサクサク動くようになりました! 自分ひとりではとても書けなかったと思います。 本当にありがとうございました。

すると、全ての回答が全文表示されます。

その他の回答 (3)

  • Yanch
  • ベストアンサー率50% (114/225)
回答No.4

RAWイメージから、DIBTIMAPを構築するプログラムの例) -------------------- /** * unsigned longの32bitイメージデータ配列から、bitmapデータを構築する * * @param[in] hdc デバイスコンテキストハンドル * @param[in] unsigned long *pImage 入力イメージデータ(32bitの配列 * @param[in] width イメージの横幅 * @param[in] height イメージの縦幅 * @return ビットマップのハンドル(失敗時にはNULL) */ HBITMAP createBitmapFromImage32(HDC hdc, unsigned long *pImage , int width, int height) { BITMAPINFOHEADER bmih; BITMAPINFOHEADER *lpbmih; BITMAPINFO bmi; DWORD fdwInit; CONST VOID *lpbInit; BITMAPINFO *lpbmi; UINT fuUsage; HBITMAP hBitmap; memset(&bmih, 0, sizeof(BITMAPINFOHEADER)); bmih.biSize = sizeof(bmih); bmih.biWidth = width; bmih.biHeight = -height; bmih.biPlanes = 1; bmih.biBitCount = 32; bmih.biCompression = BI_RGB; bmih.biSizeImage = 0; bmih.biXPelsPerMeter = 0; bmih.biYPelsPerMeter = 0; bmih.biClrUsed = 0; bmih.biClrImportant = 0; memset(&bmi, 0, sizeof(BITMAPINFO)); bmi.bmiHeader = bmih; lpbmih = &bmih; fdwInit = CBM_INIT; lpbInit = pImage; lpbmi = &bmi; fuUsage = DIB_RGB_COLORS; hBitmap = CreateDIBitmap(hdc, lpbmih, fdwInit, lpbInit, lpbmi, fuUsage); return hBitmap; } void Cimage_binView::writeImg(void) { CClientDC myDC(this); CDC *pDC = m_pict.GetDC(); int col,row; int I; CRgn myRgn; RECT rect; m_pict.GetClientRect(&rect); myRgn.CreateRectRgn(rect.left, rect.top, rect.right, rect.bottom); pDC->SelectObject(&myRgn); unsigned long image[256][256]; unsigned char scrollPos = m_sbar1.GetScrollPos(); //画像出力 for(col=0;col<256;col++) for(row=0;row<256;row++) { I=IMG[col][row]; if(I<scrollPos) I=0;//スクロールバーの値より小さければ黒 else I=255;//大きければ白にする } image[row][col] = RGB(I, I, I); } HBITMAP hBitmap = createBitmapFromImage32(pDC->m_hDC, image, 256, 256); // 後は、BitBlt等を使って、描画する。 // 例えば、 BITMAP bitmap; memset(&bitmap, 0, sizeof(BITMAP)); GetObject(hBitmap, sizeof(BITMAP), &bitmap); HDC hdcDest = pDC->m_hDC; int nXDest = 0; int nYDest = 0; int nWidth = bitmap.bmWidth; int nHeight = bitmap.bmHeight; HDC hdcSrc = CreateCompatibleDC(hdcDest); int nXSrc = 0; int nYSrc = 0; DWORD dwRop = SRCCOPY; SelectObject(hdcSrc, hBitmap); BOOL boResult = BitBlt(hdcDest, nXDest, nYDest, nWidth, nHeight , hdcSrc, nXSrc, nYSrc, dwRop); DeleteDC(hdcSrc); } -------------------- こんな感じの関数を用意しておいてあげれば、RAWイメージからBITMAPデータを作成 出来ると思います。 RAWイメージのデータ構造もunsigned long(32bit)の配列にしておいてあげる必要がありますけどね。 HBITMAPが得られれば、後は、GDC関数での描画も出来ますので、 SetPixel()関数を呼び出す回数も減らせるのではないでしょうか。 サンプルプログラムは、コンパイルやテストしていないので、バグがあると思いますが、 ご了承ください。 >>forループなしのプログラム >とは2値化処理の部分で(各ピクセルにアクセスせずに)可能なのでしょうか? forなしには出来ませんでした。すいません。

snake_sato
質問者

お礼

ご丁寧にありがとうございます。 とても勉強になりました。

すると、全ての回答が全文表示されます。
回答No.2

 こんばんは。  動作が遅すぎるのは、  pDC->SetPixel(row,col,RGB(I,I,I));  の仕業です。 「画像処理 SetPixel 重い」(検索)  http://www.google.co.jp/search?hl=ja&q=%E7%94%BB%E5%83%8F%E5%87%A6%E7%90%86%E3%80%80SetPixel%E3%80%80%E9%87%8D%E3%81%84&lr=  爆裂に重たい事で有名です。  DWORD型の配列を用意して(又は動的に)  DWORD m_arrdwImage[256*256];  I = IMG[col][row];  m_arrdwImage[idx] = RGB(I,I,I);  の様に変換してから  SetDIBitsToDevice(pDC, , , , , m_arrdwImage);  でpDCに向かって描写すると速くなると思います。  「SetDIBitsToDevice()API」  http://msdn.microsoft.com/ja-jp/library/cc410591.aspx  流れは以下の様な感じです。 //外に出しておく const int nScrollPos = m_sbar1.GetScrollPos(); //変換 for(col=0;col<256;col++) { for(row=0;row<256;row++) { I=IMG[col][row];     //大きければ白にする if(I<nScrollPos)I=0; else I=255; //一次元のインデックス const int idx = (row * 256) + col; //32ビットの配列に代入する m_arrdwImage[idx] = RGB(I,I,I); } } //ココで描写する ヘッダーの初期化などはURLを参考 ::SetDIBitsToDevice(pDC, , , , , m_arrdwImage);

snake_sato
質問者

補足

ありがとうございます。 URLを見ましたが、よくわからない部分があります。 int SetDIBitsToDevice( HDC hdc, // デバイスコンテキストのハンドル int XDest, // 転送先長方形の左上隅の x 座標 int YDest, // 転送先長方形の左上隅の y 座標 DWORD dwWidth, // 転送元長方形の幅 DWORD dwHeight, // 転送元長方形の高さ int XSrc, // 転送元長方形の左下隅の x 座標 int YSrc, // 転送元長方形の左下隅の y 座標 UINT uStartScan, // 配列内の最初の走査行 UINT cScanLines, // 走査行の数 CONST VOID *lpvBits, // DIB ビットからなる配列 CONST BITMAPINFO *lpbmi, // ビットマップ情報 UINT fuColorUse // RGB 値またはパレットインデックス ); lpvBits  バイト配列として格納されている、DIB の色データへのポインタを指定します。詳細については、「解説」を参照してください。 lpbmi  DIB についての情報を保持している 1 個の 構造体へのポインタを指定します。 ということですが、 CONST VOID *lpvBitsにm_arrdwImage CONST BITMAPINFO *lpbmiにビットマップヘッダ... と言うことでしょうか? ビットマップのヘッダについても勉強した方が良いのでしょうか? よろしくお願いします。

すると、全ての回答が全文表示されます。
  • Yanch
  • ベストアンサー率50% (114/225)
回答No.1

パフォーマンスチューニングの話ですね。 こちらのプログラムの場合、 GetScrollPos()と、SetPixel()の呼び出し回数が多すぎるために、遅くなって ると予測出来ると思います。 ・呼び出し回数   GetScroll():65536回   SetPixel():65536回 なので、これを減らしてあげれば、速度が速くなると思われます。 CreateDIBitmap()とか、BitBlt()が有効かもしれないので、試してみてください。 あと、for ループなしのプログラムに書き換え可能と思われますので、 そちらも試してみてください。

snake_sato
質問者

補足

ありがとうございます。 GetScroll();をループの前に配置したら大分早くなりました。 >forループなしのプログラム とは2値化処理の部分で(各ピクセルにアクセスせずに)可能なのでしょうか?

すると、全ての回答が全文表示されます。

関連するQ&A