星期三, 10月 24, 2007

GDI+處理Tiff

鳥毅公司的文件管理系統採用tiff檔案,由於某案子需要把某些檔案裏的幾篇抽出來轉成pdf,想一想用GDI+和.Net寫比較簡單。

先參考A simple TIFF management class,用他的TiffManager在JoinTiffImages這個method結合tiff檔時出現 "無效參數" (或Invalid Parameter )的錯誤訊息,是在 Image.SaveAdd 裏的 EncoderParameters 出錯。

查MSDN討論區有人問但都沒有解,最後查到Save images into a multi-page TIFF file or add images to an existing TIFF file,這位bijulsoni老大說:
The most probable reason for this error is that the image on which you are trying to apply CCITT4 compression is not a bi-tonal image. The CCITT4 compression is applicable to bi-tonal images only. So I have given code for converting an image to a bi-tonal image. The code given in the sample application uses .NET GDI+ classes for storing images.
用人話說:Tiff的CCITT4 壓縮只能用在雙色影像檔(就是黑白啦),所以必須用bijulsoni寫的method轉成雙色。由於鳥毅還得做浮水印,所以只好用LZW壓成灰階(大約3~5倍大)

另外在轉檔的過程中,遇到記憶體不足。是因為Image物件的記憶體不會自動釋放,原因我不知 :P 我在不使用物件後加上 Bitmap.Dispose()就搞定。
所以TiffManager裏JoinTiffImages有些小地方要改:

 public void JoinTiffImages (string[] imageFiles, string outFile, EncoderValue compressEncoder)
 {
  //If only one page in the collection, copy it directly to the target file.
  if (imageFiles.Length == 1) {
   File.Copy (imageFiles [0], outFile, true);
   return;
  }
  //use the save encoder
  Encoder enc = Encoder.SaveFlag;
  EncoderParameters ep = new EncoderParameters (2);
  ep.Param [0] = new EncoderParameter (enc, (long)EncoderValue.MultiFrame);
  ep.Param [1] = new EncoderParameter (Encoder.Compression, (long)compressEncoder);

  Bitmap bitmaps = null;
  int frame = 0;
  ImageCodecInfo info = GetEncoderInfo ("image/tiff");

  foreach (string strImageFile in imageFiles) {
   if (frame == 0) {
    bitmaps = (Bitmap)Image.FromFile (strImageFile);
    bitmaps.Save (outFile, info, ep);
   } else {
    //save the intermediate frames
    ep.Param [0] = new EncoderParameter (enc, (long)EncoderValue.FrameDimensionPage);

    Bitmap bm = (Bitmap)Image.FromFile (strImageFile);
    bitmaps.SaveAdd (bm, ep);
    bm.Dispose ();
   }
   if (frame == imageFiles.Length - 1) {
    //flush and close.
    ep.Param [0] = new EncoderParameter (enc, (long)EncoderValue.Flush);
    bitmaps.SaveAdd (ep);
   }
   frame++;
  }
  bitmaps.Dispose ();
  return;
 }
浮水印是參下以下兩篇:
Creating a Watermarked Photograph with GDI+ for .NET
Watermark Maker Source: Watermark Project
不過我最後用ImageMagick效果比較好,檔案也小一點。原來TiffManager拆圖存成tiff格式太大,後來我改用png格式儲存比jpeg還小,執行速度也快,供各位參考。

Tiff轉PDF不用考慮,還是ImageMagick,還會把檔案壓小一點,只要一行指令搞定
convert xx.tif xx.pdf
結論:
GDI+雖然方便,但陷阱不少,使用時要小心。另一點是GDI+效率不高,中小企業內部用還不錯,若放在online高流量web或是寫影像處理軟體,還是用3rd party的API 吧!

後記:
本來要把意見貼到Code Project原篇,結果出現以下錯誤:(asp... 唉)

1 則留言:

匿名 提到...

為了這問題找了好久..看到您的文章,終於解決了~太感謝了^^