2008-09-21 14 views
94

क्या छवि के आयाम (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

यह मदद मिलेगी अगर आप सवाल उचित में थोड़ा अधिक विशिष्ट थे। टैग ने मुझे .NET और C# बताया है, और आप मानक लाइब्रेरी चाहते हैं, लेकिन इन होस्टिंग प्रतिबंधों का क्या उल्लेख है? – wnoise

+0

यदि आपके पास System.Windows.Media.Imaging नामस्थान (WPF में) तक पहुंच है, तो यह SO प्रश्न देखें: http://stackoverflow.com/questions/784734/using-wpf-imaging-classes-getting-image-dimensions -with-read-the-whole? lq = 1 – Charlie

उत्तर

96

आपका सबसे अच्छा शर्त के रूप में हमेशा एक अच्छी तरह से परीक्षण किया पुस्तकालय को मिल रहा है:

यहाँ GIFs की जाँच के लिए अपने मूल कोड है, मैं भी एक साथ PNG का के लिए कुछ थप्पड़ मारा है। हालांकि, अगर आप ने कहा कि मुश्किल है, इसलिए यहाँ कुछ कुशल काफी हद तक अपरीक्षित कोड है कि मामलों का एक उचित संख्या के लिए काम करना चाहिए है:

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 पर जोड़ते हैं, जिसमें कुंजी "जादू बिट्स" की सरणी होती है जो दिए गए प्रारूप की प्रत्येक फ़ाइल की शुरुआत में दिखाई देती है और मान एक फ़ंक्शन होता है जो स्ट्रीम से आकार निकालता है।अधिकांश प्रारूप काफी सरल हैं, केवल वास्तविक स्टिंकर जेपीईजी है।

+6

सहमत, जेपीईजी बेकार है। बीटीडब्ल्यू - उन लोगों के लिए एक नोट जो भविष्य में इस कोड का उपयोग करना चाहते हैं: यह वास्तव में अनचाहे है। मैं इसे एक अच्छे कंघी के साथ चला गया हूं, और यहां मैंने जो पाया है: बीएमपी प्रारूप में एक और (प्राचीन) शीर्षलेख भिन्नता है जहां आयाम 16-बिट हैं; प्लस ऊंचाई नकारात्मक हो सकती है (तब संकेत छोड़ दें)। जेपीईजी के लिए - 0xC0 एकमात्र हेडर नहीं है। 0xC4 और 0xCC को छोड़कर मूल रूप से 0xC0 से 0xCF के सभी वैध हेडर होते हैं (आप उन्हें आसानी से अंतःस्थापित जेपीजी में प्राप्त कर सकते हैं)। और, चीजों को और मजेदार बनाने के लिए, ऊंचाई 0 हो सकती है और बाद में 0xDC ब्लॉक में निर्दिष्ट की जा सकती है। देखें http://www.w3.org/Graphics/JPEG/itu-t81.pdf –

+1

@ विल्क्स-, क्या आप अधिक संपूर्ण कोड साझा कर सकते हैं? – Pedro77

+0

मूल (मार्कर == 0xC0) को विस्तारित करने के लिए उपरोक्त DecodeJfif विधि को ट्वीक किया गया है ताकि 0xC1 और 0xC2 को भी स्वीकार किया जा सके। इन अन्य स्टार्ट-ऑफ-फ्रेम शीर्षलेख एसओएफ 1 और एसओएफ 2 समान बाइट स्थितियों में चौड़ाई/ऊंचाई एन्कोड करते हैं। एसओएफ 2 काफी आम है। –

22

आप WPF इमेजिंग वर्गों का उपयोग करने की कोशिश की है? System.Windows.Media.Imaging.BitmapDecoder, आदि?

मेरा मानना ​​है कि कुछ प्रयास यह सुनिश्चित करने में थे कि हेडर जानकारी को निर्धारित करने के लिए उन कोडेक्स केवल फ़ाइल का सबसेट पढ़ लें। यह एक चेक लायक है।

+0

धन्यवाद। यह उचित लगता है, लेकिन मेरी होस्टिंग में .NET 2. –

+1

उत्कृष्ट उत्तर है। यदि आप प्रोजेक्टेशनकोर को अपनी परियोजना में संदर्भ प्राप्त कर सकते हैं, तो यह रास्ता तय करने का तरीका है। – ojrac

+0

मेरे यूनिट परीक्षणों में, ये कक्षाएं जीडीआई से बेहतर प्रदर्शन नहीं करती हैं ... अभी भी जेपीईजी आयाम पढ़ने के लिए ~ 32K की आवश्यकता है। – Nariman

-1

यह फ़ाइल प्रारूप पर निर्भर करेगा। आमतौर पर वे इसे फ़ाइल के प्रारंभिक बाइट्स में बताएंगे। और, आमतौर पर, एक अच्छा छवि-पढ़ने का कार्यान्वयन इसे ध्यान में रखेगा। मैं आपको .NET के लिए एक बिंदु पर इंगित नहीं कर सकता।

1

हां, आप बिल्कुल ऐसा कर सकते हैं और कोड फ़ाइल प्रारूप पर निर्भर करता है। मैं एक इमेजिंग विक्रेता (Atalasoft) के लिए काम करता हूं, और हमारा उत्पाद प्रत्येक कोडेक के लिए GetImageInfo() प्रदान करता है जो कम से कम आयामों को ढूंढने के लिए करता है और कुछ अन्य डेटा प्राप्त करने में आसान होता है।

यदि आप अपना खुद का रोल करना चाहते हैं, तो मैं wotsit.org से शुरू करने का सुझाव देता हूं, जिसमें सभी छवि प्रारूपों के लिए विस्तृत चश्मा हैं और आप देखेंगे कि फ़ाइल की पहचान कैसे करें और इसमें जानकारी कहां मिल सकती है।

यदि आप सी के साथ काम करने में सहज महसूस कर रहे हैं, तो इस जानकारी को प्राप्त करने के लिए मुफ्त jpeglib का उपयोग किया जा सकता है। मैं शर्त लगाता हूं कि आप इसे .NET पुस्तकालयों के साथ कर सकते हैं, लेकिन मुझे नहीं पता कि कैसे।

+0

यह मानना ​​सुरक्षित है कि ['नई AtalaImage (filepath) का उपयोग कर। Width'] (http://www.atalasoft.com/docs/joltimage/docs103/com/atalasoft/imaging/AtalaImage.html#AtalaImage (java.lang.String)) कुछ करता है समान? – drzaus

+0

या बस ['Atalasoft.Imaging.Codec.RegisteredDecoders.GetImageInfo (fullPath) .Size'] (https://www.atalasoft.com/cs/forums/thread/13163.aspx) – drzaus

+1

पहला (AtalaImage) पढ़ता है पूरी छवि - दूसरा (GetImageInfo) छवि जानकारी ऑब्जेक्ट के तत्व प्राप्त करने के लिए न्यूनतम मेटाडेटा पढ़ता है। –

12

मैं कुछ महीने पहले इसी तरह की तलाश कर रहा था। मैं एक जीआईएफ छवि के प्रकार, संस्करण, ऊंचाई और चौड़ाई को पढ़ना चाहता था लेकिन ऑनलाइन उपयोगी कुछ भी नहीं मिला।

सौभाग्य GIF के मामले में, सभी आवश्यक जानकारी पहले 10 बाइट्स में था:

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

पीएनजी थोड़ा अधिक जटिल हैं (चौड़ाई और ऊंचाई प्रत्येक 4-बाइट हैं):

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

जैसा ऊपर बताया गया है, wotsit छवि और डेटा स्वरूपों पर विस्तृत चश्मा के लिए एक अच्छी साइट है हालांकि पीएनजी चश्मे pnglib पर अधिक विस्तृत हैं। हालांकि, मुझे लगता है कि PNG और GIF प्रारूपों पर विकिपीडिया प्रविष्टि प्रारंभ करने के लिए सबसे अच्छी जगह है।

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); 
    } 
} 
8

