生成16位灰度BitmapData并保存到文件

5

我正在尝试从随机数据中用C#生成16位灰度位图,但在Marshal.Copy上崩溃了。

这是我的代码:

   Bitmap b16bpp;
    private void GenerateDummy16bitImage()
    {

        b16bpp = new Bitmap(IMAGE_WIDTH, IMAGE_HEIGHT, System.Drawing.Imaging.PixelFormat.Format16bppGrayScale);

        var rect = new Rectangle(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
        var bitmapData = b16bpp.LockBits(rect, ImageLockMode.WriteOnly, b16bpp.PixelFormat);
        // Calculate the number of bytes required and allocate them.
        var numberOfBytes = bitmapData.Stride * IMAGE_HEIGHT * 2;
        var bitmapBytes = new short[numberOfBytes];
        // Fill the bitmap bytes with random data.
        var random = new Random();
        for (int x = 0; x < IMAGE_WIDTH; x++)
        {
            for (int y = 0; y < IMAGE_HEIGHT; y++)
            {

                var i = ((y * IMAGE_WIDTH) + x) * 2; // 16bpp

                // Generate the next random pixel color value.
                var value = (short)random.Next(5);

                bitmapBytes[i] = value;         // BLUE
                bitmapBytes[i + 1] = value;     // GREEN
                bitmapBytes[i + 2] = value;     // RED
              //  bitmapBytes[i + 3] = 0xFF;      // ALPHA
            }
        }
        // Copy the randomized bits to the bitmap pointer.
        var ptr = bitmapData.Scan0;
        Marshal.Copy(bitmapBytes, 0, ptr, numberOfBytes);//crashes here

        // Unlock the bitmap, we're all done.
        b16bpp.UnlockBits(bitmapData);

        b16bpp.Save("random.bmp", ImageFormat.Bmp);
        Debug.WriteLine("saved");
    }

异常是: 发生了类型为'System.AccessViolationException'的未处理异常mscorlib.dll 这不是我的代码。我在与32位位图相关的情况下找到了它并加以修改。但我想我可能错过了一些东西,因为我对C#还很陌生。
基本上,我所需要的就是将shorts数组包装成BitmapData。

并不会导致崩溃,但是Format16bppGrayScale意味着每个像素没有单独的RGB分量,只有一个16位值(使用UInt16而不是带符号的short类型)。 - Dai
@Dai 如果我按照你的建议去做,Marshal.Copy函数会抱怨参数错误,因为它无法处理Int16[]。 - Michael IV
1
顺便说一句,我不明白那些投票关闭问题的人在想什么。这个问题有什么问题吗????我已经在网上搜索了2到3个小时,但没有找到解决方法。 - Michael IV
1
你搜索的时间长短与你的问题好坏无关。我投票关闭是因为(当时)除了“它崩溃了,这是我的代码”之外,你没有给我们任何细节。 - Blorgbeard
上传到GPU以用作高度图。 - Michael IV
显示剩余2条评论
2个回答

30

这适用于System.Drawing.Imaging.PixelFormat.Format16bppGrayScale:

    private static void SaveBmp(Bitmap bmp, string path)
    {
        Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);

        BitmapData bitmapData = bmp.LockBits(rect, ImageLockMode.ReadOnly, bmp.PixelFormat);

        var pixelFormats = ConvertBmpPixelFormat(bmp.PixelFormat);

        BitmapSource source = BitmapSource.Create(bmp.Width,
                                                  bmp.Height,
                                                  bmp.HorizontalResolution,
                                                  bmp.VerticalResolution,
                                                  pixelFormats,
                                                  null,
                                                  bitmapData.Scan0,
                                                  bitmapData.Stride * bmp.Height,
                                                  bitmapData.Stride);

        bmp.UnlockBits(bitmapData);


        FileStream stream = new FileStream(path, FileMode.Create);

        TiffBitmapEncoder encoder = new TiffBitmapEncoder();

        encoder.Compression = TiffCompressOption.Zip;
        encoder.Frames.Add(BitmapFrame.Create(source));
        encoder.Save(stream);

        stream.Close();
    }

    private static System.Windows.Media.PixelFormat ConvertBmpPixelFormat(System.Drawing.Imaging.PixelFormat pixelformat)
    {
        System.Windows.Media.PixelFormat pixelFormats = System.Windows.Media.PixelFormats.Default;

        switch (pixelformat)
        {
            case System.Drawing.Imaging.PixelFormat.Format32bppArgb:
                pixelFormats = PixelFormats.Bgr32;
                break;

            case System.Drawing.Imaging.PixelFormat.Format8bppIndexed:
                pixelFormats = PixelFormats.Gray8;
                break;

            case System.Drawing.Imaging.PixelFormat.Format16bppGrayScale:
                pixelFormats = PixelFormats.Gray16;
                break;
        }

        return pixelFormats;
    }

