Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Multimedia (https://www.delphipraxis.net/16-multimedia/)
-   -   Delphi JPEG CompressionQuality ermitteln (https://www.delphipraxis.net/124880-jpeg-compressionquality-ermitteln.html)

e-gon 26. Nov 2008 20:23


JPEG CompressionQuality ermitteln
 
Hallo,

kennt jemand eine Möglichkeit aus einer JPEG-Grafik die Komprimierungsrate zu ermitteln?

Delphi-Quellcode:
Quality := JPEG.CompressionQuality
geht leider nicht. Quality ist immer 0. Das liegt wahrscheinlich daran, dass die Komprimierung eines JPEGs nicht direkt in der Datei abgelegt wird sondern nur indirekt in die Huffman-Tabelle steht und die eher bescheidene JPEG-Unit nicht in der Lage ist die Qualität zu ermitteln.
Aber vielleicht kennt jemand eine andere Möglichkeit die Komprimierung auszulesen?

Danke für Eure Antwort!

e-gon

e-gon 26. Nov 2008 21:05

Re: JPEG CompressionQuality ermitteln
 
Sorry, hat sich erledigt!
Ich habe vergessen JPEG zu initialisieren... http://www.delphipraxis.net/posting.php?mode=iframe
ouch!

Schwedenbitter 16. Okt 2017 15:42

AW: Re: JPEG CompressionQuality ermitteln
 
Auch wenn das schon etwas älter ist. Ich wärme es auf, weil ich auch gern die Lösung wüsste und es einer der ersten Funde ist, wenn man in der DP und auch bei google sucht. Es wäre daher toll, wenn dann in genau diesem Beitrag eine/die Lösung stünde.

Zitat:

Zitat von e-gon (Beitrag 850930)
Ich habe vergessen JPEG zu initialisieren... http://www.delphipraxis.net/posting.php?mode=iframe

a) Wie geht dieses "initialisieren"? Ich würde auch gern die richtige CompressionQuality in Erfahrung bringen.
b) Der Link funktioniert leider nicht (mehr?), so dass ich das nicht nachvollziehen kann.

TiGü 17. Okt 2017 08:17

AW: JPEG CompressionQuality ermitteln
 
Hast du eine TJPEGImage-Instanz vorzuliegen?
Dann kannst du doch das über die Property auslesen:
http://docwiki.embarcadero.com/Libra...ressionQuality

TiGü 17. Okt 2017 08:39

AW: JPEG CompressionQuality ermitteln
 
Ach guck, das ist ja immer 90?! Das wird anscheinend wirklich gar nicht ausgelesen.

Schwedenbitter 17. Okt 2017 09:03

AW: JPEG CompressionQuality ermitteln
 
Zitat:

Zitat von TiGü (Beitrag 1383484)
Ach guck, das ist ja immer 90?! Das wird anscheinend wirklich gar nicht ausgelesen.

Ich wollte mir gerade die Mühe machen, mal ein paar Bilder mit unterschiedlichen Raten zu packen und Dich die CompressionQuality auslesen zu lassen :twisted:
Das wird nicht ausgelesen, sondern beim Erstellen der Klasse in Create mittels
Delphi-Quellcode:
FQuality := JPEGDefaults.CompressionQuality;
gesetzt. Das wiederum ist eben 90. Wenn man mit der Suche nach FQuality forscht, stellt man schnell fest, dass diese nur gelesen wird. :roll:

TiGü 17. Okt 2017 12:14

AW: JPEG CompressionQuality ermitteln
 
Schade, ich dachte man kann es über WIC und die Metadaten auslesen, aber in meiner Beispieldatei (mit Smartphone gemacht und Paint.NET bearbeitet) gibt es kein Compression-Feld.

Programme wie IrfanView geben ja das richtige aus...hm...grübel!

Kannst ja mal trotzdem versuchen.

Delphi-Quellcode:
uses
  Winapi.Windows, Winapi.Wincodec, Winapi.ActiveX;

...

function GetJpegCompression(const AJpegFilename: string): USHORT;
var
  HR: HRESULT;
  ImagingFactory: IWICImagingFactory;
  Decoder: IWICBitmapDecoder;
  Frame: IWICBitmapFrameDecode;
  MetaDataReader: IWICMetadataQueryReader;
  MetaDataPathName: PWideChar;
  Value: PROPVARIANT;
  IFDReader: IWICMetadataQueryReader;
  Compression: USHORT;

  procedure DebugShowAllPropertyNames(const AMetaDataQueryReader: IWICMetadataQueryReader);
  var
    iValue: Longint;
    Enumerator: IEnumString;
    MetaDataPathName: PWideChar;
  begin
    // Spaßeshalber alle Metadaten auflisten
    iValue := 0;
    HR := AMetaDataQueryReader.GetEnumerator(Enumerator);
    if Succeeded(HR) then
    begin
      while Enumerator.Next(1, MetaDataPathName, @iValue) = S_OK do
      begin
        OutputDebugString(MetaDataPathName);
      end;
    end;
  end;

