将位图转换为字节数组


233

使用C#,有没有更好的方式来在Windows转换Bitmap到一个byte[]比保存到临时文件和读取使用的结果FileStream

Answers:


418

有几种方法。

图像转换器

public static byte[] ImageToByte(Image img)
{
    ImageConverter converter = new ImageConverter();
    return (byte[])converter.ConvertTo(img, typeof(byte[]));
}

这很方便,因为它不需要很多代码。

内存流

public static byte[] ImageToByte2(Image img)
{
    using (var stream = new MemoryStream())
    {
        img.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
        return stream.ToArray();
    }
}

除了将文件保存到内存而不是磁盘之外,此操作与您正在执行的操作等效。尽管有更多代码,但您可以选择ImageFormat,并且可以在保存到内存或磁盘之间轻松地对其进行修改。

资料来源:http : //www.vcskicks.com/image-to-byte.php


2
您还可以使用bitmap.Lockbits和Marshal.Copy一个简单的,没有任何中间memorystreams等快速复制
deegee

4
请注意,该ImageConverter方法会将图像另存为Png,从而导致文件很大。
Skolima 2014年

8
您无需在使用“使用”时关闭流
LastTribunal 2015年

2
谁能给我更多有关此数组结构的更多信息吗?它是否以x = 0开头并在每个y中循环,然后递增x?然后像这样存储它:[0,0,1,1,1,1,0,0,1,1]?
雷蒙德·蒂默曼斯

1
ImageConverter不是您可能使用的.net标准MemoryStream
亚历山大,

95

一个MemoryStream的可对此很有帮助。您可以将其放在扩展方法中:

public static class ImageExtensions
{
    public static byte[] ToByteArray(this Image image, ImageFormat format)
    {
        using(MemoryStream ms = new MemoryStream())
        {
            image.Save(ms, format);
            return ms.ToArray();
        }
    }
}

您可以像这样使用它:

var image = new Bitmap(10, 10);
// Draw your image
byte[] arr = image.ToByteArray(ImageFormat.Bmp);

对于ImageConverter,我部分不同意prestomanifto的回答。 不要使用ImageConverter。 从技术上讲,它没有什么错,但是它使用对象的装箱/拆箱的事实告诉我,它是来自.NET框架旧的暗处的代码,并且不适合与图像处理一起使用(将其转换为字节是过大的杀伤力) []至少),尤其是当您考虑以下内容时。

我看了看 ImageConverter .Net框架使用的代码,在内部它使用的代码几乎与我上面提供的代码相同。它创建一个new MemoryStreamBitmap以提供时的格式保存,然后返回该数组。ImageConverter通过使用跳过创建类的额外开销MemoryStream


可爱。会的!我认为您需要处置MemoryStream,但是-是否愿意进行更新?
杰里米·麦基

我已经更新了我的答案,其中讨论了一些原因,如您选择的答案所建议的那样,为什么不使用ImageConverter以及增加处理方式。
Christopher Currens 2011年

很好用的扩展方法,我喜欢!
Dude Pascalou 2012年

3
+1用于查看ImageConverter并报告您的研究结果。但是我不认为您发现的内容值得声明“请勿使用ImageConverter”。它肯定会提供其他有用的服务,从字节数组到图像,例如设置图像分辨率(dpi)。而且“创建ImageConverter类的额外开销”可以忽略不计,并且无论您使用它多少次,都只需要执行一次。
RenniePet

1
我发现ImageConverter也很有用,因为它可以自动确定图像的类型-例如,如果您有图像的字节数组,但不知道格式-您可以尝试读取标题并从中获取提示,但是,好吧,...使用(Image img =(Image)myImgConverter.ConvertFrom(byteImage))然后检查img.PixelFormat等更加容易..-当然,这取决于您要执行的操作
hello_earth 2014年

45

您也可以只是元帅。复制位图数据。没有中间的内存流等,并且没有快速的内存副本。这应该适用于24位和32位位图。

