TIFF Tags auslesen und verstehen mit Python

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
würmchen
User
Beiträge: 255
Registriert: Mittwoch 7. November 2007, 14:17

Hallo Alle zusammen.
Ich habe ein TIFF Bild mit einem speziellen HeaderTag mit Informationen vom Gerät, dass das Tiff aufgenommen hat.
Leider gibt es nicht viel Software die dieses Tag auslesen können und ich brauch diese Informationen um sie in einer Datenbank zu schreiben.

Zum einen hab ich die Information wie das Tag aufgebaut ist: http://download.dejung.net/tiff.html

Zum anderen habe ich die Ausgabe von tiffinfo:

Code: Alles auswählen

TIFFReadDirectory: Warning, Image_012.tif: invalid TIFF directory; tags are not sorted in ascending order.
TIFFReadDirectory: Warning, Image_012.tif: unknown field with tag 37706 (0x934a) encountered.
TIFFReadDirectory: Warning, Image_012.tif: unknown field with tag 37707 (0x934b) encountered.
TIFF Directory at offset 0xdf01 (57089)
  Image Width: 1024 Image Length: 1024
  Resolution: 1.14647e+07, 1.14647e+07 pixels/cm
  Bits/Sample: 16
  Compression Scheme: None
  Photometric Interpretation: min-is-black
  Rows/Strip: 1
  Planar Configuration: single image plane
  Tag 37706: 65459
  Tag 37707: 2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,0,7,0,0,0,255,255,1,0,17,0,67,79,68,69,100,105,116,80,114,111,112,101,
114,116,105,101,115,1,0,0,0,50,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,128,
1,0,0,0,40,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,255,255,1,0,16,0,67,79,68,
68,87,111,114,100,80,114,111,112,101,114,116,121,1,0,0,0,114,1,0,0,255,255,255,0,255,255,1,0,15,0,67,79,68,66,111,
111,108,80,114,111,112,101,114,116,121,1,0,0,0,104,1,0,0,1,0,0,0,6,128,1,0,0,0,124,1,0,0,1,0,0,0,255,255,1,0,17,0,67,79,
68,83,116,114,105,110,103,80,114,111,112,101,114,116,121,1,0,0,0,94,1,0,0,0,9,128,1,0,0,0,84,1,0,0,0,0,0,0,0,0,0,3,0,0,
............hier noch einiges mehr..............,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Kann mir jemand helfen, wie ich aus diesen Nummern passen auf die Header Informationen zugreifen kann?
BlackJack

@würmchen: Tauchen die Tags auf wenn Du das Bild mit PIL einliest?

Code: Alles auswählen

from PIL import Image
img = Image.open('bild.tiff')
img.load()
print img.tag.tags
Ansonsten gäbe es noch pylibtiff, das sowohl einen `ctypes`-Wrapper für die `libtiff` als auch eine reine Python-Implementierung enthält.

Für die "unbekannten" Tags müsstest Du Anhand der Beschreibung der Daten selber für eine entsprechende Umwandlung sorgen. Mit dem `struct`-Modul aus der Standardbibliothek, oder mit `construct`.
würmchen
User
Beiträge: 255
Registriert: Mittwoch 7. November 2007, 14:17

Danke für die Hinweise, ich werde es versuchen mal umzusetzen. Melde mich dann wenn ich weitere Probleme habe...


EDIT: PIL bringt mir nur ein paar Tags, aber nicht den den ich brauche:

Code: Alles auswählen

>>> print img.tag.tags.keys()
[256, 257, 258, 259, 262, 296, 273, 278, 282, 283]
Und PyLibTiff gibts als Download nur für Windows, oder stell ich mich gerade blöd an?
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

würmchen hat geschrieben:Und PyLibTiff gibts als Download nur für Windows, oder stell ich mich gerade blöd an?
Den Quelltext gibts im SVN-Repository.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
würmchen
User
Beiträge: 255
Registriert: Mittwoch 7. November 2007, 14:17

hallo noch mal,
also ich hab dann dank leonidas die src gefunden und das Paket installiert, allerdings macht es "Probleme".

Code: Alles auswählen

