2013-12-18 41 views
2

我正在創建一個過程來提取TIFF,對其中一個圖像執行操作,然後重新生成TIFF。中間的操作並不重要,然而,提取和重建在處理方面證明是非常昂貴的。如何優化此代碼以創建TIFF文件和/或應考慮哪些備用庫來提高性能?

const string tifPath = "14 page.TIFF"; 
MemoryStream[] imageStreams; 
var stream = new MemoryStream(); 

//extract tiff 
using (var imageFile = Image.FromFile(tifPath)) 
{ 
    var imagePageCount = imageFile.GetFrameCount(FrameDimension.Page); 
    imageStreams = new MemoryStream[imagePageCount]; 
    var frameDimension = new FrameDimension(imageFile.FrameDimensionsList[0]); 

    for (var i = 0; i < imagePageCount; i++) 
    { 
     imageFile.SelectActiveFrame(frameDimension, i); 
     var bmp = new Bitmap(imageFile); 

     bmp.Save(stream, ImageFormat.Bmp); 
     imageStreams[i] = stream; 
    } 
} 

//build tiff 
var encoder = Encoder.SaveFlag; 
var encoderInfo = ImageCodecInfo.GetImageEncoders().First(i => i.MimeType == "image/tiff"); 
var encoderParameters = new EncoderParameters(1); 
encoderParameters.Param[0] = new EncoderParameter(encoder, (long)EncoderValue.MultiFrame); 

stream = new MemoryStream(); 
var firstImage = new Bitmap(Image.FromStream(imageStreams[0])); 
firstImage.Save(stream, encoderInfo, encoderParameters); 
encoderParameters.Param[0] = new EncoderParameter(encoder, (long)EncoderValue.FrameDimensionPage); 

for (var i = 1; i < imageStreams.Length; i++) 
{ 
    var img = Image.FromStream(imageStreams[i]); 
    firstImage.SaveAdd(img, encoderParameters); 
} 

encoderParameters.Param[0] = new EncoderParameter(encoder, (long)EncoderValue.Flush); 
firstImage.SaveAdd(encoderParameters); 

我發現在某些地方清理我的流有幫助。與微軟和其他圖書館(LeadTools和LibTiff)平均約5秒。我沒有設置使用C#解決方案,但試圖找到一種方法儘可能地壓縮它。

+0

和你的問題是? – JohnFx

+0

如何使當前代碼更快。使用庫來代替GDI +。 – Jeremy

+0

@ liori:我不是在尋求一個簡單的意見。我正在尋找速度非常緩慢的過程。我知道圖像處理的成本很高,但是我從Google找到的所有多頁TIFF信息都只涉及如何處理這些信息。 – Jeremy

回答

2

我無法找到並行化創建過程的好方法。試圖從構建在Parallel.ForEach之外的MemoryStream構建一個Image只會引發一般的GDI +錯誤。

const string tifPath = "14 Page.TIFF"; 

//extract tiff 
var imageFile = Image.FromFile(tifPath); 
var imagePageCount = imageFile.GetFrameCount(FrameDimension.Page); 
var imageStreams = new Bitmap[imagePageCount]; 
var rangePartioner = Partitioner.Create(0, imagePageCount); 
var frameDimension = new FrameDimension(imageFile.FrameDimensionsList[0]); 
imageFile.Dispose(); 

Parallel.ForEach(rangePartioner, (range, loopState) => 
{ 
    for (var i = range.Item1; i < range.Item2; i++) 
    { 
     using (var imageFile2 = Image.FromFile(tifPath)) 
     { 
      imageFile2.SelectActiveFrame(frameDimension, i); 
      imageStreams[i] = new Bitmap(imageFile2); 
     } 
    } 
}); 

//build tiff 
var encoder = Encoder.SaveFlag; 
var encoderInfo = ImageCodecInfo.GetImageEncoders().First(i => i.MimeType == "image/tiff"); 
var encoderParameters = new EncoderParameters(1); 
encoderParameters.Param[0] = new EncoderParameter(encoder, (long) EncoderValue.MultiFrame); 

var stream2 = new MemoryStream(); 

using(var firstImage = imageStreams[0]) 
{ 
    firstImage.Save(stream2, encoderInfo, encoderParameters); 

    encoderParameters.Param[0] = new EncoderParameter(encoder, (long) EncoderValue.FrameDimensionPage); 

    for (var i = 1; i < imageStreams.Length; i++) 
    { 
     firstImage.SaveAdd(imageStreams[i], encoderParameters); 
    } 

    encoderParameters.Param[0] = new EncoderParameter(encoder, (long) EncoderValue.Flush); 
    firstImage.SaveAdd(encoderParameters); 
}   

這削掉了大約2秒。仍然在尋找處理下半場的好方法。

編輯:增加了建議的變化只轉換圖像,而不是保存到流。 編輯:增加了修復程序的其餘部分。

+0

我有這個作品,我在下半場的嘗試包裹在try catch中。該錯誤消息只是「在GDI +中發生的一般錯誤」。我試圖保存初始的新圖像和流作爲文件,但無法逃脫無用的錯誤信息。 – Jeremy

+0

1)看看你是否可以在每個線程上使用Image.FromStream,而不是多次讀取文件到內存中。 2)擺脫imageStreams [i],因爲它引入了處理器之間的虛假共享。你的代碼現在正在「順序」運行,因爲所有的處理器都在爲imageStreams [i]捱餓。你可以使用聚合模式(谷歌並行foreach聚合)。如果需要,使用線程本地存儲imageStreams並稍後將它們放入imageStreams數組中。 –

+0

嘗試SelectActiveFrame時,Image.FromStream導致通用的GDI +錯誤。在這種情況下使用流只會導致問題。 – Jeremy

0

開始分析您的代碼並查看哪些部分需要時間。很難承擔事情。與建築物一樣,您可以並行提取TIFF。那會讓你加速4倍。

+0

我只熟悉通過列表來產生線程。你如何線程化一個依賴頁面順序的進程? – Jeremy

+0

謝謝。在注意到它的速度之後,我對代碼進行了描述。分析報告指出,Save和SaveAdd方法非常昂貴。 根據您的建議,我將提取的部分放到了一個Parallel.ForEach中,作爲潛在的答案發布。至於創作過程......並非如此。 – Jeremy

+0

你爲什麼要創建新的位圖? imageFile2.Save(stream,ImageFormat.bmp)不起作用嗎? –