JPEG, PNG, GIF - Resize

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
gocher
Beiträge: 298
Registriert: Di 23. Nov 2010, 23:41
OS, Lazarus, FPC: Ubuntu/Win, Lazarus trunk, FPC trunk
CPU-Target: 32Bit/64Bit
Wohnort: Geldern
Kontaktdaten:

JPEG, PNG, GIF - Resize

Beitrag von gocher »

Hallo zusammen,
ich suche nun schon seit Wochen nach einer Alternative um JPEG, PNG und GIF Dateien zu skalieren also z.B. für Vorschauansichten, z.Z. setze ich die GDIPlus ein mit der kann man auch die internen Vorschauansichten nutzen.
Mit FPImage dauert das Laden des Bildes schon 3-mal so lange wie wie der komplette Vorgang mit GDIPlus (Edit: Achtung Korrektur weiter unten)!
Ich habe auch Vampyre Imaging getestet, das dauert der gesamte Vorgang ca. 6-mal so lange wie mit GDIPlus!
Und das Schlimmste daran ist ich habe es verglichen mit den GDIPlus-Werten ohne die Nutzung der internen Vorschauansichten die nur bei sehr kleinen Abbildungen in Frage kommt und nun kommt mein Problem: "Ich möchte eigentlich auf Linux umstellen und dort gibt es keine GDIPlus" :cry:

Bei meinem Messungen habe ich ein Bild (JPEG) 4752 x 3168, 6960KB verwendet um eine 300 x 200 Voransicht zu generieren.

GDIPlus: 422 ms
Vampyre Imaging: 2481 ms
FPImage: 2840 ms (Edit: Achtung Korrektur weiter unten)

Habt Ihr bessere Erfahrungen oder noch besser eine Alternative?
Zuletzt geändert von gocher am So 29. Sep 2013, 11:46, insgesamt 1-mal geändert.
MfG Gocher
akt. Projekt: Webserver(HTTPS HTTP/2) mit integrierten CMS in Free Pascal - www.gocher.me


gocher
Beiträge: 298
Registriert: Di 23. Nov 2010, 23:41
OS, Lazarus, FPC: Ubuntu/Win, Lazarus trunk, FPC trunk
CPU-Target: 32Bit/64Bit
Wohnort: Geldern
Kontaktdaten:

Re: JPEG, PNG, GIF - Resize

Beitrag von gocher »

ThumbResize: 312 ms :) Qualität des Resultates allerdings schlechter :(
Aber auf jeden Fall eine Möglichkeit, vielleicht habe ich auch noch nicht alle möglichen Einstellungen gefunden.
Ich melde mich hierzu noch einmal und stelle dann den Test und das Ergebnis ein.

Auf jeden Fall schon einmal, vielen Dank!
MfG Gocher
akt. Projekt: Webserver(HTTPS HTTP/2) mit integrierten CMS in Free Pascal - www.gocher.me

gocher
Beiträge: 298
Registriert: Di 23. Nov 2010, 23:41
OS, Lazarus, FPC: Ubuntu/Win, Lazarus trunk, FPC trunk
CPU-Target: 32Bit/64Bit
Wohnort: Geldern
Kontaktdaten:

Re: JPEG, PNG, GIF - Resize

Beitrag von gocher »

Nach ein paar kleinen Änderungen:
Writer.CompressionQuality := 95;
Writer.ProgressiveEncoding := true;
und aspect ratio eingebaut
GDIPlus: 359 ms
GDIPlus: 359 ms
Tiger~GDIPlus.jpg (30.41 KiB) 2934 mal betrachtet
FpRawResize: 327 ms
FpRawResize: 327 ms
Tiger~FpRawResize.jpg (31.97 KiB) 2934 mal betrachtet
ThumbResize: 358 ms
ThumbResize: 358 ms
Tiger~ThumbResize.jpg (29.93 KiB) 2934 mal betrachtet
Edit: FPImage (Canvas): 405 ms (Korrektur :))
Edit: FPImage (Canvas): 405 ms (Korrektur :))
Tiger~FPImage.jpg (29.93 KiB) 2924 mal betrachtet
Die Qualität von FpRawResize ist schlecht ThumbResize kommt schon näher an die von GDIPlus heran.
Ich wüsste nun auch im Augenblick nicht mehr an welcher Schraube ich noch drehen könnte!

Code: Alles auswählen

program resize;
{$APPTYPE CONSOLE}
{$MODE objfpc}{$H+}
 
