Delphi-PRAXiS
Seite 6 von 8   « Erste     456 78      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Multimedia (https://www.delphipraxis.net/16-multimedia/)
-   -   Delphi JPG-Datei drehen und speichern -> Verlust der Exif-Daten (https://www.delphipraxis.net/52770-jpg-datei-drehen-und-speichern-verlust-der-exif-daten.html)

jsp 7. Jul 2020 06:39

AW: JPG-Datei drehen und speichern -> Verlust der Exif-Daten
 
Hallo zusammen

ich werfe hier mal https://www.imageen.com/ in die Runde.
Kostet zwar was... Ich war immer sehr zufrieden damit.

Gruss, Jörn

himitsu 7. Jul 2020 07:42

AW: JPG-Datei drehen und speichern -> Verlust der Exif-Daten
 
Zitat:

Zitat von Benmik (Beitrag 1468993)
Seit Jahrzehnten ist der Algorithmus bekannt,

Ich hatte mal vor 'ner Weile versucht rauszufinden wie die Blöcke codiert sind und hab dHäfte schonwieder vergessen, aber mir ist so als wenn es nicht für alles eine 1:1 Variante in gedrehter Ausrichtung gab,
aber ich darf mich da auch gern getäuscht haben.

Hab den Artikel jetzt auch nur schnell überflogen und schau später nochmal genauer rein, aber selbst im Abschnitt "Results of lossless transcoding" sieht es so aus, als wenn es einen "winzigen" Unterschied gibt, womit des demnach doch nicht ganz "verlustlos" ist. :angle:

Sinspin 7. Jul 2020 08:27

AW: JPG-Datei drehen und speichern -> Verlust der Exif-Daten
 
Zitat:

Zitat von himitsu (Beitrag 1469004)
Hab den Artikel jetzt auch nur schnell überflogen und schau später nochmal genauer rein, aber selbst im Abschnitt "Results of lossless transcoding" sieht es so aus, als wenn es einen "winzigen" Unterschied gibt, womit des demnach doch nicht ganz "verlustlos" ist. :angle:

Ja, es gibt einen Unterschied. Und zwar wenn die Dimensionen des Bildes nicht durch 8 teilbar sind. Dann sind am Rand Blöcke die nicht mit gedreht werden können. Die werden dann abgeschnitten bzw. weggelassen.
IrfanView weist in den Einstellungen für das Rotationstool auch drauf hin.

Willie1 7. Jul 2020 10:28

AW: JPG-Datei drehen und speichern -> Verlust der Exif-Daten
 
Ich drehe JPEG's seit Jahren mit GDI+. Das geht schnell, ist verlustfrei und die Meta-Daten bleiben erhalten. Ich programmiere nur in der Windows-Welt.
W.

Benmik 7. Jul 2020 10:29

AW: JPG-Datei drehen und speichern -> Verlust der Exif-Daten
 
Zitat:

Zitat von Sinspin (Beitrag 1469005)
Die werden dann abgeschnitten bzw. weggelassen.

Kommt auf das Programm an. Es gibt auch welche - weiß jetzt nicht, ob IrfanView dazugehört - die bieten an, entweder den über den 8-Pixel-Block hinausgehenden Rand abzuschneiden oder ihn (und nur ihn) verlustbehaftet mitzuverarbeiten.

Zitat:

Zitat von jsp (Beitrag 1469003)
ich werfe hier mal https://www.imageen.com/ in die Runde.

ImageEn ist ein Hammer, allerdings teuer und meiner Meinung nach in manchen Routinen nicht das Schnellste. Wenn man seinem Programm Bildbearbeitung verpassen will, dann hat man mit ImageEn eine Luxuslösung. ImageEn ist übrigens ein Programm, das die oben genannte Option anbietet.

Zitat:

Zitat von himitsu (Beitrag 1469004)
doch nicht ganz "verlustlos" ist.

Bis auf die 8-Pixel-Grenze lassen sich die Scanlines (das ist jetzt leider sehr laienhaft ausgedrückt) einfach "umsortieren", das ist vollständig verlustfrei. Interessant ist, dass ein so "gedrehtes" JPG nicht mehr die exakt gleiche Größe hat wie das ungedrehte Bild. Es gibt auch irgendwo eine Erklärung, warum das so ist.

Allerdings hat die verlustfreie Drehung unter Umständen ein paar Nebenwirkungen, über die der Arzt oder Apotheker nicht informiert. Wie der TE schon bemerkt hat, geht bei manchen Programmen die EXIF-Sektion verloren. Das sind natürlich keine akzeptablen Programme. Schon etwas tiefergehender ist, dass nach der Drehung die Werte für Breite und Höhe in den EXIF-Daten vertauscht werden müssen; das machen natürlich auch nicht alle. Ganz doll ist, dass manchmal - und zwar auch bei IrfanView - eingebettete Vorschaubilder einfach gekappt werden. Die Sony-Systemkameras zum Beispiel betten in alle JPG nicht nur das "normale" kleine Vorschaubild (Thumbnail auf Deutsch), sondern noch ein weiteres Full HD-Bild von 1920x1080 Pixel ein (die JPEG-Spezifikation erlaubt ja beliebig viele davon). Einmal gedreht - weg isses! Die allermeisten Fotografen ahnen nicht mal, dass überhaupt eins da ist; sie bemerken allenfalls, dass die Datei nach dem (ersten!) Drehen plötzlich viel kleiner ist als vorher und schließen messerscharf, dass die Drehung nicht verlustfrei war. Wer das weiß und die großen Bilder sowieso nicht nutzt (es gibt praktisch kein Programm, das zusätzliche Vorschaubilder anzeigt), dann kann man seine Sony-Bilder durch zweimaliges verlustfreies Drehen hin und zurück platzsparend verkleinern.

Auch noch für Irritation bei vielen Anfänger-Drehern sorgt, dass das Orientation Tag nach jedem beliebigen Drehen immer auf 1 (Top Left) zurückgesetzt wird, auch wenn das Bild zum Beispiel auf hochkant gedreht wird. Mit etwas Nachdenken kommt man darauf, warum das so sein muss.
Für besonders viel Irritation sorgt es, wenn ein Drehprogramm das Orientation Tag auf 1 zurückgesetzt, aber die Werte für Breite und Höhe nicht angepasst hat. Dann zeigt natürlich auch der Explorer das Bild falsch an und dann ist das Rätselraten groß: "Mein Bild wird überhaupt nicht gedreht!".

Und wem man auch noch Beachtung schenken sollte, ist das Dateidatum (d.h. alle drei). Die sollten nach dem Drehen wieder zurückgesetzt werden, eleganterweise auf das EXIF-Datum.

[EDIT] Da mich das Thema - wie gesagt - laufend interessiert, habe ich mal Test mit drei Codevarianten gemacht: NativeJPG, JPEGEX und FreeImage. 10 JPG von durchschnittlich 6 MB wurden um 90° gedreht. Bei allen drei blieben die EXIF-Informationen erhalten. NativeJPG benötigte im Durchschnitt 1.100 msec, die beiden anderen um die 300 für das reine Drehen.

Nebenerkenntnis: Das "Kappen" der eingebetteten Vorschaudatei konnte ich - auch bei IrfanView - nicht (mehr) beobachten. Den Algorithmus des verlustfreien Drehens kann man übrigens in sdJpegLossless.pas studieren.

Zitat:

Zitat von Willie1 (Beitrag 1469011)
Ich drehe JPEGs seit Jahren mit GDI+. Das geht schnell, ist verlustfrei und die Meta-Daten bleiben erhalten.

Wie/womit genau machst du das? Das würde mich interessieren.

Benmik 8. Jul 2020 10:55

AW: JPG-Datei drehen und speichern -> Verlust der Exif-Daten
 
Hier noch die Verwendung von GDI+. Nur einfaches Beispiel, ohne Ressourcenschutz oder Ähnliches. Leider bietet die Save-Procedure nicht die Option des Überschreibens, daher ein bisschen Nachbau. GDI+ ist sehr schnell. Zu beachten ist, dass hier die Blockgröße 16 Pixel beträgt, Höhe/Breite also ein Vielfaches von 16 sein müssen.
Frustrierend ist, dass GDI+ als 32-Bit-DLL natürlich wieder mal nicht in 64-Bit-Anwendungen benutzt werden kann.
Delphi-Quellcode:
uses ... GDIPAPI, GDIPOBJ,GDIPUTIL;

procedure DreheMitGDIPlus1;
Var i:integer;
  DatListe:TStringDynArray;
  GPImage: TGPImage;
  EncoderCLSID: TGUID;
  Ergebnis : Status;
  TempDatname:string;
const
  Verz = 'C:\Test\';
begin
  DatListe := TDirectory.GetFiles(Verz,'*.jpg');
  GetEncoderClsid('image/jpeg', EncoderCLSID);
  For i := 0 to High(DatListe) do begin
    GPImage := TGPImage.Create(DatListe[i]);
    GPImage.RotateFlip(Rotate90FlipNone);
    TempDatname := TPath.ChangeExtension(DatListe[i],'$$$');
    Ergebnis := GPImage.Save(TempDatname,EncoderCLSID);
    GPImage.Free;
    If Ergebnis = Ok then begin
      If DeleteFile(DatListe[i])
        then RenameFile(TempDatname,DatListe[i])
        else DeleteFile(TempDatname);
    end;
  end;
end;
Die nachfolgende Version ist die "volle". An ihr ist besonders interessant, dass sie nicht nur selbständig ermittelt, ob das JPG überhaupt gedreht werden muss, sondern auch den Zugriff auf weitere GDI-Funktionen bietet. Auch kann außer dem PropertyTagOrientation noch eine Vielzahl von weiteren EXIF-Markern ausgelesen werden.
Delphi-Quellcode:
uses ... GDIPAPI, GDIPOBJ,GDIPUTIL;

procedure DreheMitGDIPlus2;
Var i:integer;
  DatListe:TStringDynArray;
  GPImage: TGPImage;
  PPropItem: PPropertyItem;
  BufferSize: Cardinal;
  Orientation: Byte;
  RotateBy: EncoderValue;
  EncoderCLSID: TGUID;
  EncoderParams: TEncoderParameters;
  EncoderTransformValue:integer;
  Ergebnis : Status;
  TempDatname:string;
const
  Verz = 'C:\Test\';
begin
  DatListe := TDirectory.GetFiles(Verz,'*.jpg');
  GetEncoderClsid('image/jpeg', EncoderCLSID);
  FillChar(EncoderParams, SizeOf(EncoderParams), 0);
  EncoderParams.Count := 1;
  EncoderParams.Parameter[0].Guid := EncoderTransformation;
  EncoderParams.Parameter[0].Type_ := EncoderParameterValueTypeLong;
  EncoderParams.Parameter[0].NumberOfValues := 1;
  For i := 0 to High(DatListe) do begin
    GPImage := TGPImage.Create(DatListe[i]);
    BufferSize := GPImage.GetPropertyItemSize(PropertyTagOrientation);
    If BufferSize > 0 then begin
      GetMem(PPropItem, BufferSize);
      Try
        GPImage.GetPropertyItem(PropertyTagOrientation, BufferSize, PPropItem);
        Orientation := PByte(PPropItem.value)^;
        case Orientation of
          3: RotateBy := EncoderValueTransformRotate180;
          6: RotateBy := EncoderValueTransformRotate90;
          8: RotateBy := EncoderValueTransformRotate270;
          else Continue;
        end;
        If (Orientation in [3,6,9]) then begin
          EncoderTransformValue := Ord(RotateBy);
          EncoderParams.Parameter[0].Value := @EncoderTransformValue;
          TempDatname := TPath.ChangeExtension(DatListe[i],'$$$');
          Ergebnis := GPImage.Save(WideString(TempDatname),EncoderCLSID,@EncoderParams);
          GPImage.Free;
          If Ergebnis = Ok then begin
            If DeleteFile(DatListe[i])
              then RenameFile(TempDatname,DatListe[i])
              else DeleteFile(TempDatname);
          end;
        end;
      Finally
        FreeMem(PPropItem);
      end;
    end;
  end;
end;

Willie1 9. Jul 2020 18:10

AW: JPG-Datei drehen und speichern -> Verlust der Exif-Daten
 
Zitat:

Zitat von Benmik (Beitrag 1468993)
Schlichtes verlustfreies Drehen mache ich seit Jahren mit reinen Delphi-Bordmitteln - und noch einiges mehr.

Hallo, dann gehen die Meta-Daten verloren.
Mir fällt auf, dass beim Drehen mit GDI+ das eingebettete Thumbnail nicht mit gedreht wird und außerdem wird das Orientation-Tag nicht angepasst. Der erste Fehler ist ein Schönheitsfehler, der zweite schon ärgerlich.
Willie.

Willie1 9. Jul 2020 19:09

AW: JPG-Datei drehen und speichern -> Verlust der Exif-Daten
 
Zitat:

Zitat von Benmik (Beitrag 1468993)
Schlichtes verlustfreies Drehen mache ich seit Jahren mit reinen Delphi-Bordmitteln - und noch einiges mehr.

Hallo, dann gehen die Meta-Daten verloren.
Mir fällt auf, dass beim Drehen mit GDI+ das eingebettete Thumbnail nicht mit gedreht wird und außerdem wird das Orientation-Tag nicht angepasst. Der erste Fehler ist ein Schönheitsfehler, der zweite schon ärgerlich.
Ich weise in meinem Programm darauf hin, dass die Drehung nicht verlustfrei ist, falls sich L/B nicht durch 16 teilen lassen.
Bennik, du hast dir die Antwort schon selbst gegeben, so sieht mein Quelltext in etwa aus.

Willie.

P.S. Ich musste mich neu anmelden und mein ursprünglicher Beitrag war schon gepostet!

TiGü 10. Jul 2020 08:50

AW: JPG-Datei drehen und speichern -> Verlust der Exif-Daten
 
Zitat:

Zitat von Benmik (Beitrag 1469074)
Frustrierend ist, dass GDI+ als 32-Bit-DLL natürlich wieder mal nicht in 64-Bit-Anwendungen benutzt werden kann.

Wie meinst du das?

Die Datei GdiPlus.dll kommt bei Windows 7, 8, 10 (64-Bit) sowohl in 32-Bit als auch in 64-Bit mit.
Man sollte nicht den "Fehler" machen, eine eigene Version der DLL mitzudeployen.
Siehe:
C:\Windows\System32\GdiPlus.dll (64-Bit)
C:\Windows\SysWOW64\GdiPlus.dll (32-Bit)

(Ja, sind teilweise auch nur symbolische Links auf andere Orte, aber im 64-Bit Windows kommt die DLL in beiden Bitness-Varianten vor).

DataCool 10. Jul 2020 17:35

AW: JPG-Datei drehen und speichern -> Verlust der Exif-Daten
 
Zitat:

Zitat von Benmik (Beitrag 1469074)
Hier noch die Verwendung von GDI+. Nur einfaches Beispiel, ohne Ressourcenschutz oder Ähnliches. Leider bietet die Save-Procedure nicht die Option des Überschreibens, daher ein bisschen Nachbau. GDI+ ist sehr schnell. Zu beachten ist, dass hier die Blockgröße 16 Pixel beträgt, Höhe/Breite also ein Vielfaches von 16 sein müssen.
Frustrierend ist, dass GDI+ als 32-Bit-DLL natürlich wieder mal nicht in 64-Bit-Anwendungen benutzt werden kann.
Delphi-Quellcode:
uses ... GDIPAPI, GDIPOBJ,GDIPUTIL;

procedure DreheMitGDIPlus1;
Var i:integer;
  DatListe:TStringDynArray;
  GPImage: TGPImage;
  EncoderCLSID: TGUID;
  Ergebnis : Status;
  TempDatname:string;
const
  Verz = 'C:\Test\';
begin
  DatListe := TDirectory.GetFiles(Verz,'*.jpg');
  GetEncoderClsid('image/jpeg', EncoderCLSID);
  For i := 0 to High(DatListe) do begin
    GPImage := TGPImage.Create(DatListe[i]);
    GPImage.RotateFlip(Rotate90FlipNone);
    TempDatname := TPath.ChangeExtension(DatListe[i],'$$$');
    Ergebnis := GPImage.Save(TempDatname,EncoderCLSID);
    GPImage.Free;
    If Ergebnis = Ok then begin
      If DeleteFile(DatListe[i])
        then RenameFile(TempDatname,DatListe[i])
        else DeleteFile(TempDatname);
    end;
  end;
end;
Die nachfolgende Version ist die "volle". An ihr ist besonders interessant, dass sie nicht nur selbständig ermittelt, ob das JPG überhaupt gedreht werden muss, sondern auch den Zugriff auf weitere GDI-Funktionen bietet. Auch kann außer dem PropertyTagOrientation noch eine Vielzahl von weiteren EXIF-Markern ausgelesen werden.
Delphi-Quellcode:
uses ... GDIPAPI, GDIPOBJ,GDIPUTIL;

procedure DreheMitGDIPlus2;
Var i:integer;
  DatListe:TStringDynArray;
  GPImage: TGPImage;
  PPropItem: PPropertyItem;
  BufferSize: Cardinal;
  Orientation: Byte;
  RotateBy: EncoderValue;
  EncoderCLSID: TGUID;
  EncoderParams: TEncoderParameters;
  EncoderTransformValue:integer;
  Ergebnis : Status;
  TempDatname:string;
const
  Verz = 'C:\Test\';
begin
  DatListe := TDirectory.GetFiles(Verz,'*.jpg');
  GetEncoderClsid('image/jpeg', EncoderCLSID);
  FillChar(EncoderParams, SizeOf(EncoderParams), 0);
  EncoderParams.Count := 1;
  EncoderParams.Parameter[0].Guid := EncoderTransformation;
  EncoderParams.Parameter[0].Type_ := EncoderParameterValueTypeLong;
  EncoderParams.Parameter[0].NumberOfValues := 1;
  For i := 0 to High(DatListe) do begin
    GPImage := TGPImage.Create(DatListe[i]);
    BufferSize := GPImage.GetPropertyItemSize(PropertyTagOrientation);
    If BufferSize > 0 then begin
      GetMem(PPropItem, BufferSize);
      Try
        GPImage.GetPropertyItem(PropertyTagOrientation, BufferSize, PPropItem);
        Orientation := PByte(PPropItem.value)^;
        case Orientation of
          3: RotateBy := EncoderValueTransformRotate180;
          6: RotateBy := EncoderValueTransformRotate90;
          8: RotateBy := EncoderValueTransformRotate270;
          else Continue;
        end;
        If (Orientation in [3,6,9]) then begin
          EncoderTransformValue := Ord(RotateBy);
          EncoderParams.Parameter[0].Value := @EncoderTransformValue;
          TempDatname := TPath.ChangeExtension(DatListe[i],'$$$');
          Ergebnis := GPImage.Save(WideString(TempDatname),EncoderCLSID,@EncoderParams);
          GPImage.Free;
          If Ergebnis = Ok then begin
            If DeleteFile(DatListe[i])
              then RenameFile(TempDatname,DatListe[i])
              else DeleteFile(TempDatname);
          end;
        end;
      Finally
        FreeMem(PPropItem);
      end;
    end;
  end;
end;

Danke @Benmik

Das kommt dem sehr nahe was ich gesucht habe.

Jetzt werden die Bilder in Delphi "korrekt" angezeigt nur im Windows-Explorer Vorschau noch falsch ... Weil der Orientation Tag nach der Drehung noch auf dem "alten" Wert steht. Eigentlich müßte er auf "1" zurück gesetzt werden.
Was mich zu meiner nächsten Frage führt :

Wie genau funktioniert
Delphi-Quellcode:
 GPImage.SetPropertyItem(PPropItem^);
Speichert die Aufruf schon die entsprechende Information in die Datei ? Oder muss man explizit .Save aufrufen ?
Sorry, für die blöden Fragen, finde leider keine gescheite Doku zu GDI+.

Gretes Data


Alle Zeitangaben in WEZ +1. Es ist jetzt 17:24 Uhr.
Seite 6 von 8   « Erste     456 78      

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