有没有一种廉价的方式来获得图像的尺寸(jpg,png,...)?最好,我想只使用标准类库(因为托管限制)来实现这一点.我知道读取图像标题并自己解析它应该相对容易,但似乎这样的东西应该已经存在了.此外,我已经验证以下代码片段读取整个图像(我不想要):
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); } } }
ICR.. 102
你最好的选择是找到一个经过良好测试的图书馆.但是,你说这很难,所以这里有一些狡猾的未经测试的代码应该适用于相当多的案例:
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> imageFormatDecoders = new Dictionary >() { { 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 }, }; /// /// Gets the dimensions of an image. /// /// The path of the image to get the dimensions of. ///The dimensions of the specified image. ///The image was of an unrecognized format. 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; } } } } ////// Gets the dimensions of an image. /// /// The path of the image to get the dimensions of. ///The dimensions of the specified image. ///The image was of an unrecognized format. 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
键中,该键是"魔术位"数组,它出现在给定格式的每个文件的开头,值是从流中提取大小的函数.大多数格式都很简单,唯一真正的stinker是jpeg.
你最好的选择是找到一个经过良好测试的图书馆.但是,你说这很难,所以这里有一些狡猾的未经测试的代码应该适用于相当多的案例:
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> imageFormatDecoders = new Dictionary >() { { 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 }, }; /// /// Gets the dimensions of an image. /// /// The path of the image to get the dimensions of. ///The dimensions of the specified image. ///The image was of an unrecognized format. 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; } } } } ////// Gets the dimensions of an image. /// /// The path of the image to get the dimensions of. ///The dimensions of the specified image. ///The image was of an unrecognized format. 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
键中,该键是"魔术位"数组,它出现在给定格式的每个文件的开头,值是从流中提取大小的函数.大多数格式都很简单,唯一真正的stinker是jpeg.
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 +对图像数据进行昂贵的分析,从而严重减少加载时间. 这个问题为这个问题提供了更多的启示.
您是否尝试过使用WPF Imaging类?System.Windows.Media.Imaging.BitmapDecoder
等等?
我相信一些努力是为了确保这些编解码器只读取文件的一个子集以确定头信息.值得一试.
几个月前我正在寻找类似的东西.我想阅读GIF图像的类型,版本,高度和宽度,但在网上找不到任何有用的东西.
幸运的是,在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
如上所述,wotsit是对图像和数据格式详细规格良好的网站虽然在PNG规范PNGLIB是更详细.但是,我认为维基百科关于PNG和GIF格式的条目是最佳起点.
这是我检查GIF的原始代码,我还为PNG打了一些东西:
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);
}
}
基于到目前为止的答案和一些额外的搜索,似乎在.NET 2类库中没有它的功能.所以我决定写自己的.这是一个非常粗略的版本.目前,我只需要它用于JPG.所以它完成了阿巴斯发布的答案.
没有错误检查或任何其他验证,但我目前需要它用于有限的任务,并且最终可以轻松添加.我在一些图像上测试了它,它通常不会从图像中读取更多的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); } } } }