अब तक और कुछ अतिरिक्त खोजों के आधार पर, ऐसा लगता है कि .NET 2 क्लास लाइब्रेरी में इसके लिए कोई कार्यक्षमता नहीं है। तो मैंने अपना खुद का लिखने का फैसला किया। यहां इसका एक बहुत ही कठिन संस्करण है। फिलहाल, मुझे केवल जेपीजी के लिए इसकी आवश्यकता थी। तो यह अब्बास द्वारा पोस्ट उत्तर पूरा करता है।

कोई त्रुटि जांच या कोई अन्य सत्यापन नहीं है, लेकिन मुझे वर्तमान में सीमित कार्य के लिए इसकी आवश्यकता है, और इसे अंततः आसानी से जोड़ा जा सकता है। मैंने इसे कई छवियों पर परीक्षण किया, और आमतौर पर यह छवि से 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); 
      } 
     } 

    } 
} 
+0

जब मैं कोशिश करता हूं तो चौड़ाई और ऊंचाई उलट जाती है। –

19
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 + रोकता है। This question विषय पर अधिक प्रकाश डालता है।

+1

मैंने आपके समाधान का उपयोग आईसीआर के समाधान के साथ मिश्रित अंतिम संसाधन के रूप में किया था। जेपीईजी के साथ समस्याएं थीं, और इसके साथ हल किया गया। – Zorkind

+1

मैंने हाल ही में एक परियोजना में यह कोशिश की जहां मुझे 2000+ छवियों (जेपीजी और पीएनजी ज्यादातर, बहुत मिश्रित आकार) के आकार से पूछताछ करना पड़ा, और यह 'नया बिटमैप() 'का उपयोग करके पारंपरिक तरीके से वास्तव में बहुत तेज़ था। – AeonOfTime

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); 
संबंधित मुद्दे