uses
  Classes, Sysutils,
   FPImage,  FPWriteJPEG, 
{$ifdev canvas}
  FPCanvas, FPImgCanv, FPReadJPEG
{$else}
  FPReadJPEGthumb, FPThumbResize //Theo
{$endif};
 
var
  InFile, OutFile: string;
  AWidth, AHeight: word;
  Image, DestImage: TFPMemoryImage;
{$ifdef canvas}
  Canvas: TFPImageCanvas; 
{$endif}
  Reader: TFPReaderJPEG;
  Writer: TFPWriterJPEG;
  area: TRect;
  StartTime: DWord;
begin
  StartTime := GetTickCount();
  InFile := 'C:\Lazarus\projects\ImageResize\thumbview\Tiger.jpg';
  OutFile := 'C:\Lazarus\projects\ImageResize\thumbview\Tiger~.jpg';
  AWidth := 350;
  AHeight := 200;
  Image := TFPMemoryImage.Create(0, 0);
  try
    Image.UsePalette := false;
    Reader := TFPReaderJPEG.Create;
    try
      Reader.Performance := jpBestQuality;//jpBestSpeed;
      Reader.MinHeight := AHeight; //important!
      Reader.MinWidth := AWidth;  //important!
      Image.LoadFromFile(InFile, Reader);
    finally
      Reader.Free;
    end;
    if AWidth = 0 then AWidth := Image.Width;
    if AHeight = 0 then AHeight := Image.Height;
    // Scale image whilst preserving aspect ratio
    if (Image.Width / Image.Height) > (AWidth / AHeight) then
      AHeight := Round(AWidth / (Image.Width / Image.Height))
    else if (Image.Width / Image.Height) < (AWidth / AHeight) then
      AWidth := Round(AHeight * (Image.Width / Image.Height));
{$ifdef canvas}
      DestImage := TFPMemoryImage.Create(AWidth, AHeight); 
      Canvas := TFPImageCanvas.Create(DestImage);
      try
        Canvas.StretchDraw(0, 0, AWidth, AHeight, Image);
      finally
        Canvas.Free;
      end;
{$else}
    DestImage := ThumbResize(Image, AWidth, AHeight, area);
//    DestImage := FpRawResize(AWidth, AHeight, Image);
{$endif}
    try
      Writer := TFPWriterJPEG.Create;
      try
        Writer.CompressionQuality := 95;
        Writer.ProgressiveEncoding := true;
        DestImage.SaveToFile(OutFile, Writer);
      finally
        Writer.Free;
      end;
    finally
      DestImage.free;
    end;
    WriteLn(IntToStr(GetTickCount() - StartTime))
  finally
    Image.Free;
  end;
end.  
Zuletzt geändert von gocher am So 29. Sep 2013, 12:08, insgesamt 2-mal geändert.
MfG Gocher
akt. Projekt: Webserver(HTTPS HTTP/2) mit integrierten CMS in Free Pascal - www.gocher.me

Euklid
Lazarusforum e. V.
Beiträge: 2808
Registriert: Fr 22. Sep 2006, 10:38
OS, Lazarus, FPC: Lazarus v2.0.10, FPC 3.2.0
Wohnort: Hessen
Kontaktdaten:

Re: JPEG, PNG, GIF - Resize

Beitrag von Euklid »

gocher hat geschrieben: Habt Ihr bessere Erfahrungen oder noch besser eine Alternative?
Also wir verwenden die StretchDraw: http://lazarus-ccr.sourceforge.net/docs ... hdraw.html

Die ist auch recht fix - Zeiten habe ich jetzt aber keine gemessen ;)

- Euklid

Christian
Beiträge: 6079
Registriert: Do 21. Sep 2006, 07:51
OS, Lazarus, FPC: iWinux (L 1.x.xy FPC 2.y.z)
CPU-Target: AVR,ARM,x86(-64)
Wohnort: Dessau
Kontaktdaten:

Re: JPEG, PNG, GIF - Resize

Beitrag von Christian »

Das ProgressiveEncoding sollts an der stelle eher langsamer machen.
W.m.k.A.h.e.m.F.h. -> http://www.gidf.de/

gocher
Beiträge: 298
Registriert: Di 23. Nov 2010, 23:41
OS, Lazarus, FPC: Ubuntu/Win, Lazarus trunk, FPC trunk
CPU-Target: 32Bit/64Bit
Wohnort: Geldern
Kontaktdaten:

