【问题标题】:Byte array to image conversion字节数组到图像的转换
【发布时间】:2012-02-07 09:37:10
【问题描述】:

我想将字节数组转换为图像。

这是我获取字节数组的数据库代码:

public void Get_Finger_print()
{
    try
    {
        using (SqlConnection thisConnection = new SqlConnection(@"Data Source=" + System.Environment.MachineName + "\\SQLEXPRESS;Initial Catalog=Image_Scanning;Integrated Security=SSPI "))
        {
            thisConnection.Open();
            string query = "select pic from Image_tbl";// where Name='" + name + "'";
            SqlCommand cmd = new SqlCommand(query, thisConnection);
            byte[] image =(byte[]) cmd.ExecuteScalar();
            Image newImage = byteArrayToImage(image);
            Picture.Image = newImage;
            //return image;
        }
    }
    catch (Exception) { }
    //return null;
}

我的转换代码:

public Image byteArrayToImage(byte[] byteArrayIn)
{
    try
    {
        MemoryStream ms = new MemoryStream(byteArrayIn,0,byteArrayIn.Length);
        ms.Write(byteArrayIn, 0, byteArrayIn.Length);
        returnImage = Image.FromStream(ms,true);//Exception occurs here
    }
    catch { }
    return returnImage;
}

当我到达带有注释的行时,出现以下异常:Parameter is not valid.

如何解决导致此异常的原因?

【问题讨论】:

标签: c# arrays byte bytebuffer


【解决方案1】:

您正在向内存流写入两次,而且您也没有在使用后处理流。 您还要求图像解码器应用嵌入式颜色校正。

试试这个:

using (var ms = new MemoryStream(byteArrayIn))
{
    return Image.FromStream(ms);
}

【讨论】:

  • 你也可以在初始化后显式地使内存流不可写:new MemoryStream(byteArrayIn, false)
  • 这违反了 MSDN 中针对 Image.FromStream() 的规范,其中指出“您必须在图像的生命周期内保持流打开。”另见stackoverflow.com/questions/3290060/…
【解决方案2】:

也许我遗漏了一些东西,但对我来说,这个单行适用于包含 JPEG 文件图像的字节数组。

Image x = (Bitmap)((new ImageConverter()).ConvertFrom(jpegByteArray));

编辑:

在此处查看此答案的更新版本:How to convert image in byte array

【讨论】:

  • 啊谢谢,终于有一个好答案了。为什么内存流有这么多答案,这给我带来了很多问题。非常感谢!
  • 好答案!这可以在单独的函数中使用,而所有其他使用 MemoryStream 的提案不能(Stream 必须在 Image 的生命周期内保持打开)
【解决方案3】:
public Image byteArrayToImage(byte[] bytesArr)
{
    using (MemoryStream memstr = new MemoryStream(bytesArr))
    {
        Image img = Image.FromStream(memstr);
        return img;
    }
}

【讨论】:

  • 虽然这通常不是一个好主意,但我在内存流之前放置了一个 GC.Collect()。当我将大量大型图形文件作为字节数组预加载到内存中,然后在查看过程中将它们转换为图像时,内存不足。
  • 这对 3 年前发布的 existing answer 没有任何影响,并且存在同样的过早处理流的问题。
【解决方案4】:

我想指出@isaias-b 提供的解决方案中有一个错误。

该解决方案假定stride 等于行长度。但这并不总是正确的。由于 GDI 执行的内存对齐,stride 可以大于行长度。必须考虑到这一点。否则会产生无效的移位图像。每行中的填充字节将被忽略。

步幅是单行像素(扫描线)的宽度,四舍五入到四字节边界。

固定代码:

using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

public static class ImageExtensions
{
    public static Image ImageFromRawBgraArray(this byte[] arr, int width, int height, PixelFormat pixelFormat)
    {
        var output = new Bitmap(width, height, pixelFormat);
        var rect = new Rectangle(0, 0, width, height);
        var bmpData = output.LockBits(rect, ImageLockMode.ReadWrite, output.PixelFormat);

        // Row-by-row copy
        var arrRowLength = width * Image.GetPixelFormatSize(output.PixelFormat) / 8;
        var ptr = bmpData.Scan0;
        for (var i = 0; i < height; i++)
        {
            Marshal.Copy(arr, i * arrRowLength, ptr, arrRowLength);
            ptr += bmpData.Stride;
        }

        output.UnlockBits(bmpData);
        return output;
    }
}

为了说明它会导致什么,让我们生成PixelFormat.Format24bppRgb 101x101 的渐变图像:

var width = 101;
var height = 101;
var gradient = new byte[width * height * 3 /* bytes per pixel */];
for (int i = 0, pixel = 0; i < gradient.Length; i++, pixel = i / 3)
{
    var x = pixel % height;
    var y = (pixel - x) / width;
    gradient[i] = (byte)((x / (double)(width - 1) + y / (double)(height - 1)) / 2d * 255);
}

如果我们将整个数组原样复制到bmpData.Scan0 指向的地址,我们将得到以下图像。图像移位,因为部分图像被写入填充字节,这被忽略了。这也是最后一行不完整的原因:

但是如果我们将逐行移动的目标指针复制bmpData.Stride值,就会生成有效的图像:

步幅也可以是负数:

如果步幅为正,则位图是自上而下的。如果步幅为负,则位图为自下而上。

但我没有使用过这样的图片,这超出了我的注意范围。

相关回答:C# - RGB Buffer from Bitmap different from C++