begin
  Result := 0;
  HR := CoCreateInstance(CLSID_WICImagingFactory, nil, CLSCTX_INPROC_SERVER or CLSCTX_LOCAL_SERVER, IUnknown, ImagingFactory);
  if Succeeded(HR) then
  begin
    HR := ImagingFactory.CreateDecoderFromFilename(PChar(AJpegFilename), TGUID.Empty, GENERIC_READ,
      WICDecodeMetadataCacheOnDemand, Decoder);
    if Succeeded(HR) then
    begin
      HR := Decoder.GetFrame(0, Frame);
      if Succeeded(HR) then
      begin
        HR := Frame.GetMetadataQueryReader(MetaDataReader);
        if Succeeded(HR) then
        begin
          // https://msdn.microsoft.com/en-us/library/windows/desktop/ee719904(v=vs.85).aspx#_jpeg_metada

          MetaDataPathName := '/app1/ifd';
          PropVariantInit(Value);
          HR := MetaDataReader.GetMetadataByName(MetaDataPathName, Value);
          if Succeeded(HR) then
          begin
            if Value.vt = VT_UNKNOWN then
            begin
              HR := IUnknown(Value.ppunkVal).QueryInterface(IID_IWICMetadataQueryReader, IFDReader);
              PropVariantClear(Value);
              if Succeeded(HR) then
              begin
                DebugShowAllPropertyNames(IFDReader);

                PropVariantInit(Value);
                // das sollte eigentlich Compression sein -> bei mir leider nicht vorhanden
                MetaDataPathName := '/{ushort=259}';
                HR := IFDReader.GetMetadataByName(MetaDataPathName, Value);
                if Succeeded(HR) then
                begin
                  if Value.vt = VT_UI2 then
                  begin
                    HR := PropVariantToUInt16(Value, Compression);
                    if Succeeded(HR) then
                    begin
                      Result := Compression;
                    end;
                  end;
                end;
              end;
            end;
          end;
        end;
      end;
    end;
  end;
end;

TiGü 17. Okt 2017 12:44

AW: JPEG CompressionQuality ermitteln
 
Schade, mit GDI+ komm ich auch nicht ran.

Delphi-Quellcode:
function GdipCheck(const AResult: GPSTATUS): Boolean;
begin
  Result := AResult = GPSTATUS.Ok;
end;

function GdiPlusGetJpegCompression(const AJpegFilename: string): USHORT;
var
  token: ULONG;
  Input: TGdiplusStartupInput;
  Output: TGdiplusStartupOutput;
  status: GPSTATUS;
  image: GpImage;
  size: UINT;
  propertyItemID:PROPID;
  propertyItem: TPropertyItem;  
begin
  Result := 0;
  FillChar(Input, SizeOf(Input), 0);
  Input.GdiplusVersion := 1;
  FillChar(Output, SizeOf(Output), 0);
  GdiplusStartup(token, @Input, @Output);
  try
    status := GdipCreateBitmapFromFile(PChar(AJpegFilename), image);
    if GdipCheck(status) then
    begin  
      propertyItemID := PropertyTagJPEGQuality; // PropertyTagCompression
      status := GdipGetPropertyItemSize(image, propertyItemID, size);
      if GdipCheck(status) then
      begin
        ZeroMemory(@propertyItem, SizeOf(propertyItem));
        status := GdipGetPropertyItem(nil, propertyItemID, size, @propertyItem);
        if GdipCheck(status) then
        begin            
          Result := PWORD(propertyItem.value)^;
        end;
      end;
    end;
  finally
    GdiplusShutdown(token);
  end;
end;

Schwedenbitter 25. Okt 2017 09:15

AW: JPEG CompressionQuality ermitteln
 
Ich habe das - etwas unorthodox - für mich selbst jetzt so gelöst:
Delphi-Quellcode:
Function CalcCompressionQuality(Const JPG: TJPEGImage;
   Const MustFit: Boolean = False): TJPEGQualityRange;
Var
   aMS            : TMemoryStream;
   aBMP            : TBitmap;
   aJPG            : TJPEGImage;
   aSize            : Int64;
   lQ, hQ         : Integer;
   Piv, oldPiv      : Integer;                        // Ausgangswert = 0
