在窗体上将字节数组显示为图像

Show an array of bytes as an image on a form

我写了一些代码来将字节数组显示为图像。有一个字节数组,其中每个元素代表一个 8 位灰度图像的值。零等于最黑像素,255 代表最白像素。我的目标是将这个 w*w 像素的灰度图像转换为 pictureBox1.Image 接受的某种东西。 这是我的代码:

namespace ShowRawImage
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();         
        }

        private void button1_Click(object sender, EventArgs e)
        {
            int i = 0, j = 0, w = 256;
            byte[] rawIm = new byte[256 * 256];
            for(i = 0; i < w; ++i)
            {
                for (j = 0; j < w; ++j)
                {
                    rawIm[i * w + j] = (byte)j; // BitConverter.GetBytes(j);
                }
            }

            MemoryStream mStream = new MemoryStream();
            mStream.Write(rawIm, 0, Convert.ToInt32(rawIm.Length));
            Bitmap bm = new Bitmap(mStream, false);// the error occurs here
            mStream.Dispose();
            pictureBox1.Image = bm;

        }
    }
}

但是我得到这个错误: 参数无效。
The error snapshot
我的错误在哪里?

编辑: 在下一步中,我将显示 16 位灰度图像。

如评论中所述 - 位图不仅仅是一个数组。因此,为了达到您的目标,您可以创建所需大小的位图并使用 Bitmap.SetPixel:

设置像素
Bitmap bm = new Bitmap(w, w);
for(var i = 0; i < w; ++i)
{
    for (var j = 0; j < w; ++j)
    {
        bm.SetPixel(i,j, Color.FromArgb(j, j, j));
    }
}

Bitmap(Stream, bool) 构造函数需要一个具有实际图像格式(例如 PNG、GIF 等)的流以及 header、调色板和可能的压缩图像数据。

要从原始数据创建 Bitmap,您需要使用 Bitmap(int width, int height, int stride, PixelFormat format, IntPtr scan0) 构造函数,但这也很不方便,因为您需要一个可以作为 [=17 传递的固定原始数据=].

如果您只创建带有灰度调色板的 8bpp 位图并手动设置像素,则最好:

var bmp = new Bitmap(256, 256, PixelFormat.Format8bppIndexed);

// making it grayscale
var palette = bmp.Palette;
for (int i = 0; i < 255; i++)
    palette.Entries[i] = Color.FromArgb(i, i, i);
bmp.Palette = palette;

现在您可以以字节形式访问其原始内容,其中 0 为黑色,255 为白色:

var bitmapData = bmp.LockBits(new Rectangle(Point.Empty, bmp.Size), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
for (int y = 0; y < bitmapData.Height; y++)
{
    for (int x = 0; x < bitmapData.Width; x++)
    {
        unsafe
        {
            ((byte*) bitmapData.Scan0)[y * bitmapData.Stride + x] = (byte)x;
        }
    }
}
bmp.UnlockBits(bitmapData);

结果图片:

但是如果你不想使用不安全的代码,或者你想按颜色设置像素,你可以使用this library (disclaimer: written by me) that supports efficient manipulation而不考虑实际的PixelFormat。使用该库可以像这样重写最后一个块:

using (IWritableBitmapData bitmapData = bmp.GetWritableBitmapData())
{
    IWritableBitmapDataRow row = bitmapData.FirstRow;
    do
    {
        for (int x = 0; x < bitmapData.Width; x++)
            row[x] = Color32.FromGray((byte)x); // this works for any pixel format
            // row.SetColorIndex(x, x); // for the grayscale 8bpp bitmap created above
    } while (row.MoveNextRow());
}

或者像这样,使用 Parallel.For(这只是因为在您的示例中所有行都相同,所以图像是水平渐变):

using (IWritableBitmapData bitmapData = bmp.GetWritableBitmapData())
{
    Parallel.For(0, bitmapData.Height, y =>
    {
        var row = bitmapData[y];
        for (int x = 0; x < bitmapData.Width; x++)
            row[x] = Color32.FromGray((byte)x); // this works for any pixel format
            // row.SetColorIndex(x, x); // for the grayscale 8bpp bitmap created above
    });
}