如今的数码相机拍摄出来的照片概况上看都是很通俗的JPEG的图片,但凡是还包含着诸如相片拍摄时应用的相机临盆商、型号、光圈值、快门速度等各类附加信息,这就是所谓的Exif信息。Exif是一种图像文件格局,只是文件的后缀名还是沿用大师熟悉的jpg罢了。把握Exif信息对进修进步摄影技巧很有帮助。
C#.NET作为一种现代的全能开辟说话,对EXIF也有着较好的支撑。在.NET中,我们可以用PropertyItem对象来获取EXIF。
取得PropertyItem很简单。
Image img = Image.FromFile("支撑Exif的图片文件");
PropertyItem[] pt = img.PropertyItems;
如许就可以了,Exif信息都已载入到PropertyItem数组中了。
此中PropertyItem的ID,Type,Value属性是最首要的。
ID可以独一注解当前PropertyItem的含义。比如,0 x010F代表相机建造商,0 x8827代表ISO速度,0 x829D代表相机F值。这在MSDN中有很是详尽的介绍。我们起首须要花必然的精力将这些数字翻译成人类可懂得的天然说话。这个实现起来很简单然则很无聊,做成HashTable或本身写一堆case都可以,看小我喜好吧。
Value永远都是字节数组,但具体内容的取值办法随Type不合而不合。
Type是个整数,默示的类型在MSDN中是这么描述的:
1 指定 Value 为字节数组。
2 指定 Value 为空终止 ASCII 字符串。若是将类型数据成员设置为 ASCII 类型,则应当将 Len 属性设置为包含空终止的字符串长度。例如,字符串“Hello”的长度为 6。
3 指定 Value 为无符号的短(16 位)整型数组。
4 指定 Value 为无符号的长(32 位)整型数组。
5 指定 Value 数据成员为无符号的长整型对数组。每一对都默示一个分数;第一个整数是分子,第二个整数是分母。
6 指定 Value 为可以包含任何数据类型的值的字节数组。
7 指定 Value 为有符号的长(32 位)整型数组。
10 指定 Value 为有符号的长整型对数组。每一对都默示一个分数;第一个整数是分子,第二个整数是分母。
所以取Exif的算法重点在于如何按照Type值将Value字节数组变成人类可以懂得的值。
获取每一条Exif信息算法的框架可能是如许的:
foreach (PropertyItem p in pt)
{
switch(p.Type)
{
case 1:
CurrentExifDetail = GetValueOfType1(byte[] b);
break;
case 2:
CurrentExifDetail = GetValueOfType2(byte[] b);
break;
...
}
CurrentExifInfo = 翻译ID到人类可读文字(p.ID.ToString()) + ":" + CurrentExifDetail;
}
举个实际例子来看看。比如,当我们扫描PropertyItem到D == 0 x0110,发明其Type = 2,申明Value里的值就是C格局的通俗字符数组,一个个取出来就是人类可以浏览的字符串了。
public string GetValueOfType2(byte[] b)
{
return System.Text.Encoding.ASCII.GetString(b);
}
在接下来的文章中,我们将细心评论辩论各类Type应当如何取值。
还是边看个实例边聊吧,我们会评论辩论一些常用的ExifPropertyTagID,并懂得如何获得它们的值。请紧记,MSDN是很好的资料。
随便打开张我拍的照片,按前次说的办法扫描每一个Exif属性项目。
第一项的ID是0 x010F。查MSDN,发明是“Null-terminated character string that specifies the manufacturer of the equipment used to record the image.”,本来是相机的建造商啊。再一看,Type == 2,嗯,还真不抵触,调用解Type==2的函数:
private static string GetValueOfType2(byte[] b)
{
return System.Text.Encoding.ASCII.GetString(b);
}
于是获得如许的文字:
EASTMAN KODAK COMPANY
没错,我的恰是柯达。持续往下看,第二项的ID是0 x0110,惯例子MSDN,发明是“Null-terminated character string that specifies the model name or model number of the equipment used to record the image.”,本来是型号。Type还是2,持续调用刚才的函数,成果就是:
KODAK Z7590 ZOOM DIGITAL CAMERA
完全正确,加十分。
接着往下走,看到有个ID==0 x8827(ISO Speed)的类型是3。查MSDN得知是16位无符号整数。把Value[1]左移8位放在Value[0]之前就可以了。
private static string GetValueOfType3(byte[] b)
{
f (b.Length != 2) return "无效的类型(3)";
return Convert.ToUInt16(b[1] << 8 | b[0]).ToString();
}
我的值是80,没错。0 xA002和0 xA003一路描述了图片的原始大小,也可用这个办法求出来。
Type==4和Type==3景象类似,只不过Type=4时默示一个32位的无符号整数。
private static string GetValueOfType4(byte[] b)
{
if (b.Length != 4) return "无效的类型(4)";
return Convert.ToUInt32(b[3] << 24 | b[2] << 16 | b[1] << 8 | b[0]).ToString();
}
例子是ID==0 x0202(JPEG Inter Length)
Type==5也斗劲多,例如曝光时候(0 x829A)。
这种类型包含了两个无符号的长整型数字。但处理惩罚起来也并不难。把8个字节分成两份,Value[0~3]是分母,Value[4~7]是分子。但同样别忘了移位。
private static string GetValueOfType5(byte[] b)
{
if (b.Length != 8) return "无效的类型(5)";
UInt32 fm, fz;
fm = 0;
fz = 0;
fz = Convert.ToUInt32(b[7] << 24 | b[6] << 16 | b[5] << 8 | b[4]);
fm = Convert.ToUInt32(b[3] << 24 | b[2] << 16 | b[1] << 8 | b[0]);
return fm.ToString() + "/" + fz.ToString();
}
我的样本照片曝光时候是1/500。同类型的斗劲常用的ID还有0 x829D(F-Number),0 x011A,0 x011B(分别代表x/y标的目标上的辨别率),0 x9202(Lens aperture)等。
Type==7的斗劲乱,至少我的柯达图片不克不及被用同一的体式格式解码。0 x9000(Exif版本),0 xA000(Exif FPX版本)可以这么求:
private static string GetValueOfType7A(byte[] b)
{
string rtn = "";
for (int i = 0; i < b.Length; i++)
{
rtn += ((char)b[i]).ToString();
}
return rtn;
}
但0 x9101(Exif紧缩设备)须要这么解码才正确:
private static string GetValueOfType7B(byte[] b)
{
string rtn = "";
for (int i = 0; i < b.Length; i++)
{
rtn += b[i].ToString();
}
return rtn;
}
搞不懂,谁知道请告诉我。
最后看一下类型10。其实和类型5差不久不多,只不过类型10是有符号的,这里也就不久不多罗嗦了。