获取图像尺寸而不读取整个文件

有没有一种廉价的方式来获得图像的尺寸(JPG,PNG,…)? 最好,我想实现这个只使用标准类库(因为托pipe限制)。 我知道,阅读图片标题并自己parsing它应该相对容易,但似乎这样的东西应该已经存在了。 另外,我已经validation了下面这段代码可以读取整个图像(我不想):

using System; using System.Drawing; namespace Test { class Program { static void Main(string[] args) { Image img = new Bitmap("test.png"); System.Console.WriteLine(img.Width + " x " + img.Height); } } } 

一如既往的最好的select是find一个经过良好testing的库。 不过,你说这很难,所以这里有一些基本上未经testing的代码,应该适用于相当多的情况:

 using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; namespace ImageDimensions { public static class ImageHelper { const string errorMessage = "Could not recognize image format."; private static Dictionary<byte[], Func<BinaryReader, Size>> imageFormatDecoders = new Dictionary<byte[], Func<BinaryReader, Size>>() { { new byte[]{ 0x42, 0x4D }, DecodeBitmap}, { new byte[]{ 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }, DecodeGif }, { new byte[]{ 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }, DecodeGif }, { new byte[]{ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }, DecodePng }, { new byte[]{ 0xff, 0xd8 }, DecodeJfif }, }; /// <summary> /// Gets the dimensions of an image. /// </summary> /// <param name="path">The path of the image to get the dimensions of.</param> /// <returns>The dimensions of the specified image.</returns> /// <exception cref="ArgumentException">The image was of an unrecognized format.</exception> public static Size GetDimensions(string path) { using (BinaryReader binaryReader = new BinaryReader(File.OpenRead(path))) { try { return GetDimensions(binaryReader); } catch (ArgumentException e) { if (e.Message.StartsWith(errorMessage)) { throw new ArgumentException(errorMessage, "path", e); } else { throw e; } } } } /// <summary> /// Gets the dimensions of an image. /// </summary> /// <param name="path">The path of the image to get the dimensions of.</param> /// <returns>The dimensions of the specified image.</returns> /// <exception cref="ArgumentException">The image was of an unrecognized format.</exception> public static Size GetDimensions(BinaryReader binaryReader) { int maxMagicBytesLength = imageFormatDecoders.Keys.OrderByDescending(x => x.Length).First().Length; byte[] magicBytes = new byte[maxMagicBytesLength]; for (int i = 0; i < maxMagicBytesLength; i += 1) { magicBytes[i] = binaryReader.ReadByte(); foreach(var kvPair in imageFormatDecoders) { if (magicBytes.StartsWith(kvPair.Key)) { return kvPair.Value(binaryReader); } } } throw new ArgumentException(errorMessage, "binaryReader"); } private static bool StartsWith(this byte[] thisBytes, byte[] thatBytes) { for(int i = 0; i < thatBytes.Length; i+= 1) { if (thisBytes[i] != thatBytes[i]) { return false; } } return true; } private static short ReadLittleEndianInt16(this BinaryReader binaryReader) { byte[] bytes = new byte[sizeof(short)]; for (int i = 0; i < sizeof(short); i += 1) { bytes[sizeof(short) - 1 - i] = binaryReader.ReadByte(); } return BitConverter.ToInt16(bytes, 0); } private static int ReadLittleEndianInt32(this BinaryReader binaryReader) { byte[] bytes = new byte[sizeof(int)]; for (int i = 0; i < sizeof(int); i += 1) { bytes[sizeof(int) - 1 - i] = binaryReader.ReadByte(); } return BitConverter.ToInt32(bytes, 0); } private static Size DecodeBitmap(BinaryReader binaryReader) { binaryReader.ReadBytes(16); int width = binaryReader.ReadInt32(); int height = binaryReader.ReadInt32(); return new Size(width, height); } private static Size DecodeGif(BinaryReader binaryReader) { int width = binaryReader.ReadInt16(); int height = binaryReader.ReadInt16(); return new Size(width, height); } private static Size DecodePng(BinaryReader binaryReader) { binaryReader.ReadBytes(8); int width = binaryReader.ReadLittleEndianInt32(); int height = binaryReader.ReadLittleEndianInt32(); return new Size(width, height); } private static Size DecodeJfif(BinaryReader binaryReader) { while (binaryReader.ReadByte() == 0xff) { byte marker = binaryReader.ReadByte(); short chunkLength = binaryReader.ReadLittleEndianInt16(); if (marker == 0xc0) { binaryReader.ReadByte(); int height = binaryReader.ReadLittleEndianInt16(); int width = binaryReader.ReadLittleEndianInt16(); return new Size(width, height); } binaryReader.ReadBytes(chunkLength - 2); } throw new ArgumentException(errorMessage); } } } 

希望代码是相当明显的。 要添加一个新的文件格式,你可以将它添加到imageFormatDecoders ,关键字是在给定格式的每个文件的开始处出现的“魔术位”的数组,并且该值是从stream中提取大小的函数。 大多数格式都很简单,唯一真正的问题是jpeg。

你有没有尝试过使用WPF图像类? System.Windows.Media.Imaging.BitmapDecoder等?

我相信一些努力是确保这些编解码器只读取文件的一个子集以确定标题信息。 这是值得一检查。

 using (FileStream file = new FileStream(this.ImageFileName, FileMode.Open, FileAccess.Read)) { using (Image tif = Image.FromStream(stream: file, useEmbeddedColorManagement: false, validateImageData: false)) { float width = tif.PhysicalDimension.Width; float height = tif.PhysicalDimension.Height; float hresolution = tif.HorizontalResolution; float vresolution = tif.VerticalResolution; } } 

validateImageData设置为false可以防止GDI +执行昂贵的图像数据分析,从而严重缩短加载时间。 这个问题揭示了这个问题 。

几个月前我正在寻找类似的东西。 我想读取一个GIF图像的types,版本,高度和宽度,但在网上找不到任何有用的东西。

幸运的是,在GIF的情况下,所有需要的信息都在前10个字节中:

 Type: Bytes 0-2 Version: Bytes 3-5 Height: Bytes 6-7 Width: Bytes 8-9 

PNG稍微复杂一些(宽度和高度都是4字节):

 Width: Bytes 16-19 Height: Bytes 20-23 

如上所述,虽然pnglib中的PNG规范要详细得多,但wotsit是图像和数据格式详细规范的好网站。 不过,我认为维基百科对PNG和GIF格式的入口是最好的开始。

这是我的原始代码来检查GIF,我也一起巴掌一些PNGs:

 using System; using System.IO; using System.Text; public class ImageSizeTest { public static void Main() { byte[] bytes = new byte[10]; string gifFile = @"D:\Personal\Images&Pics\iProduct.gif"; using (FileStream fs = File.OpenRead(gifFile)) { fs.Read(bytes, 0, 10); // type (3 bytes), version (3 bytes), width (2 bytes), height (2 bytes) } displayGifInfo(bytes); string pngFile = @"D:\Personal\Images&Pics\WaveletsGamma.png"; using (FileStream fs = File.OpenRead(pngFile)) { fs.Seek(16, SeekOrigin.Begin); // jump to the 16th byte where width and height information is stored fs.Read(bytes, 0, 8); // width (4 bytes), height (4 bytes) } displayPngInfo(bytes); } public static void displayGifInfo(byte[] bytes) { string type = Encoding.ASCII.GetString(bytes, 0, 3); string version = Encoding.ASCII.GetString(bytes, 3, 3); int width = bytes[6] | bytes[7] << 8; // byte 6 and 7 contain the width but in network byte order so byte 7 has to be left-shifted 8 places and bit-masked to byte 6 int height = bytes[8] | bytes[9] << 8; // same for height Console.WriteLine("GIF\nType: {0}\nVersion: {1}\nWidth: {2}\nHeight: {3}\n", type, version, width, height); } public static void displayPngInfo(byte[] bytes) { int width = 0, height = 0; for (int i = 0; i <= 3; i++) { width = bytes[i] | width << 8; height = bytes[i + 4] | height << 8; } Console.WriteLine("PNG\nWidth: {0}\nHeight: {1}\n", width, height); } } 

基于到目前为止的答案和一些额外的search,似乎在.NET 2类库中没有它的function。 所以我决定写我自己的。 这是一个非常粗糙的版本。 目前,我只需要JPG。 所以它完成了阿巴斯发布的答案。

没有错误检查或任何其他validation,但我目前需要一个有限的任务,它可以最终轻松地添加。 我在一些图像上testing了它,通常不会从图像中读取更多的6K。 我想这取决于EXIF数据的数量。

 using System; using System.IO; namespace Test { class Program { static bool GetJpegDimension( string fileName, out int width, out int height) { width = height = 0; bool found = false; bool eof = false; FileStream stream = new FileStream( fileName, FileMode.Open, FileAccess.Read); BinaryReader reader = new BinaryReader(stream); while (!found || eof) { // read 0xFF and the type reader.ReadByte(); byte type = reader.ReadByte(); // get length int len = 0; switch (type) { // start and end of the image case 0xD8: case 0xD9: len = 0; break; // restart interval case 0xDD: len = 2; break; // the next two bytes is the length default: int lenHi = reader.ReadByte(); int lenLo = reader.ReadByte(); len = (lenHi << 8 | lenLo) - 2; break; } // EOF? if (type == 0xD9) eof = true; // process the data if (len > 0) { // read the data byte[] data = reader.ReadBytes(len); // this is what we are looking for if (type == 0xC0) { width = data[1] << 8 | data[2]; height = data[3] << 8 | data[4]; found = true; } } } reader.Close(); stream.Close(); return found; } static void Main(string[] args) { foreach (string file in Directory.GetFiles(args[0])) { int w, h; GetJpegDimension(file, out w, out h); System.Console.WriteLine(file + ": " + w + " x " + h); } } } } 

是的,你可以绝对做到这一点,代码取决于文件格式。 我为一家影像供应商( Atalasoft )工作,我们的产品为每个编码器提供了一个GetImageInfo(),这个编码器能够最小化地找出尺寸和一些其他容易获取的数据。

如果你想自己推出,我build议从wotsit.org开始,它有几乎所有的图像格式的详细规格,你会看到如何识别文件,以及在哪里可以find的信息。

如果你对C很熟练,那么免费的jpeglib也可以用来获取这些信息。 我敢打赌,你可以用.NET库来做到这一点,但我不知道如何。

我做了这个PNG文件

  var buff = new byte[32]; using (var d = File.OpenRead(file)) { d.Read(buff, 0, 32); } const int wOff = 16; const int hOff = 20; var Widht =BitConverter.ToInt32(new[] {buff[wOff + 3], buff[wOff + 2], buff[wOff + 1], buff[wOff + 0],},0); var Height =BitConverter.ToInt32(new[] {buff[hOff + 3], buff[hOff + 2], buff[hOff + 1], buff[hOff + 0],},0); 

这将取决于文件格式。 通常他们会在文件的早期字节中进行说明。 而且,通常,一个好的图像阅读实施将考虑到这一点。 虽然我不能指出你的.NET。