Begin
   aMS:= TMemoryStream.Create;                     // TMemoryStream erzeugen
   aBMP:= TBitmap.Create;                           // TBitmap erzeugen
   aJPG:= TJPEGImage.Create;                        // TJPEGImage erzeugen
   Try
      JPG.SaveToStream(aMS);                        // in Stream ablegen
      aSize:= aMS.Size;                              // Originalgröße ermitteln
      aBMP.Assign(JPG);                              // Bild ins TBitmap kopieren
      lQ:=   Low(Result);                           // untere Grenze
      hQ:=   High(Result);                        // obere Grenze
      Piv:= (hQ - lQ) Div 2;                        // in der Mitte anfangen
      Repeat
         aMS.Clear;                                 // Stream leeren
         aJPG.CompressionQuality:= Piv;            // Kompressionsrate setzen
         aJPG.Assign(aBMP);                        // Bitmap kopieren/komprimieren
         aJPG.SaveToStream(aMS);                     // JPG in Stream kopieren
         oldPiv:= Piv;                              // altes Pivot-Element merken
         If (aMS.Size > aSize) Then                  // Ergebnis ist zu groß
         Begin
            hQ:= Piv;                              // obere Grenze = aktueller Wert
            Piv:= Piv - ((hQ -  lQ) Div 2);         // neuen Wert berechnen
         End
         Else Begin                                 // Ergebnis kleiner oder gleich
            lQ:= Piv;                              // untere Grenze = aktueller Wert
            Piv:= Piv + ((hQ - lQ) Div 2);         // neuen Wert berechnen
         End;
      Until (Piv = oldPiv);                        // noch näher geht es nicht

      If (MustFit) And                              // auf keinen Fall größer !!!
         (aMS.Size > aSize) Then                     // immer noch zu groß
            Result:= Pred(Piv)                     //   => eine Nummer kleiner
      Else   Result:= Piv;            // aMS.Size = aSize => exakten Wert übergeben
   Finally
      aMS.Free;                                    // TMemoryStream freigeben
      aBMP.Free;                                    // TBitmap freigeben
      aJPG.Free;                                    // TJPEGImage freigeben
   End;
End;
Vermutlich lässt sich das sogar noch optimieren. Vielleicht findet über die Zeit jemand eine saubere(re) Lösung.

mensch72 25. Okt 2017 09:36

AW: JPEG CompressionQuality ermitteln
 
Die Kompressionsrate eines JPG errechnet sich doch mathematisch rückwärts ganz simpel aus der JPG-Dateigröße im Verhältnis zum Speicherbedarf des RGB-PixelArrays:
x = (JPGfileSize*100%) / (Width*Height*3)

Wer es auf die meist 32Bit(ARGB) Speichergröße von Bitmaps im PC beziehen will rechnet eben mit "x4"
x = (JPGfileSize*100%) / (Width*Height*4)

JPGheader und JPGmetadaten sind genau wie "BitmapInfoHeader" hier zu vernachlässigen, weil es rückwärts eh stets nur eine Näherung ist.

Medium 25. Okt 2017 11:37

AW: JPEG CompressionQuality ermitteln
 
CompressionQuality <> Kompressionsrate

Schwedenbitter 25. Okt 2017 11:39

AW: JPEG CompressionQuality ermitteln
 
Zitat:

Zitat von mensch72 (Beitrag 1384081)
Die Kompressionsrate eines JPG errechnet sich doch mathematisch rückwärts ganz simpel aus der JPG-Dateigröße im Verhältnis zum Speicherbedarf des RGB-PixelArrays:

Gibt es dazu eine Quelle?
Und ist
Zitat:

Kompressionsrate
gleichbedeutend mit
Delphi-Quellcode:
CompressionQuality
?

Zitat:

Zitat von mensch72 (Beitrag 1384081)
x = (JPGfileSize*100%) / (Width*Height*3)

Wer es auf die meist 32Bit(ARGB) Speichergröße von Bitmaps im PC beziehen will rechnet eben mit "x4"
x = (JPGfileSize*100%) / (Width*Height*4)

JPGheader und JPGmetadaten sind genau wie "BitmapInfoHeader" hier zu vernachlässigen, weil es rückwärts eh stets nur eine Näherung ist.

Wofür stehen die
Zitat:

100%
? Also einfach
Delphi-Quellcode:
1
und damit die Dateigröße?

Schwedenbitter 25. Okt 2017 11:44

AW: JPEG CompressionQuality ermitteln
 
Zitat:

Zitat von Medium (Beitrag 1384104)
CompressionQuality <> Kompressionsrate

Danke. Ich glaube, mensch72 und ich reden aneinander vorbei.
Mich interessiert nicht die Kompressionsrate der Datei(größe).

mensch72 25. Okt 2017 17:21

AW: JPEG CompressionQuality ermitteln
 
würdest du mir bitte deine "CompressionQuality" definieren?

Für mich bestimmt sich nunmal die "CompressionQuality" aus dem relativen Größenerhältnis von max. unkomprimierten Bilddaten und der Größe der komprimierten Bilddaten.

Wenn du Richtung Bildqualität bzw. Informationsverlust denkst, dann entscheidet da mehr die Kompressionssoftware und deren Algos&Vorfilter.
Ein AdobePhotoshop kann Bilder ohne groß auffälligen & fürs menschliche Auge sichtbaren Qualitätsverlust viel "kleiner" packen.

Wenn man ein 1GB Bild mit Delphi und Photoshop mit welchem "Kompressions-Wert" auch immer auf eine vergleichbare Dateigröße von 100MB also "1:10" komprimiert, wird warum auch immer die optische/gefühlte Bildqualität im durch Photoshop erzeugtem Bild höher sein... das rückwärts ohne org. Daten aus der Jpeg Datei zu bestimmen, also die bessere "CompressionQuality" im Sinne resultierend besserer Bildqualität zu ermitteln ist aus meiner Sicht nicht möglich. Nach meiner Methode des "relativen Größenvergleichs" wären beide Dateien gleich gut komprimiert und hätten für meine Software die selbe "CompressionQuality".

nahpets 25. Okt 2017 18:02