4
这个回答需要更多的认可。在搜索了数小时后,我终于找到有人回答了如何保存16bpp灰度图像而不需要外部库的问题,但是他们只有一个投票。 - Gorchestopher H
2
值得注意的是,这需要导入PresentationCore - Nyerguds
Format32bppArgb的翻译是不正确的。Bgr32是一种没有alpha通道的格式。 - Nyerguds
这正是我所需要的。 - Jacob Robbins
产品广告宣传的功能完美无误,开箱即用。我绝对同意需要升级。 - SoLaR
这种解决方案的缺点是它不是线程安全的,因为它使用了BitmapSource。 - Joe Sonderegger

7

我纠正了一些你的错误(主要是尺寸错误)。但是在b16bpp.Save()处仍然会崩溃,因为GDI+不支持保存16位灰度图像

Bitmap b16bpp;
private void GenerateDummy16bitImage()
{

    b16bpp = new Bitmap(IMAGE_WIDTH, IMAGE_HEIGHT, System.Drawing.Imaging.PixelFormat.Format16bppGrayScale);

    var rect = new Rectangle(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
    var bitmapData = b16bpp.LockBits(rect, ImageLockMode.WriteOnly, b16bpp.PixelFormat);
    // Calculate the number of bytes required and allocate them.
    var numberOfBytes = bitmapData.Stride * IMAGE_HEIGHT;
    var bitmapBytes = new short[IMAGE_WIDTH * IMAGE_HEIGHT];
    // Fill the bitmap bytes with random data.
    var random = new Random();
    for (int x = 0; x < IMAGE_WIDTH; x++)
    {
        for (int y = 0; y < IMAGE_HEIGHT; y++)
        {

            var i = ((y * IMAGE_WIDTH) + x); // 16bpp

            // Generate the next random pixel color value.
            var value = (short)random.Next(5);

            bitmapBytes[i] = value;         // GRAY
        }
    }
    // Copy the randomized bits to the bitmap pointer.
    var ptr = bitmapData.Scan0;
    Marshal.Copy(bitmapBytes, 0, ptr, bitmapBytes.Length);

    // Unlock the bitmap, we're all done.
    b16bpp.UnlockBits(bitmapData);

    b16bpp.Save("random.bmp", ImageFormat.Bmp);
    Debug.WriteLine("saved");
}

我的更改解释:

  • bitmapData.Stride已经是IMAGE_WIDTH * BytesPerPixel,所以您不需要乘以2
  • 由于您将bitmapBytes声明为short [],因此它必须具有像素的图像大小,而不是字节数
  • 这意味着您也不需要将i乘以2
  • 由于您有一个灰度图像,它没有蓝色,绿色和红色通道,而是一个单一的16位灰度通道
  • Marshal.Copy使用“数组单位”而不是字节表示长度

总的来说,您试图将数组复制8次大到位图中。


实际上,“保存到文件”部分是为了调试目的 :) 我真的需要在应用程序中使用那个位图。感谢您的帮助。谢谢。 - Michael IV

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接