delphi 有没有办法加快TPNGImage上SaveToStream的速度?

zf2sa74q  于 2022-11-04  发布在  其他
关注(0)|答案(3)|浏览(262)

我有一个函数可以将TBitmap(我绘制的)转换为TPngImage,然后将其保存到流中,以便其他方法可以使用它。之所以使用Png,是因为它为报表输出(excel、html)创建了较小的图像。问题是SaveToStream似乎花费了太多的时间,比将TBitmap转换为TPngImage或将TStream与png一起使用要多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个图像测试的,下面是时间:
第一步:7秒
第二步:93秒
第三步:6秒
为什么保存到流这么慢?有什么优化建议吗?
使用 Delphi XE7

编辑

下面是一个简单的bmp转换为PNG然后保存到流中的例子(MCVE)。为了另一个验证,我添加了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;
mitkmikd

mitkmikd1#

这是用70000个图像测试的,下面是时间:
第一步:7秒
第二步:93秒
第三步:6秒
为什么保存到Stream这么慢?
让我们来分析一下数字:
第1步:7秒= 7000毫秒。7000 / 70000 =每张图像0.1毫秒
第2步:93秒= 93000毫秒。93000 / 70000 =每张图像约1.33毫秒
第3步:6秒= 6000毫秒。6000 / 70000 =每张图像约0.086毫秒
你认为1.33毫秒每SaveToStream()是慢的吗?你只是做了很多,所以它们会随着时间的推移而增加,仅此而已。
也就是说,内存中的PNG数据没有被压缩。当数据被保存时,它被压缩了。所以这是速度变慢的一个原因。而且,保存PNG会对流进行大量的写入,这会导致流执行多次内存(重新)分配(TPNGImage在保存过程中也会执行内部内存分配),所以这是另一个速度变慢的原因。
有什么优化建议吗?
对于压缩开销您无能为力,但至少可以在调用SaveToStream()之前将TMemoryStream.Capacity预先设置为一个合理的值,以减少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;
  ...

如果这对你来说还不够快,考虑使用线程来并行处理多个图像,不要按顺序处理它们。

hlswsv35

hlswsv352#

你指定压缩级别了吗?我没有注意到

PNGImage.CompressionLevel := 1;

它可以在0到9的范围内。默认情况下,它是7。如果将它设置为1,则速度会明显加快,而输出流大小的增加将可以忽略不计。

bjp0bcyl

bjp0bcyl3#

也许不是直接相关的,但我有一个问题,读/写(在RAM中),因为我使用的是TMemoryStream(逐字节)。
在我的情况下,我可以调整我的代码,使其在纯RAM中工作,使用SetLength(MyEncripted,TheSize);SetLength(MyClear,TheSize);,而不是TMemoryStream.ReadTMemoryStream.Write
我所做的是一个简单的“概念”(可以用纯RAM字符串变量来完成),用一个TMemoryStream来写MyEncripted[i]:=Chr(Ord(MyClear[i]) xor Ord(MyKey[i]));
我的测量时间:

  • 将TMemoryStream用于633千字节=〉3 '21”
  • 使用633千字节的RAM字符串类型变量=〉接近瞬时,小于0.1”
  • 将TMemoryStream用于6.3兆字节=〉33'30”
  • 使用6.3兆字节的RAM字符串类型变量=〉接近瞬时,小于0.1”
  • 将TMemoryStream用于63兆字节=〉5小时35分钟'00”
  • 使用63兆字节的RAM字符串类型变量=〉接近瞬时,小于0.1”
  • 将TMemoryStream用于633兆字节=〉55 h50 '00”
  • 将RAM字符串类型变量用于633兆字节=〉小于1”

注:我没有进一步的时间,因为55小时是真的很大,足以看到发生了什么;但它看起来是线性的。

TMemoryStream太慢了,太慢了,在RAM中不到一秒钟就能完成的事情,在使用TMemoryStream时可能要花两天以上的时间。写入(以逐字节逻辑)。

所以我很久以前就决定再也不使用任何TMemoryStream了。
希望这能帮助理解TMemoryStream与直接RAM String变量相比是什么级别的SLOW。

相关问题