>>> from libtiff import TIFF
>>> tif = TIFF.open('Image_012.tif', mode='r')
TIFFReadDirectory: Warning, Image_012.tif: invalid TIFF directory; tags are not sorted in ascending order.
TIFFReadDirectory: Warning, Image_012.tif: unknown field with tag 37706 (0x934a) encountered.
TIFFReadDirectory: Warning, Image_012.tif: unknown field with tag 37707 (0x934b) encountered.
>>> tif
<TIFF object at 0x26460e0>
>>> image = tif.read_image()
>>> image
array([[4512, 4334, 4863, ..., 5294, 5359, 5370],
       [4226, 4870, 5050, ..., 4729, 4737, 4940],
       [4806, 4955, 4292, ..., 4870, 5074, 5597],
       ..., 
       [5327, 5877, 5544, ..., 5254, 4765, 5726],
       [5642, 5597, 5286, ..., 5236, 4969, 4707],
       [5636, 5227, 5857, ..., 5009, 5000, 4512]], dtype=uint16)
>>> tif.get_tag_name(37707)
>>> tif.get_tag_name(256)
'TIFFTAG_IMAGEWIDTH'
Mit TIFFfile und TIFFimage bin ich auch irgendwie nicht weiter gekommen, aber da gibt es schon mal keine Fehlermeldung beim einlesen.

Code: Alles auswählen

>>> from libtiff import TIFFfile, TIFFimage
>>> tif = TIFFfile('Image_012.tif')
>>> print tif.get_info()
Number of subfile types: 1
--------------------------------------------------
Subfile type: 0
Number of images: 1
ImageLength: 1024
ImageWidth: 1024
SamplesPerPixel: 1
SampleFormat: ['BYTE']
Compression: Uncompressed
Predictor: None
PhotometricInterpretation: BlackIsZero
Orientation: TopLeft
PlanarConfiguration: Chunky
XResolution: [(11464743L, 1L)]
YResolution: [(11464743L, 1L)]
ResolutionUnit: Centimeter
>>> ifd = tif.get_first_ifd()
>>> ifd.get_value(37707)
IFD.get_value: no default value defined tiff_data.default_tag_values dict for 37707 IFD tag
>>> ifd.get_value('37707')
IFD.get_value: no default value defined tiff_data.default_tag_values dict for '37707' IFD tag
Hat da Jemand mehr Erfahrung wie ich überhaupt erstmal an die Informationen in dem Tag kommen kann. Anleitung scheint es ja nicht zu geben und der Quelltext zeigt mir jetzt auch nicht gerade Funktionen mit denen ich unbekannte Tags auslesen kann.
BlackJack

@würmchen: Bei der ersten Variante hast Du ja den numerischen Tag-Wert in einen Namen umgewandelt. Dass es da für 37707 nichts gibt, sollte klar sein, das ist ja kein Standard-Tag. Was passiert denn, wenn Du die `GetField()`-Methode verwendest um den Wert von dem Tag zu holen?

Kannst Du eventuell ein Beispielbild zur Verfügung stellen?
würmchen
User
Beiträge: 255
Registriert: Mittwoch 7. November 2007, 14:17

Hier hab ich das Bild mal zur Verfügung gestellt:
http://download.dejung.net/Image_012.tif

Hm, ich hatte den Versuch vorhin auch gemacht wohl nur vergessen copy paste zu machen. Aber da bekomm ich einfach nichts zurück:

Code: Alles auswählen

>>> tif.GetField(37707)
>>> tif.GetField(256)
1024L
Aber wie man sehen kann, für den bekannten 256 Tag geht es...

Ich glaube, er scheint da praktisch erst garnichts einzulesen weil das Field unbekannt ist.


Ich hab mich eben mal durch den Quellcode versucht zu arbeiten von pylibtiff und da ist mir jetzt bei libtiff_ctypes.py aufgefallen, da gibt es ein dictionary tifftags, indem die Namen definiert werden für die zweite Variante (wenn ich jetzt nicht durcheinander bin), aber ich versteh nicht wie das funktioniert.
BlackJack

@würmchen: Ich habe dann auch mal in den Quelltext geschaut. Und bei der `ctypes`-Variante werden tatsächlich nur dem Modul bekannte Tags ausgelesen, weil in `tifftags` hinterlegt ist welchen Typ die Daten haben und eine Funktion die das in einen Python-Typ umwandelt.