Re: JPEG, PNG, GIF - Resize

Beitrag von gocher »

Christian hat geschrieben:Das ProgressiveEncoding sollts an der stelle eher langsamer machen.
Wenn ich beim selben Ausgangsbild also 4752x3168 6,79MB bleibe und daraus 799x533 erzeuge, dann
macht es einen Unterschied von 16 ms länger und ca. 16kB weniger aus (mit: 671 ms, 167.710 Bytes / ohne: 655 ms, 183.985 Bytes) also für die Übertragung im Internet (der Angedachte Einsatzzweck) egal!
Aber Danke, man sollte alles überprüfen!
MfG Gocher
akt. Projekt: Webserver(HTTPS HTTP/2) mit integrierten CMS in Free Pascal - www.gocher.me

Benutzeravatar
theo
Beiträge: 10900
Registriert: Mo 11. Sep 2006, 19:01

Re: JPEG, PNG, GIF - Resize

Beitrag von theo »

gocher hat geschrieben: Die Qualität von FpRawResize ist schlecht ThumbResize kommt schon näher an die von GDIPlus heran.
Ich wüsste nun auch im Augenblick nicht mehr an welcher Schraube ich noch drehen könnte!
FpRawResize wird von ThumbResize aufgerufen um ganz grosse Bilder vor dem Resampling schnell etwas zu verkleinern.
Es ist nicht zum separaten Gebrauch gedacht.
Ob man ein 10000 * 10000 Bild auf 100 * 100 resamplet oder erst mal "schlecht" auf 1000 * 1000 und dann feiner auf 100 * 100, diesen Unterschied siehst du nicht.
Es geht aber viel schneller. Genau das macht ThumbResize mit Hilfe von FpRawResize.
Zuletzt geändert von theo am So 29. Sep 2013, 12:17, insgesamt 1-mal geändert.

gocher
Beiträge: 298
Registriert: Di 23. Nov 2010, 23:41
OS, Lazarus, FPC: Ubuntu/Win, Lazarus trunk, FPC trunk
CPU-Target: 32Bit/64Bit
Wohnort: Geldern
Kontaktdaten:

Re: JPEG, PNG, GIF - Resize

Beitrag von gocher »

Euklid hat geschrieben: Also wir verwenden die StretchDraw: http://lazarus-ccr.sourceforge.net/docs ... hdraw.html

Die ist auch recht fix - Zeiten habe ich jetzt aber keine gemessen ;)

- Euklid
Ich habe nun StretchDraw von TFPImageCanvas getestet das Ergebnis habe ich oben an den entsprechenden Stellen eingebaut, nicht schlecht :D , immer weiter so mit den Ideen.

Ich werde mich nun an PNG und GIF heranmachen Ergebnisse folgen.
MfG Gocher
akt. Projekt: Webserver(HTTPS HTTP/2) mit integrierten CMS in Free Pascal - www.gocher.me

Benutzeravatar
theo
Beiträge: 10900
Registriert: Mo 11. Sep 2006, 19:01

Re: JPEG, PNG, GIF - Resize

Beitrag von theo »

gocher hat geschrieben: Ich habe nun StretchDraw von TFPImageCanvas getestet das Ergebnis habe ich oben an den entsprechenden Stellen eingebaut, nicht schlecht :D , immer weiter so mit den Ideen.
Schau dir doch mal den Code von ThumbResize an. Da ist StretchDraw und FpRawResize drin.
Ich weiss gar nicht, was du hier immer neu erfindest. ;-)

gocher
Beiträge: 298
Registriert: Di 23. Nov 2010, 23:41
OS, Lazarus, FPC: Ubuntu/Win, Lazarus trunk, FPC trunk
CPU-Target: 32Bit/64Bit
Wohnort: Geldern
Kontaktdaten:

Re: JPEG, PNG, GIF - Resize

Beitrag von gocher »

