• 締切済み

CImage GetBitsメソッドについて

はじめまして質問させて頂きたい事がございます。 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]からはいってると 解釈したのですが、正常な値(画像ソフトの値)が取得できませんでした。 どなたかお詳しい方がおりましたら ご教授お願いいたします。 お手数ではございますが サンプルコードを記載していただけるとありがたいです。 以上になります。 ご回答よろしくお願いいたします。

みんなの回答

  • redfox63
  • ベストアンサー率71% (1325/1856)
回答No.3

確かに 配列の添え字がマイナスになるのは精神衛生上良くないかもしれません しかし考えてみてください GetBitsで取得したポインタと関連があるのはGetPitchとGetBPPの2つです この2つのデータを使ってボトムアップ/トップダウンの2種類の処理を1種類のコードでやるとすれば先の回答のようにならざるを得ないかと思います またもともとこの領域を確保するのには アドレスの小さいほうの指示と大きさだと思います コレが一般的ですし末尾を指示して前方の領域を確保なんてのは聞いたことないですからね このGetBitsで確保した領域を取得すあたりにブレークポイントを置いてデバッグウィンドウのメモリーを表示させます アドレスが表示されているコンボボックスに pStart[enter]と入力します するとGetBitsで取得した領域のデータが表示されると思います このウィンドウをスクロールさせて 16進データ以外の部分があることに気づきませんか? コレは現在デバッグ中のアプリで管理していない領域を示しています ポインタを使って アクセスする場合どの範囲までアクセス可能なのかは プログラマの裁量によります コレで範囲指定を間違えると 例外が発生してアプリが落ちます 与えられたデータからアクセスを考えると -になるのも仕方がないと考えます データの仕様に基づくアクセス方法なのです 逆の考え方をすれば nPitchがマイナスなら pStartを先に逆算しておいて x,yを加算していく(nPitchの符号も反転させて)といったことも可能ですよね ただ2度手間になりますけど ・・・

javaTarou
質問者

お礼

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 --------------

  • redfox63
  • ベストアンサー率71% (1325/1856)
回答No.2

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の符号で増減といった単純化が出来ます

javaTarou
質問者

お礼

redfox63 様 ご丁寧な回答ありがとうございます。 理解できました。 C言語はあまり詳しくないので初歩的な質問で申し訳ないのですが、 配列の要素数にマイナスでアクセスすることは問題ないのでしょうか? ポインタを移動させるだけなので問題ないと思うのですが buf[-100]などの表記に疑問を感じましたので。 先ほどの座標の21から1にアクセスするには 結果的にはbuf[0 - xxx]になると思います。 最終的には左上を原点とした(左上が(0,0)) 位置から指定座標のRGB取得したいため、 たとえば(1, 1)の値の場合、先ほどの「7」の位置になり pStart[0]からマイナスのアクセスになります。(pStart[0 -xxx]) このような仕様でメモリを確保しているのだから 仕方ないのでしょうか? 左上を原点(左上が(0,0)) pStart[0]からメモリに確保されているバイト数分 ポインタをデクリメントして本来の(左上の)開始座標の 別途ポインタを作成し、そこからインクリメントしてアクセスしていった方が宜しいのでしょうか? 最後の質問にしたいと思います。 よろしくお願いいたします。

  • redfox63
  • ベストアンサー率71% (1325/1856)
回答No.1

もしかして ビットの深さが 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] だと思いますよ ・・・

javaTarou
質問者

お礼

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からプラス分アクセスすればいいことになりますが、 この取得方法は一般的なのでしょか? なにか気をつけなければいけないようなことが あったら教えていただきたく思います。 以上です。 ご教授よろしくお願いいたします。

