プロが教える店舗&オフィスのセキュリティ対策術

はじめまして質問させて頂きたい事がございます。

VC++ 2005 MFCを使用しております。

ビットマップ画像からCImageを作成し、
GetBits()を使用してポインタから直接RGB値を取得したと考えております。

MSDNには
------------------------------------------
取得したポインタと GetPitch の戻り値を使用すると、
イメージ内のピクセルを個別に指定して変更できます。
------------------------------------------

と書いてありますが、よく分かりませんでした。

ためしに4*4ピクセルのビットマップ画像をLoadして
以下のように実装してみました。
----------------------------------------------
//ピッチ取得
int pit = m_image.GetPitch();

//バイト数取得
int byt = m_image.GetBPP();

//ポインタ取得
int* rgb;
rgb = (int*)m_image.GetBits();

CString str;
str.Format(_T("%d, "), GetRValue(rgb[0]));
::TRACE(str);
str.Format(_T("%d, "), GetGValue(rgb[0]));
::TRACE(str);
str.Format(_T("%d, "), GetBValue(rgb[0]));
::TRACE(str);
----------------------------------------------

ピッチがマイナスで戻ってきたので
左下隅を起点とする逆方向 (下から上) にrgb[0]からはいってると
解釈したのですが、正常な値(画像ソフトの値)が取得できませんでした。

どなたかお詳しい方がおりましたら
ご教授お願いいたします。

お手数ではございますが
サンプルコードを記載していただけるとありがたいです。

以上になります。
ご回答よろしくお願いいたします。

A 回答 (3件)

確かに 配列の添え字がマイナスになるのは精神衛生上良くないかもしれません



しかし考えてみてください
GetBitsで取得したポインタと関連があるのはGetPitchとGetBPPの2つです
この2つのデータを使ってボトムアップ/トップダウンの2種類の処理を1種類のコードでやるとすれば先の回答のようにならざるを得ないかと思います
またもともとこの領域を確保するのには アドレスの小さいほうの指示と大きさだと思います
コレが一般的ですし末尾を指示して前方の領域を確保なんてのは聞いたことないですからね

このGetBitsで確保した領域を取得すあたりにブレークポイントを置いてデバッグウィンドウのメモリーを表示させます
アドレスが表示されているコンボボックスに pStart[enter]と入力します
するとGetBitsで取得した領域のデータが表示されると思います
このウィンドウをスクロールさせて 16進データ以外の部分があることに気づきませんか?

コレは現在デバッグ中のアプリで管理していない領域を示しています
ポインタを使って アクセスする場合どの範囲までアクセス可能なのかは プログラマの裁量によります
コレで範囲指定を間違えると 例外が発生してアプリが落ちます

与えられたデータからアクセスを考えると -になるのも仕方がないと考えます
データの仕様に基づくアクセス方法なのです

逆の考え方をすれば nPitchがマイナスなら pStartを先に逆算しておいて x,yを加算していく(nPitchの符号も反転させて)といったことも可能ですよね
ただ2度手間になりますけど ・・・
    • good
    • 0
この回答へのお礼

redfox63 様
ご回答ありがとうございます。

GetBitsが21のポインタを返してきて
21以前の値が前方の領域に確保から仕方ないのですね。

>末尾を指示して前方の領域を確保なんてのは聞いたことないですからね
CImage GetBitsの仕様として解釈して宜しいということですよね。

>逆の考え方をすれば nPitchがマイナスなら pStartを先に
>逆算しておいて x,yを加算していく(nPitchの符号も反転させて)
>といったことも可能ですよね
>ただ2度手間になりますけど ・・・
これも考えました。確かに2度手間ですね。
管理しやすいですけど。

-------------
1, 2, 3, 4, 5
6, 7, 8, 9, 10
11,12,13,14,15
16,17,18,19,20
21,22,23,24,25
--------------

お礼日時:2007/07/19 16:28

BYTE* pStart = m_image.GetBits();


