2010-11-03 21 views
1

Je me demandais s'il y avait un moyen peu coûteux d'obtenir la largeur et la hauteur d'un JPEG après le chargement d'un tableau d'octets.Taille de l'image d'analyse de JPEG

Je sais que JpegBitmapDecoder peut obtenir la largeur et la hauteur des pixels du JPEG, mais il charge aussi beaucoup d'informations, ce qui, à mon avis, serait une opération coûteuse.

Existe-t-il un autre moyen d'obtenir la largeur et la hauteur à partir du tableau d'octets sans le décoder?

Merci

Répondre

14

pour une raison inconnue, au lieu d'aller au lit, je suis allé travailler à ce sujet.

Voici un code qui résout ce problème avec des exigences de stockage minimales.

void Main() 
{ 
    var [email protected]"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); 
    } 
} 
+1

+1 Je suppose que c'est ce que cela signifie d'aller au-delà de fournir une réponse! – BrokenGlass

+0

J'ai essayé ceci, mais la séquence s'est terminée avec 0xe1 au lieu de 0xe0, et le codage ASCII est sorti comme EXIF. Je suppose que c'est un type différent d'image JPEG, mais cela signifie que cette solution ne fonctionne pas pour toutes les images JPEG. – Grungondola

+0

@Grungondola Vous avez raison ... il y a toute une série de fichiers '.jpg' qui ne fonctionneront pas avec le code ci-dessus. Après des recherches approfondies, je peux dire que de loin la meilleure façon d'extraire des métadonnées d'image est avec [imagemagick] (http://www.imagemagick.org/script/binary-releases.php) ... Pour fournir le meilleur isolement possible le processus d'hébergement, nous appelons le 'magick.exe identifier 'autonome avec' System.Diagnostics.Process' et analysons les méta-données de la sortie standard du processus. Assez à l'épreuve des balles. – spender

2

J'ai lu un article à ce sujet CodeProject un ans couple dos :) Je ne suis pas 100% sûr de savoir comment il est bon, et ne l'ai pas testé moi-même, mais certainement l'auteur heureux avec ça; aussi ses tests prouvent qu'il est beaucoup plus rapide que de lire l'image entière, comme vous vous en doutez :)

Voici l'article lui-même .. J'espère que c'est ce dont vous avez besoin! http://www.codeproject.com/KB/cs/ReadingImageHeaders.aspx
Le morceau de code que vous cherchez au sujet ici commence:
http://www.codeproject.com/KB/cs/ReadingImageHeaders.aspx#premain3

UPD: Vérifiez également les commentaires dans le fond .. Surtout le dernier (en haut), on y .. Pourrait être utile rendre plus générique

en outre, plus en profondeur, l'information avancée peut être repris ici: http://www.codeproject.com/KB/graphics/iptc.aspx