有没有一种廉价的方式来获取图像的尺寸(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);
}
}
}
答案 0 :(得分:101)
您最好的选择是找到经过良好测试的图书馆。但是,你说这很难,所以这里有一些狡猾的未经测试的代码应该适用于相当多的案例:
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
,其中键是“魔术位”数组,它出现在给定格式的每个文件的开头,值是从中提取大小的函数。流。大多数格式都很简单,唯一真正的东西就是jpeg。
答案 1 :(得分:21)
您是否尝试过使用WPF Imaging类? System.Windows.Media.Imaging.BitmapDecoder
等等?
我相信一些努力是为了确保这些编解码器只读取文件的子集以确定标头信息。值得一试。
答案 2 :(得分:21)
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
的{{1}}会阻止GDI +对图像数据进行代价高昂的分析,从而严重降低加载时间。 This question更多地阐述了这一主题。
答案 3 :(得分:12)
幸运的是,在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是有关图像和数据格式详细规范的好网站,尽管pnglib的PNG规范更为详细。但是,我认为维基百科上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);
}
}
答案 4 :(得分:8)
基于到目前为止的答案和一些额外的搜索,似乎在.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);
}
}
}
}
答案 5 :(得分:2)
我为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);
答案 6 :(得分:2)
更新了ICR的答案,以支持渐进式jPegs和WebP:)
internal static class ImageHelper
{
const string errorMessage = "Could not recognise 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 },
{ new byte[] { 0x52, 0x49, 0x46, 0x46 }, DecodeWebP },
};
/// <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 unrecognised 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(StartsWith(magicBytes, kvPair.Key))
{
return kvPair.Value(binaryReader);
}
}
}
throw new ArgumentException(errorMessage, "binaryReader");
}
private static bool StartsWith(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(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(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 = ReadLittleEndianInt32(binaryReader);
int height = ReadLittleEndianInt32(binaryReader);
return new Size(width, height);
}
private static Size DecodeJfif(BinaryReader binaryReader)
{
while(binaryReader.ReadByte() == 0xff)
{
byte marker = binaryReader.ReadByte();
short chunkLength = ReadLittleEndianInt16(binaryReader);
if(marker == 0xc0 || marker == 0xc2) // c2: progressive
{
binaryReader.ReadByte();
int height = ReadLittleEndianInt16(binaryReader);
int width = ReadLittleEndianInt16(binaryReader);
return new Size(width, height);
}
if(chunkLength < 0)
{
ushort uchunkLength = (ushort)chunkLength;
binaryReader.ReadBytes(uchunkLength - 2);
}
else
{
binaryReader.ReadBytes(chunkLength - 2);
}
}
throw new ArgumentException(errorMessage);
}
private static Size DecodeWebP(BinaryReader binaryReader)
{
binaryReader.ReadUInt32(); // Size
binaryReader.ReadBytes(15); // WEBP, VP8 + more
binaryReader.ReadBytes(3); // SYNC
var width = binaryReader.ReadUInt16() & 0b00_11111111111111; // 14 bits width
var height = binaryReader.ReadUInt16() & 0b00_11111111111111; // 14 bits height
return new Size(width, height);
}
}
答案 7 :(得分:1)
是的,你绝对可以这样做,代码取决于文件格式。我为成像供应商(Atalasoft)工作,我们的产品为每个编解码器提供了一个GetImageInfo(),它最小化以找出维度和其他一些易于获取的数据。
如果您想自己动手,我建议从wotsit.org开始,它具有几乎所有图像格式的详细规格,您将看到如何识别文件以及可以找到信息的位置。
如果您习惯使用C语言,那么免费的jpeglib也可用于获取此信息。我敢打赌,你可以用.NET库做到这一点,但我不知道如何。
答案 8 :(得分:-1)
这将取决于文件格式。通常他们会在文件的早期字节中说出来。而且,通常,良好的图像阅读实施将考虑到这一点。我不能指出你的.NET。