トップ回答者
Bitmap画像を読み込み、Byte配列に入れて関数にポインタで渡す方法

質問
-
プログラム初心者です。
教えてください。よろしくお願いいたします。
VS2005のC#でプログラムを作成しています。
下の関数をC#の Form1のあるボタンを押したタイミングで呼び出しています。(DLL内の関数です)
第1引数は、入力画像の画素データへのポインタを渡します。
第2引数は、出力画像の画素データへのポインタを渡します。あるボタンを押した時に、400(W)×300(H)のBitmap画像またはTIFF画像を先に読み込んで
関数の引数として渡したいのですが、読み込んだ画像をどんなふうに変換して引数に渡すのかが
わかりません。(読み込んだ後、byte型に変換してポインタで渡す)
// 読み込んだ画像(画素)データを渡してある処理をするDLL内の関数(私が作成した関数ではありません)
short result = ImageConvert( ref byte inputImage , out byte outputImage )
※戻り値は、エラーNo.どなたか、ご教授のほどよろしくお願いいたします。
回答
-
// 返ってくるのは 8bpp 固定ってことで良いのかな? 4 バイト境界の考慮も必要だと思うけど……。
よくある方法は、Bitmap オブジェクトの LockBits メソッドを使ってビットマップの生データを表す BitmapData オブジェクトを取得し、その Scan0 プロパティを使用することです。ただし、この値は IntPtr 型(配列の先頭アドレスを指すポインタ)になっています。
その ImageConvert メソッドなるものの(C# 側の)シグネチャは変更できないんでしょうか? C/C++ 的には自然でも、C# 的にはいかんともしがたいのですが。DllImport してる部分を変更しても良いのなら、引数を IntPtr に変更すれば Scan0 の値を直接使えますよ。
-
外池と申します。
確認なんですが・・・、データを渡す相手のDLL内の関数というのは、やはり、C#で作られたものなのでしょうか? (言い換えると、Managedなものでしょうか?)
根本的には、Bitmapクラスには、Lockbitsというメソッドがあり、メモリ上の画像データのバイト列に直接アクセスするためのBitmapData型のオブジェクトを取得することができます。BitmapData型のオブジェクトにはScan0というプロパティーがあって、メモリ上の画像データの先頭へのポインターになっています。
で、BitmapクラスのLockbitsメソッドのドキュメントには、おそらく、ご質問の内容にほぼ合致したサンプルプログラムが載っています。
参考になれば幸いです。
(ホームページを再開しました) -
グレースケールですか。最初から質問文に含めておいて欲しかったですね。
Hongliangさんや外池さんが紹介されている、BitmapクラスのLockBits()メソッドを使うとピクセルデータが取得できますが、この際に指定できるフォーマットはPixelFormatの中から選ぶことになります。
残念ながら1chグレースケール8bppは存在しません。1px 2bytesのFormat16bppGrayScaleが一番適切でしょうか。1バイトずつ不要なデータがあるのでバイト配列を作り直す必要があります。もしくはFormat8bppIndexedを使用して、こちらはパレット形式なので256階調のグレースケールに戻す作業が必要あります。
すべての返信
-
// 返ってくるのは 8bpp 固定ってことで良いのかな? 4 バイト境界の考慮も必要だと思うけど……。
よくある方法は、Bitmap オブジェクトの LockBits メソッドを使ってビットマップの生データを表す BitmapData オブジェクトを取得し、その Scan0 プロパティを使用することです。ただし、この値は IntPtr 型(配列の先頭アドレスを指すポインタ)になっています。
その ImageConvert メソッドなるものの(C# 側の)シグネチャは変更できないんでしょうか? C/C++ 的には自然でも、C# 的にはいかんともしがたいのですが。DllImport してる部分を変更しても良いのなら、引数を IntPtr に変更すれば Scan0 の値を直接使えますよ。
-
外池と申します。
確認なんですが・・・、データを渡す相手のDLL内の関数というのは、やはり、C#で作られたものなのでしょうか? (言い換えると、Managedなものでしょうか?)
根本的には、Bitmapクラスには、Lockbitsというメソッドがあり、メモリ上の画像データのバイト列に直接アクセスするためのBitmapData型のオブジェクトを取得することができます。BitmapData型のオブジェクトにはScan0というプロパティーがあって、メモリ上の画像データの先頭へのポインターになっています。
で、BitmapクラスのLockbitsメソッドのドキュメントには、おそらく、ご質問の内容にほぼ合致したサンプルプログラムが載っています。
参考になれば幸いです。
(ホームページを再開しました) -
-
グレースケールですか。最初から質問文に含めておいて欲しかったですね。
Hongliangさんや外池さんが紹介されている、BitmapクラスのLockBits()メソッドを使うとピクセルデータが取得できますが、この際に指定できるフォーマットはPixelFormatの中から選ぶことになります。
残念ながら1chグレースケール8bppは存在しません。1px 2bytesのFormat16bppGrayScaleが一番適切でしょうか。1バイトずつ不要なデータがあるのでバイト配列を作り直す必要があります。もしくはFormat8bppIndexedを使用して、こちらはパレット形式なので256階調のグレースケールに戻す作業が必要あります。
-
外池です。
呼び出そうとしているDLLの関数が、今回取り扱おうとしているビットマップ画像のフォーマットを直接取り扱えるのでは? と期待したのですが、ダメですかね? 直接取り扱えるなら、BitmapDataのScan0から始まるバイト列をそのまま渡してやれば良いと思った次第です。
ただ、いずれにせよ、フォーマット変換が必要な場合でも、BitmapDataのScan0からデータを読み出すわけですから、使いますよね? そして、Heapメモリを確保してやって、そこにフォーマット変換しながらバイト列を書き出してやって、その上で、DLL関数をHeapに書き込んだバイト列を差すポインタを渡すことになろうかと思います。
(ホームページを再開しました) -
BitmapをImageConverterクラスのConvertToメソッドでByte配列に変換し,byte[] inputImage = new byte[400*300];で確保した配列にループを使って都合のいい形式になるよう変換しながらコピーしたらよいのではないでしょうか。普通に作ったBitmapオブジェクトは1ピクセル4byteだと思いますので,4byteおきにコピーすればよいと思います。私は主にVBを使うので,変なC#のコードかもしれませんが,次のようにしてはどうでしょうか。
Bitmap Image = new Bitmap(400, 300); ImageConverter ImgConv = new ImageConverter(); byte[] ByteArray = (byte[])ImgConv.ConvertTo(Image, typeof(byte[])); byte[] InputImage = new byte[400 * 300]; for (int i = 0; i < 400 * 300; i++) { InputImage[i] = ByteArray[i * 4]; }