Die reine Python-Variante liest alle Tags aus und unbekannte bekommen den Namen 'TAG0x…' verpasst, wobei die Punkte die Hexadezimaldarstellung des nummerischen Tag-Wertes sind. Also zum Beispiel ``'TAG' + hex(37707)`` für das Tag 37707. Beispiel-Sitzung mit Deinem Bild:

Code: Alles auswählen

In [130]: img = libtiff.TIFFfile('test.tif')

In [131]: d = img.get_first_ifd()

In [132]: d.entries_dict
Out[132]: 
{'BitsPerSample': IFDEntry(TIFFfile('test.tif'), None),
 'Compression': IFDEntry(TIFFfile('test.tif'), None),
 'ImageLength': IFDEntry(TIFFfile('test.tif'), None),
 'ImageWidth': IFDEntry(TIFFfile('test.tif'), None),
 'PhotometricInterpretation': IFDEntry(TIFFfile('test.tif'), None),
 'ResolutionUnit': IFDEntry(TIFFfile('test.tif'), None),
 'RowsPerStrip': IFDEntry(TIFFfile('test.tif'), None),
 'StripByteCounts': IFDEntry(TIFFfile('test.tif'), 61347),
 'StripOffsets': IFDEntry(TIFFfile('test.tif'), 57251),
 'TAG0x934a': IFDEntry(TIFFfile('test.tif'), None),
 'TAG0x934b': IFDEntry(TIFFfile('test.tif'), 49168),
 'XResolution': IFDEntry(TIFFfile('test.tif'), 65443),
 'YResolution': IFDEntry(TIFFfile('test.tif'), 65451)}

In [133]: x = d.get('TAG' + hex(37706))

In [134]: x
Out[134]: IFDEntry(TIFFfile('test.tif'), None)

In [135]: x.value
Out[135]: 65459

In [136]: x = d.get('TAG' + hex(37707))

In [137]: x.value
Out[137]: memmap([2, 0, 0, ..., 0, 0, 0], dtype=uint8)

In [138]: len(x.value)
Out[138]: 7921
Das `memmap`-Objekt ist ein `numpy`-Arraytyp wo die Bytes drin stehen die unter dem Tag gespeichert sind. Das müsste man also noch etwas brauchbarer aufbereiten.
würmchen
User
Beiträge: 255
Registriert: Mittwoch 7. November 2007, 14:17

Hi BlackJack,
danke für diese Lösung! Mir war das nicht aufgefallen wie der Zugriff auf die anderen Tags möglich ist...

Ich versuch gerade mit struct die einzelnen Abschnitte zu "lesen", aber irgendwie komm ich da nicht auf die richtigen Werte. Vielleicht hat da Jemand ne Ahnung was ich komplett falsch mache...

Code: Alles auswählen

>>> struct.unpack_from('l',x.value,84)
(1099511628032,)
>>> struct.unpack_from('l',x.value,564)
(0,)
>>> struct.unpack_from('l',x.value,572)
(0,)
>>> struct.unpack_from('l',x.value,580)
(0,)
84 Soll zum Beispiel die Beschleunigungsspannung sein. Diese lag bei 120kV. Der Wert der an dieser Stelle in dem Array stehen sollte ist aber ne ganze Ecke größer.
Ich frag mich jetzt, ob der Fehler an mir liegt. Kann ich an der Stelle mit dem memmap Array einfach arbeiten? Fehler gibt es jedenfall keine, aber die Werte sind nicht annähernd so wie sie sein sollten...
BlackJack

@würmchen: Kann es sein, dass auf Deinem System "long" mehr als 4 Bytes haben? Was sagt denn das hier bei Dir?

Code: Alles auswählen

In [175]: struct.calcsize('l')
Out[175]: 4
würmchen
User
Beiträge: 255
Registriert: Mittwoch 7. November 2007, 14:17

japp, sagt 8...

Kann ich python irgendwie zwingen nur 4 Bit zu nehmen, oder wo kommt die andere Größe her?
BlackJack

@würmchen: Du must halt den Typ finden, der auf Deiner Plattform nur 4 Byte verwendet. `int` ('i') wird bei Dir wahrscheinlich auch 8 Byte haben, dann könnte es also `short` ('h') sein.

Wenn das Programm portabel sein soll, müsste man den passenden Typcode für `struct` dynamisch ermitteln.
Antworten