theo hat geschrieben:
gocher hat geschrieben: Ich habe nun StretchDraw von TFPImageCanvas getestet das Ergebnis habe ich oben an den entsprechenden Stellen eingebaut, nicht schlecht :D , immer weiter so mit den Ideen.
Schau dir doch mal den Code von ThumbResize an. Da ist StretchDraw und FpRawResize drin.
Ich weiss gar nicht, was du hier immer neu erfindest. ;-)
Ich erfinde nichts neu, Euklid hat geschrieben ich solle StretchDraw mal testen, dabei ist mir aufgefallen das FPReadJPEG auch schon MinHeight und MinWidth unterstützt, ob das von Deiner Klasse FPReadJPEGthumb kommt weiß ich nicht!
Um einen Vergleich machen zu können gehe ich immer so vor:
Ein Beispiel mit Zeitmessung der Standard-Variante wird angelegt, die am Vielversprechendsten Möglichkeiten werden über Google gesucht und im Beispiel eingebaut, dann werden die Resultate verglichen. Wenn ich ratlos bin wende ich mich an ein Forum :) . Dann kommen neue Ideen wie Deine (übrigens eine sehr gute Lösung), die geben Anlass die anderen Möglichkeiten zu überprüfen ob dort ähnliche Einstellungsmöglichkeiten existieren. Das war bei FPReadJPEG der Fall also in der Standard-Variante.

Ich bin nur auf der Suche nach dem Schnellsten Weg und dabei kommt es mir darauf an das eine möglichst gute Qualität des Ergebnisses erzielt wird, in einer möglichst kurzen Zeit!
Noch einmal hierbei geht es mir erst einmal um die drei schon im Titel genannten Formate und da finde ich findet man im Netz sehr viel, aber bei mir kommt es in diesem Fall auf die Millisekunde an!
MfG Gocher
akt. Projekt: Webserver(HTTPS HTTP/2) mit integrierten CMS in Free Pascal - www.gocher.me

Benutzeravatar
theo
Beiträge: 10900
Registriert: Mo 11. Sep 2006, 19:01

Re: JPEG, PNG, GIF - Resize

Beitrag von theo »

gocher hat geschrieben:dabei ist mir aufgefallen das FPReadJPEG auch schon MinHeight und MinWidth unterstützt, ob das von Deiner Klasse FPReadJPEGthumb kommt weiß ich nicht!
Es kommt schon von FPReadJPEGthumb, ich habe aber natürlich einen Patch geschickt, damit es in die FCL kommt.
http://bugs.freepascal.org/view.php?id=16748

Mit neueren FPC Versionen kann man statt FPReadJPEGthumb auch FPReadJPEG verwenden.

gocher
Beiträge: 298
Registriert: Di 23. Nov 2010, 23:41
OS, Lazarus, FPC: Ubuntu/Win, Lazarus trunk, FPC trunk
CPU-Target: 32Bit/64Bit
Wohnort: Geldern
Kontaktdaten:

Re: JPEG, PNG, GIF - Resize

Beitrag von gocher »

Tja, nun habe ich PNG (auch teiltransparente) in die Routine eingebaut, BMP konvertiere ich nach JPEG damit es im Browser keine Probleme gibt und da bleibe ich bei GIF hängen!
Für GIF gibt es nur TFPReaderGIF, aber leider keinen TFPWriterGIF, gibt es da was, wenn auch nur Ansätze, oder muss ich für GIFs einen anderen Weg nehmen?

Anbei die erweiterte Testroutine:

Code: Alles auswählen

program resize;
{$APPTYPE CONSOLE}
{$MODE objfpc}{$H+}
 
uses
  Classes, SysUtils, FPImage, FPCanvas, FPImgCanv, FPReadJPEG, FPWriteJPEG,
  FPReadPNG, FPWritePNG, FPReadGIF, FPReadBMP;
 
function resize(const InFile, OutFile: string; const maxWidth, maxHeight: word): boolean;
type TImgType = (itJPEG, itGIF, itPNG, itBMP);
var
  ext: string;
  it: TImgType;
  AWidth, AHeight: word;
  Image, DestImage: TFPMemoryImage;
  Canvas: TFPImageCanvas;
  Reader: TFPCustomImageReader;
  Writer: TFPCustomImageWriter;
  StartTime: DWord;