AW: JPEG CompressionQuality ermitteln
 
CompressionQuality besagt, wie verlustbehaftet die Komprimierung ist. Bei 100% wird ohne Verlust komprimiert, bei kleineren Werten steigt der Verlust, d. h. die Bildqualität nimmt ab oder anders ausgedrückt: Das dekomprimierte Bild stimmt nicht mit dem Bild vor der Komprimierung überein. (Auch wenn die Bilder für das menschliche Auge gleich aussehen.)

https://de.wikipedia.org/wiki/JPEG

Schwedenbitter 25. Okt 2017 19:13

AW: JPEG CompressionQuality ermitteln
 
Zitat:

Zitat von nahpets (Beitrag 1384147)
CompressionQuality besagt, wie verlustbehaftet die Komprimierung ist. Bei 100% wird ohne Verlust komprimiert, bei kleineren Werten steigt der Verlust, d. h. die Bildqualität nimmt ab oder anders ausgedrückt: Das dekomprimierte Bild stimmt nicht mit dem Bild vor der Komprimierung überein. (Auch wenn die Bilder für das menschliche Auge gleich aussehen.)

https://de.wikipedia.org/wiki/JPEG

Dem ist nichts hinzuzufügen.
CompressionQuality ist nicht irgend ein Wert, sondern gemeint ist der von
Delphi-Quellcode:
Vcl.Imaging.jpeg.TJPEGImage.CompressionQuality
, was dem geneigten Leser aber klar wird. Und da ich das Thema nicht erstellt habe, kann ich dessen Titel nicht beeinflussen.
[EDIT]Bedeutet 100 wirklich, dass es ohne Verlust komprimiert wird? Ich denke, dass zumindest die JPEG-Version von Delphi immer verlustbehaftet ist. Ich probiere das zur Not auch aus.[/EDIT]

Zitat:

Zitat von mensch72 (Beitrag 1384143)
... Nach meiner Methode des "relativen Größenvergleichs" wären beide Dateien gleich gut komprimiert und hätten für meine Software die selbe "CompressionQuality".

Und genau das macht mein Code: er probiert aus, bei welcher CompressionQuality die gepackten Daten genauso groß wie in der Quelldatei sind :cheers:
Ab jetzt befasse ich mich damit, wie man das ganze noch mit GDI+ realisiert. Damit tue ich mich im Moment noch schwer, weil es im Internet nur extrem wenige Quellen zu Delphi und GDI+ gibt...

TiGü 26. Okt 2017 08:18

AW: JPEG CompressionQuality ermitteln
 
Zitat:

Zitat von Schwedenbitter (Beitrag 1384151)
Ab jetzt befasse ich mich damit, wie man das ganze noch mit GDI+ realisiert. Damit tue ich mich im Moment noch schwer, weil es im Internet nur extrem wenige Quellen zu Delphi und GDI+ gibt...

Guck dir mal die Unit Winapi.GDIOBJ (und Winapi.GDIPUTIL) an.
Mithilfe der ersten Unit wird ein hilfreicher Wrapper (Klassen basiert) über die flachen GDI+-Funktionen gestülpt.
Alle Methoden sind protected oder public, so dass man auch selber den Wrapper erweitern und mit hilfreichen Ableitungen arbeiten kann.
Er ähnelt der Klassen basierten C++ API und von daher ist die MSDN ein guter Anlaufpunkt für Informationen.
Auch als Delpi-Programmierer sollte man andere native Sprachen zumindest lesen können.
Der C++-Beispielcode ist in der Regel gut zu verstehen.

https://msdn.microsoft.com/en-us/lib...(v=vs.85).aspx

EWeiss 26. Okt 2017 08:39

AW: JPEG CompressionQuality ermitteln
 
So wie mir bekannt ist kannst du diese nur zuweisen..

Ich habe meinen eigenen Wrapper..

Delphi-Quellcode:
    QualityModeInvalid  = -1;
    QualityModeDefault  = 0;
    QualityModeLow      = 1; // Best performance
    QualityModeHigh     = 2; // Best rendering Quality
Delphi-Quellcode:
function GDIP_SaveImageToFile(filename: WideString; image: Cardinal; Quality: Integer
): GPSTATUS; stdcall;
var
  rSplit: TSplitStrArray;
  encoderCLSID: TGUID;
  ext: string;
  IntI: Integer;
  format: string;
  EncoderParameters: PEncoderParameters;
  SaveQuality: Integer;
  listSize: UINT;
