AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Multimedia Delphi JPG-Datei drehen und speichern -> Verlust der Exif-Daten

JPG-Datei drehen und speichern -> Verlust der Exif-Daten

Ein Thema von axelf98 · begonnen am 3. Sep 2005 · letzter Beitrag vom 31. Jul 2020
Antwort Antwort
Seite 6 von 8   « Erste     456 78   
jsp

Registriert seit: 9. Aug 2003
50 Beiträge
 
#51

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

  Alt 7. Jul 2020, 06:39
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
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu
Online

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
36.362 Beiträge
 
Delphi 10.4 Sydney
 
#52

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

  Alt 7. Jul 2020, 07:42
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.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
Delphi-Tage 2005-2014
  Mit Zitat antworten Zitat
Sinspin

Registriert seit: 15. Sep 2008
Ort: Dubai
80 Beiträge
 
Delphi 2010 Professional
 
#53

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

  Alt 7. Jul 2020, 08:27
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.
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.
Stefan
Wir zerstören die Natur und Wälder der Erde. Wir töten wilde Tiere für Trophäen. Wir produzieren Lebewesen als Massenware um sie nach wenigen Monaten zu töten. Jetzt rächt sich die Natur und tötet uns.
  Mit Zitat antworten Zitat
Willie1

Registriert seit: 28. Mai 2008
404 Beiträge
 
Delphi 10.1 Berlin Starter
 
#54

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

  Alt 7. Jul 2020, 10:28
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.
Gut hören kann ich schlecht, schlecht sehen kann ich gut - Ersteres stimmt nicht, das zweite schon.
  Mit Zitat antworten Zitat
Benmik

Registriert seit: 11. Apr 2009
361 Beiträge
 
Delphi 10.3 Rio
 
#55

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

  Alt 7. Jul 2020, 10:29
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.

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.

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.

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.

Geändert von Benmik ( 7. Jul 2020 um 16:20 Uhr)
  Mit Zitat antworten Zitat
Benmik

Registriert seit: 11. Apr 2009
361 Beiträge
 
Delphi 10.3 Rio
 
#56

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

  Alt 8. Jul 2020, 10:55
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;

Geändert von Benmik ( 8. Jul 2020 um 14:50 Uhr) Grund: Blockgröße 16 Pixel
  Mit Zitat antworten Zitat
Willie1

Registriert seit: 28. Mai 2008
404 Beiträge
 
Delphi 10.1 Berlin Starter
 
#57

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

  Alt 9. Jul 2020, 18:10
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.
  Mit Zitat antworten Zitat
Willie1

Registriert seit: 28. Mai 2008
404 Beiträge
 
Delphi 10.1 Berlin Starter
 
#58

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

  Alt 9. Jul 2020, 19:09
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!
Gut hören kann ich schlecht, schlecht sehen kann ich gut - Ersteres stimmt nicht, das zweite schon.

Geändert von Willie1 ( 9. Jul 2020 um 19:12 Uhr)
  Mit Zitat antworten Zitat
TiGü

Registriert seit: 6. Apr 2011
Ort: Berlin
2.413 Beiträge
 
Delphi 10.2 Tokyo Enterprise
 
#59

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

  Alt 10. Jul 2020, 08:50
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).
  Mit Zitat antworten Zitat
Benutzerbild von DataCool
DataCool

Registriert seit: 10. Feb 2003
Ort: Lingen
899 Beiträge
 
Delphi 10.3 Rio
 
#60

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

  Alt 10. Jul 2020, 17:35
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
 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
Der Horizont vieler Menschen ist ein Kreis mit Radius Null, und das nennen sie ihren Standpunkt.
  Mit Zitat antworten Zitat
Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:24 Uhr.
Powered by vBulletin® Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2020 by Daniel R. Wolf