我有一个函数将TBitmap(我绘制)转换为TPngImage,然后将其保存到流中,因此其他方法可以使用它。使用Png是因为它为报告输出创建了较小的图像(excel,html)。问题是SaveToStream似乎花费了太多时间,比将TBitmap转换为TPngImage或使用带有png的TStream多15倍。这是代码:
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;
...
这是用70000张图片测试的,时间如下:
第1步:7秒
第2步:93秒
第3步:6 s
为什么保存到Stream这么慢?有什么建议来优化这个吗?
使用Delphi XE7
修改
以下是带有简单bmp的示例(MCVE),它将转换为PNG然后保存到流中。只是为了另一个验证我添加了SaveToFile,当然需要更长的时间,但它保存到磁盘,所以我认为可以接受。
img1.bmp为49.5KB,保存的PNG为661字节。 链接到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;
答案 0 :(得分:6)
使用70000张图像进行测试,以下是时间:
步骤1:7 s
第2步:93秒
第3步:6 s
为什么保存到Stream这么慢?
让我们处理一些数字:
步骤1:7s = 7000ms。 7000/70000 =每张图像0.1ms
步骤2:93s = 93000ms。每张图像93000/70000 = ~1.33ms
步骤3:6s = 6000ms。 6000/70000 =每张图像~0.086ms
你认为每SaveToStream()
1.33毫秒是慢吗?你只是做了很多,所以他们随着时间的推移加起来,这就是全部。
话虽如此,内存中的PNG数据不会被压缩。保存数据时会压缩它。这是减速的一个原因。此外,保存PNG会对流执行大量写操作,这会导致流执行多个内存(重新)分配(TPNGImage
还会在保存期间执行内部内存分配),这是另一个减速。
有什么建议可以优化这个吗?
您无法对压缩开销做任何事情,但在调用TMemoryStream.Capacity
之前,您至少可以将SaveToStream()
预先设置为合理的值,以减少TMemoryStream
的内存重新分配需要在写作期间执行。你不需要与它完全一致。如果写入流导致其Size
超过其当前Capacity
,则会相应地增加其Capacity
。由于您已经处理了70000张图片,请考虑它们的平均大小并为其添加更多KB,并将其用作初始Capacity
。
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;
...
如果仍然不够快,请考虑使用线程并行处理多个图像。不要按顺序处理它们。
答案 1 :(得分:2)
您是否指定了压缩级别?我没注意到像
这样的东西PNGImage.CompressionLevel := 1;
你的代码中的。它可以在0到9的范围内。默认情况下,它是7.如果将其设置为1,它将显着更快,而输出流大小增加可以忽略不计。