私はVB.NETのカメラ(この場合はモノクローム)で作業しています。カメラのAPIはC++で、メーカーはラッパーを提供しています。これらの場合に典型的なように、画像はByte
配列として返されます。私の場合は、1ピクセルあたり8ビットなので、元に戻すためのビットパックはありません。バイト配列をビットマップインスタンス(.NET)に変換するにはどうすればよいですか?
私はflabbergastedでしたが、このタイプのものはほとんどサポートしていません。オンラインでの検索私はさまざまなトピックを見つけましたが、多くの場合、画像データに対応するバイト配列を持つ人がいます(つまり、イメージファイルをバイト配列に読み込んだ後、ヘッダーとすべてを含む)。
この場合、生のピクセル値の配列を、特に画面上に表示できるものに変換する必要があります。
.NETに組み込まれている8bpp形式のインデックスが作成されているため(パレットが必要です)、パレットの設定がサポートされていないため、これを行うための組み込み方法はありません。再び、これは本当に私を驚かせました。私が見つけた最も有望な話題はThisです。
これを行うために私が見つけた唯一の方法は、Bitmap
を作成し、ピクセル値をピクセル単位でコピーすることです。私の場合は、SetPixel
を使用すると、8 MPixelの画像が秒になりました。が最も速いi7になりました。
SetPixel
の代わりにLockBits
を使用して、イメージを構成する(アンマネージド)バイト配列にアクセスすることができます。 .NETで配列を操作し、それを管理されていない空間にコピーする必要があるので、無駄に思えます。私はすでに元の画像データを1回コピーしていますので(元の画像は再利用できるようになります)、SetPixel
を使用するよりもはるかに高速ですが、これはまだ無駄です。ここで
私はVBのコードは次のとおりです。
8メガピクセルの画像の場合Public Function GetBitmap(ByRef TheBitmap As System.Drawing.Bitmap) As Integer
Dim ImageData() As Byte
Dim TheWidth, TheHeight As Integer
'I've omitted the code here (too specific for this question) which allocates
'ImageData and then uses Array.Copy to store a copy of the pixel values
'in it. It also assigns the proper values to TheWidth and TheHeight.
TheBitmap = New System.Drawing.Bitmap(TheWidth, TheHeight, System.Drawing.Imaging.PixelFormat.Format24bppRgb)
Dim TheData As System.Drawing.Imaging.BitmapData = TheBitmap.LockBits(
New System.Drawing.Rectangle(0, 0, TheWidth, TheHeight), Drawing.Imaging.ImageLockMode.ReadWrite, TheBitmap.PixelFormat)
Dim Image24(Math.Abs(TheData.Stride) * TheHeight) As Byte
'Then we have two choices: to do it in parallel or not. I tried both; the sequential version is commented out below.
System.Threading.Tasks.Parallel.For(0, ImageData.Length, Sub(i)
Image24(i * 3 + 0) = ImageData(i)
Image24(i * 3 + 1) = ImageData(i)
Image24(i * 3 + 2) = ImageData(i)
End Sub)
'Dim i As Integer
'For i = 0 To ImageData.Length - 1
' Image24(i * 3 + 0) = ImageData(i)
' Image24(i * 3 + 1) = ImageData(i)
' Image24(i * 3 + 2) = ImageData(i)
'Next
System.Runtime.InteropServices.Marshal.Copy(Image24, 0, TheData.Scan0, Image24.Length)
TheBitmap.UnlockBits(TheData)
'The above based on
'http://msdn.microsoft.com/en-us/library/system.drawing.imaging.bitmapdata.aspx
Return 1
End Function
シーケンシャルバージョンは、最大TheBitmap
を作成(および含む)TheBitmap.UnlockBits()
への呼び出しから約220ミリ秒を消費します。パラレルバージョンはずっと矛盾していますが、60〜80ミリ秒の範囲です。私は4台のカメラから同時に取得し、余裕を持ってディスクにストリーミングすることを考慮して、これはかなり残念です。 APIにはC++のサンプルコードが付属しており、4つのカメラすべてからフルフレームレート(17fps)で同時に表示できます。もちろん、を扱うことはありません。GDIは生のピクセル値の配列にアクセスするからです。
要約すると、単にピクセル値の配列を取得してBitmap
インスタンスに「埋め込む」直接の方法がないため、管理対象/非管理バリアを渡る必要があります。この場合、8bppは表示可能な形式ではないため、インデックス付きイメージでパレットを「設定」できないため、ピクセル値を24bpp Bitmap
にコピーしています。
良い方法はありますか?
FROMSTREAMを試してみては、ストリームは、私は私の質問でこれを説明しようとした---だけではなく、ピクセル値、(ヘッダ情報付き)「完全な」ビットマップが含まれている必要があります。 – pelesl
bmpヘッダー情報を参照して更新されました。 fromstreamを使用して128バイトをあらかじめ追加しておくと、処理速度が大幅に向上します。 –
これは試してみる価値があります。特に、ビットマップヘッダーの前にグレースケールパレットを追加すると、ピクセル値を3回コピーして24 bppにする必要はありません。 – pelesl