How to display raw data as an image (Visual Studio c#)

前端 未结 2 1431
一整个雨季
一整个雨季 2021-01-04 07:31

I will be receiving some raw data that will be stored in a byte array, where each 2 bytes is a pixel value (16 bits/px). To start with, the array will contain 100x100*2 byte

相关标签:
2条回答
  • 2021-01-04 07:50

    The main problem is that PixelFormat.Format16bppGrayScale is not supported (at least on my Win 8.1 x64 system). So you have to convert image to rgb before displaying:

    private void Form1_Load(object sender, EventArgs e)
    {
        //Create pixel data to put in image, use 2 since it is 16bpp
        Random r = new Random();
        int width = 100;
        int height = 100;
        byte[] pixelValues = new byte[width * height * 2];
        for (int i = 0; i < pixelValues.Length; ++i)
        {
            // Just creating random pixel values for test
            pixelValues[i] = (byte)r.Next(0, 256);
        }
    
        var rgbData = Convert16BitGrayScaleToRgb48(pixelValues, width, height);
        var bmp = CreateBitmapFromBytes(rgbData, width, height);
    
        // display bitmap
        pictureBox1.Image = bmp;
    }
    
    private static byte[] Convert16BitGrayScaleToRgb48(byte[] inBuffer, int width, int height)
    {
        int inBytesPerPixel = 2;
        int outBytesPerPixel = 6;
    
        byte[] outBuffer = new byte[width * height * outBytesPerPixel];
        int inStride = width * inBytesPerPixel;
        int outStride = width * outBytesPerPixel;
    
        // Step through the image by row
        for (int y = 0; y < height; y++)
        {
            // Step through the image by column
            for (int x = 0; x < width; x++)
            {
                // Get inbuffer index and outbuffer index
                int inIndex = (y * inStride) + (x * inBytesPerPixel);
                int outIndex = (y * outStride) + (x * outBytesPerPixel);
    
                byte hibyte = inBuffer[inIndex + 1];
                byte lobyte = inBuffer[inIndex];
    
                //R
                outBuffer[outIndex] = lobyte;
                outBuffer[outIndex + 1] = hibyte;
    
                //G
                outBuffer[outIndex + 2] = lobyte;
                outBuffer[outIndex + 3] = hibyte;
    
                //B
                outBuffer[outIndex + 4] = lobyte;
                outBuffer[outIndex + 5] = hibyte;
            }
        }
        return outBuffer;
    }
    
    private static Bitmap CreateBitmapFromBytes(byte[] pixelValues, int width, int height)
    {
        //Create an image that will hold the image data
        Bitmap bmp = new Bitmap(width, height, PixelFormat.Format48bppRgb);
    
        //Get a reference to the images pixel data
        Rectangle dimension = new Rectangle(0, 0, bmp.Width, bmp.Height);
        BitmapData picData = bmp.LockBits(dimension, ImageLockMode.ReadWrite, bmp.PixelFormat);
        IntPtr pixelStartAddress = picData.Scan0;
    
        //Copy the pixel data into the bitmap structure
        System.Runtime.InteropServices.Marshal.Copy(pixelValues, 0, pixelStartAddress, pixelValues.Length);
    
        bmp.UnlockBits(picData);
        return bmp;
    }
    

    Idea was taken from this thread.

    0 讨论(0)
  • 2021-01-04 08:10

    Use this Bitmap constructor:

    public Bitmap(
        int width,
        int height,
        int stride,
        PixelFormat format,
        IntPtr scan0
    )
    

    You pass it the shape of your bitmap, the stride (how many bytes per line, including padding), pixel format and the pixel data as a void * pointer. You can create the latter with Marshal.AllocHGlobal and fill it in as normal with pointer operations. Don't forget to free this memory after you create your bitmap.

    Edit to account for updated question:

    Simply call IntPtr.ToPointer() to get back a pointer. If you're familiar with C, the rest should be cake:

    var p=(char *)hglobal.ToPointer();  // bad name by the way, it's not a handle, it's a pointer
    p[0]=0;                             // access it like any normal pointer
    

    However, you can use the Marshaller to copy memory for you from managed to unmanaged (getting your hands dirty is usually frowned upon in C#):

    Marshal.Copy(dataArray, 0, hglobal, dataArray.Length); // again, terrible name
    

    A Bitmap is an Image (as in, it derives from it), however you're using Graphics.DrawImage() wrong. As the error says, it's not a static method, you draw it to a specific graphic context. Now what that graphic context is, that's up to you:

    • If you want to paint it in response to WM_PAINT, use the Paint event -- it provides you with a special Graphics object set up with clipping and everything as instructed by the windowing system.
    • If you want to paint it on a bitmap to be later displayed somehow (the common use, also called double buffering), use Graphics.FromImage() on the source bitmap then draw your bitmap over it.

    You can (and should) delete your virtual memory buffer as soon as you get the result back from the Bitmap constructor. Don't leak memory, use a try..finally construct.

    0 讨论(0)
提交回复
热议问题