字节数组到图像的转换


101

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

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

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.

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


您是否检查过查询中的图像字节有效?您可以执行File.WriteAllBytes(“ myimage.jpg”,byteArrayIn)进行验证。
Holstebroe 2012年

Answers:


110

您正在两次写入内存流,也没有在使用后处置该流。您还要求图像解码器应用嵌入的色彩校正。

尝试以下方法:

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

您还可以在初始化后明确地使内存流不可写:new MemoryStream(byteArrayIn,false)
Holstebroe

28
这违反了MSDN中有关Image.FromStream()的规范,该规范中说:“必须在图像的生命周期内保持流打开。” 另请参见stackoverflow.com/questions/3290060/...
RenniePet

81

也许我缺少了一些东西,但是对我来说,这种单线可以很好地处理包含JPEG文件图像的字节数组。

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

编辑:

请参阅此处以获取此答案的更新版本:如何在字节数组中转换图像


AAAh,谢谢,最后一个很好的答案。为什么内存流有这么多答案,这会给我带来很多问题。非常感谢 !
朱利安50年5

好答案!这可以在单独的函数中使用,而所有其他使用MemoryStream的建议都不能使用(必须在Image的生存
期内

20
public Image byteArrayToImage(byte[] bytesArr)
{
    using (MemoryStream memstr = new MemoryStream(bytesArr))
    {
        Image img = Image.FromStream(memstr);
        return img;
    }
}

尽管通常这不是一个好主意,但我将GC.Collect()放在Memory流之前。当我将大量大型图形文件作为字节数组预加载到内存中,然后在查看期间将其转换为图像时,内存不足。
Kayot

9

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

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

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

固定代码:

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缓冲区与C ++不同


6

所有给出的答案均假定字节数组包含以已知文件格式表示的数据,例如:gif,png或jpg。但是我最近遇到了一个问题,试图将byte[]包含线性化BGRA信息的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;
    }
}

这是在此站点上发布的解决方案的略微变化。


作为参考MSDN:Bitmap(Int32,Int32):“此构造函数创建的Pixelmap枚举值为Format32bppArgb的位图。”,这意味着字节[0]为蓝色,字节[1]为绿色,字节[2]为红色,字节[3]为alpha,字节[4]为蓝色,依此类推。
布朗

1
谢谢您添加所有需要的“使用”。大多数人都忘记了这一点,找到所有这些都很痛苦。
Casey Crookston '18年

6

有一个简单的方法如下,您可以使用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;
}

5

尝试(更新)

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

2
使用相同的字节数组初始化字节数组后,将字节数组写入内存流几乎没有意义。实际上,我不确定MemoryStream是否允许写入超出构造函数中指定的长度。
Holstebroe'2

2

您尚未将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;
}

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

2

这是受Holstebroe的回答启发的,并在此处加上注释:从字节数组获取Image对象

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


0

在大多数情况下,这种情况是在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'))

0

首先安装此软件包:

安装软件包SixLabors.ImageSharp-版本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)));
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.