int nPitch = m_image.GetPitch();
int nBPP = m_image.GetBPP();
// BYTEが8bitだから ビット深度を8で除算
int offset = nBPP / 8;
BYTE RGB{3];

for( y=0; y < m_image.Height; y++ ) {
  for ( x = 0; x < nPitch / offset; x++ ) {
    RGB[0] = pStart[ nPitch * y + x * offset + 0 );
    RGB[1] = pStart[ nPitch * y + x * offset + 1 );
    RGB[2] = pStart[ nPitch * y + x * offset + 2 );
  }
}
といった具合で上手くいきませんか ...

xループの最初は nPitch*yが0になる x*offsetも0 +0だから pStart[0]
2回目は nPitch*yは0 x*offsetが3 +0だから pStart[3]
といった具合に xループは増加していき
yループはnPictchにより 増加または減少でアクセスできると思いますよ

ビットマップが ボトムアップなのかトップダウンなのかでGetBits()が返す位置が変ると思います
トップダウンなら1の位置
ボトムアップなら21に位置
こうすれば X軸方向は単純に増加、Y方向はnPitchの符号で増減といった単純化が出来ます
    • good
    • 0
この回答へのお礼

redfox63 様
ご丁寧な回答ありがとうございます。
理解できました。

C言語はあまり詳しくないので初歩的な質問で申し訳ないのですが、
配列の要素数にマイナスでアクセスすることは問題ないのでしょうか?

ポインタを移動させるだけなので問題ないと思うのですが
buf[-100]などの表記に疑問を感じましたので。

先ほどの座標の21から1にアクセスするには
結果的にはbuf[0 - xxx]になると思います。

最終的には左上を原点とした(左上が(0,0))
位置から指定座標のRGB取得したいため、
たとえば(1, 1)の値の場合、先ほどの「7」の位置になり
pStart[0]からマイナスのアクセスになります。(pStart[0 -xxx])

このような仕様でメモリを確保しているのだから
仕方ないのでしょうか?

左上を原点(左上が(0,0))
pStart[0]からメモリに確保されているバイト数分
ポインタをデクリメントして本来の(左上の)開始座標の
別途ポインタを作成し、そこからインクリメントしてアクセスしていった方が宜しいのでしょうか?

最後の質問にしたいと思います。
よろしくお願いいたします。

お礼日時:2007/07/19 15:19

もしかして ビットの深さが 24とかじゃないですか?


コレだと 3バイトで一組なので 32ビットのintではうまくないですよ
BYTE型のポインタでアクセスしないとダメなように思います

BYTE* rgb = (BYTE*)m_image.GetBits();
rgb[0], rgb[1], rgb[2] で RGBの各要素にアクセス
右隣は rgb[3], rgb[4], rgb[5] 感じで
次の行は
rgb[nPitch+0],rgb[nPitch+1],rgb[nPitch+2]
だと思いますよ ・・・
    • good
    • 0
この回答へのお礼

redfox63 様
ご回答ありがとうございます。

教えていただいたとおり
BYTE型のポインタにしてメモリを確認したとこと、
正常に値が入っていることを確認できました。

RGBは逆順で、BGRの順で格納されるのですね。

追記で質問させていただきたいことがあるのですが、

仮に5*5ピクセルの場合
ピッチが[-16](3バイト * 5ピクセル + 1)でしたので、
元のBMP画像の座標を以下のよう定義した場合
-------------
1, 2, 3, 4, 5
6, 7, 8, 9, 10
11,12,13,14,15
16,17,18,19,20
21,22,23,24,25
--------------

GetBits()の戻り値は
21のB値になると思うのですが(以下pStart)、

1~25のすべてのRGB値を取得するためには
pStartからそれぞれ計算して
元座標の21未満はpStartからマイナス分アクセスし、
21以上はpStartからプラス分アクセスすればいいことになりますが、
この取得方法は一般的なのでしょか?

なにか気をつけなければいけないようなことが
あったら教えていただきたく思います。

以上です。
ご教授よろしくお願いいたします。

お礼日時:2007/07/19 11:35

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