begin

  ext := ExtractFileExt(filename);
  rSplit := Split(IMAGETYPES, ';');

  for IntI := 0 to High(rSplit) do
  begin
    if rSplit[IntI] = ext then
    begin
      case IntI of
        0:
          format := 'image/jpeg';
        1:
          format := 'image/png';
        2:
          format := 'image/bmp';
        3:
          format := 'image/gif';
        4:
          format := 'image/tiff';
        5:
          format := 'image/x-icon';
        6:
          format := 'image/x-emf';
        7:
          format := 'image/x-wmf';
      else
        begin
          result := UnknownImageFormat;
          exit;
        end;
      end;
      break;
    end;
  end;
  SkinEngine.GetEncoderClsid(format, encoderCLSID);

  GdipGetEncoderParameterListSize(Image, @encoderCLSID, listSize);
  getmem(EncoderParameters, listSize);

  GdipGetEncoderParameterList(Image, @encoderClsid, listSize, EncoderParameters);

  SaveQuality := max(min(Quality, 100), 0);
  if (SaveQuality = 0) then
    SaveQuality := 100;

  EncoderParameters.Count := 1;
  EncoderParameters.Parameter[0].Guid := EncoderQuality;
  EncoderParameters.Parameter[0].NumberOfValues := 1;
  EncoderParameters.Parameter[0].Type_ := 4;
  EncoderParameters.Parameter[0].Value := @SaveQuality;

  Result := GdipSaveImageToFile(image, PWideChar(filename), @encoderCLSID, EncoderParameters);

  freemem(EncoderParameters, listSize);
end;
so könnte das aussehen.

gruss

Schwedenbitter 26. Okt 2017 10:06

AW: JPEG CompressionQuality ermitteln
 
Zitat:

Zitat von TiGü (Beitrag 1384187)
...
Guck dir mal die Unit Winapi.GDIOBJ (und Winapi.GDIPUTIL) an.
Mithilfe der ersten Unit wird ein hilfreicher Wrapper (Klassen basiert) über die flachen GDI+-Funktionen gestülpt.
...

Die hatte ich schon entdeckt. Mir fehlen im Grunde "nur" 3 Dinge:
  1. Wie speichert man das Originalbild in einen Stream, ohne neu zu komprimieren (zur Ermittlung der Ausgangsgröße)?
  2. Wie setzt man die Kompressionsrate? Das hat EWeiss schon sehr schön gezeigt. Auch wenn das recht kompliziert aussieht. Danke dafür!
  3. Wie speichert man die Datei dieses Mal neu komprimiert testweise in einen Stream und ermittelt dessen Größe? (Also wie 1.)

Zitat:

Zitat von TiGü (Beitrag 1384187)
Auch als Delpi-Programmierer sollte man andere native Sprachen zumindest lesen können.
Der C++-Beispielcode ist in der Regel gut zu verstehen.

https://msdn.microsoft.com/en-us/lib...(v=vs.85).aspx

msdn.microsoft.com ist immer mein erster Anlaufpunkt. Oft haben sie auch Code dabei.
Ich stufe mich selbst aber als Laien - also nix Delpi-Programmierer - ein und versuche trotzdem halbwegs zu verstehen, was ich tue. Und da stolpere ich über so Sachen wie ISTREAM & Co. die scheinbar so gar nichts mit dem mir bekannnten
Delphi-Quellcode:
T(Memory)Stream
gemeinsam haben. Und ich sehe auch am übersetzten Code von EWeiss, dass es offenbar doch nicht einfacher geht, wie ich es erhofft hatte. Mit GDI+ werden es scheinbar zwangsläufig mehr als die 20 Zeilen...

P.S. Gibt es eine Funktion, mit der man TStatus in String übersetzen lassen kann? Ich kann das auch selbst. Aber wenn es das gibt, muss ich mir nicht die Arbeit machen.

TiGü 26. Okt 2017 12:42

AW: JPEG CompressionQuality ermitteln
 
Zitat:

Zitat von Schwedenbitter (Beitrag 1384197)
Die hatte ich schon entdeckt. Mir fehlen im Grunde "nur" 3 Dinge:
  1. Wie speichert man das Originalbild in einen Stream, ohne neu zu komprimieren (zur Ermittlung der Ausgangsgröße)?
  2. Wie setzt man die Kompressionsrate? Das hat EWeiss schon sehr schön gezeigt. Auch wenn das recht kompliziert aussieht. Danke dafür!
  3. Wie speichert man die Datei dieses Mal neu komprimiert testweise in einen Stream und ermittelt dessen Größe? (Also wie 1.)

msdn.microsoft.com ist immer mein erster Anlaufpunkt. Oft haben sie auch Code dabei.
Ich stufe mich selbst aber als Laien - also nix Delpi-Programmierer - ein und versuche trotzdem halbwegs zu verstehen, was ich tue. Und da stolpere ich über so Sachen wie ISTREAM & Co. die scheinbar so gar nichts mit dem mir bekannnten
Delphi-Quellcode:
T(Memory)Stream
gemeinsam haben. Und ich sehe auch am übersetzten Code von EWeiss, dass es offenbar doch nicht einfacher geht, wie ich es erhofft hatte. Mit GDI+ werden es scheinbar zwangsläufig mehr als die 20 Zeilen...

P.S. Gibt es eine Funktion, mit der man TStatus in String übersetzen lassen kann? Ich kann das auch selbst. Aber wenn es das gibt, muss ich mir nicht die Arbeit machen.

Kann ich auf all diese Fragen und offenen Punkte mit einem Quellcodeschnipsel antworten?
Beim Result musste sehen wie du es brauchst.
Komprimierte Größe im Verhältnis zu unkomprimierte Größe oder andersrum.
Ggf. hauste da noch ne * 100 dazu. Wie du magst.

