GDIによるメモリ上からの画像データ読み込みに関するエラーについて

このQ&Aのポイント
  • VC++/CLIの開発環境で、unsigned char型の配列からGdiplus::Bitmap型を作成する際に発生するエラーについて解説します。
  • 画像データのバイト数を利用してメモリを確保し、配列からGdiplus::Bitmap型を作成するコードを実装しましたが、CopyMemory時にエラーが発生します。
  • 同様の画像データ配列を使用して、System::Drawing::Bitmap型に格納した場合には正常に表示されることを確認していますが、メモリ関連の問題が原因でGdiplus::Bitmap型には正しく変換できません。
回答を見る
  • ベストアンサー

GDIによるメモリ上からの画像データ読み込みに関して

開発環境はVC++/CLIです。 アンマネージ型のCOMオブジェクトの扱いで困っております。 JPEGやPNGなどの画像データを保存したunsigned char型配列から、Gdiplus::Bitmap型を作ろうとしています。 unsigned int imagesize; // 画像のバイト数が格納されている array<unsigned char>^ image = gcnew array<unsigned char>(imagesize); //画像のデータが格納されている 事前にこのようなデータを用意し、以下のように記述しました。 HGLOBAL hResourceBuffer = GlobalAlloc(GMEM_MOVEABLE , imagesize); void* pResourceBuffer = GlobalLock(hResourceBuffer); CopyMemory(pResourceBuffer, &image, imagesize); IStream* pIStream = NULL; CreateStreamOnHGlobal(hResourceBuffer, TRUE, &pIStream) data->bmp = new Gdiplus::Bitmap(pIStream); pIStream->Release(); GlobalUnlock(hResourceBuffer); GlobalFree(hResourceBuffer); しかしながら、上記のコードだと、CopyMemory時に「保護されたメモリに書き込もうとした」といったエラーが発生します。 Webで色々検索しましたが、全て似たような記述で動作していました。 何が問題なのでしょうか? 尚、上記と同様の画像データ配列を使って、System::Drawing::Bitmap型に格納したとき、画像が正常に表示されることを確認しております。 画像のバイト数に於いても取得したデータに間違いはなく、やはりメモリ関連の問題だと思うのですが…。 MemoryStream^ memst = gcnew MemoryStream(image); Bitmap^ bmp = gcnew Bitmap(memst); memst->Close(); Graphics^ im = pictureBox1->CreateGraphics(); im->DrawImage(bmp, 0, 0, 100, 100); delete bmp;

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

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

マネージドのポインタをそのままアンマネージ関数に渡しているのが原因ではないでしょうか pin_ptr<unsigned char>p = &image[0]; unsigned char *pbuf = p; CopyMemory( pResourceBuffer, p, imagesize ); といった具合で出来ると思います

NTKEXE
質問者

お礼

マネージ型のポインタと、アンマネージ型のポインタの互換性は無いのですか…。 悩んでいたところを助けていただき、どうも有難う御座いました。

