2015-08-06 16 views
6

Tôi có một chức năng chuyển đổi TBitmap (mà tôi vẽ) sang TPngImage và sau đó lưu nó vào luồng, vì vậy các phương pháp khác có thể sử dụng nó. Png được sử dụng bởi vì nó tạo ra hình ảnh nhỏ hơn cho đầu ra báo cáo (excel, html). Vấn đề là SaveToStream dường như mất quá nhiều thời gian, nhiều hơn 15 lần so với chuyển đổi TBitmap sang TPngImage hoặc sử dụng TStream với png. Đây là mã:Bất kỳ cách nào để tăng tốc độ SaveToStream trên TPNGImage?

var 
BitmapImage: TBitmap;  
PNGImage: TPngImage; 
PngStream: TStream;   
begin 
    // draw on BitmapImage 
    ... 
    PNGImage := TPngImage.Create; 
    PNGStream := TMemoryStream.Create; 
    Try 
    PNGImage.Assign(BitmapPicture.Bitmap); // Step 1: assign TBitmap to PNG 
    PNGImage.SaveToStream(PNGStream); // Step 2: save PNG to stream 
    WS.Shapes.AddPicture(PNGStream,PNGImage.Width,PNGImage.Height); // Step 3: Add PNG from Stream to Excel 
    finally 
    PNGImage.Free; 
    PNGStream.Free; 
    end; 
... 

này được thử nghiệm với 70000 hình ảnh và đây là những timings:
Bước 1: 7 s

Bước 2: 93 s

Bước 3: 6 s

Tại sao lưu vào luồng quá chậm? Bất kỳ đề xuất nào để tối ưu hóa điều này?

Sử dụng Delphi XE7

EDIT

Dưới đây là ví dụ (MCVE) với bmp đơn giản mà được chuyển đổi sang PNG và sau đó lưu vào suối. Chỉ vì mục đích xác minh khác, tôi đã thêm SaveToFile, điều này tất nhiên mất nhiều thời gian hơn, nhưng nó được lưu vào đĩa, vì vậy tôi cho rằng có thể chấp nhận được.

img1.bmp là 49,5KB, PNG đã lưu là 661 byte. liên kết đến img1.bmp = http://www.filedropper.com/img1_1

TMemoryStreamAccess = class(TMemoryStream) 
    end; 

procedure TForm1.Button1Click(Sender: TObject); 
var BitmapImage:TBitmap; 
    PNGImage:TPngImage; 
    PNGStream:TMemoryStream;//TStream; 
    i,t1,t2,t3,t4,t5,t6: Integer; 
    vFileName:string; 
begin 

    BitmapImage:=TBitmap.Create; 
    BitmapImage.LoadFromFile('c:\tmp\img1.bmp'); 

    t1:=0; t2:=0; t3:=0; t4:=0; t5:=0; t6:=0; 

    for i := 1 to 70000 do 
    begin 

    PNGImage:=TPngImage.Create; 
    PNGStream:=TMemoryStream.Create; 
    try 

     t1:=GetTickCount; 
     PNGImage.Assign(BitmapImage); 
     t2:=t2+GetTickCount-t1; 

     t3:=GetTickCount; 
     TMemoryStreamAccess(PNGStream).Capacity := 1000; 
     PNGImage.SaveToStream(PNGStream); 
     // BitmapImage.SaveToStream(PNGStream); <-- very fast! 
     t4:=t4+GetTickCount-t3; 

    finally 
     PNGImage.Free; 
     PNGstream.Free 
    end; 

    end; 

    showmessage('Assign = '+inttostr(t2)+' - SaveToStream = '+inttostr(t4)); 
end; 
+1

Sẽ rất tuyệt nếu bạn có thể cung cấp MCVE –

+0

@DavidHeffernan Bạn không chắc chắn MCVE là gì ...? –

+0

Bạn có thể tra cứu bằng tìm kiếm trên web –

Trả lời

6

này được thử nghiệm với 70000 hình ảnh và đây là những timings:

Bước 1: 7 s

Bước 2: 93 s

Bước 3: 6 s

Tại sao lưu vào luồng quá chậm?

Hãy crunch một số con số:

Bước 1: 7s = 7000ms. 7000/70000 = 0,1 mili giây cho mỗi hình ảnh

Bước 2: 93s = 93000ms. 93000/70000 = ~ 1.33ms mỗi hình ảnh

Bước 3: 6s = 6000ms. 6000/70000 = ~ 0,086ms trên mỗi hình ảnh

