315技术社区's Archiver

购买请咨询客服QQ:604164

wolaile 发表于 2008-5-11 10:24

利用C#为数码照片添加拍照日期

[font=宋体][size=10.5pt]现在人们具备一台数码相机已经不是什么新鲜事了,更何况[/size][/font][size=10.5pt]500[/size][font=宋体][size=10.5pt][size=2]万像素以上的数码相机更是逐渐成了主流。相比较以前以胶卷为感光介质的普通相机,数码相机可以将所照图像即刻转换成计算机可识别的图像文件格式以便浏览、共享和打印。虽然数码相机在技术和方便性上都远远高于普通相机,但是笔者发现所有已经生成的图像文件以及打印的数码照片上都没有拍照日期,若想在日后拍照时加上该选项也不是容易事,翻遍了数码相机的说明书竟然没有关于怎样在照片上显示拍照日期的帮助。而且,随着数码图像文件的不断增加,面对成本成本的相册要想回顾一下其到底是在何时拍照的将会非常困难,尤其像笔者这样不辞辛苦的记录小儿生长历程的朋友更感觉如此。虽然在桌面电脑上利用我的电脑浏览所拍照的图像文件时,在窗口底部任务栏上或者图像文件属性窗口的摘要页都可以清楚地显示出拍照日期,但是要想将拍照日期绘制到图像上且能够打印到数码照片上却没有工具可以做到。其实,拍照日期已经保存在了图像文件里,我们需要自己动手编程获取拍照日期并在图像的右下角将其绘制出来,然后保存新生成的图像文件并拷贝到数码照片打印店进行打印,我们就可以获得具备拍照日期的数码照片了,如下图

[img=266,345]http://image.it168.com/cms/2008-1-16/Image/2008116102054.jpg[/img]

[img=296,343]http://image.it168.com/cms/2008-1-16/Image/2008116102110.jpg[/img]
[/size][size=2]<!--[if !supportLists]-->一、<!--[endif]-->[font=宋体]简介[/font][/size][size=2] [/size][size=2][/size]


[size=2][/size]
[size=2][font=宋体]目前大部分数码相机都将所拍照的图像保存成[/font]JPG[font=宋体]格式,而像拍照日期这样的信息统称为[/font]EXIF[font=宋体]信息。[/font]EXIF[font=宋体]是英文[/font]ExchangeableImageFile([font=宋体]可交换图像文件[/font])[font=宋体]的缩写,最初由日本电子工业发展协会[/font](JEIDA--JapanElectronicIndustryDevelopmentAssociation)[font=宋体]制订,[/font][font=宋体]当时[/font]JEITA[font=宋体]决定为数码相机厂商专门制定一套标准,随着数码相机的发展,其普及趋势越来越明显,于是[/font]JEITA[font=宋体]对[/font]Exif[font=宋体]标准进行了升级,目前最新版本为[/font]2.2[font=宋体]。其实[/font]EXIF[font=宋体]也是一种图像文件格式,[/font]EXIF[font=宋体]信息就是由数码相机在拍摄过程中采集一系列的信息,然后把信息放置在[/font]JPG[font=宋体]文件或者[/font]TIFF[font=宋体]文件的头部,也就是说[/font]EXIF[font=宋体]信息是镶嵌在图像文件格式内的一组拍摄参数,主要包括摄影时的光圈、快门、[/font]ISO[font=宋体]、拍照日期时间等各种与当时摄影条件相关的信息、相机品牌型号、色彩编码,甚至还包括拍摄时录制的声音以及全球定位系统([/font]GPS[font=宋体])等信息。简单的说,它就好像是傻瓜相机的日期打印功能一样,只不过[/font]EXIF[font=宋体]信息所记录的资讯更为详尽和完备。需要注意的是,用图像处理软件编辑过的数码图像文件有可能会丢失其[/font]EXIF[font=宋体]信息。[/font]
[/size]

