- SubIFDs are now shown as 'subifd'

- Legend size depends on number of items
- Linebreak names on big blocks
- Count number of unused bytes in file and ifds
- Little bit of clean up
This commit is contained in:
Wim Pomp
2021-07-15 13:48:38 +02:00
parent 2cfd117cbf
commit 8b50b0258f
3 changed files with 67 additions and 54 deletions

View File

@@ -6,7 +6,7 @@ with open("README.md", "r") as fh:
setuptools.setup( setuptools.setup(
name="tiffexplore", name="tiffexplore",
packages=["tiffexplore"], packages=["tiffexplore"],
version="2021.07.0", version="2021.07.1",
author="Wim Pomp", author="Wim Pomp",
author_email="wimpomp@gmail.com", author_email="wimpomp@gmail.com",
description="Explore a tiff structure.", description="Explore a tiff structure.",

View File

@@ -89,10 +89,10 @@ class PaintBox(QtWidgets.QWidget):
class Legend(PaintBox): class Legend(PaintBox):
def __init__(self, parent): def __init__(self, parent):
self.parent = parent self.parent = parent
self.color = {'header': 'red', 'ifd': 'cyan', 'tagdata': 'lightgreen', 'image': 'yellow', 'empty': 'white', self.color = {'header': 'red', '(sub)ifd': 'cyan', 'tagdata': 'lightgreen', 'image': 'yellow', 'empty': 'white',
'shared tagdata': 'green', 'shared image': 'orange', 'unknown': 'gray'} 'shared tagdata': 'green', 'shared image': 'orange', 'unknown': 'gray'}
super().__init__() super().__init__()
self.setFixedHeight(120) self.setFixedHeight(15 * len(self.color))
self.show() self.show()
def paintEvent(self, *args, **kwargs): def paintEvent(self, *args, **kwargs):
@@ -107,8 +107,9 @@ class Legend(PaintBox):
class Bar(PaintBox): class Bar(PaintBox):
def __init__(self, parent): def __init__(self, parent):
self.parent = parent self.parent = parent
self.color = {'header': 'red', 'ifd': 'cyan', 'tagdata': 'lightgreen', 'image': 'yellow', 'empty': 'white', self.color = {'header': 'red', 'ifd': 'cyan', 'subifd': 'cyan', 'tagdata': 'lightgreen', 'image': 'yellow',
'HEADER': 'red', 'IFD': 'blue', 'TAGDATA': 'green', 'IMAGE': 'orange', 'EMPTY': 'white'} 'empty': 'white', 'HEADER': 'red', 'IFD': 'blue', 'SUBIFD': 'blue', 'TAGDATA': 'green',
'IMAGE': 'orange', 'EMPTY': 'white'}
self.tiff = None self.tiff = None
self.bar = tiffread.assignments() self.bar = tiffread.assignments()
super().__init__() super().__init__()
@@ -128,7 +129,8 @@ class Bar(PaintBox):
self.parent.verticalLayoutWidget.setFixedHeight(self.bar.max_addr) self.parent.verticalLayoutWidget.setFixedHeight(self.bar.max_addr)
for key, value in self.bar.items(): for key, value in self.bar.items():
self.drawRectangle(qp, (0, value[0], 125, value[1]), self.color.get(key[0], "gray")) self.drawRectangle(qp, (0, value[0], 125, value[1]), self.color.get(key[0], "gray"))
self.drawText(qp, (0, value[0], 125, value[1]), ('_'.join(('{}',) * len(key))).format(*key).lower()) self.drawText(qp, (0, value[0], 125, value[1]),
key[0].lower() + ('\n' if value[1] > 20 else ' ') + ' '.join([f'{k}' for k in key[1]]))
qp.end() qp.end()
def get_bar(self, scale=100, min_size=10, max_size=1000): def get_bar(self, scale=100, min_size=10, max_size=1000):
@@ -141,7 +143,7 @@ class Bar(PaintBox):
size = min_size if size < min_size else max_size size = min_size if size < min_size else max_size
if not (key[0].lower() == 'empty' and value[1] == 1): if not (key[0].lower() == 'empty' and value[1] == 1):
if key[0].lower() == 'empty': if key[0].lower() == 'empty':
bar[('empty', value[0] + value[1] // 2)] = (pos, size) bar[('empty', (value[0] + value[1] // 2,))] = (pos, size)
else: else:
if len(item) > 1: if len(item) > 1:
bar[(key[0].upper(),) + key[1:]] = (pos, size) bar[(key[0].upper(),) + key[1:]] = (pos, size)
@@ -152,47 +154,43 @@ class Bar(PaintBox):
return bar return bar
def mousePressEvent(self, event): def mousePressEvent(self, event):
keys, vals = zip(*self.bar.get_assignment(event.localPos().y())) ((code, key), *_), _ = zip(*self.bar.get_assignment(event.localPos().y()))
key, val = keys[0], vals[0] if code.lower() == 'empty':
if key[0].lower() == 'empty': addr = key[0]
addr = key[1]
else: else:
addr = self.tiff.addresses[(key[0].lower(),) + key[1:]] addr = self.tiff.addresses[(code.lower(), key)]
addr = addr[0] + addr[1] // 2 addr = addr[0] + addr[1] // 2
keys, addrs = zip(*self.parent.tiff.addresses.get_assignment(addr)) keys, (addr, *_) = zip(*self.parent.tiff.addresses.get_assignment(addr))
addr = addrs[0]
text = [('_'.join(('{}',) * len(key))).format(*key) for key in keys] text = [' '.join([f'{k}' for k in (c.lower(), *k)]) for c, *k in keys]
text.append('') text.append('')
text.append(f'Adresses: {addr[0]} - {sum(addr)}') text.append(f'Adresses: {addr[0]} - {sum(addr)}')
text.append(f'Length: {addr[1]}') text.append(f'Length: {addr[1]}')
if key[0].lower() == 'header': if code.lower() == 'header':
text.append(f'\nFile size: {len(self.tiff)}') text.append(f'\nFile size: {len(self.tiff)}')
text.append(f'Unused bytes in file: {self.tiff.get_empty()}')
text.append(f'Byte order: {self.tiff.byteorder}') text.append(f'Byte order: {self.tiff.byteorder}')
text.append(f'Big tiff: {self.tiff.bigtiff}') text.append(f'Big tiff: {self.tiff.bigtiff}')
text.append(f'Tag size: {self.tiff.tagsize}') text.append(f'Tag size: {self.tiff.tagsize}')
text.append(f'Tag number format: {self.tiff.tagnoformat}') text.append(f'Tag number format: {self.tiff.tagnoformat}')
text.append(f'Offset size: {self.tiff.offsetsize}') text.append(f'Offset size: {self.tiff.offsetsize}')
text.append(f'Offset format: {self.tiff.offsetformat}') text.append(f'Offset format: {self.tiff.offsetformat}')
text.append(f'First ifd offset: {self.tiff.offsets[0]}') text.append(f'First ifd offset: {self.tiff.offsets[(0,)]}')
if key[0].lower() == 'ifd': if code.lower() in ('ifd', 'subifd'):
text.append(f'Number of tags: {self.tiff.nTags[key[1]]}\n') text.append(f'Number of tags: {self.tiff.nTags[key]}')
text.extend([self.tiff.fmt_tag(k, v)+'\n' for k, v in self.tiff.tags[key[1]].items()]) text.append(f'Unused bytes in ifd: {self.tiff.get_empty(key)}\n')
if not isinstance(key[1], str): text.extend([self.tiff.fmt_tag(k, v) + '\n' for k, v in self.tiff.tags[key].items()])
text.append(f'Next ifd offset: {self.tiff.offsets[key[1] + 1]}') text.append(f'Next ifd offset: {self.tiff.offsets.get(key[:-1] + (key[-1] + 1,))}')
if key[0].lower() == 'tagdata': if code.lower() == 'tagdata':
text.append('\n' + self.tiff.fmt_tag(key[2], self.tiff.tags[key[1]][key[2]])) text.append('\n' + self.tiff.fmt_tag(key[-1], self.tiff.tags[key[:-1]][key[-1]]))
if key[0].lower() == 'image': if code.lower() == 'image' and len(key) == 2:
try: im = self.tiff.asarray(key[0], key[1])
im = self.tiff.asarray(key[1], key[2]) if im is not None:
if im is not None: text.append(f'\nStrip size: {im.shape}')
text.append(f'\nStrip size: {im.shape}') text.append(f'Data type: {im.dtype}')
text.append(f'Data type: {im.dtype}') text.append(f'Min, max: {im.min()}, {im.max()}')
text.append(f'Min, max: {im.min()}, {im.max()}') text.append(f'Mean, std: {im.mean()}, {im.std()}')
text.append(f'Mean, std: {im.mean()}, {im.std()}') self.parent.setImage(im)
self.parent.setImage(im)
except Exception:
pass
else: else:
self.parent.setImage() self.parent.setImage()
self.parent.properties.setText('\n'.join(text)) self.parent.properties.setText('\n'.join(text))

View File

@@ -3,8 +3,6 @@ import tifffile
import numpy as np import numpy as np
from traceback import format_exc from traceback import format_exc
ifdcodes = {330: 'sub', 34665: 'exif', 34853: 'gps', 40965: 'inter'}
class tiff(): class tiff():
def __init__(self, file): def __init__(self, file):
self.file = file self.file = file
@@ -16,34 +14,37 @@ class tiff():
self.tags = {} self.tags = {}
self.get_file_len() self.get_file_len()
self.addresses = assignments(len(self)) self.addresses = assignments(len(self))
self.offsets = [] self.offsets = {}
self.nTags = {} self.nTags = {}
self.tagsread = set() self.tagsread = set()
try: try:
self.offsets = [self.read_header()] self.read_ifd_offsets(self.read_header())
idx = 0 self.read_tags()
while 0 < self.offsets[-1] < len(self):
self.offsets.append(self.read_ifd(self.offsets[-1], idx))
idx += 1
self.readtags()
except Exception: except Exception:
print(format_exc()) print(format_exc())
def readtags(self): def read_ifd_offsets(self, offset, ifdtype=tuple()):
idx = 0
self.offsets[ifdtype + (idx,)] = offset
while 0 < self.offsets[ifdtype + (idx,)] < len(self):
self.offsets[ifdtype + (idx + 1,)] = self.read_ifd(ifdtype + (idx,))
idx += 1
def read_tags(self):
while len(set(self.tags.keys()) - self.tagsread): while len(set(self.tags.keys()) - self.tagsread):
for idx in set(self.tags.keys()) - self.tagsread: for idx in set(self.tags.keys()) - self.tagsread:
tags = self.tags[idx] tags = self.tags[idx]
if idx not in self.tagsread: if idx not in self.tagsread:
if 273 in tags and 279 in tags: if 273 in tags and 279 in tags:
for i, a in enumerate(zip(tags[273][-1], tags[279][-1])): for i, a in enumerate(zip(tags[273][-1], tags[279][-1])):
self.addresses[('image', idx, i)] = a self.addresses[('image', (*idx, i))] = a
for code, id in ifdcodes.items(): for code in (330, 400, 34665, 34853, 40965):
if code in tags: if code in tags:
if len(tags[code][3]) == 1: if len(tags[code][3]) == 1:
self.offsets.append(self.read_ifd(tags[code][3][0], f'{id}_{idx}')) self.read_ifd_offsets(tags[code][3][0], (*idx, code))
else: else:
for i, offset in enumerate(tags[code][3]): for i, offset in enumerate(tags[code][3]):
self.offsets.append(self.read_ifd(offset, f'{id}_{idx}_{i}')) self.read_ifd_offsets(tags[code][3][0], (*idx, code, i))
self.tagsread.add(idx) self.tagsread.add(idx)
@staticmethod @staticmethod
@@ -92,13 +93,14 @@ class tiff():
self.offsetformat = 'I' self.offsetformat = 'I'
self.offsetsize = 4 self.offsetsize = 4
self.offset = struct.unpack(self.byteorder + self.offsetformat, self.fh.read(self.offsetsize))[0] self.offset = struct.unpack(self.byteorder + self.offsetformat, self.fh.read(self.offsetsize))[0]
self.addresses[('header',)] = (0, 4 + self.bigtiff * 4 + self.offsetsize) self.addresses[('header', (0,))] = (0, 4 + self.bigtiff * 4 + self.offsetsize)
return self.offset return self.offset
def read_ifd(self, offset, idx): def read_ifd(self, idx):
""" Reads an IFD of the tiff file """ Reads an IFD of the tiff file
wp@tl20200214 wp@tl20200214
""" """
offset = self.offsets[idx]
self.fh.seek(offset) self.fh.seek(offset)
nTags = struct.unpack(self.byteorder + self.tagnoformat, self.fh.read(struct.calcsize(self.tagnoformat)))[0] nTags = struct.unpack(self.byteorder + self.tagnoformat, self.fh.read(struct.calcsize(self.tagnoformat)))[0]
self.nTags[idx] = nTags self.nTags[idx] = nTags
@@ -121,7 +123,7 @@ class tiff():
toolong = struct.calcsize(dtype) * count > self.offsetsize toolong = struct.calcsize(dtype) * count > self.offsetsize
if toolong: if toolong:
caddr = struct.unpack(self.byteorder + self.offsetformat, self.fh.read(self.offsetsize))[0] caddr = struct.unpack(self.byteorder + self.offsetformat, self.fh.read(self.offsetsize))[0]
self.addresses[('tagdata', idx, code)] = (caddr, dtypelen * count) self.addresses[('tagdata', (*idx, code))] = (caddr, dtypelen * count)
cp = self.fh.tell() cp = self.fh.tell()
self.fh.seek(caddr) self.fh.seek(caddr)
else: else:
@@ -144,9 +146,22 @@ class tiff():
self.fh.seek(offset + struct.calcsize(self.tagnoformat) + self.tagsize * nTags) self.fh.seek(offset + struct.calcsize(self.tagnoformat) + self.tagsize * nTags)
nifd = struct.unpack(self.byteorder + self.offsetformat, self.fh.read(self.offsetsize))[0] nifd = struct.unpack(self.byteorder + self.offsetformat, self.fh.read(self.offsetsize))[0]
self.addresses[('ifd', idx)] = (offset, 2 * struct.calcsize(self.tagnoformat) + nTags * self.tagsize) self.addresses[('sub' * (len(idx) > 1) + 'ifd', idx)] = (offset, 2 * struct.calcsize(self.tagnoformat)
+ nTags * self.tagsize)
return nifd return nifd
def get_empty(self, ifd=None):
empty = 0
if ifd is None:
for ((code, *key), value), *_ in self.addresses.get_assignments():
if code == 'empty':
empty += value[-1]
elif code in ('ifd', 'subifd'):
empty += self.get_empty(key[0])
else:
empty = sum([self.offsetsize - v[2] if v[2] < self.offsetsize else 0 for v in self.tags[ifd].values()])
return empty
def __getitem__(self, item): def __getitem__(self, item):
if not isinstance(item, slice): if not isinstance(item, slice):
item = slice(item, item+1) item = slice(item, item+1)
@@ -163,7 +178,7 @@ class tiff():
self.tiff.close() self.tiff.close()
self.close() self.close()
def close(self, *args, **kwargs): def close(self):
self.fh.close() self.fh.close()
def get_bytes(self, part): def get_bytes(self, part):