Разобрать размер изображения из JPEG - PullRequest
1 голос
/ 04 ноября 2010

Мне было интересно, был ли недорогой способ получить ширину и высоту JPEG после загрузки массива байтов.

Я знаю, JpegBitmapDecoder может получить ширину и высоту пикселя JPEG, но он загружаетсятакже много информации, которая, как я полагаю, была бы дорогой операцией.

Есть ли другой способ получить ширину и высоту из массива байтов, не расшифровывая его?

Спасибо

Ответы [ 2 ]

17 голосов
/ 04 ноября 2010

По какой-то неизвестной причине вместо того, чтобы ложиться спать, я пошел работать над этим.

Вот код, который решает эту проблему с минимальными требованиями к хранилищу.

void Main()
{
    var filePath=@"path\to\my.jpg";
    var bytes=File.ReadAllBytes(filePath);
    var dimensions=GetJpegDimensions(bytes);
    //or
    //var dimensions=GetJpegDimensions(filePath);
    Console.WriteLine(dimensions);
}
public static Dimensions GetJpegDimensions(byte[] bytes)
{
    using(var ms=new MemoryStream(bytes))
    {
        return GetJpegDimensions(ms);
    }
}
public static Dimensions GetJpegDimensions(string filePath)
{
    using(var fs=File.OpenRead(filePath))
    {
        return GetJpegDimensions(fs);
    }
}
public static Dimensions GetJpegDimensions(Stream fs)
{
    if(!fs.CanSeek) throw new ArgumentException("Stream must be seekable");
    long blockStart;
    var buf = new byte[4];
    fs.Read(buf, 0, 4);
    if(buf.SequenceEqual(new byte[]{0xff, 0xd8, 0xff, 0xe0}))
    {
        blockStart = fs.Position;
        fs.Read(buf, 0, 2);
        var blockLength = ((buf[0] << 8) + buf[1]);
        fs.Read(buf, 0, 4);
        if(Encoding.ASCII.GetString(buf, 0, 4) == "JFIF" 
            && fs.ReadByte() == 0)
        {
            blockStart += blockLength;
            while(blockStart < fs.Length)
            {
                fs.Position = blockStart;
                fs.Read(buf, 0, 4);
                blockLength = ((buf[2] << 8) + buf[3]);
                if(blockLength >= 7 && buf[0] == 0xff && buf[1] == 0xc0)
                {
                    fs.Position += 1;
                    fs.Read(buf, 0, 4);
                    var height = (buf[0] << 8) + buf[1];
                    var width = (buf[2] << 8) + buf[3];
                    return new Dimensions(width, height);
                }
                blockStart += blockLength + 2;
            }
        }
    }
    return null;
}

public class Dimensions
{
    private readonly int width;
    private readonly int height;
    public Dimensions(int width, int height)
    {
        this.width = width;
        this.height = height;
    }
    public int Width
    {
        get{return width;}
    }
    public int Height
    {
        get{return height;}
    }
    public override string ToString()
    {
        return string.Format("width:{0}, height:{1}", Width, Height);
    }
}
2 голосов
/ 04 ноября 2010

Я прочитал статью о CodeProject пару лет назад :) Я не уверен на 100%, насколько она хороша, и сам не проверял, но автор определенно доволен ею;также его тесты доказывают, что это намного быстрее, чем чтение всего изображения, как и следовало ожидать:)

Вот сама статья. Надеюсь, это то, что вам нужно!http://www.codeproject.com/KB/cs/ReadingImageHeaders.aspx
Кусок кода, который вы ищете, начинается здесь:
http://www.codeproject.com/KB/cs/ReadingImageHeaders.aspx#premain3

UPD: Кроме того, проверьте комментарии внизу .. Особенно последний (верхний)там .. Может быть полезно сделать его более общим

Кроме того, здесь можно получить более подробную и расширенную информацию: http://www.codeproject.com/KB/graphics/iptc.aspx

...