Delphi-Quellcode:
function GetCompressRatio(const AJpegFilename: string): Double;
var
  Status: Winapi.GDIPAPI.TStatus;
  Image: Winapi.GDIPOBJ.TGPImage;
  OriginalStream, CompressStream: System.Classes.TMemoryStream;
  OriginalStreamAdapter, CompressStreamAdapter: Winapi.ActiveX.IStream;
  ImageFormat, EncoderCLSID: TGUID;
  EncoderParams: Winapi.GDIPAPI.TEncoderParameters;
  SaveQuality: Vcl.Imaging.jpeg.TJPEGQualityRange; // Integer geht natürlich auch!
begin
  Result := 0;
  OriginalStream := TMemoryStream.Create;
  CompressStream := TMemoryStream.Create;

  // mit soOwned räumt der TStreamAdapter den TMemoryStream auf
  OriginalStreamAdapter := TStreamAdapter.Create(OriginalStream, soOwned);
  CompressStreamAdapter := TStreamAdapter.Create(CompressStream, soOwned);

  Image := Winapi.GDIPOBJ.TGPImage.Create(AJpegFilename);
  try
    if Assigned(Image) and (Image.GetWidth > 0) and (Image.GetHeight > 0) then
    begin
      Status := Image.GetRawFormat(ImageFormat);
      if (Status = TStatus.Ok) and (ImageFormat = ImageFormatJPEG) then
      begin
        if Winapi.GDIPUTIL.GetEncoderClsid('image/jpeg', EncoderCLSID) > -1 then
        begin
          // Bild OHNE Komprimierung im Stream speichern
          Status := Image.Save(OriginalStreamAdapter, EncoderCLSID);
          OutputDebugString(PWideChar(Winapi.GDIPUTIL.GetStatus(Status)));

          SaveQuality := 50; // oder was du magst für die Kompressionsrate

          FillChar(EncoderParams, SizeOf(EncoderParams), 0);
          EncoderParams.Count := 1;
          EncoderParams.Parameter[0].Guid := EncoderQuality;
          EncoderParams.Parameter[0].NumberOfValues := 1;
          EncoderParams.Parameter[0].Type_ := EncoderParameterValueTypeLong;
          EncoderParams.Parameter[0].Value := @SaveQuality;

          // Bild MIT Komprimierung im Stream speichern
          Status := Image.Save(CompressStreamAdapter, EncoderCLSID, @EncoderParams);
          OutputDebugString(PWideChar(Winapi.GDIPUTIL.GetStatus(Status)));

          Result := CompressStream.Size / OriginalStream.Size;
        end;
      end
      else
        OutputDebugString(PWideChar(Winapi.GDIPUTIL.GetStatus(Status)));
    end;
  finally
    Image.Free;
  end;
end;

EWeiss 26. Okt 2017 12:58

AW: JPEG CompressionQuality ermitteln
 
Wenn du mit TStatus GPStatus meinst jo..

Delphi-Quellcode:
  Status =(
    Ok,
    GenericError,
    InvalidParameter,
    OutOfMemory,
    ObjectBusy,
    InsufficientBuffer,
    NotImplemented,
    Win32Error,
    WrongState,
    Aborted,
    FileNotFound,
    ValueOverflow,
    AccessDenied,
    UnknownImageFormat,
    FontFamilyNotFound,
    FontStyleNotFound,
    NotTrueTypeFont,
    UnsupportedGdiplusVersion,
    GdiplusNotInitialized,
    PropertyNotFound,
    PropertyNotSupported
  );
  TStatus = Status;
Delphi-Quellcode:
  cStatus : Array[TStatus] of String =(
    'Ok',
    'GenericError',
    'InvalidParameter',
    'OutOfMemory',
    'ObjectBusy',
    'InsufficientBuffer',
    'NotImplemented',
    'Win32Error',
    'WrongState',
    'Aborted',
    'FileNotFound',
    'ValueOverflow',
    'AccessDenied',
    'UnknownImageFormat',
    'FontFamilyNotFound',
    'FontStyleNotFound',
    'NotTrueTypeFont',
    'UnsupportedGdiplusVersion',
    'GdiplusNotInitialized',
    'PropertyNotFound',
    'PropertyNotSupported'
  );
gruss

Schwedenbitter 26. Okt 2017 14:17

AW: JPEG CompressionQuality ermitteln
 
Danke Euch allen für die tatkräftige Hilfe!
Zitat:

Zitat von EWeiss (Beitrag 1384231)
Wenn du mit TStatus GPStatus meinst jo..

Genau die meine ich. Und wenn ich Deinen Code richtig deute, gibt es entsprechendes also weder in
Delphi-Quellcode:
Winapi.GDIPAPI
,
Delphi-Quellcode:
Winapi.GDIPOBJ
noch
Delphi-Quellcode:
Winapi.GDIPUTIL
?

Ich habe das jetzt mal umgestellt und es klappt (jedenfalls bei mir) tatsächlich:
Delphi-Quellcode:
Function CalcCompressionQuality(Const Image: GPIMAGE;
   Const MustFit: Boolean = False): TJPEGQualityRange;