begin
  result := false;
  StartTime := GetTickCount();
  ext := LowerCase(ExtractFileExt(InFile));
  if (ext = '.jpg') or (ext = '.jpeg') then it := itJPEG
  else if (ext = '.png') then it := itPNG
  else if (ext = '.gif') then it := itGIF
  else if (ext = '.bmp') then it := itBMP
  else Exit;
  AWidth := maxWidth;
  AHeight := maxHeight;
  Image := TFPMemoryImage.Create(0, 0);
  try
    case it of
    itJPEG: Reader := TFPReaderJPEG.Create;
    itPNG: Reader := TFPReaderPNG.Create;
    itGIF: Reader := TFPReaderGIF.Create;
    itBMP: Reader := TFPReaderBMP.Create;
    end;
    try
      case it of
      itJPEG: begin
                TFPReaderJPEG(Reader).Performance := jpBestQuality;//jpBestSpeed;
                TFPReaderJPEG(Reader).MinHeight := AHeight;
                TFPReaderJPEG(Reader).MinWidth := AWidth;
              end;
      end;
      Image.LoadFromFile(InFile, Reader);
    finally
      Reader.Free;
    end;
    WriteLn('Zeit nach (LoadImageFromFile): ' + IntToStr(GetTickCount() - StartTime));
    if AWidth = 0 then AWidth := Image.Width;
    if AHeight = 0 then AHeight := Image.Height;
    // Scale image whilst preserving aspect ratio
    if (Image.Width / Image.Height) > (AWidth / AHeight) then
      AHeight := Round(AWidth / (Image.Width / Image.Height))
    else if (Image.Width / Image.Height) < (AWidth / AHeight) then
      AWidth := Round(AHeight * (Image.Width / Image.Height));
    WriteLn('Resizing images to: ' + IntToStr(AWidth) + 'x' + IntToStr(AHeight));
    DestImage := TFPMemoryImage.Create(AWidth, AHeight);
    try
      Canvas := TFPImageCanvas.Create(DestImage);
      try
        Canvas.StretchDraw(0, 0, AWidth, AHeight, Image);
      finally
        Canvas.Free;
      end;
      WriteLn('Zeit nach (ResizeImage): ' + IntToStr(GetTickCount() - StartTime));
      case it of
      itJPEG: Writer := TFPWriterJPEG.Create;
      itPNG: Writer := TFPWriterPNG.Create;
      itGIF: Writer := TFPWriterPNG.Create; //<----mein Problem
      itBMP: Writer := TFPWriterJPEG.Create;
      end;
      try
        case it of
        itJPEG, itBMP: begin
                  TFPWriterJPEG(Writer).CompressionQuality := 95;
                  TFPWriterJPEG(Writer).ProgressiveEncoding := true;
                end;
        itPNG, itGIF : begin
                 TFPWriterPNG(Writer).UseAlpha := true;
               end;
        end;
        DestImage.SaveToFile(OutFile, Writer);
        result := true;
      finally
        Writer.Free;
      end;
      WriteLn('Zeit nach (SaveImageToFile): ' + IntToStr(GetTickCount() - StartTime))
    finally
      DestImage.Free;
    end;
  finally
    Image.free;
  end;
end;
 
var sPath: string;
begin
  sPath := ExtractFilePath(ParamStr(0));
  resize(sPath + 'Tiger.bmp', sPath + 'Tiger~.jpg', 350, 200);
end.
MfG Gocher
akt. Projekt: Webserver(HTTPS HTTP/2) mit integrierten CMS in Free Pascal - www.gocher.me

gocher
Beiträge: 298
Registriert: Di 23. Nov 2010, 23:41
OS, Lazarus, FPC: Ubuntu/Win, Lazarus trunk, FPC trunk
CPU-Target: 32Bit/64Bit
Wohnort: Geldern
Kontaktdaten:

Re: JPEG, PNG, GIF - Resize

Beitrag von gocher »

Und da ist noch eine Frage bei mir offen, gibt es eine Möglichkeit an Stelle der Eigentlichen Bilddaten für sehr kleine Thumbnails bis zu 100x100 Pixel die internen Vorschau-Ansichten (Integral low-res Exif thumbnail) zu nutzen.

Diese Möglichkeit erlaubt es bei GDIPlus (GdipGetImageThumbnail) ein Thumbnail in dieser Größe in 31 ms zu erzeugen, das ist mehr als 10-mal so schnell wie die beschriebene Methode oder fpthumbresize!
MfG Gocher
akt. Projekt: Webserver(HTTPS HTTP/2) mit integrierten CMS in Free Pascal - www.gocher.me

Benutzeravatar
theo
Beiträge: 10900
Registriert: Mo 11. Sep 2006, 19:01

Re: JPEG, PNG, GIF - Resize

Beitrag von theo »

gocher hat geschrieben: Für GIF gibt es nur TFPReaderGIF, aber leider keinen TFPWriterGIF, gibt es da was, wenn auch nur Ansätze, oder muss ich für GIFs einen anderen Weg nehmen?
Vampyre oder OpBitmap können Gif schreiben.
in FPC / Lazarus ist GIF readonly afaik.

Antworten