現在人們具備一台數碼相機已經不是什麼新鮮事了,更何況500萬像素以上的 數碼相機更是逐漸成了主流。相比較以前以膠卷為感光介質的普通相機,數碼相 機可以將所照圖像即刻轉換成計算機可識別的圖像文件格式以便浏覽、共享和打 印。雖然數碼相機在技術和方便性上都遠遠高於普通相機,但是筆者發現所有已 經生成的圖像文件以及打印的數碼照片上都沒有拍照日期,若想在日後拍照時加 上該選項也不是容易事,翻遍了數碼相機的說明書竟然沒有關於怎樣在照片上顯 示拍照日期的幫助。而且,隨著數碼圖像文件的不斷增加,面對成本成本的相冊 要想回顧一下其到底是在何時拍照的將會非常困難,尤其像筆者這樣不辭辛苦的 記錄小兒生長歷程的朋友更感覺如此。雖然在桌面電腦上利用我的電腦浏覽所拍 照的圖像文件時,在窗口底部任務欄上或者圖像文件屬性窗口的摘要頁都可以清 楚地顯示出拍照日期,但是要想將拍照日期繪制到圖像上且能夠打印到數碼照片 上卻沒有工具可以做到。其實,拍照日期已經保存在了圖像文件裡,我們需要自 己動手編程獲取拍照日期並在圖像的右下角將其繪制出來,然後保存新生成的圖 像文件並拷貝到數碼照片打印店進行打印,我們就可以獲得具備拍照日期的數碼 照片了,如下圖:
一、簡介
目前大部分數碼相機都將所拍照的圖像保存成JPG格式,而像拍照日期這樣的 信息統稱為EXIF信息。EXIF是英文ExchangeableImageFile(可交換圖像文件)的 縮寫,最初由日本電子工業發展協會(JEIDA-- JapanElectronicIndustryDevelopmentAssociation)制訂,當時JEITA決定為數 碼相機廠商專門制定一套標准,隨著數碼相機的發展,其普及趨勢越來越明顯, 於是JEITA對Exif標准進行了升級,目前最新版本為2.2。其實EXIF也是一種圖像 文件格式,EXIF信息就是由數碼相機在拍攝過程中采集一系列的信息,然後把信 息放置在JPG文件或者TIFF文件的頭部,也就是說EXIF信息是鑲嵌在圖像文件格 式內的一組拍攝參數,主要包括攝影時的光圈、快門、ISO、拍照日期時間等各 種與當時攝影條件相關的信息、相機品牌型號、色彩編碼,甚至還包括拍攝時錄 制的聲音以及全球定位系統(GPS)等信息。簡單的說,它就好像是傻瓜相機的 日期打印功能一樣,只不過EXIF信息所記錄的資訊更為詳盡和完備。需要注意的 是,用圖像處理軟件編輯過的數碼圖像文件有可能會丟失其EXIF信息。
所以,要想在圖像上繪制拍照日期首先必須讀取EXIF信息,然後將讀取出來 的拍照日期繪制在圖像表面,我們將以500萬像素分辨率為2592x1944的JPG圖像 文件為對象,使用Visual Studio .Net 2005 的C#來編寫一工具程序來實現上述 功能。
二、技術背景
EXIF信息以鍵值對的方式保存在數碼JPG圖像文件的頭部,在.Net平台中所有 圖像文件頭部信息統稱為元數據,我們可以使用GDI+讀取現有的元數據,也可以 將新的元數據寫入圖像文件中。GDI+ 將單獨的元數據段存儲在 PropertyItem 對象中,讀取 Image 對象的 PropertyItems 屬性以便從某個文件中檢索所有的 元數據。PropertyItems 屬性返回一個 PropertyItem 對象的數組。 PropertyItem 對象具有以下四個屬性:Id、Value、Len 和 Type。Id用於標識 元數據項的標記,下表顯示一些Id 的值:
十六進制值 說明 0x0320 圖像標題 0x010F 設備制造商 0x0110 設備型號 0x0132 拍照時間 0x829A Exif曝光時間 0x5090 亮度表 .... ....Value為數組值,這些值的格式由 Type 屬性確定。Len屬性指向的值的數組 長度(以字節表示)。Type屬性指向數組中值的數據類型。下表顯示由 Type 屬 性值指示的格式:
數值 說明 1 一個 Byte 2 ASCII 編碼的 Byte 對象的數組 3 16 位整數 4 32 位整數 5 包含兩個表示有理數的 Byte 對象的數組 6 未使用 7 未定義 8 未使用 9 SLong 10 SRational我們所感興趣的ID值就是0x0132即圖片拍照時間,對應的標記為 PropertyTagDateTime,而在聯機的MSDN中我們發現了更詳細的關於EXIF屬性的 GDI+的描述,PropertyTagDateTime值的類型為PropertyTagTypeASCII,它以 ASCII編碼的形式保存數據,我們在獲取數據後就按照ASCII進行解碼,將一些列 字節轉換為日期/時間的字符串。
在進行下一步之前,我們可以先用文本編輯軟件如UltraEdit打開要操作的圖 片文件實際看看頭文件到底是怎樣的,如下圖:
我們發現裡面的日期格式為:2006:06:07 16:33:41,這個格式既不是標准的 日期/時間格式也不是當前系統設置的格式,所以還需要對日期/時間格式進行格 式化。
獲得了拍照日期/時間後,從指定的圖片文件來創建Graphics對象,在該 Graphics對象上繪制先前我們獲取的拍照日期/時間。
三、程序實現
啟動Visual Studio .Net 2005 創建名為PicStamp的Visual C# 項目,選擇 Windows 應用程序模版。在默認的窗體上放置一個listBox組件用於保存需要繪 制拍照日期的圖片文件列表,一個textBox組件用於設置繪制後的圖片文件所放 置的文件夾,五個Button組件,分別用於向listBox添加圖像文件、清空列表框 、選擇放置繪制後的圖片的文件夾、實際繪制操作以及退出示例程序,一個選擇 文件對話框用於挑選圖片文件,一個選擇文件夾對話框用於選擇圖片文件要放置 的文件夾,程序運行界面如下:
我們自定義一個函數GetExifProperties用於返回圖片文件的Exif信息,代碼 如下:
//獲取圖像文件的所有元數據屬性,以PropertyItem數組的格式保存
public static PropertyItem[] GetExifProperties(string fileName)
{
FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
//通過指定的數據流來創建Image
System.Drawing.Image image = System.Drawing.Image.FromStream (stream,true,false);
return image.PropertyItems;
}
獲得所有元數據後,需要挑選出我們所感興趣的拍照日期/時間屬性所對應的 值,代碼如下:
//遍歷所有元數據,獲取拍照日期/時間
private string GetTakePicDateTime(System.Drawing.Imaging.PropertyItem [] parr)
{
Encoding ascii = Encoding.ASCII ;
//遍歷圖像文件元數據,檢索所有屬性
foreach (System.Drawing.Imaging.PropertyItem p in parr)
{
//如果是PropertyTagDateTime,則返回該屬性所對應的值
if (p.Id==0x0132)
{
return ascii.GetString(p.Value);
}
}
//若沒有相關的EXIF信息則返回N/A
return "N/A";
}
循環處理圖片文件列表框中的文件,並重新格式化獲取的拍照日期/時間,然 後通過Graphics對象將其繪制到數碼圖像的表面並保存為新文件,代碼如下:
……
……
for (int i = 0; i < listBox1.Items.Count; i++)
{
pi = GetExifProperties(listBox1.Items[i].ToString() );
//獲取元數據中的拍照日期時間,以字符串形式保存
TakePicDateTime = GetTakePicDateTime(pi);
//分析字符串分別保存拍照日期和時間的標准格式
SpaceLocation = TakePicDateTime.IndexOf(" ");
dt = TakePicDateTime.Substring(0, SpaceLocation);
dt=dt.Replace(":", "-");
tm = TakePicDateTime.Substring(SpaceLocation+1, TakePicDateTime.Length - SpaceLocation-2);
TakePicDateTime = dt + " " + tm;
//由列表中的文件創建內存位圖對象
Pic = new Bitmap(listBox1.Items[i].ToString());
//由位圖對象創建Graphics對象的實例
g = Graphics.FromImage(Pic);
//在Graphics表面繪制數碼照片的日期/時間戳
g.DrawString(TakePicDateTime, normalContentFont, new SolidBrush (normalContentColor),
Pic.Width - 700, Pic.Height - 200);
//將添加日期/時間戳後的圖像進行保存
Pic.Save(textBox1.Text + Path.GetFileName(listBox1.Items [i].ToString()));
//釋放內存位圖對象
Pic.Dispose();
}
……
……
四、總結
該程序在Visual Studio .Net 2005 C# + Windows XP SP2下運行成功。通過 實際使用該程序可以批量且有效地將數碼圖片拍照日期/時間繪制到圖像表面, 我們是以分辨率為2592x1944的JPG圖像文件為繪制對象,讀者可以根據實際圖片 尺寸適當調整源碼中拍照日期/時間的字體、大小以及位置。本文僅演示了如何 讀取EXIF信息,讀者可以稍加改動就可以修改EXIF信息並加以保存。還有需要注 意的是,正像本文開頭所提到的,任何圖像編輯軟件對數碼照片的編輯都有可能 使EXIF信息丟失,本文示例程序也不例外,經過繪制後的數碼圖片確實會丟失一 些EXIF信息,但是所有關鍵信息並沒有丟失。
本文配套源碼