3

8 ビットまたは 16 ビットのグレースケール ピクセル データを、.NET フレームワークがサポートできるファイル形式に変換できる必要があります。

私が利用できるデータは、幅、高さ、方向 (左下) と、1 ピクセルあたり 2 バイトにパックされた 4096 階調のグレー (12 ビット解像度) としてのピクセル形式です。

たとえば、各ピクセルの範囲は 0 から 4096 で、各ピクセルは 2 バイトです。

Bitmap コンストラクターで PixelFormat.Format16bppGrayScale を使用しようとしましたが、GDI+ 例外がスローされます。私が読んだすべてのことは、この形式はサポートされておらず、MSDN が間違っていると言っています。

このピクセル バッファーを .NET ビットマップ形式 (Format32bppArgb など) に変換し、画質の低下をできるだけ少なくしたいと考えています。

誰でも方法を知っていますか?

4

3 に答える 3

6

以下の例を参照してください。ルックアップテーブル(LUT)を事前に計算し、それを使用して各ピクセルを変換します。このバージョンは12ビットの場合をカバーしています。8ビットの場合、コードは非常に似ていますが、ピクセル形式間で一般化することは困難です。

12ビットGSから事実上8ビットGSに変換すると、データが失われます。ただし、LUTテーブルを調整して、より狭い範囲の入力値に焦点を合わせ、コントラストを高めることができます(例:DICOMウィンドウの中心/ウィンドウの幅)。

class Program
{
    static void Main( string[] args )
    {
        // Test driver - create a Wedge, convert to Bitmap, save to file
        //
        int width = 4095;
        int height = 1200;
        int bits = 12;

        byte[] wedge = Wedge( width, height, bits );

        Bitmap bmp = Convert( wedge, width, height, bits );

        string file = "wedge.png";

        bmp.Save( file );

        Process.Start( file );
    }

    static Bitmap Convert( byte[] input, int width, int height, int bits )
    {
        // Convert byte buffer (2 bytes per pixel) to 32-bit ARGB bitmap

        var bitmap = new Bitmap( width, height, PixelFormat.Format32bppArgb );

        var rect = new Rectangle( 0, 0, width, height );

        var lut = CreateLut( bits );

        var bitmap_data = bitmap.LockBits( rect, ImageLockMode.WriteOnly, bitmap.PixelFormat );

        ConvertCore( width, height, bits, input, bitmap_data, lut );

        bitmap.UnlockBits( bitmap_data );

        return bitmap;
    }

    static unsafe void ConvertCore( int width, int height, int bits, byte[] input, BitmapData output, uint[] lut )
    {
        // Copy pixels from input to output, applying LUT

        ushort mask = (ushort)( ( 1 << bits ) - 1 );

        int in_stride = output.Stride;
        int out_stride = width * 2;

        byte* out_data = (byte*)output.Scan0;

        fixed ( byte* in_data = input )
        {
            for ( int y = 0; y < height; y++ )
            {
                uint* out_row = (uint*)( out_data + ( y * in_stride ) );

                ushort* in_row = (ushort*)( in_data + ( y * out_stride ) );

                for ( int x = 0; x < width; x++ )
                {
                    ushort in_pixel = (ushort)( in_row[ x ] & mask );

                    out_row[ x ] = lut[ in_pixel ];
                }
            }
        }
    }

    static uint[] CreateLut( int bits )
    {
        // Create a linear LUT to convert from grayscale to ARGB

        int max_input = 1 << bits;

        uint[] lut = new uint[ max_input ];

        for ( int i = 0; i < max_input; i++ )
        {
            // map input value to 8-bit range
            //
            byte intensity = (byte)( ( i * 0xFF ) / max_input );

            // create ARGB output value A=255, R=G=B=intensity
            //
            lut[ i ] = (uint)( 0xFF000000L | ( intensity * 0x00010101L ) );
        }

        return lut;
    }

    static byte[] Wedge( int width, int height, int bits )
    {
        // horizontal wedge

        int max = 1 << bits;

        byte[] pixels = new byte[ width * height * 2 ];

        for ( int y = 0; y < height; y++ )
        {
            for ( int x = 0; x < width; x++ )
            {
                int pixel = x % max;

                int addr = ( ( y * width ) + x ) * 2;

                pixels[ addr + 1 ] = (byte)( ( pixel & 0xFF00 ) >> 8 );
                pixels[ addr + 0 ] = (byte)( ( pixel & 0x00FF ) );
            }
        }

        return pixels;
    }
}
于 2011-08-02T18:08:33.443 に答える
1

16b フォーマットをスプーフィングし、ColorMatrix を使用して表示前に正しくマッピングします。

Windows でこのアプローチのパフォーマンス テストを行ったことはありませんが、効率的なメモリ ストレージと 12b または 16b データのさまざまな範囲の迅速な再マッピングが必要な他のプラットフォーム (Android など) では、この手法をうまく利用しました。

私の 12/16b グレースケール データは実際には RGB565 であるため、シリアライズ、デシリアライズ、およびその他の操作に問題はありません。表示する必要がある場合は、適切なウィンドウを ARGB8888 の 8b グレースケールにマップする ColorMatrix に渡します。

誰かがこれを試してみたい場合は、マッピング アルゴリズムを投稿します。

于 2012-08-22T15:17:55.917 に答える
0

考えられる 2 つの方法:

  • 任意のバッファーを指すBitmap コンストラクターを使用します。これにより、ビットマップが破棄されるまでバッファーを保持する必要がありますが、メモリ内のビットマップ データの不要なコピーを防ぐことができます。
  • Bitmap のデータへのポインターを取得するには、LockBits メソッドを使用できます。この場合、目的の寸法と形式で通常どおりビットマップを作成します。次に、LockBits を呼び出して、ビットマップ データをバッファーにコピーします。これは遅くなりますが、データが Bitmap コンストラクターが直接受け入れることができる形式ではないため、何らかのカスタム変換が必要な場合に必要です。
于 2011-07-26T16:19:15.767 に答える