在 C# 中将原始字节转换为 gif 文件图像

Convert raw bytes to a gif file image in c#

我有原始字节数组数据,想在 c# 上将其转换为 .gif 图像。我尝试过这是一种如下所示的方式。 我想请问如何在c#中做到这一点。

我尝试了什么: 从 byte[] 构造 MemoryStream 并使用 Image.FromStream:它不起作用,因为我只有原始字节数组、要显示的图像像素以及构造函数字节数组需要的参数也是图像的元数据。

public static Image ConvertByteArraytoBitmap(byte[] bytes)
        {
            using (MemoryStream ms = new MemoryStream(bytes))
                return Image.FromStream(ms);
        }

编辑:给 John,一个原始字节数组(这张图片我比较小,字节数组只有 100 个字节):

 woDCgMKAwoDCgMKAwoDCgMKAwoDCgMKAwoDCgMKAwoDCgMKAwoDCgMKAwoDCgMKAwoDCgMKAwoDCgMKAwoDCgMKAwoDCgMKAwoDCgMKAwoDCgMKAwoDCgMKAwoDCgMKAwoDCgMKAwoDCgMKAwoDCgMKAwoDCgMKAwoDCgMKAwoDCgMKAwoDCgMKAwoDCgMKAwoDCgMKAwoDCgMKAwoDCgMKAwoDCgMKAwoDCgMKAwoDCgMKAwoDCgMKAwoDCgMKAwoDCgMKAwoA=

在 .Net 中操作 8 位图像的方法不是很简单;您需要使用 (width, height, pixelformat) 构造函数创建一个新的 Bitmap,然后使用 LockBits 打开其后备字节数组,并使用 Marshal.Copy.[= 将您的图像数据复制到其中18=]

请注意,称为 "stride" 的后备数据数组中一行的字节长度始终四舍五入为四字节的倍数,因此您必须逐行复制数据,并使用步幅跳到下一行位置,而不仅仅是 (图像宽度 * 每像素位数) 值。

不过,关于图像宽度的问题...您的字节数组只是图像的一部分。如果它只是像素数据,您将丢失所有常用的图像 header 信息,例如图像尺寸,并且假设像素格式是 8 位索引(如 gif 是),调色板。此数据不是可选的;没有它,就无法重建图像。

如果图像是灰度的,并且每个字节只代表一个亮度,您可以通过简单的 for 循环轻松生成调色板,但是:

Color[] palette = new Color[256];
for (Int32 i = 0; i < palette.Length; i++)
    palette[i] = Color.FromArgb(i, i, i);

一旦你(以某种方式)得到了缺失的信息,这就是从字节数组中制作图像的方法:

/// <summary>
/// Creates a bitmap based on data, width, height, stride and pixel format.
/// </summary>
/// <param name="sourceData">Byte array of raw source data</param>
/// <param name="width">Width of the image</param>
/// <param name="height">Height of the image</param>
/// <param name="stride">Scanline length inside the data</param>
/// <param name="pixelFormat">Pixel format</param>
/// <param name="palette">Color palette</param>
/// <param name="defaultColor">Default color to fill in on the palette if the given colors don't fully fill it.</param>
/// <returns>The new image</returns>
public static Bitmap BuildImage(Byte[] sourceData, Int32 width, Int32 height, Int32 stride, PixelFormat pixelFormat, Color[] palette, Color? defaultColor)
{
    Bitmap newImage = new Bitmap(width, height, pixelFormat);
    BitmapData targetData = newImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, pixelFormat);
    //If the input stride is larger than the width, this calculates the actual amount of bytes to copy for each line.
    Int32 newDataWidth = ((Image.GetPixelFormatSize(pixelFormat) * width) + 7) / 8;
    // Compensate for possible negative stride on BMP format data.
    Boolean isFlipped = stride < 0;
    stride = Math.Abs(stride);
    // Cache these to avoid unnecessary getter calls.
    Int32 targetStride = targetData.Stride;
    Int64 scan0 = targetData.Scan0.ToInt64();
    for (Int32 y = 0; y < height; y++)
        Marshal.Copy(sourceData, y * stride, new IntPtr(scan0 + y * targetStride), newDataWidth);
    newImage.UnlockBits(targetData);
    // Fix negative stride on BMP format.
    if (isFlipped)
        newImage.RotateFlip(RotateFlipType.Rotate180FlipX);
    // For indexed images, set the palette.
    if ((pixelFormat & PixelFormat.Indexed) != 0 && palette != null)
    {
        ColorPalette pal = newImage.Palette;
        for (Int32 i = 0; i < pal.Entries.Length; i++)
        {
            if (i < palette.Length)
                pal.Entries[i] = palette[i];
            else if (defaultColor.HasValue)
                pal.Entries[i] = defaultColor.Value;
            else
                break;
        }
        newImage.Palette = pal;
    }
    return newImage;
}

对于 8 位图像的紧凑字节数组,宽度和步幅应该相同。没有调色板或尺寸,没有办法做到这一点。

郑重声明,上面的小重复 {0xC2, 0x80} 字节数组加载为 10×20 灰度图像(它是 200 字节,而不是你说的 100),给出了这个结果(缩放到 x20):

它只是 2 个重复的字节,宽度均匀,所以你得到的只是垂直线...