fpexif: XMP daten manipulieren
- h-elsner
- Lazarusforum e. V.
- Beiträge: 282
- Registriert: Di 24. Jul 2012, 15:42
- OS, Lazarus, FPC: LINUX Mint21.1, Win10, Lazarus 2.2.4, FPC3.2.2
- CPU-Target: X86-64; arm 32bit
- Wohnort: Illertissen
- Kontaktdaten:
fpexif: XMP daten manipulieren
Ich bin begeisterter, aber ziemlich unbedarfter Nutzen der Komponente fpexif. Mit den normalen EXIF tags komme ich gut zurecht. Nun würde ich aber gerne die XMP Daten manipulieren. Die Länge des Datenblocks verändert sich dabei.
So wie ich das sehe, werden die XMP-daten nur kopiert und zurückgeschrieben. Ich habe leider keinen Ansatz gefunden, diese zu veränderen.
Geht das überhaupt? Kann man die Länge des Datenblockes vor dem Zurückschreiben irgendwie neu setzen? Ich denke mal es wird nicht so einfach gehen, da irgendwie in dem JPG herumzueditieren.
Gruß HE
So wie ich das sehe, werden die XMP-daten nur kopiert und zurückgeschrieben. Ich habe leider keinen Ansatz gefunden, diese zu veränderen.
Geht das überhaupt? Kann man die Länge des Datenblockes vor dem Zurückschreiben irgendwie neu setzen? Ich denke mal es wird nicht so einfach gehen, da irgendwie in dem JPG herumzueditieren.
Gruß HE
Re: fpexif: XMP daten manipulieren
Ist schon eine Zeit lang her, dass ich das geschrieben habe... Wenn ich mich recht erinnere (und mir jetzt den Source-Code anschaue), war das eigentliche Interesse im Lesen und Schreiben der EXIF-Werte und vielleicht noch von IPTC. Aber XMP kam eigentlich erst später rein, als sich ein User (du?) beschwert hat, dass meine Routinen das XMP-Segment aus der JPEG-Datei entfernen. Daher wird nun dieser Block gelesen und 1:1 in die Ziel-Datei zurückgeschrieben. Einen tiefergehenden Support gibt es zur Zeit nicht.
Wenn dir das wichtig ist und du dich in die Materie einarbeiten willst, kannst du mir natürlich einen Patch schicken, dann baue ich das ein.
Wenn dir das wichtig ist und du dich in die Materie einarbeiten willst, kannst du mir natürlich einen Patch schicken, dann baue ich das ein.
- h-elsner
- Lazarusforum e. V.
- Beiträge: 282
- Registriert: Di 24. Jul 2012, 15:42
- OS, Lazarus, FPC: LINUX Mint21.1, Win10, Lazarus 2.2.4, FPC3.2.2
- CPU-Target: X86-64; arm 32bit
- Wohnort: Illertissen
- Kontaktdaten:
Re: fpexif: XMP daten manipulieren
Danke für die Info, so habe ich mir schon gedacht. Und ja, das war ich. Damals hat mich das Segment auch nicht weiter interessiert, es sollte nur übernommen werden. Danke für den Update der Komponente damals.
Mal sehen, ob ich herausfinden kann, wie das ganze Funktioniert. Wenn ich Fragen habe, melde ich mich.
Gruß HE
Mal sehen, ob ich herausfinden kann, wie das ganze Funktioniert. Wenn ich Fragen habe, melde ich mich.
Gruß HE
- h-elsner
- Lazarusforum e. V.
- Beiträge: 282
- Registriert: Di 24. Jul 2012, 15:42
- OS, Lazarus, FPC: LINUX Mint21.1, Win10, Lazarus 2.2.4, FPC3.2.2
- CPU-Target: X86-64; arm 32bit
- Wohnort: Illertissen
- Kontaktdaten:
Re: fpexif: XMP daten manipulieren
An den Sourcen der Komponente von fpexif traue ich mich nicht herumzuspielen, weill ich nicht alles verstehe, was da gemacht wird. Aber ich kann nun die XMP-Daten auslesen. da einiges von fpexif geklaut/kopiert ist, wäre es wahrscheinlich nicht so schwer, das irgendwie zu integrieren. Die XMP-Daten stehen in einer TStringList zur weiteren Bearbeitung.
Das Zurückschreiben schaue ich mir jetzt an.
Edit:
XMPlines.TextLineBreakStyle:=tlbsLF; eingefügt.
Gruß HE
Code: Alles auswählen
procedure ReadXMP(const fn: string; var XMPlines: TStringList);
var
strInput: TFileStream;
strXMP: TMemoryStream;
p, n: int64;
marker: byte;
size: word;
s: string;
begin
XMPlines.Clear;
XMPlines.TextLineBreakStyle:=tlbsLF; {#10 as line ending}
strXMP:=TMemoryStream.Create;
strInput:=TFileStream.Create(fn, fmOpenRead or fmShareDenyNone);
s:='';
try
strInput.Position:=0;
p:=0;
if not ((ReadByte(strInput) = M_ID) and (ReadByte(strInput) = M_SOI)) then
exit;
while p<strInput.Size do begin
repeat
marker:=ReadByte(strInput);
until marker<M_ID;
size:=BEtoN(ReadWord(strInput))-2; // Size of the pure XMP data
p:=strInput.Position;
case marker of
M_EXIF:
begin
SetLength(s, Length(XMP_SIGNATURE));
strInput.Read(s[1], Length(XMP_SIGNATURE));
strInput.Position:=p; // Go back to begin of XMP data
if s = XMP_SIGNATURE then begin // Check if segment is XMP data
n:=strXMP.CopyFrom(strInput, size);
strXMP.Position:=0;
if n=size then
XMPlines.LoadFromStream(strXMP, true);
break; // Done, get out of here
end
else
begin
strInput.Position:=p+size; // Skip EXIF segment
end;
end;
M_EOI, M_SOS:
break;
end;
end;
finally
strInput.Free;
strXMP.Free;
end;
end;
Edit:
XMPlines.TextLineBreakStyle:=tlbsLF; eingefügt.
Gruß HE
Zuletzt geändert von h-elsner am So 6. Mär 2022, 20:00, insgesamt 1-mal geändert.
Re: fpexif: XMP daten manipulieren
Ja, so ähnlich würde ich es auch machen. Allerdings würde ich die XMP-Daten in einen XMLDocument-Baum einlesen, damit hätte man schon alle "Tags" (heißt das hier auch so?) säuberlich getrennt, könnte die gewünschten editieren und am Ende alles über einen Stream wieder in die JPEG-Dateistruktur zurückschreiben.
Hast du vielleicht ein paar Bilder mit XMP-Daten? Ich könnte mir das demnächst mal anschauen.
Hast du vielleicht ein paar Bilder mit XMP-Daten? Ich könnte mir das demnächst mal anschauen.
- h-elsner
- Lazarusforum e. V.
- Beiträge: 282
- Registriert: Di 24. Jul 2012, 15:42
- OS, Lazarus, FPC: LINUX Mint21.1, Win10, Lazarus 2.2.4, FPC3.2.2
- CPU-Target: X86-64; arm 32bit
- Wohnort: Illertissen
- Kontaktdaten:
Re: fpexif: XMP daten manipulieren
Hier sind ein paar kleine Bilder:
Gruß HE- h-elsner
- Lazarusforum e. V.
- Beiträge: 282
- Registriert: Di 24. Jul 2012, 15:42
- OS, Lazarus, FPC: LINUX Mint21.1, Win10, Lazarus 2.2.4, FPC3.2.2
- CPU-Target: X86-64; arm 32bit
- Wohnort: Illertissen
- Kontaktdaten:
Re: fpexif: XMP daten manipulieren
Ja sicher, das wäre die beste Lösung. Aber das Bearbeiten der XMP-Daten ist das kleinste Problem.wp_xyz hat geschrieben: So 6. Mär 2022, 16:38 Allerdings würde ich die XMP-Daten in einen XMLDocument-Baum einlesen, damit hätte man schon alle "Tags" (heißt das hier auch so?) säuberlich getrennt, könnte die gewünschten editieren und am Ende alles über einen Stream wieder in die JPEG-Dateistruktur zurückschreiben.
In dem Zusammenhang sehe ich aber, dass die Übergabe der XMP-Daten als MemoryStream wesentlich flexibler ist, als eine StringList zu benutzen.
Da kann jeder machen, was er will.
Beim Zurückschreiben die Länge anfügen und fertig.
Ich habe es mal so gemacht:
Code: Alles auswählen
sz: word;
...
sz:=Xstream.Size and $FFFF;
Xstream.Position:=0;
Xstream.WriteWord(NtoBE(sz+2));
...
- h-elsner
- Lazarusforum e. V.
- Beiträge: 282
- Registriert: Di 24. Jul 2012, 15:42
- OS, Lazarus, FPC: LINUX Mint21.1, Win10, Lazarus 2.2.4, FPC3.2.2
- CPU-Target: X86-64; arm 32bit
- Wohnort: Illertissen
- Kontaktdaten:
Re: fpexif: XMP daten manipulieren
Ich habe doch mal in fpexif, fpemetadata.pas gebastelt und eine function HasXMP eingeführt.
Allerdings bin ich daran gescheitert, einen XMP-Stream als Property einzurichten. Ich verstehe das ganze Konstrukt noch nicht.
Gruß HE
Die Änderungen sind gekennzeichnet. Diese Kennzeichnung kann dann weg.Allerdings bin ich daran gescheitert, einen XMP-Stream als Property einzurichten. Ich verstehe das ganze Konstrukt noch nicht.
Gruß HE
Re: fpexif: XMP daten manipulieren
Ich habe gerade meine Version von gestern hochgeladen. Es gibt da eine Klasse TXMPData (genauso wie TExifData und TIptcData), die sich um die XMP-Metadaten kümmert. Sie prüft die XMP-Kennung am Beginn des JPEG-Segments und liest dann den gesamten Block als String ins Objektfeld FData ein. Dieses ist von außen nicht direkt sichtbar, sondern kann nur mit Hilfe ein Streams (LoadFromStream, SaveToStream) ausgelesen bzw. zurückgeschrieben werden, so dass mir noch etwas Kontrolle bleibt, was mit den Daten geschieht. Beim Einlesen wird gleichzeitig ein XMLDocument erzeugt, die Nodes und ihre Attribute werden ausgelesen und in eine Stringliste als Name=Value Paare gespeichert. XMPData hat, darauf aufbauend, Properties TagByIndex[Index] und TagByName[TagNaName], um Werte auszulesen. Schreib-Unterstützung plane ich nicht, weil ich dann genau Buch führen müsste, wo die Werte herkommen (Attribut? Text-Wert welches XML-Nodes?).
Im Beispielprogramm "metadataviewer" sieht man die Anwendung, suche in der Haupt-Unit nach "XMPData".
Zurückschreiben kann man die XMP-Daten noch nicht. Was mich noch verwirrt, ist dass am Ende des XML-Bereichs Unmengen von Leerzeichen eingefügt sind. Ist denn die Länge konstant?
Im Beispielprogramm "metadataviewer" sieht man die Anwendung, suche in der Haupt-Unit nach "XMPData".
Zurückschreiben kann man die XMP-Daten noch nicht. Was mich noch verwirrt, ist dass am Ende des XML-Bereichs Unmengen von Leerzeichen eingefügt sind. Ist denn die Länge konstant?
- h-elsner
- Lazarusforum e. V.
- Beiträge: 282
- Registriert: Di 24. Jul 2012, 15:42
- OS, Lazarus, FPC: LINUX Mint21.1, Win10, Lazarus 2.2.4, FPC3.2.2
- CPU-Target: X86-64; arm 32bit
- Wohnort: Illertissen
- Kontaktdaten:
Re: fpexif: XMP daten manipulieren
Was die Entwickler der jeweiligen FW der Kameras so machen zu verstehen, habe ich schon lange aufgegeben. Vielleicht schreiben sie feste Blöcke und wollen sich Raum für spätere Änderungen reservieren oder was auch immer. Vernünftige Änderungen komme aber nie.
Auslöser für meinen Wunsch XMP-daten zu bearbeiten war die tatsache, dass die radiometrische Software für die Bilder der Wärmebildkamera nur mit der neuen Firmware funktioniert, die alte Firmware aber bessere Videosyncronisation zwischen Wärmebild- und Restlichtkamera hat.
Nun wollte ich nur zum Spaß (habe keine WB-Kamera - zu teuer) mal sehen, ob man die Daten aus der alten FW nicht so anpassen kann, dass das Auswerteprogramm sie doch akzeptiert. Das Auswerteprogramm von denen hat auch so seine Macken. Es funktioniert nicht mit jeder Lokalisierung. Deutsch (Deutschland) geht nicht, Deutsch (Schweiz) oder Englisch geht. Alles Pfusch aus meiner Sicht, aber eben auch geistige Nahrung für Rentner
.
Vielen dank für deine Mühe - ich schaue mir mal deine Version gleich mal an.
Gruß HE
Auslöser für meinen Wunsch XMP-daten zu bearbeiten war die tatsache, dass die radiometrische Software für die Bilder der Wärmebildkamera nur mit der neuen Firmware funktioniert, die alte Firmware aber bessere Videosyncronisation zwischen Wärmebild- und Restlichtkamera hat.
Nun wollte ich nur zum Spaß (habe keine WB-Kamera - zu teuer) mal sehen, ob man die Daten aus der alten FW nicht so anpassen kann, dass das Auswerteprogramm sie doch akzeptiert. Das Auswerteprogramm von denen hat auch so seine Macken. Es funktioniert nicht mit jeder Lokalisierung. Deutsch (Deutschland) geht nicht, Deutsch (Schweiz) oder Englisch geht. Alles Pfusch aus meiner Sicht, aber eben auch geistige Nahrung für Rentner

Vielen dank für deine Mühe - ich schaue mir mal deine Version gleich mal an.
Gruß HE
- h-elsner
- Lazarusforum e. V.
- Beiträge: 282
- Registriert: Di 24. Jul 2012, 15:42
- OS, Lazarus, FPC: LINUX Mint21.1, Win10, Lazarus 2.2.4, FPC3.2.2
- CPU-Target: X86-64; arm 32bit
- Wohnort: Illertissen
- Kontaktdaten:
Re: fpexif: XMP daten manipulieren
Wunderhübsch, das funktioniert wie gewünscht.
Danke!
Gruß HE
Danke!
Gruß HE
Re: fpexif: XMP daten manipulieren
Jetzt ist auch das Zurückschreiben möglich.
An dem "metadataviewer"-Demoprojekt kannst du sehen, wie ich mir das vorstelle: Wenn das Flag mdkXMP gesetzt ist, werden die XMP-Metadaten, so wie in der vorigen Nachricht beschrieben in das XMPData-Objekt eingelesen, über ein XMLDocument auseinandergepflückt, so dass sie über die Tag*-Properties ausgelesen werden können. Diese Tags lasse ich aber der Vereinfachung halber read-only.
Um XMP-Daten zu bearbeiten, schreibst du sie über das Stream-Interface (LoadFromStream) in einen Stream, der wiederum von einem Memo oder SynEdit eingelesen wird. In dem Memo kannst du sie verändern, wobei es natürlich ich deiner Verantwortung liegt, dass die XMP/XML-Struktur gültig bleibt. (Ich hab's nicht getestet, aber ich könnte mir vorstellen, dass, wenn ein Programm XMP auswertet und dort einen Fehler findet, auch der Rest der Datei nicht gelesen werden kann.) Zum Übertragen ins XMPData-Objekt schreibst du das Memo/SynEdit in einen Stream und liest diesen vom XMPData-Objekt wieder ein. Dabei wird auch das XMLDocument erneuert, so dass man die geänderten XMP-Daten auch über die Tag*-Properties abfragen kann.
[EDIT]
Über die Rolle von mdkXMP muss ich noch nachdenken. Ich will damit eigentlich verhindern, dass die ganze Datei nicht mehr gelesen werden kann, falls in der XMP-Struktur ein dämlicher XML-Fehler ist. Aber möglicherweise gehe ich da zu weit: Jetzt, wenn mdkXMP in der Menge MetadataKinds NICHT enthalten ist, wird das XMP-Segment total ignoriert, so wie in der alten Version. Aber vielleicht wäre es besser zumindest den String einzulesen, so dass man es wie oben beschrieben, noch editieren kann. In diesem fall würde das Fehlen von mdkXMP in MetadataKinds lediglich die Auswertung der XML-Struktur unterbinden - eigentlich die einzige Stelle, in der die XMP-Routinen in der Praxis abstürzen können.
An dem "metadataviewer"-Demoprojekt kannst du sehen, wie ich mir das vorstelle: Wenn das Flag mdkXMP gesetzt ist, werden die XMP-Metadaten, so wie in der vorigen Nachricht beschrieben in das XMPData-Objekt eingelesen, über ein XMLDocument auseinandergepflückt, so dass sie über die Tag*-Properties ausgelesen werden können. Diese Tags lasse ich aber der Vereinfachung halber read-only.
Um XMP-Daten zu bearbeiten, schreibst du sie über das Stream-Interface (LoadFromStream) in einen Stream, der wiederum von einem Memo oder SynEdit eingelesen wird. In dem Memo kannst du sie verändern, wobei es natürlich ich deiner Verantwortung liegt, dass die XMP/XML-Struktur gültig bleibt. (Ich hab's nicht getestet, aber ich könnte mir vorstellen, dass, wenn ein Programm XMP auswertet und dort einen Fehler findet, auch der Rest der Datei nicht gelesen werden kann.) Zum Übertragen ins XMPData-Objekt schreibst du das Memo/SynEdit in einen Stream und liest diesen vom XMPData-Objekt wieder ein. Dabei wird auch das XMLDocument erneuert, so dass man die geänderten XMP-Daten auch über die Tag*-Properties abfragen kann.
[EDIT]
Über die Rolle von mdkXMP muss ich noch nachdenken. Ich will damit eigentlich verhindern, dass die ganze Datei nicht mehr gelesen werden kann, falls in der XMP-Struktur ein dämlicher XML-Fehler ist. Aber möglicherweise gehe ich da zu weit: Jetzt, wenn mdkXMP in der Menge MetadataKinds NICHT enthalten ist, wird das XMP-Segment total ignoriert, so wie in der alten Version. Aber vielleicht wäre es besser zumindest den String einzulesen, so dass man es wie oben beschrieben, noch editieren kann. In diesem fall würde das Fehlen von mdkXMP in MetadataKinds lediglich die Auswertung der XML-Struktur unterbinden - eigentlich die einzige Stelle, in der die XMP-Routinen in der Praxis abstürzen können.
- h-elsner
- Lazarusforum e. V.
- Beiträge: 282
- Registriert: Di 24. Jul 2012, 15:42
- OS, Lazarus, FPC: LINUX Mint21.1, Win10, Lazarus 2.2.4, FPC3.2.2
- CPU-Target: X86-64; arm 32bit
- Wohnort: Illertissen
- Kontaktdaten:
Re: fpexif: XMP daten manipulieren
Hervorragend. Danke! Ich hatte gestern noch mit deiner Version Patch8201 experimentiert. Auslesen in den Stream, Bearbeiten und Zurückschreiben in einen TMemoryStream haben wunderbar funktioniert.
Eine Stringlist hängt allerdings hinten noch ein Zeilenende an, welches man entfernen muss.
Also Stringlist mit TextLineBreakStyle:=tlbsLF; und Stream Size:=Size-1; . Dann hat man genau das, was man braucht.
Jetzt teste ich das Hochladen und melde mich wieder.
Gruß HE
Eine Stringlist hängt allerdings hinten noch ein Zeilenende an, welches man entfernen muss.
Also Stringlist mit TextLineBreakStyle:=tlbsLF; und Stream Size:=Size-1; . Dann hat man genau das, was man braucht.
Jetzt teste ich das Hochladen und melde mich wieder.
Gruß HE
- h-elsner
- Lazarusforum e. V.
- Beiträge: 282
- Registriert: Di 24. Jul 2012, 15:42
- OS, Lazarus, FPC: LINUX Mint21.1, Win10, Lazarus 2.2.4, FPC3.2.2
- CPU-Target: X86-64; arm 32bit
- Wohnort: Illertissen
- Kontaktdaten:
Re: fpexif: XMP daten manipulieren
Hmm, ich kann es nicht kompilieren. Lazarus stable und trunk. Folgende Meldung:
fpemetadata.pas(130,45) Fatal: Cannot find fpeXMPReadWrite used by fpeMetadata. Check search path of package fpexif_pkg, try a clean rebuild, check implementation
Edit: Die Datei fpeXMPReadWrite.pas fehlt im Snapshot, wenn ich ihn downloade.
Gruß HE
fpemetadata.pas(130,45) Fatal: Cannot find fpeXMPReadWrite used by fpeMetadata. Check search path of package fpexif_pkg, try a clean rebuild, check implementation
Code: Alles auswählen
Compile package fpexif_pkg 0.1: Exit code 1, Errors: 1, Hints: 2
Hint: Start of reading config file C:\fpcupdeluxe\Lazarus_stable\fpc\bin\x86_64-win64\fpc.cfg
Hint: End of reading config file C:\fpcupdeluxe\Lazarus_stable\fpc\bin\x86_64-win64\fpc.cfg
Verbose: Free Pascal Compiler version 3.2.2-rrelease_3_2_2-0-g0d122c4953 [2021/12/19] for x86_64
Verbose: Copyright (c) 1993-2021 by Florian Klaempfl and others
Verbose: Target OS: Win64 for x64
Verbose: Compiling fpexif_pkg.pas
Verbose: Compiling fpemetadata.pas
fpemetadata.pas(130,45) Fatal: Cannot find fpeXMPReadWrite used by fpeMetadata. Check search path of package fpexif_pkg, try a clean rebuild, check implementation uses sections..
Verbose: Compilation aborted
Verbose: C:\fpcupdeluxe\Lazarus_stable\fpc\bin\x86_64-win64\ppcx64.exe returned an error exitcode
Edit: Die Datei fpeXMPReadWrite.pas fehlt im Snapshot, wenn ich ihn downloade.
Gruß HE
Re: fpexif: XMP daten manipulieren
Sorry. Sollte nun funktionieren.