Bạn có nghĩ rằng 1,33 ms trên SaveToStream() chậm không? Bạn chỉ đang làm rất nhiều trong số họ, vì vậy họ thêm lên theo thời gian, đó là tất cả.

Điều đó đang được nói, dữ liệu PNG trong bộ nhớ không được nén. Nó được nén khi dữ liệu được lưu. Vì vậy, đó là một lý do cho sự chậm lại. Ngoài ra, việc lưu PNG không ghi nhiều vào luồng, điều này có thể khiến luồng thực hiện nhiều phân bổ bộ nhớ (lại) (TPNGImage cũng thực hiện phân bổ bộ nhớ trong khi lưu), vì vậy đó là một sự chậm lại khác.

Bất kỳ đề xuất nào để tối ưu hóa điều này?

Không có gì bạn có thể làm về overhead nén, nhưng bạn có thể ít nhất là trước khi thiết lập các TMemoryStream.Capacity đến một giá trị hợp lý trước khi gọi SaveToStream() để giảm tái phân bổ bộ nhớ mà TMemoryStream nhu cầu để thực hiện trong văn bản. Bạn không cần phải chính xác với nó. Nếu việc ghi vào luồng sẽ làm cho số Size vượt quá số Capacity hiện tại của nó, nó sẽ chỉ tăng Capacity cho phù hợp. Vì bạn đã xử lý 70000 hình ảnh, hãy lấy kích thước trung bình của chúng và thêm một vài KB nữa vào nó, và sử dụng nó làm Capacity ban đầu của bạn.

type 
    TMemoryStreamAccess = class(TMemoryStream) 
    end; 

var 
    BitmapImage: TBitmap;  
    PNGImage: TPngImage; 
    PngStream: TMemoryStream;   
begin 
    // draw on BitmapImage 
    ... 
    PNGImage := TPngImage.Create; 
    Try 
    PNGImage.Assign(BitmapPicture.Bitmap); // Step 1: assign TBitmap to PNG 
    PNGStream := TMemoryStream.Create; 
    try 
     TMemoryStreamAccess(PNGStream).Capacity := ...; // some reasonable value 
     PNGImage.SaveToStream(PNGStream); // Step 2: save PNG to stream 
     WS.Shapes.AddPicture(PNGStream, PNGImage.Width, PNGImage.Height); // Step 3: Add PNG from Stream to Excel 
    finally 
     PNGStream.Free; 
    end; 
    finally 
    PNGImage.Free; 
    end; 
    ... 

Nếu điều đó vẫn không đủ nhanh cho bạn, hãy xem xét sử dụng các chuỗi để xử lý nhiều hình ảnh song song. Không xử lý chúng tuần tự.

+0

Tôi nghĩ SaveToStream nên nhanh như trong bộ nhớ và không giống như SaveToFile. Hình ảnh có phạm vi từ 1-10KB, một số có thể lên đến 100KB. Đây là một phần của quá trình 6minute và SaveToStream này là một trong những phần chậm nhất, với cca 93s. Tôi đã thấy một số giải pháp mà bạn có thể đặt Kích thước luồng đầu tiên (PNGStream.Size: = x) nhưng tôi không biết kích thước của PNGImage sẽ là gì trong trường hợp của tôi. –

+1

Nó nằm trong bộ nhớ, nhưng nó vẫn phải cấp phát bộ nhớ, và tùy thuộc vào kích thước của hình ảnh, nó có thể phải tái phân bổ bộ nhớ của nó nhiều lần. Bạn có thể đặt một 'Dung lượng' ban đầu (không đặt 'Kích cỡ' của nó, vì bạn không biết nó là gì) để giảm chi phí trên. Tôi đã cập nhật câu trả lời của mình. –

+0

Tôi đã thử đề xuất của bạn, nhưng dường như không giảm thời gian. Xem MCVE Tôi vừa thêm vào câu hỏi. Tôi giả định chuyển đổi từ BMP sang PNG được thực hiện với Gán. –

2

Bạn đã ấn định mức nén chưa? Tôi không nhận thấy một số thông tin như

PNGImage.CompressionLevel := 1; 

trong mã của bạn. Nó có thể nằm trong khoảng từ 0 đến 9. Theo mặc định, đó là 7. Nếu bạn đặt nó thành 1, nó sẽ nhanh hơn đáng kể, trong khi tăng kích thước luồng đầu ra sẽ không đáng kể.

Các vấn đề liên quan