【讨论】:

    【解决方案5】:

    尝试(更新)

    MemoryStream ms = new MemoryStream(byteArrayIn,0,byteArrayIn.Length);
    ms.Position = 0; // this is important
    returnImage = Image.FromStream(ms,true);
    

    【讨论】:

    • 用相同的字节数组初始化后,将字节数组写入内存流是没有意义的。实际上我不确定 MemoryStream 是否允许超出构造函数中指定的长度。
    【解决方案6】:

    所有给出的答案都假定字节数组包含已知文件格式表示的数据,例如:gif、png 或 jpg。但我最近在尝试将包含线性化 BGRA 信息的byte[]s 有效地转换为Image 对象时遇到了问题。下面的代码使用Bitmap 对象解决了这个问题。

    using System.Drawing;
    using System.Drawing.Imaging;
    using System.Runtime.InteropServices;
    public static class Extensions
    {
        public static Image ImageFromRawBgraArray(
            this byte[] arr, int width, int height)
        {
            var output = new Bitmap(width, height);
            var rect = new Rectangle(0, 0, width, height);
            var bmpData = output.LockBits(rect, 
                ImageLockMode.ReadWrite, output.PixelFormat);
            var ptr = bmpData.Scan0;
            Marshal.Copy(arr, 0, ptr, arr.Length);
            output.UnlockBits(bmpData);
            return output;
        }
    }
    

    这是在this site 上发布的解决方案的一个轻微变体。

    【讨论】:

    • 作为参考 MSDN: Bitmap(Int32, Int32): "此构造函数创建一个 PixelFormat 枚举值为 Format32bppArgb 的 Bitmap。",表示 byte[0] 为蓝色,byte[1] 为绿色,byte[2] 为红色,byte[3] 为 alpha,byte[4] 为蓝色,依此类推。
    • 感谢您添加所有需要的“使用”。大多数人都忘记了这一点,找到它们是一件很痛苦的事情。
    【解决方案7】:

    有一个简单的方法如下,你可以使用图像的FromStream 方法来做这个技巧, 只要记住使用System.Drawing;

    // using image object not file 
    public byte[] imageToByteArray(Image imageIn)
    {
        MemoryStream ms = new MemoryStream();
        imageIn.Save(ms,System.Drawing.Imaging.ImageFormat.Gif);
        return ms.ToArray();
    }
    
    public Image byteArrayToImage(byte[] byteArrayIn)
    {
        MemoryStream ms = new MemoryStream(byteArrayIn);
        Image returnImage = Image.FromStream(ms);
        return returnImage;
    }
    

    【讨论】:

      【解决方案8】:

      一行:

      Image.FromStream(new MemoryStream(byteArrayIn));
      

      【讨论】:

      • 工作正常:-)。
      【解决方案9】:

      您还没有将 returnImage 声明为任何类型的变量 :)

      这应该会有所帮助:

      public Image byteArrayToImage(byte[] byteArrayIn)
      {
          try
          {
              MemoryStream ms = new MemoryStream(byteArrayIn,0,byteArrayIn.Length);
              ms.Write(byteArrayIn, 0, byteArrayIn.Length);
              Image returnImage = Image.FromStream(ms,true);
          }
          catch { }
          return returnImage;
      }
      

      【讨论】:

      • 代码作为一个想法很有用,但当然不会像写的那样工作。 returnImage 必须在 try/catch 部分之外声明。 returnImage 也必须在 'catch' 中实例化 - 我创建一个单像素位图: var image = new Bitmap(1, 1); MemoryStream 流 = 新的 MemoryStream(); image.Save(流,ImageFormat.Jpeg); stream.Position = 0;
      【解决方案10】:

      这是受 Holstebroe 的回答的启发,加上这里的 cmets:Getting an Image object from a byte array

      Bitmap newBitmap;
      using (MemoryStream memoryStream = new MemoryStream(byteArrayIn))
          using (Image newImage = Image.FromStream(memoryStream))
              newBitmap = new Bitmap(newImage);
      return newBitmap;
      

      【讨论】:

        【解决方案11】:

        一个班轮:

        Image bmp = (Bitmap)((new ImageConverter()).ConvertFrom(imageBytes));
        

        【讨论】:

          【解决方案12】:

          大多数情况下发生这种情况是 SQL 列中的错误数据。这是插入图像列的正确方法:

          INSERT INTO [TableX] (ImgColumn) VALUES (
          (SELECT * FROM OPENROWSET(BULK N'C:\....\Picture 010.png', SINGLE_BLOB) as tempimg))
          

          大多数人都这样做是错误的:

          INSERT INTO [TableX] (ImgColumn) VALUES ('C:\....\Picture 010.png'))
          

          【讨论】:

            【解决方案13】:

            首先安装这个包:

            安装包 SixLabors.ImageSharp -Version 1.0.0-beta0007

            [SixLabors.ImageSharp][1] [1]:https://www.nuget.org/packages/SixLabors.ImageSharp

            然后使用下面的代码将字节数组转换为图像:

            Image<Rgba32> image = Image.Load(byteArray); 
            

            要获取 ImageFormat,请使用以下代码:

            IImageFormat format = Image.DetectFormat(byteArray);
            

            对于变异图像,请使用以下代码:

            image.Mutate(x => x.Resize(new Size(1280, 960)));
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2015-08-16
              • 2013-05-19
              • 1970-01-01
              • 2013-12-06
              相关资源
              最近更新 更多