如何将原始数据显示为图像 (Visual Studio c#)

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

我将接收一些存储在字节数组中的原始数据,其中每 2 个字节是一个像素值 (16 bits/px)。首先,数组将包含 100x100*2 字节(对于 100x100 像素的图像来说足够了)。我想在表格 window 中显示此数据。最后,我想用新数据刷新图像,使它看起来像视频流。不需要严格的帧率。如何才能做到这一点? C# 中的任何代码示例?

编辑: 在对数十个类似问题提出一些建议和评论之后,我仍然无法解决这个问题。这是我正在尝试做的事情的总体思路,但图像未显示在窗体的图片框中。 我的实施具体有什么问题以及如何解决

// array of data I collected
byte[] dataArray = new byte[100 * 100 * 2]; 
//create a pointer to the data
IntPtr hglobal = Marshal.AllocHGlobal(100 * 100 * 2);

// copy my array to global
Marshal.Copy(dataArray, 0, hglobal, dataArray.Length);
// create a bitmap: 100x100 pixels, 2bytes/pixel, 16bitgrayscale
Bitmap newBitmap = new Bitmap(100, 100, 2 * 100, PixelFormat.Format16bppGrayScale, hglobal);

// display bitmap
pictureBox1.Image = newBitmap;

// free the memory
Marshal.FreeHGlobal(hglobal);

使用这个 Bitmap 构造函数:

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

您将位图的形状、步幅(每行多少字节,包括填充)、像素格式和像素数据作为 void * 指针传递给它。您可以使用 Marshal.AllocHGlobal 创建后者并使用指针操作正常填充它。创建位图后不要忘记释放此内存。

编辑以解决更新后的问题:

只需调用 IntPtr.ToPointer() 即可取回指针。如果你熟悉C,剩下的应该是蛋糕:

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

但是,您可以使用 Marshaller 将内存从托管复制到非托管(在 C# 中,动手操作通常是不受欢迎的):

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

A Bitmap 是一个 Image(例如,它派生自它),但是您使用的 Graphics.DrawImage() 是错误的。正如错误所说,它不是静态方法,您将它绘制到特定的图形上下文中。现在该图形上下文是什么,由您决定:

  • 如果您想响应 WM_PAINT 绘制它,请使用 Paint 事件——它为您提供了一个特殊的 Graphics 对象,该对象设置了剪裁和所有内容通过窗口系统。
  • 如果你想在位图上绘制它以便稍后以某种方式显示(常见用途,也称为双缓冲),请在源位图上使用 Graphics.FromImage() 然后在其上绘制你的位图。

一旦从 Bitmap 构造函数返回结果,您就可以(并且应该)删除虚拟内存缓冲区。不要泄漏内存,使用 try..finally 结构。

主要问题是不支持 PixelFormat.Format16bppGrayScale(至少在我的 Win 8.1 x64 系统上)。所以你必须在显示之前将图像转换为rgb:

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;
}

想法来自 this thread