因為有人 留話 在詢問怎麼利用 WPF 處理這些動作,而這些又都是 Microsoft 文件及範例沒有寫的很清楚的部份… 有些範例有提到,但是 Microsoft 內建的 CODEC 正常,3RD PARTY 的 CODEC (像我碰到的 Canon Raw Codec) 就搞半天也搞不出來…。因為之前寫 MediaFiler 正好都碰過這些釘子,就順手寫起來以免以後又忘掉.. :P
1. 讀取 Metadata
講 METADATA 也許會有些人一頭霧水,講 EXIF 搞不好還比較多人知道… EXIF 的規格有點混亂,就像 RSS 一樣,有好幾個派係,Microsoft 乾脆在 WPF 裡跳出來直接稱它做 METADATA,也搭配了 METADATA QUERY LANGUAGE 來避開像 EXIF 一堆必需事先定義好 PROPERTY NAME 的麻煩事…
所以所有的動作都集中在兩件事上面,一個是怎麼操作 METADATA ? 另一個是我要的資料到底藏在那? (你得知道什麼樣的 QUERY 才抓的到你要的資料?)
先看簡單的,貼段 SAMPLE CODE 就搞定了..
SAMPLE 1. 如何讀取 METADATA ?
string srcFile = @"SampleRawFile.CR2";
//
// sample 1. read metadata
// FileStream fs = File.OpenRead(srcFile);
BitmapMetadata metadata = BitmapDecoder.Create(
fs,
BitmapCreateOptions.None,
BitmapCacheOption.None).Frames[0].Metadata as BitmapMetadata;
foreach (string query in queries)
{
object result = metadata.GetQuery(query);
Console.WriteLine("query[{0}]: {1}", query, result);
}
fs.Close();
扣掉 COMMENTS 只有12行… 哈哈,看起來好像也沒什麼好講的,關鍵就只有一個,STREAM別太早關掉…。以前我就吃過虧,試了好久才知道原來是我太雞婆,在第10行之前就把 fs 給關了,後面通通讀不出來…
不過關鍵的 queries (型別是 string[]
),內容到底是啥? 老實說我也不是很清楚 :P 先貼一下建立這字串陣列的 CODE:
建立 CR2 支援的 METADATA QUERY
private static string[] queries = new string[] {
"/ifd/{ushort=256}",
"/ifd/{ushort=257}",
"/ifd/{ushort=258}",
"/ifd/{ushort=259}",
"/ifd/{ushort=262}",
"/ifd/{ushort=270}",
"/ifd/{ushort=271}",
"/ifd/{ushort=272}",
"/ifd/{ushort=273}",
"/ifd/{ushort=274}",
"/ifd/{ushort=277}",
"/ifd/{ushort=278}",
"/ifd/{ushort=279}",
"/ifd/{ushort=282}",
"/ifd/{ushort=283}",
"/ifd/{ushort=284}",
"/ifd/{ushort=296}",
"/ifd/{ushort=306}",
"/ifd/{ushort=315}",
"/ifd/{ushort=34665}/{ushort=33434}",
"/ifd/{ushort=34665}/{ushort=33437}",
"/ifd/{ushort=34665}/{ushort=34855}",
"/ifd/{ushort=34665}/{ushort=36864}",
"/ifd/{ushort=34665}/{ushort=36868}",
"/ifd/{ushort=34665}/{ushort=37377}",
"/ifd/{ushort=34665}/{ushort=37378}",
"/ifd/{ushort=34665}/{ushort=37380}",
"/ifd/{ushort=34665}/{ushort=37386}",
"/ifd/{ushort=34665}/{ushort=37500}",
"/ifd/{ushort=34665}/{ushort=37510}",
"/ifd/{ushort=34665}/{ushort=40960}",
"/ifd/{ushort=34665}/{ushort=40961}",
"/ifd/{ushort=34665}/{ushort=41486}",
"/ifd/{ushort=34665}/{ushort=41487}",
"/ifd/{ushort=34665}/{ushort=41488}",
"/ifd/{ushort=34665}/{ushort=41728}",
"/ifd/{ushort=34665}/{ushort=41985}",
"/ifd/{ushort=34665}/{ushort=41986}",
"/ifd/{ushort=34665}/{ushort=41987}",
"/ifd/{ushort=34665}/{ushort=41988}",
"/ifd/{ushort=34665}/{ushort=41990}",
"/ifd/{ushort=34665}/OffsetSchema:offset"
};
因為我之前處理的目標,是在做轉檔的動作,同時要忠實的把 METADATA 也複製過去,因此得到這列表對我來說很重要,但是搞懂每個項目的意義是啥我就不管了,先列出來再說… 需要的人請自行判斷….
2. 建立縮圖
WPF 架構滿分,但是效能多少會打折扣。JPEG實在看不大出來,拿CANON的CODEC來看就很清楚了。如果是以 .CR2 -> .JPG,不作大小的縮放,CANON 自家的 DPP 大概要廿幾秒,CANON自家寫給 WPF 用的 CODEC 搭配 .NET 的 WPF 程式,則要 60 秒左右,測試的機器就我這台 Vista + 4GB RAM, CPU 是 Core2Duo E6300…
速度實在是不快,也不是用 .NET 寫效能太糟的原因,因為 VISTA 內建的秀圖程式也是靠同一個 CODEC,效能也差不多… 不過 CODEC 的架構設計的很好,如果我要的只是縮圖,那就不一樣了… 來看第二個範例:
SAMPLE 2. 建立原圖 (.CR2) 1/10 大小的 JPEG 縮圖
Stopwatch timer = new Stopwatch();
timer.Start();
FileStream fs = File.OpenRead(srcFile);
FileStream fs2 = File.OpenWrite(Path.ChangeExtension(srcFile, ".jpg"));
BitmapDecoder source = BitmapDecoder.Create(fs, BitmapCreateOptions.None, BitmapCacheOption.None);
JpegBitmapEncoder target = new JpegBitmapEncoder();
target.Frames.Add(BitmapFrame.Create(
new TransformedBitmap(source.Frames[0], new ScaleTransform(0.1, 0.1)),
null,
null,
null));
target.QualityLevel = 90;
target.Save(fs2);
fs.Close();
fs2.Close();
timer.Stop();
Console.WriteLine("Create 0.1x thumbnail: {0} ms.", timer.ElapsedMilliseconds);
範例程式也很簡單,扣掉計時的程式碼只有 16 行… 裡面兩個關鍵的參數,分別為第 11 行, 0.1 代表 Scale Transform 用的縮放比例,0.1 就是只要 1/10 的大小。如果你想要固定尺寸的縮圖,得先自行計算出這個 SCALE 的值。要單純的轉檔,就填 1.0 就好。
另一個參數是第15行的 90,它是指存成 JPEG 時要使用的 QUALITY,100 最好,越低失真越嚴重,但是相對的檔案大小也會大幅下降。一般用的 QUALITY 大約都在 75% ~ 90%,其實縮圖 75% 就夠了,反正看不大出來 (H)
我用 CANON G9 拍出來的 RAW (4000 X 3000),存成 400x300 JPEG,約要花費 1.5 秒,比原圖尺寸的 80 秒差太多了,可見 CODEC 一定針對這種需求作過最佳化,會有效避開縮圖時不必讀取的部份資料… 以加快速度。
同樣程式改成不縮圖看看,如果不縮圖的話,ScaleTransform也可以省了,直接把 Frame 加進去就好,原程式的 line 10 ~ line 14,換成這段:
不縮圖的替代程式碼
target.Frames.Add(source.Frames[0]);
嘖,只有一行也在貼… 沒錯,就是這樣而以。跑出來的時間約為 65 秒…
這部份我就沒仔細的拿 DPP 比較過了。不過當你縮圖的尺寸降低時,的確是能有效的加快速度。不過如果對於 ASP.NET WEB 應用程式來看,1.5 秒還是太慢了 @_@,十個人連上來就會想哭了,怎麼辦? 只好善用 CACHE,不然就 買本六月號 RUN PC 看看我投的那篇文章 … 哈哈…
PS: 這篇是寫給對岸的那位愛賞鳥的網友看的,希望有解決到你的問題。完成後也記得給我欣賞一下你們的攝影作品 :D
PS2: 範例程式放在這裡,請自行 下載。圖檔請放同目錄的 SampleRawFile.CR2 PS3: 家裡大人不准我買 PS3 …