WPF中使用 metadata-extractor可以轻松获取 PNG图片metadata信息

NuGet 获取地址:

PM> Install-Package MetadataExtractor -Version 2.0.0
正在尝试解析依赖项“XmpCore (≥ 5.1.3)”。
正在安装“XmpCore 5.1.3.1”。
已成功安装“XmpCore 5.1.3.1”。
正在安装“MetadataExtractor 2.0.0”。
已成功安装“MetadataExtractor 2.0.0”。
正在将“XmpCore 5.1.3.1”添加到 Mishow。
已成功将“XmpCore 5.1.3.1”添加到 Mishow。
正在将“MetadataExtractor 2.0.0”添加到 Mishow。
已成功将“MetadataExtractor 2.0.0”添加到 Mishow。

经过测试,可以使用metadata-extractor获取png图片的iptc字段元数据,就像是C#下的exiv2

C++ 读取metadata也可以参考 http://dev.exiv2.org

本文主要转载,.net c#通过Exif获取图片信息(参数)

原博客:https://www.cnblogs.com/fancyblogs/p/5639960.html

简介

想要获取图片的信息,例如快门速度、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引用到您的项目中:

完整代码:


//参考文献
//官网: 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

C# 修改PNG图片metadata信息 (含转载fancyblogs博文)的更多相关文章

  1. 修改mp3图片和信息——BesMp3Editor

    导读 BesMp3Editor, 是一款小巧的 MP3 编辑工具,可以修改.添加 MP3 上的图片.歌曲名.歌手.专辑信息. 最近想给 BesLyric-for-X 添加一个功能,为下载下来的歌曲添加 ...

  2. Android 图片Exif信息相关的获取与修改

    1 Exif是什么 Exif是一种图像文件格式,它的数据存储于JPEG格式是完全相同的,实际上Exif格式就是JPEG格式头插入了 数码照片的信息,包括拍摄的光圈.快门.平衡白.ISO.焦距.日期时间 ...

  3. FusionChart 导出图片 功能实现(转载)

    FusionChart 导出图片 功能实现(转载) http://www.cnblogs.com/jiagoushi/archive/2013/02/05/2893468.html 题目:精美Fusi ...

  4. JavaScript修改Canvas图片

    用JavaScript修改Canvas图片的分辨率(DPI)   应用场景: 仓库每次发货需要打印标签, Canvas根据从数据库读取的产品信息可以生成标签JPG, 但是这个JPG图片的默认分辨率(D ...

  5. php正则获取html图片标签信息(采集图片)

    php获取html图片标签信息(采集图片),实现图片采集及其他功能,带代码如下: <?php $str="<img src='./a.jpg'/>111111<img ...

  6. Nginx_修改Web服务器头信息(Header)里的Server值[转]

    http://blog.rekfan.com/?p=122 黑客攻击一个网站,往往需要了解服务器的架构,网站的架构等信息,了解了这些信息,就知道网站薄弱的地方在哪里了!    为了不让对方知道自己的w ...

  7. Android--操作图片Exif信息

    前言 在Android系统中,图片文件在内存中以像素点的二维数组加载,存放像素信息,还会在开头加上一些额外的照片拍摄参数信息,这些信息就是Exif.Android2.0之后,媒体库加入了操作图片Exi ...

  8. PS中如何提高修改psd图片的效率(自动选择工具)

    在photoshop中制作图片的时候,一般要养成保留psd格式的习惯,纵然普通时候jpg,png格式常用,考虑到以后可能需要修改,也应该备份一下.如果考虑到以后需要修改,可每次成品保存成两个,一个ps ...

  9. usermod - linux修改用户帐户信息

    usermod - 修改用户帐户信息 modify a user account usermod [options] user_name usermod 命令修改系统帐户文件来反映通过命令行指定的变化 ...

随机推荐

  1. Docker 命令自动补全?要的

    前言 不知道这个小伙伴有多久没用过 Docker 了, 突然对我说 Docker 命令怎么发生变化了 docker run ... #变成了 docker container run ... 他说,本 ...

  2. ucore操作系统学习(六) ucore lab6线程调度器

    1. ucore lab6介绍 ucore在lab5中实现了较为完整的进程/线程机制,能够创建和管理位于内核态或用户态的多个线程,让不同的线程通过上下文切换并发的执行,最大化利用CPU硬件资源.uco ...

  3. 基于Fisco-Bcos的区块链智能合约-简单案例实践

    一.智能合约介绍 智能合约是指把合同/协议条款以代码的形式电子化地放到区块链网络上.FISCO BCOS平台支持两种智能合约类型:Solidity智能合约与预编译智能合约 Solidity与Java类 ...

  4. JApiDocs(自动生成接口文档神器)

    JApiDocs教程 前言 作为一名优秀的程序员来说,由于涉及到要与前端进行对接,所以避免不了的就是写接口文档.写完接口文档,一旦代码返回结果,参数等出现变动,接口文档还得随之改动,十分麻烦,违背了我 ...

  5. 关于django python manage.py startapp 应用名 出错异常原因

    如题,在控制台运行python manage.py startapp sales 建立一个应用报错异常 1.应用名不能包含下划线等字符 所以app-demo 不能作为应用名被定义 2.manage.p ...

  6. 云原生网络代理(MOSN)的进化之路

    本文系云原生应用最佳实践杭州站活动演讲稿整理.杭州站活动邀请了 Apache APISIX 项目 VP 温铭.又拍云平台开发部高级工程师莫红波.蚂蚁金服技术专家王发康.有赞中间件开发工程师张超,分享云 ...

  7. db2常用操作

    1. db2建立远程节点编目及删除 db2 catalog tcpip node nodeName remote remoteIp server remotePort db2 list node di ...

  8. ATS push cache 测试

    测试 ATS 注入缓存 参考了: http://serverfault.com/questions/471684/push-content-to-apache-traffic-servers-cach ...

  9. 个人微信公众号搭建Python实现 -个人公众号搭建-被动回复消息建模(14.3.2)

    @ 目录 1.阅读官方文档 2.思考 关于作者 1.阅读官方文档 点击进入微信官方开发者文档 接收普通消息 文本消息 图片消息 语言消息 视频消息 小视频消息 地理位置消息 链接消息 接收事件消息 关 ...

  10. .NET生态系统掠影

    如果你是一名开发人员,想要进入到.NET的世界,你需要知道都有哪些可能.由于.NET Framework是..NET生态系统中最流行的技术,你可以用它来构建各种各样的应用程序,但是最近,出现了一些新的 ...