减少PNG存储大小

时间:2016-05-03 12:03:57

标签: image delphi png filesize delphi-10-seattle

为了减少web服务的文件上传大小,我们将图像文件的高度/宽度限制为最大值。
对于JPG文件,这可以正常工作:(向下)图像调整大小会导致文件大小减小。

但对于PNG文件却不是这样:在大多数情况下,我们的代码会产生更大的文件大小:

procedure TFrmImageCheckAndResize.ResizePNGImage;
var
  lSrcPNGImage, lTrgPNGImage: TdxPNGImage;
  lSrcBitmap,lDestBitMap: TcxAlphaBitmap;
  lNewWidth,lNewHeight: Integer;
  lFactor: Real;
begin
  lSrcPNGImage := TdxPNGImage.Create;
  lSrcPNGImage.LoadFromFile(FFileName);
  lSrcBitmap := TcxAlphaBitmap.CreateSize(lSrcPNGImage.Width, lSrcPNGImage.Height, True);
  lSrcBitmap.Canvas.Draw(0, 0, lSrcPNGImage);
  if lSrcPNGImage.Width > lSrcPNGImage.Height then
     if lSrcPNGImage.Width > FEditImageRes.Value then
        lFactor := lSrcPNGImage.Width
     else
        lFactor := 0
  else
     if lSrcPNGImage.Height > FEditImageRes.Value then
        lFactor := lSrcPNGImage.Height
     else
        lFactor := 0;
  if lFactor <> 0 then
  begin
     lFactor := lFactor / FEditImageRes.Value;
     lNewWidth  := Trunc(lSrcPNGImage.Width  / lFactor);
     lNewHeight := Trunc(lSrcPNGImage.Height / lFactor);
     lDestBitMap := TcxAlphaBitmap.CreateSize(lNewWidth,lNewHeight, True);
     cxSmoothResizeBitmap(lSrcBitMap, lDestBitMap, true);
     lTrgPNGImage := TdxPNGImage.CreateFromBitmap(lDestBitmap);
  end
  else
  begin
     lDestBitmap := nil; // Silence the compiler
     lTrgPNGImage := TdxPNGImage.CreateFromBitmap(lSrcBitmap);
  end;
  lTrgPNGImage.SaveToFile(StringReplace(FFileName,'.','_' + IntToStr(FEditImageRes.Value) + '.',[]));
  lSrcBitmap.Free;
  lDestBitmap.Free;
  lTrgPNGImage.Free;
  lSrcPNGImage.Free;
end;  

FFileName是从磁盘加载的图片,FEditImageRes.Value包含我们减少的最大维度。

请注意,我们使用Developer Express组件,并且此代码维护alpha通道(透明度) 我也不喜欢。

我发布了ticket with DevExpress,但这不是他们代码中的问题。

我查看了其他软件的功能:

enter image description here

在Paint.Net中,如果我将上面的890 * 161截图缩小到512 * 93,我会看到混合结果,具体取决于用于调整大小的算法:

15.697 Original.png
21.904 Resized_BiCubic.png
19.995 Resized_Bilineair.png
22.905 Resized_Fant.png
 6.729 Resized_NearestNeigbour.png

enter image description here

对于缩小为512 * 353的550x386照片,Paint.Net结果为:

375.229 Photo.png
419.122 Photo_Bicubic.png
402.277 Photo_Bilineair.png
407.959 Photo_Fant.png
416.619 Photo_NearestNeighbor.png

所以结果看起来很不可预测。

问题:
有什么我可以做的(更改为我的代码)以确保(大多数)调整大小的PNG文件实际上将减少文件大小?

2 个答案:

答案 0 :(得分:4)

有些规则规定如何使用TPngImage

中的vcl.imaging.PngImage获得最小的图像尺寸

首先,它有CompressionLevel属性,你可以设置整数值0..9,0没有压缩全部,9是最好的压缩(但最慢的一个)。默认设置为7。注意:PNG始终无损,此设置仅影响保存图像所需的时间。

其次,Filters属性,默认情况下,其值为[pfSub],但要实现最佳压缩,您应将其设置为[pfNone, pfSub, pfUp, pfAverage, pfPaeth]。这些是预测滤波器,其应用于每行图像以便使用邻居之间的相关性以获得更好的压缩。设置完所有过滤器后,将尝试各个过滤器并使用最佳过滤器。

确保InterlaceMethod属性设置为imNone。也许您的原始图像是隔行扫描的,在这种情况下,与非隔行扫描相比,文件大小增加了5..20%。

还有一种方法可以获得较小的图像尺寸,即将MaxIDATSize属性增加到比图像尺寸更多的值。要点是,PNG像素数据存储在一个或多个IDAT块中,每个块的大小为4个字节,然后是其名称的4个字节('IDAT'),然后是数据,然后是4个字节的CRC。默认情况下,每个块的大小为65535字节,因此在大图像中,您将拥有大量的内容,每个块的浪费为12个字节。但这里的增幅非常小,只有0.2%,而不是那么多。

事实上,通过这样的设置,PNGImage可以生成相当小的文件,通常比Paint.NET小10..20%,但专业程序可以更好地压缩PNG。

关于调整大小。在尝试调整屏幕大小时,通常会出现较大的文件大小。原始屏幕截图几乎没有颜色,在你的情况下它是431,很多是因为渐变。有很多相同颜色的区域可以很好地保存。调整大小后,从一种颜色到另一种颜色的每次急剧过渡都会模糊,因此会产生更多“混合”颜色,因此压缩起来非常困难。如您所见,最近邻居的文件大小最小,因为它不会创建原始图像中没有的新颜色。

你的第二个例子,550x386照片在调整大小后必须有较小的文件大小,事实上,我已经设法压缩大小调整为512x353照片到303 kB。 Paint.net根本不使用预测过滤器,这就是它对照片和其他真彩色图像进行可怕压缩的原因。

答案 1 :(得分:1)

最好的方法是使用专用工具来减小png大小。

请查看this page,了解有关如何优化png大小的一些技术细节

有些工具效果很好,我使用了http://optipng.sourceforge.net

另见this comparison of png optimization tools

相关问题