[size=2][font=宋体]所以,要想在图像上绘制拍照日期首先必须读取[/font]EXIF[font=宋体]信息,然后将读取出来的拍照日期绘制在图像表面,我们将以[/font]500[font=宋体]万像素分辨率为[/font]2592x1944[font=宋体]的[/font]JPG[font=宋体]图像文件为对象,使用[/font]Visual Studio .Net 2005 [font=宋体]的[/font]C#[font=宋体]来编写一工具程序来实现上述功能。[/font][/size]
[font=宋体][size=2]<!--[if !supportLists]-->三、<!--[endif]-->程序实现[/size][/font]
[/size][/font][size=2][font=宋体]     启动Visual Studio .Net 2005 创建名为PicStamp的Visual C# 项目,选择Windows 应用程序模版。在默认的窗体上放置一个listBox组件用于保存需要绘制拍照日期的图片文件列表,一个textBox组件用于设置绘制后的图片文件所放置的文件夹,五个Button组件,分别用于向listBox添加图像文件、清空列表框、选择放置绘制后的图片的文件夹、实际绘制操作以及退出示例程序,一个选择文件对话框用于挑选图片文件,一个选择文件夹对话框用于选择图片文件要放置的文件夹,程序运行界面如下:
[/font][img=312,301]http://image.it168.com/cms/2008-1-16/Image/2008116102629.jpg[/img]