関連するQ&A

  • vector配列の重複を無くすには?

    画像処理で各ピクセルごとのRGB値をそれぞれ取得し、 重複を除いた形で全て表示したいと考えています。 (仮に4ピクセルしかないとして、RGB(255,255,0), RGB(255,255,255),RGB(255,255,255),RGB(255,0,255) といった値が取れたときにRGB(255,255,0), RGB(255,255,255),RGB(255,0,255)のみを表示するといった感じです。) 画像ごとにピクセル数が異なり分からないので、 各ピクセルのRGB値を格納するのに動的配列vector<int*> pixelを 用い、そこにred,green,blueそれぞれの値を格納した 配列RGB[3]を格納しようと思い以下のように書いたのですが、 vector配列に配列を格納したときに重複を削除する方法が 分からず困っています。 for(int x = 0; x->width; x++) { for(int y = 0; y->height; y++) { /*getRGBはそのピクセルのRGB値を取得する仮想関数*/ RGB[0] = (int)getRGB(x,y,RED); RGB[1] = (int)getRGB(x,y,GREEN); RGB[2] = (int)getRGB(x,y,BLUE); } } pixel.push_back(RGB); と格納しても、そこからpixel配列に格納された物の中から RGBが全て一致するものを消去する方法が分かりません。 格納していたものが配列でなければ、pixelをsortして、 unique関数で重複を無くせるのでしょうが… どなたか良い方法をご教授願えませんでしょうか?

  • ビットマップイメージの変更方法

    「ビットマップイメージ ビットの深さ」 「ビットマップイメージ 編集」 などのキーワードで検索してみたのですが、何かソフトをDLして それでどうこうというのしかなかったのですが あまりそういった物は入れたくないので質問させてください。 当方、ラグナロクオンラインというネットゲームをしています。 その中でエンブレム(画像)を設定する事ができるのですが 所持している画像が32bitでしてゲーム内に反映できません。 ラグナロクオンライン公式サイトによると 画像サイズ  24(縦)×24(横)ピクセル 画像フォーマット  ビットマップ(24bit) 透過色(16進数/RGB)  #FF00FF / 255 0 255 の画像ならゲーム内に反映できるらしいのですが 所持してる画像を24bitに変更する方法ありましたら 教えてください。

  • 関数のポインタ

    char* check(int a,int b,int c) { return(\"yes\"); とした場合char* checkは何を表しているのでしょうか。 また、 char *str_copy(char d[], char s[]) { char *t=d; while(*d++ = *s++) ; return t; とした場合との違いはあるのでしょうか。 ポインタを返すときに*をつけると教わったのですがいまいち理解できなかったのですがどのような処理が行われているのでしょうか、お願いします。

  • 任意の文字列をJAVAで画像化

    表題にある通り任意の文字列をJAVAで画像化したいと思い 下記サンプルを検索して見つけたのですが作成される画像は 真っ白な画像で文字列は表示されませんでした。 任意の文字列を画像として出力するにはどうすればよいでしょうか。 import java.awt.Color; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.File; import javax.imageio.ImageIO; public class Test5 { public static void main(String[] args) { String str = "abc123"; new Test5().Create(str); System.out.println(str); } public void Create(String str) { int w=60; int h=17; try { //受け取った文字列を画像化 BufferedImage image=new BufferedImage(w,h,BufferedImage.TYPE_INT_RGB); Graphics2D g2d=image.createGraphics(); g2d.setBackground(Color.WHITE); g2d.clearRect(0,0,w,h); g2d.setColor(Color.BLACK); g2d.drawString(str,0,0); ImageIO.write(image, "JPEG", new File("c:\\test.jpg")); } catch(Exception e) { e.printStackTrace(); } } }

    • ベストアンサー
    • Java
  • VC++ スクロールバーが何ピクセル移動したか知るには?

    VC++初心者なんですが、子ウィンドウにビットマップ画像を表示して、マウスポインタの指している位置のRGBの値を表示するプログラムを作っています。 このとき、画像がある子ウィンドウにスクロールバーがあるとき、スクロールしたあとでもマウスポインタの指すx、y座標が子ウィンドウの左上になってしまいます。そこで、何ピクセル分x、y座標方向にスクロールしたかがわかって、その分マウスポインタの位置に足せばよいと考えたのですが、何ピクセル分スクロールしたかをどう調べるかわかりません。OnHScrollとOnVScrollを用いるのかな?と思ったのですが、ヘルプを読んでもイマイチわかりません。何か変数に、どれだけx方向にスクロールしたか?y方向にスクロールしたか、を格納できるような処理があれば教えてください。

  • ビットマップ画像をRGB値でエクセルに出力したいんですが・・・

    ビットマップ画像を取り込んで、1ピクセル毎のRGB値をエクセルに出力したいのですが、そのようなプログラムがわかる方やフリーソフトがありましたら教えて下さい!!! C#っていうのが一応手元にあります。 全くの素人でわかりづらくて申し訳ありません。。

  • CTime.Formatが(NULL)に??

    下記コードのstrをエディットボックスで表示すると(null)と表示されてしまいます。 スレッドを起動した中で、親クラスのstrGetTextにデータを入れておいて、 WM_MKLOGのメッセージで親クラス内のエディットボックスに取得データと時間 を表示させようと思っています。 このとき(%s)の部分が((null))と表示されてしまいます。 すいませんがどなたか教えていただけますでしょうか? { CTime ctime; CString str; ctime = CTime::GetCurrentTime(); // 現在時刻取得 str = ctime.Format("%Y%m%d%H%M%S"); // "YYYYmmddHHMMSS"形式に変換 mycls->strGetText.Format(_T("取得 0x%x (%s)\r\n"), data,str); } PostMessage(mycls->m_hWnd, WM_MKLOG, 0, 0);

  • PHPで画像処理

    あるWebサイトにある画像を定期的に取得して,PHPで画像を処理をしてサーバーに保存するプログラムを作ろうとしています. 処理したい画像の形式はGIFです.特にGIFにこだわりはないので違うフォーマットに変換してから処理してもいいとは思っています. 画像処理と言っても,特定のRGB値のピクセルを画像中から探して,その座標を取得するという単純なものなのです. そんなに難しくなさそうなのですがどうやればいいかわからないのでどなたか教えていただけませんか? GDやImageMagickが使えるのかと思って動かしてみましたが,結局使えそうな関数がみつからず頓挫しています. どなたかよろしくお願いします.

    • 締切済み
    • PHP
  • RGB値の取得について(java)

    お世話になります。 Javaプログラム上で、指定した画像のRBG値を取得するプログラムを作りましたが、得られた値が思わしくありません。最終的にグレースケール化するためにRGB値を取得したいと考えています。 例えば、原画像の左上の画素のRGB値をgimpで確認すると、それぞれ200前後の値が、プログラムで返された値は50前後になってしまいます。 以下にソースを貼ります。とりあえず、100個の画素のRGB値を取得するように設定しています。まだjavaを勉強し始めて日が浅く、低レベルの質問かもしれませんが、解決策を教えていただける方、よろしくお願いいたします。 import java.io.*; import java.lang.*; import java.awt.*; import java.awt.color.ColorSpace; import java.awt.event.*; import javax.swing.*; import javax.imageio.ImageIO; import javax.media.*; import javax.media.control.*; import javax.media.format.*; import javax.media.util.*; import java.awt.image.*; import java.applet.*; import java.awt.image.BufferedImage; import javax.imageio.ImageIO; import java.io.File; import java.io.IOException; import java.util.*; import java.text.*; public class Sample{ static BufferedImage image; public static void main(String[] args){ try { //画像ファイルのデータを読み込む image = ImageIO.read(new File("C:\\QR\\imagefile\\sa.bmp")); } catch (Exception e) { e.printStackTrace(); image = null; } int i,j; int width = image.getWidth(); int height = image.getHeight(); int gray[][] = new int[width][height]; for(i = 0;i < 1 ;i++){ for(j = 0;j < 100 ;j++){ int rgb = image.getRGB(j,i); rgb = 0-rgb; int b = rgb%256; System.out.print(" "+b); int g = (rgb/256)%256; System.out.print(" "+g); int r = rgb/256/256; System.out.print(" "+r); gray[j][i] = (int)((0.299*r + 0.587*g + 0.114*b)); } System.out.println(""); } } }

    • ベストアンサー
    • Java
  • VS2008のMFCのTextOutについて

    毎度、お世話になります。 Windows7でVS2008のMFCのFormView形式でプログラムしています。 OnButton5でTextOut文を実行していますが、最後の『pDC->TextOut(20,80,str)』 だけ表示されます。 但し、前の2個のpDC->TextOutの後にSleep(1)を追加しますと、全て表示できます。 Q1)Sleep(1)を追加しなくて、全てのTextOutが表示できる方法はありますか? ================================ void CMFCTHREADView::OnButton5(){ // TODO: この位置にコントロール通知ハンドラ用のコードを追加してください CDC* pDC=this->GetDC(); int t_endB=999; str.Format("time=%d ms ",t_endB); pDC->TextOut(20,40,str); //これだけ表示されず。 //=============== str.Format("countX=%d ",countX); pDC->TextOut(20,60,str); //これだけ表示されず。 //=============== str.Format("countA=%d countB=%d ",countA,countB); pDC->TextOut(20,80,str); //これだけ表示されます。 //==================== } =================================== 以上、宜しくお願いします。

専門家に質問してみよう