アプリ版:「スタンプのみでお礼する」機能のリリースについて

画像処理ソフトの研究を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));
 }
}

A 回答 (4件)

 こんばんは。

補足いただきました。

>>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);
    • good
    • 0
この回答へのお礼

ご丁寧に指導していただき、ありがとうございました。
お陰さまでサクサク動くようになりました!

自分ひとりではとても書けなかったと思います。
本当にありがとうございました。

お礼日時:2008/10/28 01:21

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)
{
BITMAPINFOHEADERbmih;
BITMAPINFOHEADER*lpbmih;
BITMAPINFObmi;
DWORDfdwInit;
CONST VOID*lpbInit;
BITMAPINFO*lpbmi;
UINTfuUsage;
HBITMAPhBitmap;

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等を使って、描画する。
// 例えば、
BITMAPbitmap;

memset(&bitmap, 0, sizeof(BITMAP));
GetObject(hBitmap, sizeof(BITMAP), &bitmap);

HDChdcDest= pDC->m_hDC;
intnXDest= 0;
intnYDest= 0;
intnWidth= bitmap.bmWidth;
intnHeight= bitmap.bmHeight;
HDChdcSrc= CreateCompatibleDC(hdcDest);
intnXSrc= 0;
intnYSrc= 0;
DWORDdwRop= 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なしには出来ませんでした。すいません。
    • good
    • 0
この回答へのお礼

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

お礼日時:2008/10/28 01:27

 こんばんは。



 動作が遅すぎるのは、
 pDC->SetPixel(row,col,RGB(I,I,I));
 の仕業です。

「画像処理 SetPixel 重い」(検索)
 http://www.google.co.jp/search?hl=ja&q=%E7%94%BB …

 爆裂に重たい事で有名です。

 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 …

 流れは以下の様な感じです。

//外に出しておく
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);

この回答への補足

ありがとうございます。
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にビットマップヘッダ...
と言うことでしょうか?

ビットマップのヘッダについても勉強した方が良いのでしょうか?

よろしくお願いします。

補足日時:2008/10/27 14:42
    • good
    • 0

パフォーマンスチューニングの話ですね。


こちらのプログラムの場合、
GetScrollPos()と、SetPixel()の呼び出し回数が多すぎるために、遅くなって
ると予測出来ると思います。

・呼び出し回数
  GetScroll():65536回
  SetPixel():65536回

なので、これを減らしてあげれば、速度が速くなると思われます。

CreateDIBitmap()とか、BitBlt()が有効かもしれないので、試してみてください。

あと、for ループなしのプログラムに書き換え可能と思われますので、
そちらも試してみてください。

この回答への補足

ありがとうございます。
GetScroll();をループの前に配置したら大分早くなりました。

>forループなしのプログラム
とは2値化処理の部分で(各ピクセルにアクセスせずに)可能なのでしょうか?

補足日時:2008/10/27 14:04
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!