public static byte[] BitmapToByteArray(Bitmap bitmap)
{

    BitmapData bmpdata = null;

    try
    {
        bmpdata = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
        int numbytes = bmpdata.Stride * bitmap.Height;
        byte[] bytedata = new byte[numbytes];
        IntPtr ptr = bmpdata.Scan0;

        Marshal.Copy(ptr, bytedata, 0, numbytes);

        return bytedata;
    }
    finally
    {
        if (bmpdata != null)
            bitmap.UnlockBits(bmpdata);
    }

}


1
嗨,我使用了这种方法,但是无法再次将此字节数组转换回图像。位图bitmap1 =新位图(新MemoryStream(数组)); 它抛出无效参数的异常。有没有错
霍华德

1
嗨,注释部分对我来说太小了,无法发布完整的干净代码。最简单的方法是固定数组:GCHandle handle = GCHandle.Alloc(bufferarray,GCHandleType.Pinned); 获取指向数组IntPtr的指针iptr = Marshal.UnsafeAddrOfPinnedArrayElement(bufferarray,0); 然后使用IntPtr创建一个新的位图:bitmap = new Bitmap(width,height,(width * 4),PixelFormat.Format32bppArgb,iptr); 但您必须为位图格式使用适当的位图创建参数。确保清除:iptr = IntPtr.Zero; handle.Free();
deegee

3
错误是什么?包括我在内的许多程序员都在使用这种代码样式,并且效果很好。
deegee

4
@ mini-me-我建议查找位图跨度与位图宽度之间的关系。这不是我的代码中的错误,这是Windows处理位图的方式。跨步可能会在每行的末尾(宽度)包括额外的数据,以使每行结束并从32位边界开始,以实现内存对齐和性能。
deegee 2014年


16

将图像保存到MemoryStream,然后获取字节数组。

http://msdn.microsoft.com/en-us/library/ms142148.aspx

  Byte[] data;

  using (var memoryStream = new MemoryStream())
  {
    image.Save(memoryStream, ImageFormat.Bmp);

    data = memoryStream.ToArray();
  }

图像上没有保存方法。
Prescott Chartier

@PrescottChartier上面的示例假设您使用的是派生自的类型System.Drawing.Image (请参阅:docs.microsoft.com/en-us/dotnet/api/…
Chris Baxter

是的我得到了System.Drawing.Image does not exist。所以..不,不起作用:(
Prescott Chartier

你可以看到我想要在这里做: stackoverflow.com/questions/55084620/...
普雷斯科特夏尔提埃

10

使用MemoryStream而不是FileStream,例如:

MemoryStream ms = new MemoryStream();
bmp.Save (ms, ImageFormat.Jpeg);
byte[] bmpBytes = ms.ToArray();

您可能想要ToArray而不是GetBuffer
vcsjones 2011年

GetBuffer返回一个无符号字节数组(字节数组)
Jeff Reddy

4
它可能具有不属于图像一部分的填充数据。From docs:Note that the buffer contains allocated bytes which might be unused. For example, if the string "test" is written into the MemoryStream object, the length of the buffer returned from GetBuffer is 256, not 4, with 252 bytes unused. To obtain only the data in the buffer, use the ToArray method.所以现在from的字节数组GetBuffer将返回图像以及未使用的字节,这很可能会导致图像损坏。
vcsjones 2011年

1
很高兴知道。感谢您对vcs的评论!
杰夫·雷迪

此方法使用默认压缩设置将图像另存为jpeg,这将引入可能明显降低图像质量的压缩伪像。
理查德·埃夫

8

请尝试以下操作:

MemoryStream stream = new MemoryStream();
Bitmap bitmap = new Bitmap();
bitmap.Save(stream, ImageFormat.Jpeg);

byte[] byteArray = stream.GetBuffer();

确保您正在使用:

System.Drawing & using System.Drawing.Imaging;

1
此方法使用默认压缩设置将图像另存为jpeg,这将引入可能明显降低图像质量的压缩伪像。
理查德·埃夫

同样,位图上没有Save方法。
Prescott Chartier

7

我相信您可能只是做:

ImageConverter converter = new ImageConverter();
var bytes = (byte[])converter.ConvertTo(img, typeof(byte[]));

6
MemoryStream ms = new MemoryStream();
yourBitmap.Save(ms, ImageFormat.Bmp);
byte[] bitmapData = ms.ToArray();


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.