Var
   aMS                  : TMemoryStream;
   aFormat, aEncoder      : TGUID;
   aSA                  : Winapi.ActiveX.IStream;
   aSize                  : Int64;
   lQ, hQ, Piv, oldPiv   : Integer;
   aEncParams            : Winapi.GDIPAPI.TEncoderParameters;
Begin
   Result:= 100;                                    // im Zeifel: beste Qualität!
   If (GdipGetImageRawFormat(Image, @aFormat) = Status.Ok) And
      (aFormat = ImageFormatJPEG) And               // nur bei JPEG ausführen
      (GetEncoderClsid('image/jpeg', aEncoder) > -1) Then
   Begin
      aMS:= TMemoryStream.Create;                  // TMemoryStream erzeugen
      aSA:= TStreamAdapter.Create(aMS, soOwned);   // aMS wird selbst aufgeräumt
      If (GdipSaveImageToStream(Image, aSA, @aEncoder, nil) = Status.Ok) Then
      Begin
         aSize:= aMS.Size;                           // Originalgröße ermitteln
         lQ:=   Low( Result);                     // untere Grenze
         hQ:=   High(Result);                     // obere Grenze
         Piv:= (hQ - lQ) Div 2;                     // in der Mitte anfangen
         Repeat
            aMS.Clear;                              // Stream leeren
            FillChar(aEncParams, SizeOf(aEncParams), 0);
            aEncParams.Count:= 1;
            With aEncParams.Parameter[0] Do
            Begin
               Guid:=          EncoderQuality;      // um diese Einstellung geht es
               NumberOfValues:= 1;
               Type_:=         EncoderParameterValueTypeLong;
               Value:=         @Piv               // CompressionQuality setzen
            End;
            oldPiv:= Piv;                           // altes Pivot-Element merken

            If (GdipSaveImageToStream(Image, aSA, @aEncoder, @aEncParams) = Status.Ok) Then
            Begin
               If (aMS.Size > aSize) Then            // Ergebnis ist zu groß
               Begin
                  hQ:= Piv;                        // obere Grenze = aktueller Wert
                  Piv:= Piv - ((hQ - lQ) Div 2);   // neuen Wert berechnen
               End
               Else Begin                           // Ergebnis kleiner oder gleich
                  lQ:= Piv;                        // untere Grenze = aktueller Wert
                  Piv:= Piv + ((hQ - lQ) Div 2);   // neuen Wert berechnen
               End;
            End
            Else Break;                              // bei Fehlern abbrechen
         Until (Piv = oldPiv);                     // noch näher geht es nicht

         aSA:= nil;   // sicherheitshalber (http://www.delphipraxis.net/1384246-post23.html)

         If (MustFit) And                           // auf keinen Fall größer !!!
            (aMS.Size > aSize) Then                  // immer noch zu groß
               Result:= Pred(Piv)                  // => eine Nummer kleiner
         Else   Result:= Piv;                        // aMS.Size = aSize => exakten Wert übergeben
      End;
   End;
End;
Eine Frage habe ich trotzdem noch: Wohin verschwindet
Delphi-Quellcode:
Winapi.ActiveX.IStream
?

Dieser Stream kümmert sich um den ihm zugewiesenen
Delphi-Quellcode:
TMemoryStream
, so dass ich den nicht freigeben darf/muss. Aber trotz fehlender Freigabe von
Delphi-Quellcode:
Winapi.ActiveX.IStream
bringt mir mein Programm bei
Delphi-Quellcode:
ReportMemoryLeaksOnShutdown:= True;
am Ende keine Meldung.
Das wäre mir wichtig, weil mein Programm genauso lang läuft wieder Rechner und damit am Tag im Maximum mehrere hundert Bilder verarbeiten muss. Dass es am Ende des Programms automatisch aufgeräumt wird, reicht mir also leider nicht, wenn mir zwischendurch die Puste/der Speicher aus geht.
Delphi-Quellcode:
Winapi.GDIPOBJ.TGPImage.Free
ruft auch nur
Delphi-Quellcode:
GdipDisposeImage();
auf...

EWeiss 26. Okt 2017 14:23

AW: JPEG CompressionQuality ermitteln
 
Zitat:

gibt es entsprechendes also weder in
Nein gibt es nicht.

Der Aufruf ist einfach.
bsp.
Delphi-Quellcode:
var
  Result: GPStatus;
....
Result := GdipSaveImageToFile(img, FullName, @encoderCLSID, nil);

lblStatus.Caption := 'Status = ' + (cStatus[Result]);
Zitat:

ruft auch nur GdipDisposeImage(); auf...
Der IStream gibt sich selbst frei wenn der Referenz Counter auf 0 ist.
Ein IStream := nil sollte ausreichen.

GdipDisposeImage gibt den Speicher frei vom geladenen GDI+ Image. Auch das ist vollkommen ausreichend.

gruss

TiGü 26. Okt 2017 16:00

AW: JPEG CompressionQuality ermitteln
 
Zitat:

Zitat von Schwedenbitter (Beitrag 1384245)
Danke Euch allen für die tatkräftige Hilfe!
Zitat:

