获取图像的尺寸不读取整个文件 [英] Getting image dimensions without reading the entire file

查看:144
本文介绍了获取图像的尺寸不读取整个文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有没有一种廉价的方式来获得图像(JPG,PNG,...)的尺寸是多少? preferably,我想这一点使用(因为托管限制)只有标准类库来实现。我知道这应该是比较容易读取图像标题和解析它自己,但似乎这样的事情应该是已经存在。此外,我已经验证了下面这段code读取整个图像(我不希望):

 使用系统;
使用System.Drawing中;

命名空间测试
{
    类节目
    {
        静态无效的主要(字串[] args)
        {
            图片IMG =新位图(test.png);
            的System.Console.WriteLine(img.Width +×+ img.Height);
        }
    }
}
 

解决方案

您最好的选择一如既往的是要找到一个很好的测试库。但是,你说是困难的,所以这里是一些狡猾的大部分都未经检验code,它应该工作的案件相当数量的:

 使用系统;
使用System.Collections.Generic;
使用System.Drawing中;
使用System.IO;
使用System.Linq的;

命名空间ImageDimensions
{
    公共静态类ImageHelper
    {
        常量字符串的errorMessage =无法识别的图像格式。

        私有静态字典< byte []的,Func键< BinaryReader在,大小和GT;> imageFormatDe codeRS =新字典< byte []的,Func键< BinaryReader在,大小和GT;>()
        {
            {新的byte [] {0x42后,送出0x4d},德$ C $的CBitmap},
            {新的byte [] {0X47,0x49,将0x46,0x38,0x37符号,0x61},德codeGIF},
            {新的byte [] {0X47,0x49,将0x46,0x38,0x39,0x61},德codeGIF},
            {新的byte [] {0x89上,为0x50,0x4E,0X47,0X0D,0x0A的,0x1A的,0x0A的},德codePng},
            {新的byte [] {0xFF时,为0xD8},德codeJfif},
        };

        ///<总结>
        ///获取的图像的尺寸。
        ///< /总结>
        ///< PARAM NAME =路径>在图像的路径获得的尺寸和LT; /参数>
        ///<返回>指定图像的尺寸和LT; /回报>
        ///<异常CREF =ArgumentException的>该形象是无法识别的格式< /异常>
        公共静态尺寸GetDimensions(字符串路径)
        {
            使用(BinaryReader在BinaryReader在新= BinaryReader在(File.OpenRead(路径)))
            {
                尝试
                {
                    返回GetDimensions(BinaryReader在);
                }
                赶上(ArgumentException的E)
                {
                    如果(e.Message.StartsWith(的errorMessage))
                    {
                        抛出新的ArgumentException(的errorMessage,路径,E);
                    }
                    其他
                    {
                        扔ê;
                    }
                }
            }
        }

        ///<总结>
        ///获取的图像的尺寸。
        ///< /总结>
        ///< PARAM NAME =路径>在图像的路径获得的尺寸和LT; /参数>
        ///<返回>指定图像的尺寸和LT; /回报>
        ///<异常CREF =ArgumentException的>该形象是无法识别的格式< /异常>
        公共静态尺寸GetDimensions(BinaryReader在BinaryReader在)
        {
            INT maxMagicBytesLength = imageFormatDe coders.Keys.OrderByDescending(X => x.Length)。首先()长度。

            byte []的magicBytes =新的字节[maxMagicBytesLength]

            的for(int i = 0; I< maxMagicBytesLength; I + = 1)
            {
                magicBytes [I] = binaryReader.ReadByte();

                的foreach(在imageFormatDe codeRS VAR kvPair)
                {
                    如果(magicBytes.StartsWith(kvPair.Key))
                    {
                        返回kvPair.Value(BinaryReader在);
                    }
                }
            }

            抛出新的ArgumentException(的errorMessageBinaryReader在);
        }

        私有静态布尔StartsWith(此字节[] thisBytes,byte []的thatBytes)
        {
            的for(int i = 0; I< thatBytes.Length; I + = 1)
            {
                如果(thisBytes [I]!= thatBytes [I])
                {
                    返回false;
                }
            }
            返回true;
        }

        私有静态短ReadLittleEndianInt16(BinaryReader在这个BinaryReader在)
        {
            字节[]字节=新字节[的sizeof(短);
            的for(int i = 0; I<的sizeof(短); I​​ + = 1)
            {
                字节[的sizeof(短) -  1  -  I] = binaryReader.ReadByte();
            }
            返回BitConverter.ToInt16(字节,0);
        }

        私有静态诠释ReadLittleEndianInt32(BinaryReader在这个BinaryReader在)
        {
            字节[]字节=新字节[的sizeof(INT)];
            的for(int i = 0; I<的sizeof(int)的;我+ = 1)
            {
                字节[的sizeof(int)的 -  1  -  I] = binaryReader.ReadByte();
            }
            返回BitConverter.ToInt32(字节,0);
        }

        私有静态尺寸德$ C $的CBitmap(BinaryReader在BinaryReader在)
        {
            binaryReader.ReadBytes(16);
            INT宽度= binaryReader.ReadInt32();
            INT高= binaryReader.ReadInt32();
            返回新尺寸(宽,高);
        }

        私有静态尺寸德codeGIF(BinaryReader在BinaryReader在)
        {
            INT宽度= binaryReader.ReadInt16();
            INT高= binaryReader.ReadInt16();
            返回新尺寸(宽,高);
        }

        私有静态尺寸德codePng(BinaryReader在BinaryReader在)
        {
            binaryReader.ReadBytes(8);
            INT宽度= binaryReader.ReadLittleEndianInt32();
            INT高= binaryReader.ReadLittleEndianInt32();
            返回新尺寸(宽,高);
        }

        私有静态尺寸德codeJfif(BinaryReader在BinaryReader在)
        {
            而(binaryReader.ReadByte()== 0xFF的)
            {
                字节标记= binaryReader.ReadByte();
                短chunkLength = binaryReader.ReadLittleEndianInt16();

                如果(标记==为0xC0)
                {
                    binaryReader.ReadByte();

                    INT高= binaryReader.ReadLittleEndianInt16();
                    INT宽度= binaryReader.ReadLittleEndianInt16();
                    返回新尺寸(宽,高);
                }

                binaryReader.ReadBytes(chunkLength  -  2);
            }

            抛出新的ArgumentException(的errorMessage);
        }
    }
}
 

但愿code是相当明显的。要添加新的文件格式,你用的关键是的神奇位,它出现在的每一个文件的开头阵列添加到 imageFormatDe codeRS 定格式,值为一个函数,其提取从该流的大小。大多数的格式很简单,唯一真正的臭鬼为JPEG。

Is there a cheap way to get the dimensions of an image (jpg, png, ...)? Preferably, I would like to achieve this using only the standard class library (because of hosting restrictions). I know that it should be relatively easy to read the image header and parse it myself, but it seems that something like this should be already there. Also, I’ve verified that the following piece of code reads the entire image (which I don’t want):

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

解决方案

Your best bet as always is to find a well tested library. However, you said that is difficult, so here is some dodgy largely untested code that should work for a fair number of cases:

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

Hopefully the code is fairly obvious. To add a new file format you add it to imageFormatDecoders with the key being an array of the "magic bits" which appear at the beginning of every file of the given format and the value being a function which extracts the size from the stream. Most formats are simple enough, the only real stinker is jpeg.

这篇关于获取图像的尺寸不读取整个文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