関連するQ&A

  • 二つの画像を並べて表示

    ひとりで独学でVC++2005を勉強してるのですが、pictureBoxでつまずいます。初心者ですがわかりやすく教えて頂けたらと思います。 やりたい事はタイトル通り、画像を単純に並べて表示したいだけなのですが、WEB場で検索しても的を得た答えが見つかりませんでした。 二つの画像を読み込んで、新しいBitmapデータに書き込めばいいのかと思ったのですが、どうやっていいのか見当が付きません。 Bitmap^ bmpA = gcnew Bitmap("testAA.bmp"); Bitmap^ bmpB = gcnew Bitmap("testAB.bmp"); int w = bmpA->Width + bmpB->Width; int h = bmpA->Height + bmpB->Height; Bitmap^ bmp = gcnew Bitmap(w,h); //ここで何やっていいかわかりません。 pictureBox1->Image = bmp; 最終的にはタイル的にどんどん並べて行きたいと考えています。 助言の方よろしくお願いします。

  • ビットマップ画像の色取得のご相談

    ビットマップ画像の色取得のご相談 WinXPでVC++2008ExpressEditionを使用してWinアプリケーションで画像処理をしようとしています.そこで,現在ビットマップのカラー画像を二値化(閾値判別分析法)するために,各色の抽出してグレースケール化を試みているのですが,色々試したのですがエラーがとれないので,もし間違い等ありましたら,ご助言いただけたらなと思います.何卒よろしくお願いいたします. 【エラー内容】 'System.ArgumentOutOfRangeException' のハンドルされていない例外が System.Drawing.dll で発生しました。追加情報: パラメータは正の値で、高さより小さい値指定しなければなりません。 【流れ】(ボタンを押したら以下三つを実行) 1.ファイルの読み込みピクチャーボックスに表示 2. ビットマップ画像の色の抽出 3. グレースケール化 /*ここから*/ OpenFileDialog^ OpenDlg = gcnew OpenFileDialog; //ファイルを開くダイアログ OpenDlg->Filter ="画像ファイル(*.bmp,*.jpg,*.jpeg,*.png,*.tif,*.tiff,*.ico)|*.bmp;*.jpg;*.jpeg;*.png;*.tif;*.tiff;*.ico"; OpenDlg->ShowDialog(); //ダイアログの表示 if (OpenDlg->FileName == "") { //ファイル名が指定されなかった場合 return; } Bitmap^ bmp; bmp = gcnew Bitmap(OpenDlg->FileName); //Bitmapをファイルより作成 pictureBox1->Image = bmp; //ピクチャボックスへ画像の表示 Bitmap^ bmp1 = gcnew Bitmap(pictureBox1->Image); unsigned char Image_in[480][640][3]; //Image_in[Y][X][3] unsigned char (*Gray)[640] = new unsigned char[480][640]; //(*Gray)[X] = new unsigned char[Y][X] double Y; int i, j; //iがY方向,jがX方向 Color color1; unsigned char R,G,B; // 赤,緑,青成分の抽出 for( i = 1; i < 480; i++) //Y方向 { for( j = 1; j < 640; j++) //X方向 { ////////////////////////////////////////////// /*この辺がエラーの原因だと思うのですが・・・*/ color1 = bmp->GetPixel(i,j); R = color1.R; G = color1.G; B = color1.B; Image_in[i][j][0] = R; Image_in[i][j][1] = G; Image_in[i][j][2] = B; } } //RGB to Gray カラー画像をグレースケール化 for( i = 0; i < 480; i++) //Y方向 { for( j = 0; j < 640; j++) //X方向 { Y = 0.299*Image_in[i][j][0] + 0.587*Image_in[i][j][1] + 0.114*Image_in[i][j][2]; Gray[i][j] = Y; } }

  • Bitmapデータ型の画像幅の拡大

    現在Visual studio 2005のフォームアプリケーションでプログラミグを行っている者です。以下のプログラムの中に画像の幅であるw,hという変数があるのですが、私の作ろうとしている画像処理の関係上、この画像データの幅を倍にしたいです(例:3*w,3*h)。しかし、変数宣言(例:int 3*w)やfor文の中で倍にしようとしても、ビルドはできるものの"アプリケーションのコンポーネントで、ハンドルされていない例外が発生しました。・・・パラメータは正の値で、高さより小さい値指定しなければなりません。"とでて、実行できません。おそらく倍にしてあげたとこで、倍になった部分の画像データがわからないためこういったエラーが出てしまうのだと考えています。どうにかして、BITMAPデータ型で読み込んだ画像の幅を倍の数値を得たいのですが、エラーのでないようにするためにはどのようにしてあげればいいのでしょうか?わかる方がいたらよろしくお願いします。 プログラムは以下のとおりです。 #pragma once // 省略 // } #pragma endregion private:Bitmap^ pic; private:array< Bitmap^>^ bmp; // 原画像格納 // private: System::Void Form1_Load(System::Object^ sender, System::EventArgs^ e) { bmp[0] = gcnew Bitmap("画像ファイル1",true); pictureBox1->Image = bmp[0]; pic = gcnew Bitmap("画像ファイル2",true); } private: System::Void button2_Click(System::Object^ sender, System::EventArgs^ e) { int x,y,a,b; /* x,y: 画像の座標 a,b: 複合画像の座標 */ int w = pic->Width; //ここでpic->Width*3としてもエラー /* 複合画像の横幅 */ int h = pic->Height; /* 複合画像の縦幅 */ x = 0; y = 0; a = 0; b = 0; for(y = 0; y < h; y++){ //ここで3*hとしてもエラー for(x = 0; x < w; x+=3){ pic->SetPixel( x, y, bmp[1]->GetPixel( x, y ) ); } } pictureBox2->Image = pic; } }; }

  • VC++2005で高解像度画像の取り扱い。

    いつも質問ばかりで申し訳ないのですが、独学で勉強していて聞ける人がいないので協力して頂けたらと思います。 VC++2005のOS Windows32bit環境で高解像度(10240pixel × 10240pixel以上)の画像を取り扱うにはどうしたら良いのでしょうか? Bitmap^ bmp = gcnew Bitmap( 10240, 10240 ); ~省略 pictureBox1->Image = bmp ; のような書き方をして、エラーが出てる場所は Bitmap^ bmp = gcnew Bitmap( 10240, 10240 ); の部分です。 Bitmap^ bmp = gcnew Bitmap( 5120, 5120 ); だと平気です。 メモリーが不足しています。と警告が出てるのですが、はやり32bit環境では限界なのでしょうか? PCには8GB(うち5GB、RAMディスク)のメモリーを積んでますが、そう言う問題でもないですよね。 PhotoShopやAftterEffectとかはかなりの高解像度でも表示出来てるので不可能では無いとは思うのですが、初心者レベルでは無謀なのでしょうか? 何か打開策があれば教えて頂けたら助かります。 よろしくお願いします。

  • VC++6.0 ライブラリを用いたTIFF画像の読み込み

    タイトル通りのことを行おうと、libtiffなるTIFFのライブラリをWEBより落としてきてVC++6.0でプログラミングをしています。 TIFF画像をBITMAP画像にするべく以下のようにソースを記述しました。 HDIB CTiff::LoadTIFFinDIB(CString path) { TIFF *image; unsigned long imageLength; unsigned long imageWidth; unsigned int BitsPerSample; unsigned long LineSize; unsigned int SamplePerPixel; unsigned long RowsPerStrip; int PhotometricInterpretation; long nrow; unsigned long row; char *buf; LPBITMAPINFOHEADER lpDIB; HDIB hDIB; char *lpBits; HGLOBAL hStrip; int i,l; int Align; CSize size; char *p = new char[path.GetLength()+1]; strcpy(p, path); image = TIFFOpen(p, "r"); delete [] p; if(!image) goto TiffOpenError; ・・・・・・・・・・・・・・・・・・・・・・ ・・・・・・・・・・・・・・・・・・・・・・ ところが、TIFFOpenで絶対Access Violationのエラーで落ちてしまいます。 この原因がわかる方、ご教授ください。 ちなみにOSはWindows2000 Proです。

  • とあるプログラムを教えてほしいのですが

    はじめましてこんばんは hommado と申します。 じつは先週の授業でこんな問題を出されたんですがもしできたらとあるプログラムを教えてほしいのです。 問題は 「キーボードから何階調にするのかを読み込むことで、入力画像LAX.bmpを任意の階調数に変換する」という プログラムです。 一応下に素体のプログラムをおいたんで、其のプログラムに何か付け足す感じでお願いします。「/*******↓基本的には、この範囲に画像処理プログラムを書く****/」から 「 /********↑**************************************************/」の中にプログラムを書くかんじなのでもし分かったら教えてください。お願いします。 あと何か他の所に付け足すようなところがあったら教えてください ここから元のプログラム // Bitmapファイルを読み込んで, // 別のファイルに出力するだけのプログラムです #include<stdio.h> #define XSIZE 256 /* 画像の横サイズ*/ #define YSIZE 256 /* 画像の縦サイズ*/ void main(void) { int x,y; char fni[40],fno[40]; /* 入力ファイルと出力ファイルの名前を格納するための配列*/ unsigned char head[1078],buf[YSIZE][XSIZE]; /* 入力ファイル(ビットマップファイル)のヘッダ情報と輝度値情報を格納するための配列*/ unsigned char in_image[YSIZE][XSIZE]; /* 入力画像の画素の輝度値を格納するための配列*/ unsigned char out_image[YSIZE][XSIZE]; /* 出力画像の画素の輝度値を格納するための配列*/ FILE *fp,*fp2; /* ファイルポインタ*/ printf("ファイル名を入れてください:"); scanf("%s",fni); fp=fopen(fni,"rb"); /* 読み込み& バイナリモードでオープンする*/ /* 配列head にビットマップファイルのヘッダ情報が格納されます*/ fread(head,sizeof(unsigned char),1078,fp); /* unsigned char 型のデータ×個を配列head に読み込む*/ /* 配列buf にビットマップファイルの輝度値情報が格納されます*/ fread(buf,sizeof(unsigned char),XSIZE*YSIZE,fp); for(y=0;y<YSIZE;y++){ for(x=0;x<XSIZE;x++){ in_image[y][x]=buf[y][x]; /* 画像の左下の画素が座標buf[0][0] です*/ } } printf("読み込み終了しました!\n"); printf("出力ファイル名を入れてください:"); scanf("%s",fno); /*******↓基本的には、この範囲に画像処理プログラムを書く****/       ここにプログラムを書いてください!      (もしここ以外で、他の所で付け足すプログラムがあったら何行目に       何を書くのかも教えてください) /********↑**************************************************/ fp2=fopen(fno,"wb"); /* 書き込み& バイナリモードでオープンする*/ /* 配列head の内容を出力ファイルに書き込む*/ fwrite(head,sizeof(unsigned char),1078,fp2); /* 配列out_image の内容を出力ファイルに書き込む*/ fwrite(out_image,sizeof(unsigned char),XSIZE*YSIZE,fp2); fclose(fp); /* ファイルをクローズする*/ fclose(fp2); /* ファイルをクローズする*/ printf("作業完了!\n"); }

  • VBAで画像処理

    以下のプログラムは、C言語で記載されたプラグラムで、画像に鏡映変換を施すプログラムです。 これを、ExcelVBAでやりたいとおもうのですが、 特に、一度Excelのセルに読みだして、そこで数字を処理してトしたいと考えています。 (単に画像Excelの機能だけをつかってを反転するだけならば、たいしたことではないので 自分で判るのですが、一度配列なりセルに画素情報を読み込む方法は、見当もつかないので 知りたいと思っています。) ということで、特に以下の(1)、(2)に注意して、ExcelVBAで下記のプログラムを変換する方法を教えてください。よろしくお願いします。 (1)特に、以下の部分の記述がどう変化するか、教えてください。 void main(void) { char input[100], output[100]; printf("入力画像ファイル名(input.bmp):"); scanf("%s", input); printf("出力画像ファイル名(output.bmp):"); scanf("%s", output); /* 画像の入力 */ readBmp(input, image_in); /* RGB24ビットカラーBMP画像を配列に格納 */ /* 画像処理 */ mirror(image_in, image_out); /* 鏡像を作る */ /* 画像の出力 */ writeBmp(image_out, output); /* RGB24ビット画像をファイルに出力 */ } (2)特に、一度BMPの画素情報を、Excelのセルに読み込めるようにしていただけると助かります。 ========= #include <stdio.h> #include <stdlib.h> /* 最大画像サイズ */ #define Y_SIZE 1280 #define X_SIZE 1280 /* BMPファイル用 */ typedef short INT2; typedef long INT4; INT2 bfType; INT4 bfSize; INT2 bfReserved1, bfReserved2; INT4 bfOffBits; INT4 biSize, biWidth, biHeight; INT2 biPlanes, biBitCount; INT4 biCompression, biSizeImage, biXPelsPerMeter, biYPelsPerMeter, biClrUsed, biClrImportant; #define HIGH 255 /* 2値画像の白 */ #define LOW 0 /* 2値画像の黒 */ #define LEVEL 256 /* 濃度レベル数 */ unsigned char image_in[Y_SIZE][X_SIZE][3]; /* 入力カラー画像配列 */ unsigned char image_out[Y_SIZE][X_SIZE][3]; /* 出力カラー画像配列 */ /* 鏡像を作る(左右逆) */ void mirror( unsigned char in[Y_SIZE][X_SIZE][3], unsigned char out[Y_SIZE][X_SIZE][3]) { int i,j,k; for (i=0; i<biHeight; i++) for (j=0; j<biWidth; j++) for (k=0; k<3; k++) out[i][j][k] = in[i][biWidth-1-j][k]; } void main(void) { char input[100], output[100]; printf("入力画像ファイル名(input.bmp):"); scanf("%s", input); printf("出力画像ファイル名(output.bmp):"); scanf("%s", output); /* 画像の入力 */ readBmp(input, image_in); /* RGB24ビットカラーBMP画像を配列に格納 */ /* 画像処理 */ mirror(image_in, image_out); /* 鏡像を作る */ /* 画像の出力 */ writeBmp(image_out, output); /* RGB24ビット画像をファイルに出力 */ }

  • フォーム間のデータ受け渡し

    現在VIsual Studio 2005のフォームアプリケーションを使ってプログラミングしています。ボタンを押すことで新たな子フォームを作成し、親フォームから子フォームへグローバル関数で宣言しているbmp[],picture[],red[]などのデータを渡したいのですがどうすればいいのかわからず困っております。子フォームから親フォームへテキストボックスなどの値を渡す方法などはわかったのですが、それをどう応用していいのかもわからない状況です。最終的には親フォームのbmp[0]におけるred[0]が1(画像処理されている)なら子フォームでbmp[0]を表示させたいと思っています。わかる方がいましたらどうかご教授ください。よろしくお願いします。以下がプログラムとなっております。 *** 親フォーム *** #pragma once #include "pic2.h" namespace pic { using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; using namespace System::IO; // 省略 // public ref class Form1 : public System::Windows::Forms::Form { public: Form1(void) { InitializeComponent(); // //TODO: ここにコンストラクタ コードを追加します // bmp = nullptr; Array::Resize( bmp, 20 ); Array::Resize( picture, 20 ); Array::Resize( bmpr, 20 ); this->red = gcnew array<int>(20); } // 省略 // private: System::Windows::Forms::PictureBox^ pictureBox1; private:array< Bitmap^>^ bmp; // 原画像格納 // private:array< Bitmap^>^ bmpr; // 処理画像格納 // private:array< PictureBox^>^ picture; private:array< int>^ red; // 処理:1 不処理:0 // // 省略 // #pragma endregion private: System::Void Form1_Load(System::Object^ sender, System::EventArgs^ e) { bmp[0] = gcnew Bitmap("ファイル名",true); } private: System::Void button1_Click_1(System::Object^ sender, System::EventArgs^ e) { pic2 ^p2 = gcnew pic2(); p2->ShowDialog();     /* ボタンを押すことで新たなフォーム作成 */ } private: System::Void pictureBox1_Click(System::Object^ sender, System::EventArgs^ e) { int x,y; int w = bmpr[0]->Width; int h = bmpr[0]->Height; if(red[0] == 1){ bmp[0] = gcnew Bitmap("ファイル名",true); pictureBox1->Image = bmp[0]; red[0] = 0; return; } if(red[0] == 0){ // 画像処理 // pictureBox1->Image = bmpr[0]; red[0] = 1; } } }; *** 子フォーム *** #pragma once //#include "Form1.h" #include "pic3.h" using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; namespace pic { /// <summary> /// pic2 の概要 /// public ref class pic2 : public System::Windows::Forms::Form { public: pic2(void) { InitializeComponent(); // //TODO: ここにコンストラクタ コードを追加します // } // 省略 // } #pragma endregion private: System::Void pic2_Load(System::Object^ sender, System::EventArgs^ e) { if(親フォームのred[0]==1ならば){ pictureBox1->Image = bmp[0] } } private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) {   // 新たな子フォームpic3作成 // } private: System::Void button2_Click(System::Object^ sender, System::EventArgs^ e) { this->Close(); } }; }

  • VC++6.0でGDI+利用してメモリリーク?

    Microsoft VC++6.0 で、画像を表示するアプリケーションを作っていて、GDI+ で様々な画像を呼び出せるようにしています。 Gdiplus::Bitmap* pGdiBitmap = Gdiplus::Bitmap::FromFile( ファイル名 ) ; で呼び出し、 HBITMAP hBitmap ; pGdiBitmap->GetHBITMAP(color , hBitmap) によりHBITMAPを取得し、 CBitmap* pBitmap をクラス内に用意し、BitBlt() 関数で画像データを pBitmap にコピーし、その後 delete pGdiBitmap で削除しています。CBitmap* pBitmap にデータを保持していればいいので。 しかし、ファイルを「開く→閉じる」を繰り返し20回や30回行うと、画像を読み込めなくなりました。 最初に調べたのが、 「pGdiBitmap を読み込んだ後に pGdiBitmap->GetHBITMAP() を実行すると戻り値が、 OutOfMemory = 3」 でした。 そこで、タスクマネージャでメモリを見ながら画像を何度も開くと、開いた画像を閉じてもメモリはそんぽままで、画像を開く度にメモリが増え、150Mとかになって、メモリ不足になることがわかりました。  これは、delete pGdiBitmap ではダメと言うことですか?それとも、プログラムがおかしいでしょうか。 Windows7 , Microsoft VC++6.0 Professional Edition , (Microsoft Platform SDK) class CMyPicture { public (コンストラクタなど) void LoadPicture(LPCTSTR lpszFilename); protected: CBitmap* m_pBitmap ; } void CMyPicture::LoadPicture(LPCTSTR lpszFilename) { HBITMAP hBitmap ; WCHAR wPath[MAX_PATH] ; ※ファイル名 lpszFilename をMultiByteToWideChar()で変換済み Gdiplus::Bitmap* pGdiBitmap = Bitmap::FromFile(wPath , TRUE) ; Status r = pGdiBitmap->GetHBITMAP( Color(0,0,0,0) , &hBitmap) ; ※ CDC dcMemory などを用意してBitBlt()で hBitmap から(CBitmap*)m_pBitmap に転送 delete pGdiBitmap ; } こんな感じです。 画像を開いて表示すること自体は成功していますが、画像を閉じてもメモリをどんどん使ってしまいます。 根本的に何か間違っていますか?GDI+をVC++6.0で使っていては限界なのでしょうか。 どなたか、教えていただけると助かります。

  • プログラムで、bmpをjpgに替えたらエラーして困ってます・・・。

    マネージ形式でbmpの画像の情報をlabel1などで表示できたんですけど、ファイル名の形式をjpgにしたら、情報が表示できずにエラーしました。 どこがまちがってるか、教えてください。 エラーした、プログラムをのせるんでアドバイスお願いします。 private: System::Void button3_Click(System::Object^ sender, System::EventArgs^ e) {   Bitmap^image2; image2 = gcnew Bitmap( "画像名.jpg");   pictureBox1->Image = image2;        label3->Text = String::Format("幅(width) 高さ(height): {0}", image2->Size); } この、プログラムで、bmgの時は、image2 = gcnew Bitmap( "画像名.bmp");で、組んでエラーはなっかたんですけど、jpgの時は、image2 = gcnew Bitmap( "画像名.jpg");でしたら、エラーします。 MSDNで探してもわからず、色々試したんですがエラーしかでません。 ネットで検索してもあまりいい例が見つかりませんでした。適切なアドバイスお願いします。