File.ReadAllBytes无法正确读取PNG图像像素

4

我正在尝试使用File.ReadAllBytes(string)方法读取.png图像的字节,但没有成功。

我的图像尺寸为2464x2056x3(15,197,952 字节),但这个方法返回的数组大小约为12,000,000字节。

我试过一个相同大小的白色图像,得到了一个25,549字节的字节数组,并检查了字节数组,发现了各种各样的值,很明显这不正确,因为这是一个白色图像。

我使用的代码如下:

var frame = File.ReadAllBytes("C:\\workspace\\white.png");

我也尝试先将图像作为Image对象打开,然后使用以下代码获取字节数组:
using (var ms = new MemoryStream())
{
  var imageIn = Image.FromFile("C:\\workspace\\white.png");
  imageIn.Save(ms, imageIn.RawFormat);
  var array = ms.ToArray();
}

但结果和以前一样...

你有什么想法吗?

我该如何读取字节数组?


3
你期望文件内容每像素恰好为3个字节吗?PNG有内置压缩和其他元数据。 - gunr2171
ReadAllBytes 的功能正常。您已经读取了文件数据的字节数组。您是想读取像素数据吗? - Panagiotis Kanavos
你想用那张图片做什么?为什么你需要像素值?这很重要。ImageBitmap是为在屏幕上绘制而创建的,当你尝试在像素级别应用变换时会使生活变得困难。另一方面,System.Graphics命名空间中的其他类允许你指定转换,这些转换被翻译成Windows GDI+命令,使它们快速但受限制。对于一般的图像处理,最好使用例如ImageSharp。 - Panagiotis Kanavos
@PanagiotisKanavos 是的,我想要读取像素数据,一个大小为(2056x2464x3)的数组,其中包含RGB的所有像素值,也许我没有正确命名。 - juan carlos
@PanagiotisKanavos 我需要像素数据的数组,以便稍后将其转换为EmguCV Mat。情况是我有一个提供该数组的真实相机,但我正在尝试创建一个虚拟相机用于测试,它只是从磁盘读取图像并将数组数据传递给真实相机。 - juan carlos
在这种情况下,特别是如果您希望您的代码跨平台使用,请使用ImageSharp。System.Drawing本质上是Windows GDI+ API的包装器,并且使用有限的GDI +句柄需要被释放。 - Panagiotis Kanavos
1个回答

6

PNG是一种压缩格式
了解更多信息:便携式网络图形 - 维基百科

这意味着二进制表示并不是您期望的实际像素值。

您需要某种PNG解码器才能从压缩数据中获取像素值。

这篇文章可能会指引您正确的方向:.Net 2.0中读取PNG图像文件。请注意,这篇文章相当古老,也许有更新的方法可以做到。

顺便说一下:即使是像BMP这样的非压缩格式也有一个头文件,因此您不能简单地读取二进制文件并以一种微不足道的方式获取像素值。


更新: 下面演示了从PNG文件中获取像素值的一种方法:

using System.Drawing.Imaging;

byte[] GetPngPixels(string filename)
{
    byte[] rgbValues = null;

    // Load the png and convert to Bitmap. This will use a .NET PNG decoder:
    using (var imageIn = Image.FromFile(filename))
    using (var bmp = new Bitmap(imageIn))
    {
        // Lock the pixel data to gain low level access:
        BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);

        // Get the address of the first line.
        IntPtr ptr = bmpData.Scan0;

        // Declare an array to hold the bytes of the bitmap.
        int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
        rgbValues = new byte[bytes];

        // Copy the RGB values into the array.
        System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);

        // Unlock the pixel data:
        bmp.UnlockBits(bmpData);
    }

    // Here rgbValues is an array of the pixel values.
    return rgbValues;
}

这个方法将返回一个你期望大小的字节数组。
为了能够在 opencv(或任何类似用途)中使用数据,我建议你改进我的代码示例,并返回图像元数据(宽度、高度、步幅、像素格式)。你需要这些元数据来构造一个cv::Mat


@JonasH 在你提出问题后,我阅读了相关文档并理解了你刚才所解释的内容。谢谢! - wohlstad
谢谢!它起作用了。我不得不将bmp.PixelFormat更改为PixelFormat.24bppRgb,因为由于某种原因它将bmp识别为32bpp,但实际图像只有24bpp。 - juan carlos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接