[font=宋体][size=10.5pt]我们自定义一个函数[/size][/font][font=宋体][size=10.5pt]GetExifProperties[/size][/font][/size][font=宋体][size=10.5pt][size=2]用于返回图片文件的Exif信息,代码如下:
[/size][/size][/font]
[size=13px][size=2][img]http://www.cnblogs.com/Images/OutliningIndicators/None.gif[/img][color=#008000]//[/color][color=#008000]获取图像文件的所有元数据属性,以PropertyItem数组的格式保存[/color][/size][color=#008000]
[size=2][img]http://www.cnblogs.com/Images/OutliningIndicators/None.gif[/img][/size][/color][size=2][color=#0000ff]public[/color]
[color=#0000ff]static[/color][color=#000000] PropertyItem[] GetExifProperties([/color][color=#0000ff]string[/color][/size][size=2][color=#000000] fileName)
[img]http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif[/img][img]http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif[/img][/color][img]http://www.cnblogs.com/Images/dot.gif[/img][/size][size=2][color=#000000]{
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]      FileStream stream [/color][color=#000000]=[/color]
[color=#0000ff]new[/color][/size][size=2][color=#000000] FileStream(fileName, FileMode.Open, FileAccess.Read);
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]      [/color][color=#008000]//[/color][color=#008000]通过指定的数据流来创建Image[/color][/size][color=#008000]
[size=2][img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img][/size][/color][size=2][color=#000000]      System.Drawing.Image image [/color][color=#000000]=[/color][color=#000000] System.Drawing.Image.FromStream(stream,[/color][color=#0000ff]true[/color][color=#000000],[/color][color=#0000ff]false[/color][/size][size=2][color=#000000]);
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]      [/color][color=#0000ff]return[/color][/size][color=#000000][size=2] image.PropertyItems;
[img]http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif[/img]}[/size][/color]
[/size]
[size=2][font=宋体][size=10.5pt]获得所有元数据后,需要挑选出我们所感兴趣的拍照日期[/size][/font][size=10.5pt]/[/size][/size][font=宋体][size=10.5pt][size=2]时间属性所对应的值,代码如下:
[/size][/size][/font]
[size=13px][size=2][img]http://www.cnblogs.com/Images/OutliningIndicators/None.gif[/img][color=#008000]//[/color][color=#008000]遍历所有元数据,获取拍照日期/时间[/color][/size][color=#008000]
[size=2][img]http://www.cnblogs.com/Images/OutliningIndicators/None.gif[/img][/size][/color][size=2][color=#0000ff]private[/color]
[color=#0000ff]string[/color][/size][size=2][color=#000000] GetTakePicDateTime(System.Drawing.Imaging.PropertyItem[] parr)
[img]http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif[/img][img]http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif[/img][/color][img]http://www.cnblogs.com/Images/dot.gif[/img][/size][size=2][color=#000000]{
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]     Encoding ascii [/color][color=#000000]=[/color][/size][size=2][color=#000000] Encoding.ASCII ;
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]     [/color][color=#008000]//[/color][color=#008000]遍历图像文件元数据,检索所有属性[/color][/size][color=#008000]
[size=2][img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img][/size][/color][size=2]
[color=#0000ff]foreach[/color][color=#000000] (System.Drawing.Imaging.PropertyItem p [/color][color=#0000ff]in[/color][/size][size=2][color=#000000] parr)
[img]http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif[/img][img]http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif[/img]     [/color][img]http://www.cnblogs.com/Images/dot.gif[/img][/size][size=2][color=#000000]{
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]          [/color][color=#008000]//[/color][color=#008000]如果是PropertyTagDateTime,则返回该属性所对应的值[/color][/size][color=#008000]
[size=2][img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img][/size][/color][size=2]
[color=#0000ff]if[/color][color=#000000] (p.Id[/color][color=#000000]==[/color][color=#800080]0x0132[/color][/size][size=2][color=#000000])
[img]http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif[/img][img]http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif[/img]          [/color][img]http://www.cnblogs.com/Images/dot.gif[/img][/size][size=2][color=#000000]{
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]               [/color][color=#0000ff]return[/color][/size][color=#000000][size=2] ascii.GetString(p.Value);
[img]http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif[/img]          }[/size][/color][color=#000000]
[size=2][img]http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif[/img]     }[/size][/color][color=#000000]
[size=2][img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]     [/size][/color][size=2][color=#008000]//[/color][color=#008000]若没有相关的EXIF信息则返回N/A[/color][/size][color=#008000]
[size=2][img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img][/size][/color][size=2]
[color=#0000ff]return[/color]
[color=#800000]"[/color][color=#800000]N/A[/color][color=#800000]"[/color][/size][color=#000000][size=2];
[img]http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif[/img]}[/size][/color]
[/size]
[size=2][font=宋体][size=10.5pt]循环处理图片文件列表框中的文件,并重新格式化获取的拍照日期[/size][/font][size=10.5pt]/[/size][font=宋体][size=10.5pt]时间,然后通过[/size][/font][size=10.5pt]Graphics[/size][/size][font=宋体][size=10.5pt][size=2]对象将其绘制到数码图像的表面并保存为新文件,代码如下:
[/size][/size][/font]
[size=13px][size=2][img]http://www.cnblogs.com/Images/OutliningIndicators/None.gif[/img][color=#0000ff]for[/color][color=#000000] ([/color][color=#0000ff]int[/color][color=#000000] i [/color][color=#000000]=[/color]
[color=#800080]0[/color][color=#000000]; i [/color][color=#000000]<[/color][color=#000000] listBox1.Items.Count; i[/color][color=#000000]++[/color][/size][size=2][color=#000000])
[img]http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif[/img][img]http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif[/img][/color][img]http://www.cnblogs.com/Images/dot.gif[/img][/size][size=2][color=#000000]{
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]     pi [/color][color=#000000]=[/color][/size][size=2][color=#000000] GetExifProperties(listBox1.Items[i].ToString() );
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]     [/color][color=#008000]//[/color][color=#008000]获取元数据中的拍照日期时间,以字符串形式保存[/color][/size][color=#008000]
[size=2][img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img][/size][/color][size=2][color=#000000]     TakePicDateTime [/color][color=#000000]=[/color][/size][size=2][color=#000000] GetTakePicDateTime(pi);
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]     [/color][color=#008000]//[/color][color=#008000]分析字符串分别保存拍照日期和时间的标准格式[/color][/size][color=#008000]
[size=2][img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img][/size][/color][size=2][color=#000000]     SpaceLocation [/color][color=#000000]=[/color][color=#000000] TakePicDateTime.IndexOf([/color][color=#800000]"[/color]
[color=#800000]"[/color][/size][size=2][color=#000000]);
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]     dt [/color][color=#000000]=[/color][color=#000000] TakePicDateTime.Substring([/color][color=#800080]0[/color][/size][size=2][color=#000000], SpaceLocation);
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]     dt[/color][color=#000000]=[/color][color=#000000]dt.Replace([/color][color=#800000]"[/color][color=#800000]:[/color][color=#800000]"[/color][color=#000000], [/color][color=#800000]"[/color][color=#800000]-[/color][color=#800000]"[/color][/size][size=2][color=#000000]);
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]     tm [/color][color=#000000]=[/color][color=#000000] TakePicDateTime.Substring(SpaceLocation[/color][color=#000000]+[/color][color=#800080]1[/color][color=#000000], TakePicDateTime.Length [/color][color=#000000]-[/color][color=#000000] SpaceLocation[/color][color=#000000]-[/color][color=#800080]2[/color][/size][size=2][color=#000000]);
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]     TakePicDateTime [/color][color=#000000]=[/color][color=#000000] dt [/color][color=#000000]+[/color]
[color=#800000]"[/color]
[color=#800000]"[/color]
[color=#000000]+[/color][/size][size=2][color=#000000] tm;
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]     [/color][color=#008000]//[/color][color=#008000]由列表中的文件创建内存位图对象[/color][/size][color=#008000]
[size=2][img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img][/size][/color][size=2][color=#000000]     Pic [/color][color=#000000]=[/color]
[color=#0000ff]new[/color][/size][size=2][color=#000000] Bitmap(listBox1.Items[i].ToString());
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]     [/color][color=#008000]//[/color][color=#008000]由位图对象创建Graphics对象的实例[/color][/size][color=#008000]
[size=2][img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img][/size][/color][size=2][color=#000000]     g [/color][color=#000000]=[/color][/size][size=2][color=#000000] Graphics.FromImage(Pic);
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]     [/color][color=#008000]//[/color][color=#008000]在Graphics表面绘制数码照片的日期/时间戳[/color][/size][color=#008000]
[size=2][img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img][/size][/color][size=2][color=#000000]     g.DrawString(TakePicDateTime, normalContentFont, [/color][color=#0000ff]new[/color][/size][size=2][color=#000000] SolidBrush(normalContentColor),   
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]Pic.Width [/color][color=#000000]-[/color]
[color=#800080]700[/color][color=#000000], Pic.Height [/color][color=#000000]-[/color]
[color=#800080]200[/color][/size][size=2][color=#000000]);
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]     [/color][color=#008000]//[/color][color=#008000]将添加日期/时间戳后的图像进行保存[/color][/size][color=#008000]
[size=2][img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img][/size][/color][size=2][color=#000000]     Pic.Save(textBox1.Text [/color][color=#000000]+[/color][/size][size=2][color=#000000] Path.GetFileName(listBox1.Items[i].ToString()));
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]     [/color][color=#008000]//[/color][color=#008000]释放内存位图对象[/color][/size][color=#008000]
[size=2][img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img][/size][/color][color=#000000][size=2]     Pic.Dispose();
[img]http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif[/img]}[/size][/color]
[/size]
[size=2]<!--[if !supportLists]-->四、<!--[endif]-->[font=宋体]总结[/font]
[/size]

[size=2][font=宋体]该程序在[/font]Visual Studio .Net 2005 C# + Windows XP SP2[font=宋体]下运行成功。通过实际使用该程序可以批量且有效地将数码图片拍照日期[/font]/[font=宋体]时间绘制到图像表面,我们是以分辨率为[/font]2592x1944[font=宋体]的[/font]JPG[font=宋体]图像文件为绘制对象,读者可以根据实际图片尺寸适当调整源码中拍照日期[/font]/[font=宋体]时间的字体、大小以及位置。本文仅演示了如何读取[/font]EXIF[font=宋体]信息,读者可以稍加改动就可以修改[/font]EXIF[font=宋体]信息并加以保存。还有需要注意的是,正像本文开头所提到的,任何图像编辑软件对数码照片的编辑都有可能使[/font]EXIF[font=宋体]信息丢失,本文示例程序也不例外,经过绘制后的数码图片确实会丢失一些[/font]EXIF[font=宋体]信息,但是所有关键信息并没有丢失。[/font]
[/size]

页: [1]

Powered by Discuz! Archiver 6.1.0  © 2001-2007 Comsenz Inc.