DOBON.NET DOBON.NETプログラミング掲示板過去ログ

バイト配列->Image変換での「パラメータが有効ではありません」エラー

環境/言語:[WindowsXP,C#,,NetFrameworkVer=4]
分類:[.NET]

いつも参考にさせて頂いております。
C#より呼び出したアンマネージDLLからバイト配列の画像データを受け取った後、ImageConverterを使用してImageに変換しようとすると、「'System.ArgumentException' のハンドルされていない例外が System.Drawing.dll で発生しました。追加情報: 使用されたパラメーターが有効ではありません。」のエラーが発生します。

アンマネージDLL側の宣言は以下のようにBYTEのポインタを受け取るようになっていますので呼び出す前に「byte[] DataBuf = new byte[IMGXSIZE * IMGYSIZE];」でバッファを確保しています。
このDataBufはメンバ変数としています。
【アンマネージDLL側宣言 void GetImage(BYTE* pImg)】
C#側ではDllImportを使用しており以下のようにしています。
【C#側DLL宣言 void GetImage(byte[] pImg)】

デバッグで確認したところ、バイト配列には値が格納されているようです。
やはりバイト配列の受け渡しに問題があるのでしょうか?
何かご存知の方がいらっしゃいましたら宜しくお願い致します。
■No28243に返信(pooさんの記事)
> バイト配列の画像データを受け取った後、ImageConverterを使用してImageに変換

その画像データが、どのような形式のバイナリになっているのか分かりませんが、
バイト配列を MemoryStream に書き込んでから、それを
Image.FromStream メソッド経由で読み込むことはできないでしょうか?
バイト配列のサイズが width * height ってことは、GetImage で手に入るのは 8bpp の画像ですか? それなら普通パレット情報も必要ですけど。あと単に width * height だと 4 バイト境界は大丈夫かしら。
ImageConverter はファイル形式のデータからのロードしか対応してないはずなので、生のピクセルデータを渡してもロードできません。
それに生のピクセルデータを画像として解釈するのに必要な、縦横ピクセル数、ピクセル形式といった情報が足りませんから、どのみちそのままでは何ともしがたいところです。
それらの情報が分かっているなら、Bitmap(Int32, Int32, PixelFormat) コンストラクタを使って Bitmap オブジェクトを生成し、LockBits でロックして、BitmapData の Scan0 に Masrhal.Copy するのが楽でしょうね。
魔界の仮面弁士さん、お世話になります。
実は以下のようにも試してみたのですが、Image.FromStreamメソッドからImageオブジェクトを作成する所で同じエラーでストップしてしまいます。
MemoryStream ms = new MemoryStream(DataBuf);
Image img = Image.FromStream(ms);

DLLは提供されたもので、説明としては「バッファに画像データを格納します」
との記述があり、使用例でもバッファを確保して呼び出しているだけでしたので
どのような形式のバイナリになっているかは分からないんです。
この場合、情報が足りないという事で画像としての表示は出来ないと
いうことになるのでしょうか?
Hongliangさん、お世話になります。
先程、魔界の仮面弁士さんにも御報告したのですが、DLLは提供されたもので、
どのような形式のバイナリになっているかは分からないんです・・・。
■No28247に返信(pooさんの記事)
> 先程、魔界の仮面弁士さんにも御報告したのですが、DLLは提供されたもので、
> どのような形式のバイナリになっているかは分からないんです・・・。

仕様は提供元に問い合わせてください。 (有名な DLL なのでしょうか?)

DLL を持っている poo さんに分からないデータ仕様が、掲示板の向こうにいる
我々に分かるはずもありませんし(予想することはできますが)。


とりあえず、バイナリの先頭にビットマップヘッダーを付けてから読み込むか、
もしくは Hongliang さんの案のように、Scan0 に Marshal.Copy してみては
如何でしょうか。
■No28248に追記(魔界の仮面弁士の記事)
> とりあえず、バイナリの先頭にビットマップヘッダーを付けてから読み込むか、
> もしくは Hongliang さんの案のように、Scan0 に Marshal.Copy してみては
> 如何でしょうか。

DLL が無いので試しようが無いのですが、例えば
 void GetImage(IntPtr pImg)
な宣言を作っておいて、こんな感じでどうでしょう。


// フォーマットが分からないので、グレースケール画像と仮定
PixelFormat bmpPixelFormat = PixelFormat.Format8bppIndexed;
Bitmap bmp = new Bitmap(IMGXSIZE, IMGYSIZE, bmpPixelFormat);
ColorPalette palette = bmp.Palette; // パレットのコピーを取得
Color[] entries = palette.Entries;
for (int c = 0; c < entries.Length; c++)
  entries[c] = Color.FromArgb(c, c, c); // グレースケールなパレットを作る
bmp.Palette = palette;  // 作成したパレットを書き戻す

// ロック
Rectangle rect = new Rectangle(Point.Empty, bmp.Size);
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.WriteOnly, bmpPixelFormat);

GetImage(bmpData.Scan0);    // 画像取得APIの呼び出し

// アンロック
bmp.UnlockBits(bmpData);
pictureBox1.Image = bmp;
魔界の仮面弁士さん、お世話になります。
魔界の仮面弁士さんとHongliangさんがおっしゃったように、
「Bitmapコンストラクタを使ってBitmap オブジェクトを生成->LockBitsでロック
して、BitmapDataのScan0にMasrhal.Copy」で実現する事が出来ました!
フォーマットが分からなかったので、とりあえずFormat8bppIndexedとしました。
フォーマットについては、提供先に問い合わせてみます。
出来た!と思って御礼を書こうと思ったら、魔界の仮面弁士さんが
コードまで記述して下さってました。ご丁寧にありがとうございます。
以下、参考までにコードを記述しておきます。

Bitmap bmp = new Bitmap(IMGXSIZE, IMGYSIZE, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
// Pixelデータにアクセスする領域を設定する
Rectangle rect = new Rectangle(0, 0, IMGXSIZE, IMGYSIZE);
// BitmapDataクラスのインスタンスを生成し、LockBits関数でBitmapをLockする。
System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
// BitmapData内の各種項目を抜き出す。
IntPtr ptr = bmpData.Scan0;

Marshal.Copy(DataBuf, 0, ptr, DataBuf.Length);
// Bitmapのロックを解除する。
bmp.UnlockBits(bmpData);

以上、魔界の仮面弁士さん、Hongliangさん、本当にありがとうございました。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板