Zitat von EWeiss (Beitrag 1384231)
Wenn du mit TStatus GPStatus meinst jo..

Genau die meine ich. Und wenn ich Deinen Code richtig deute, gibt es entsprechendes also weder in
Delphi-Quellcode:
Winapi.GDIPAPI
,
Delphi-Quellcode:
Winapi.GDIPOBJ
noch
Delphi-Quellcode:
Winapi.GDIPUTIL
?

Zitat:

Zitat von EWeiss (Beitrag 1384246)
Zitat:

gibt es entsprechendes also weder in
Nein gibt es nicht.

Der Aufruf ist einfach.
bsp.


:shock:
Öh...das ist jetzt schon bissel fremdschämen mit euch. :oops:
In meinen Beispielcode habe ich dreimal folgende Zeile verwendet:

Delphi-Quellcode:
OutputDebugString(PWideChar(Winapi.GDIPUTIL.GetStatus(Status)));


Ich will das Wichtigste nochmal herausstellen:

Delphi-Quellcode:
Winapi.GDIPUTIL.GetStatus(Status);

Ich will ja jetzt nicht kritisch gucken, aber die Unit Winapi.GDIPUTIL ist gerade mal knapp 389 Zeilen lang.
Das hättest du durch leichtes durch scrollen aber auch selber finden können.

TiGü 26. Okt 2017 16:03

AW: JPEG CompressionQuality ermitteln
 
Zitat:

Zitat von Schwedenbitter (Beitrag 1384245)
Eine Frage habe ich trotzdem noch: Wohin verschwindet
Delphi-Quellcode:
Winapi.ActiveX.IStream
?

Dieser Stream kümmert sich um den ihm zugewiesenen
Delphi-Quellcode:
TMemoryStream
, so dass ich den nicht freigeben darf/muss. Aber trotz fehlender Freigabe von
Delphi-Quellcode:
Winapi.ActiveX.IStream
bringt mir mein Programm bei
Delphi-Quellcode:
ReportMemoryLeaksOnShutdown:= True;
am Ende keine Meldung.
Das wäre mir wichtig, weil mein Programm genauso lang läuft wieder Rechner und damit am Tag im Maximum mehrere hundert Bilder verarbeiten muss. Dass es am Ende des Programms automatisch aufgeräumt wird, reicht mir also leider nicht, wenn mir zwischendurch die Puste/der Speicher aus geht.

Bitte mit Interfaces, Reference Counting und dergleichen beschäftigen, dann werden alle Fragen beantwortet. Das Forum hier hat dazu einige Threads und Tutorials.
Auch einfach mal in TStreamAdapter.Destroy einen Haltepunkt setzen und den Call Stack nachvollziehen.

Medium 26. Okt 2017 16:21

AW: JPEG CompressionQuality ermitteln
 
Zitat:

Zitat von Schwedenbitter (Beitrag 1384151)
[EDIT]Bedeutet 100 wirklich, dass es ohne Verlust komprimiert wird? Ich denke, dass zumindest die JPEG-Version von Delphi immer verlustbehaftet ist. Ich probiere das zur Not auch aus.[/EDIT]

Es wird im Normalfall NICHT verlustfrei komprimiert, auch bei einer Qualität von 100. Der Wert ist nicht als Prozent zu verstehen, sondern ist in der Regel entweder ein Faktor für die Quantisierungsmatrizen, oder eine diskrete Auswahl für die gewünschte Qualität optimierte Matrizen. Aber Quantisiert wird immer, und das ist der Schritt über den der Großteil an "Fehler" ins Bild kommt. Und da bei der Verarbeitung (besonders bei der DCT) ohnehin zwangsweise Rundungsfehler auftreten, würde man selbst komplett ohne Quantisierung nicht bei 1:1 Bildern landen.

Und selbst ohne diese: JPEG betreibt sog. 4:2:2 Color Subsampling. Das heisst, das Bild wird zunächst von RGB zu YCrCb umgewandelt, und die C-Komponenten nachher in Breite und Höhe halbiert. (Das geht ganz gut, da das Menschliche Auge Farbunterschiede eh nicht so gut auflösen kann wie Helligkeitsunterschiede.) Der Standard lässt prinzipiell auch anderes Subsampling zu (4:4:4, 4:1:1, etc.), aber als Standardwert wird praktisch überall 4:2:2 verwendet. Zumindest dort, wo man es nicht genau wüsste dass man etwas anderes nutzt. D.h. Rundungsfehler treten eh schon bei der Farbraumumwandlung auf, und dann eben noch "Fehler" durch das vierteln der Farbinformation. Noch bevor überhaupt transformiert wird oder der "CompressionQuality"-Wert in den Ablauf einfließt.

(Ja, ich habe schon mal einen JPEG Kompressor komplett zu Fuß gebaut.)

EWeiss 26. Okt 2017 16:31

AW: JPEG CompressionQuality ermitteln
 
Zitat:

(Ja, ich habe schon mal einen JPEG Kompressor komplett zu Fuß gebaut.)
Interessant :)

gruss


Alle Zeitangaben in WEZ +1. Es ist jetzt 03:50 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz