Generate 16-bit grayscale BitmapData and save to file

前端 未结 2 2025
迷失自我
迷失自我 2020-11-30 11:54

I am trying to generate 16bit grayscale Bitmap in C# from a random data.But it crashed on Marshal.Copy.

Here is my code:

   Bitmap b16bpp;
    privat         


        
相关标签:
2条回答
  • 2020-11-30 12:37

    I have corrected some of your mistakes (mostly wrong sizes). But it will still crash on b16bpp.Save(), because GDI+ does not support saving 16bit grayscale images.

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

    Explanation of my changes:

    • bitmapData.Stride is already IMAGE_WIDTH * BytesPerPixel so you don't need to multiply by 2
    • as you declared bitmapBytes as short[] it has to have the size of the image in pixels not in bytes
    • that means you also do not need to multiply i by 2
    • since you have a grayscale image it does not have a blue, green and red channel, but one single 16bit gray channel
    • Marshal.Copy takes the length in "array units" not in bytes

    All in all you tried to copy an array 8 times to large into the bitmap.

    0 讨论(0)
  • 2020-11-30 12:44

    This works for 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;
        }
    
    0 讨论(0)
提交回复
热议问题