zoukankan      html  css  js  c++  java
  • .net c#通过Exif获取图片信息(参数)

    简介

          想要获取图片的信息,例如快门速度、ISO值等等,我们可以通过读取Exif中存储的信息。Exif(Exchangeable Image File)是存储在JPEG格式照片头部的一段信息,相机和手机拍摄的照片都会携带这些信息,但是需要注意,PS的照片的时候采用低质量保存会丢失这些信息。在PS中保存为10-12等级的时候不会丢失,在美图秀秀中保存质量为100%不会丢失。软件在处理的时候也会将自己的信息写入Exif,所以也可以通过这种方式判断是否为原图,或者图片是否经过处理。

         本文中我介绍两种方式获取Exif。一是C#自带的Image.PropertyItems 属性(了解),二是通过第三方控件metadata-extractor获取(推荐)。

    一、通过Image.PropertyItems 属性获取照片信息

    Image.PropertyItems 属性中有几个重要属性,Id:为int型,不同的Id表示不同的参数的;Value:表示参数的值,byte[]型;Len:为int型,表示Value的长度,以字节为单位;Type:short型,表示Value的取数方法。Type主要有以下几个类型:

    type=1 时 Value 为字节数组。

    type=2 时 Value 为空终止 ASCII 字符串。如果将类型数据成员设置为 ASCII 类型,则应该将 Len 属性设置为包括空终止的字符串长度。例如,字符串“Hello”的长度为 6

    type=3 时 Value 为无符号的短(16 位)整型数组。

    type=4 时 Value 为无符号的长(32 位)整型数组。

    type=5 时 Value 数据成员为无符号的长整型对数组。每一对都表示一个分数;第一个整数是分子,第二个整数是分母。

    type=6 时 Value 为可以包含任何数据类型的值的字节数组。

    type=7 时 Value 为有符号的长(32 位)整型数组。

    type=10 时 Value 为有符号的长整型对数组。每一对都表示一个分数;第一个整数是分子,第二个整数是分母。

    参考文献:http://blog.csdn.net/yang073402/article/details/5470127

    在使用Image.PropertyItems属性时需要引用:using System.Drawing

     下面是代码:

    #region 通过PropertyItems获取照片参数
    
            /// <summary>
            /// 表示参数的结构
            /// </summary>
            public struct Exif 
            {
                /// <summary>
                /// 数据的ID
                /// </summary>
                public string Id;
                /// <summary>
                /// 数据类型
                /// </summary>
                public int Type;
                /// <summary>
                /// 数据中值的字节长度
                /// </summary>
                public int Length;
    
                /// <summary>
                /// 根据ID对应的中文名
                /// </summary>
                public string Name;
    
                /// <summary>
                /// 根据原字节解析的参数值
                /// </summary>
                public string Value;
            }
    
            /// <summary>将字节通过ASCII转换为字符串
            /// </summary>
            /// <param name="bt">原字节</param>
            /// <returns></returns>
            private static string ToStrOfByte(this byte[] bt)
            {
                return Encoding.ASCII.GetString(bt);
            }
    
            /// <summary>将字节转换为int
            /// </summary>
            /// <param name="bt">原字节</param>
            /// <returns></returns>
            private static int ToUnInt16(this byte[] bt)
            {
                return Convert.ToUInt16(bt[1] << 8 | bt[0]);
            }
    
            /// <summary>将原两组字节转换为uint
            /// </summary>
            /// <param name="bt">原字节</param>
            /// <param name="isFirst">是否转第一个字节组</param>
            /// <returns></returns>
            private static uint ToUnInt32(this byte[] bt,bool isFirst=true)
            {
                return isFirst ? Convert.ToUInt32(bt[3] << 24 | bt[2] << 16 | bt[1] << 8 | bt[0]) : Convert.ToUInt32(bt[7] << 24 | bt[6] << 16 | bt[5] << 8 | bt[4]);
            }
    
            /// <summary>获取曝光模式
            /// </summary>
            /// <param name="value">曝光模式值</param>
            /// <returns></returns>
            private static string ExposureMode(int value)
            {
                var rt = "Undefined";
                switch (value)
                {
                    case 0:
                        rt = "自动"; break;
                    case 1:
                        rt = "手动控制"; break;
                    case 2:
                        rt = "程序控制"; break;
                    case 3:
                        rt = "光圈优先"; break;
                    case 4:
                        rt = "快门优先"; break;
                    case 5:
                        rt = "夜景模式"; break;
                    case 6:
                        rt = "运动模式"; break;
                    case 7:
                        rt = "肖像模式"; break;
                    case 8:
                        rt = "风景模式"; break;
                    case 9:
                        rt = "其他模式"; break;
                }
                return rt;
            }
    
            /// <summary>获取测光模式
            /// </summary>
            /// <param name="value">测光模式值</param>
            /// <returns></returns>
            private static string MeteringMode(int value)
            {
                var rt = "Unknown";
                switch (value)
                {
                    case 0:
                        rt = "Unknown"; break;
                    case 1:
                        rt = "平均测光"; break;
                    case 2:
                        rt = "中央重点平均测光"; break;
                    case 3:
                        rt = "点测光"; break;
                    case 4:
                        rt = "多点测光"; break;
                    case 5:
                        rt = "评价测光"; break;
                    case 6:
                        rt = "局部测光"; break;
                    case 255:
                        rt = "其他测光"; break;
                }
                return rt;
            }
    
            /// <summary>获取闪光灯模式
            /// </summary>
            /// <param name="value">闪光灯值</param>
            /// <returns></returns>
            private static string FlashMode(int value)
            {
                var rt = "Unkown";
                switch (value)
                {
                    case 0:
                        rt = "未使用"; break;
                    case 1:
                        rt = "使用闪光灯"; break;
                }
                return rt;
            }
    
            /// <summary>获取白平衡模式
            /// </summary>
            /// <param name="value">白平衡值</param>
            /// <returns></returns>
            private static string WhiteBalance(int value)
            {
                var rt = "Unkown";
                switch (value)
                {
                    case 0: rt = "自动";//Unkown
                        break;
                    case 1: rt = "日光";
                        break;
                    case 2: rt = "荧光灯";
                        break;
                    case 3: rt = "白炽灯";
                        break;
                    case 17: rt = "标准光源A";
                        break;
                    case 18: rt = "标准光源B";
                        break;
                    case 19: rt = "标准光源C";
                        break;
                    case 255: rt = "其他";
                        break;
                }
                return rt;
            }
    
            /// <summary>通过Id获取Exif中关键名称和值
            /// </summary>
            /// <param name="pId">ID</param>
            /// <param name="pType">类型</param>
            /// <param name="pBytes">字节值</param>
            /// <returns></returns>
            private static Exif InfoOfExif(int pId,int pType,byte[] pBytes)
            {
    
                var rt=new Exif {
                    Id ="0X"+pId.ToString("X"), 
                    Length = pBytes.Length, 
                    Type = pType
                };
                uint fm;
                uint fz;
                switch (pId)
                {
                    case 0x010F:
                        rt.Name = "相机制造商";
                        rt.Value = pBytes.ToStrOfByte();
                        break;
                    case 0x0110:
                        rt.Name = "相机型号";
                        rt.Value = pBytes.ToStrOfByte();
                        break;
                    case 0xA433:
                        rt.Name = "镜头制造商";
                        rt.Value = pBytes.ToStrOfByte();
                        break;
                    case 0xA434:
                        rt.Name = "镜头型号";
                        rt.Value = pBytes.ToStrOfByte();
                        break;
                    case 0x9003:
                        rt.Name = "拍摄时间";
                        var temp=pBytes.ToStrOfByte().Split(' ');
                        rt.Value =temp[0].Replace(":","/")+" "+temp[1];
                        break;
                    case 0x0132:
                        rt.Name = "修改时间";
                        temp=pBytes.ToStrOfByte().Split(' ');
                        rt.Value =temp[0].Replace(":","/")+" "+temp[1];
                        break;
                    case 0x0131:
                        rt.Name = "软件";
                        rt.Value = pBytes.ToStrOfByte();
                        break;
                    case 0xA002:
                        rt.Name = "图像高度";
                        rt.Value = pBytes.ToUnInt16()+" px";
                        break;
                    case 0xA003:
                        rt.Name = "图像宽度";
                        rt.Value = pBytes.ToUnInt16()+" px";
                        break;
                    case 0x011A:
                         fm=pBytes.ToUnInt32(false); 
                         fz= pBytes.ToUnInt32();
                        rt.Value = fm == 1 ? fz.ToString() : fz + "/" + fm;
                        rt.Value+=" dpi";
                        rt.Name = "水平方向分辨率";
                        break;
                    case 0x011B:
                        fm=pBytes.ToUnInt32(false); 
                         fz= pBytes.ToUnInt32();
                        rt.Value = fm == 1 ? fz.ToString() : fz + "/" + fm;
                        rt.Value += " dpi";
                        rt.Name = "垂直方向分辨率";
                        break;
                    case 0x8822:
                        rt.Value = ExposureMode(pBytes.ToUnInt16());
                        rt.Name = "曝光程序";
                        break;
                    case 0x9207:
                        rt.Value = MeteringMode(pBytes.ToUnInt16());
                        rt.Name = "测光模式";
                        break;
                    case 0x829A:
                        fm=pBytes.ToUnInt32(false); 
                        fz= pBytes.ToUnInt32();
                        //分母大于分子写为1/XXX,分母小于分子,写为保留一位小数
                        rt.Value = fm>fz ? "1/"+fm/fz:((double)fz/fm).ToString("0.0");
                        rt.Value += "";
                        rt.Name = "曝光时间";
                        break;
                    case 0x8827:
                        rt.Value = pBytes.ToUnInt16().ToString();
                        rt.Name = "ISO";
                        break;
                    case 0x920A:
                        fm=pBytes.ToUnInt32(false); 
                         fz= pBytes.ToUnInt32();
                        rt.Value=fm==1?fz.ToString():((double)fz/fm).ToString("0.00");
                        rt.Value += " mm";
                        rt.Name = "焦距";
                        break;
                    case 0x829D:
                        rt.Value ="f/"+((double)pBytes.ToUnInt32() / pBytes.ToUnInt32(false));
                        rt.Name = "光圈";
                        break;
                    
                    case 0x9204:
                        fm = pBytes.ToUnInt32(false);
                        var fz1=Convert.ToInt32(pBytes[3] << 24 | pBytes[2] << 16 | pBytes[1] << 8 | pBytes[0]);
                        //曝光补偿要加+ -
                        rt.Value = fz1 > 0 ? "+" : "";
                        rt.Value +=fz1 == 0 ? "0" : fz1+ "/"+ fm;
                        rt.Name = "曝光补偿";
                        break;
                    case 0x9208:
                        rt.Value = WhiteBalance(pBytes.ToUnInt16());
                        rt.Name = "白平衡";
                        break;
                    case 0x9209:
                        rt.Value = FlashMode(pBytes.ToUnInt16());
                        rt.Name = "闪光灯";
                        break;
                    default: rt.Name = "其他";
                        rt.Value = "Unkown";
                        break;
                }
                return rt;
            }
    
            /// <summary>通过PropertyItems获取照片参数
            /// </summary>
            /// <param name="imgPath">照片的绝对路径</param>
            /// <returns>参数的集合</returns>
            public static IEnumerable<Exif> GetExifByPi(string imgPath)
            {
                var img = Image.FromFile(imgPath);
                var pItems = img.PropertyItems;//将"其他"信息过滤掉
                return pItems.Select(pi => InfoOfExif(pi.Id, pi.Type, pi.Value)).Where(j=>j.Name!="其他").ToList();
            }
    
            #endregion

    在调用的时候用 var piList=GetExifByPi("照片路径");这种方法需要注意以下几个方面:

    注意:

    1、Image.PropertyItems的Type中同一个类型有的时候不能用同一个方法得到,这是由于参数的表现方式不同,所以建议用Id,每一个ID用对应的方法将byte[]装换为string。

    2、不同型号的手机和相机Exif中存储方式不一样,这一点非常重要,也就是说这个方法其实无法准确获每个图片的信息。我们需要将每种相机和手机分别用不同方法获取,这个工作量太大了,幸好有第三方插件。

    二、通过metadata-extractor获取照片参数

    metadata-extractor是目前最简单易用的EXIF信息处理包,是由Drew Noakes写的。官网: https://drewnoakes.com/code/exif/  官网上面的是用的.nupkg的文件,而不是传统的.dll文件,需要通过nuget引入本地。如果不会安装和使用nuget的可以参考文献:http://www.cnblogs.com/chsword/archive/2011/09/14/NuGet_Install_OperatePackage.html  成功安装nuget后再vs中点击:工具->NuGet程序包管理器->程序包管理器控制台。

    然后在"pm>"处输入:Install-Package MetadataExtractor  可以参考:https://www.nuget.org/packages/MetadataExtractor/ 

    最后将dll引用到您的项目中:

    完整代码:

    #region 通过metadata-extractor获取照片参数
    
            //参考文献
            //官网: https://drewnoakes.com/code/exif/
            //nuget 官网:https://www.nuget.org/
            //nuget 使用: http://www.cnblogs.com/chsword/archive/2011/09/14/NuGet_Install_OperatePackage.html
            //nuget MetadataExtractor: https://www.nuget.org/packages/MetadataExtractor/
    
            /// <summary>通过MetadataExtractor获取照片参数
            /// </summary>
            /// <param name="imgPath">照片绝对路径</param>
            /// <returns></returns>
            public static Dictionary<string,string> GetExifByMe(string imgPath)
            { 
                var rmd = ImageMetadataReader.ReadMetadata(imgPath);
                var rt=new Dictionary<string,string>();
                foreach (var rd in rmd)
                {
                    foreach(var tag in rd.Tags)
                    {
                        var temp = EngToChs(tag.Name);
                        if (temp == "其他")
                        {
                            continue;
                        }
                        if (!rt.ContainsKey(temp))
                        {
                            rt.Add(temp, tag.Description);
                        }
                        
                    }
                }
                return rt;
            }
    
            /// <summary>筛选参数并将其名称转换为中文
            /// </summary>
            /// <param name="str">参数名称</param>
            /// <returns>参数中文名</returns>
            private static string EngToChs(string str)
            {
                var rt = "其他";
                switch (str)
                {
                    case "Exif Version": rt = "Exif版本";
                        break;
                    case "Model": rt = "相机型号";
                        break;
                    case "Lens Model": rt = "镜头类型";
                        break;
                    case "File Name": rt = "文件名";
                        break;
                    case "File Size": rt = "文件大小";
                        break;
                    case "Date/Time": rt = "拍摄时间";
                        break;
                    case "File Modified Date": rt = "修改时间";
                        break;
                    case "Image Height": rt = "照片高度";
                        break;
                    case "Image Width": rt = "照片宽度";
                        break;
                    case "X Resolution": rt = "水平分辨率";
                        break;
                    case "Y Resolution": rt = "垂直分辨率";
                        break;
                    case "Color Space": rt = "色彩空间";
                        break;
    
                    case "Shutter Speed Value": rt = "快门速度";
                        break;
                    case "F-Number": rt = "光圈";//Aperture Value也表示光圈
                        break;
                    case "ISO Speed Ratings": rt = "ISO";
                        break;
                    case "Exposure Bias Value": rt = "曝光补偿";
                        break;
                    case "Focal Length": rt = "焦距";
                        break;
    
                    case "Exposure Program": rt = "曝光程序";
                        break;
                    case "Metering Mode": rt = "测光模式";
                        break;
                    case "Flash Mode": rt = "闪光灯";
                        break;
                    case "White Balance Mode": rt = "白平衡";
                        break;
                    case "Exposure Mode": rt = "曝光模式";
                        break;
                    case "Continuous Drive Mode": rt = "驱动模式";
                        break;
                    case "Focus Mode": rt = "对焦模式";
                        break;
                }
                return rt;
            }
    
            #endregion

    使用的时候:var me=GetExifByMe(); 

    注意:

    1、var rmd = ImageMetadataReader.ReadMetadata(imgPath);方法里可以是照片路径和Stream类型。

    2、metadata-extractor会将所有信息读出来,而且还是英文的,所以要将里面的数据进行选取,需要的还要转换为中文。

    参考文献:
    官网: https://drewnoakes.com/code/exif/
    nuget MetadataExtractor: https://www.nuget.org/packages/MetadataExtractor/
    nuget 使用: http://www.cnblogs.com/chsword/archive/2011/09/14/NuGet_Install_OperatePackage.html

  • 相关阅读:
    ActiveMQ 即时通讯服务 浅析
    Asp.net Mvc (Filter及其执行顺序)
    ActiveMQ基本介绍
    ActiveMQ持久化消息的三种方式
    Windows Azure Virtual Machine (27) 使用psping工具,测试Azure VM网络连通性
    Azure China (10) 使用Azure China SAS Token
    Windows Azure Affinity Groups (3) 修改虚拟网络地缘组(Affinity Group)的配置
    Windows Azure Storage (22) Azure Storage如何支持多级目录
    Windows Azure Virtual Machine (26) 使用高级存储(SSD)和DS系列VM
    Azure Redis Cache (2) 创建和使用Azure Redis Cache
  • 原文地址:https://www.cnblogs.com/fancyblogs/p/5639960.html
Copyright © 2011-2022 走看看