- 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(
name="tiffexplore",
packages=["tiffexplore"],
version="2021.07.0",
version="2021.07.1",
author="Wim Pomp",
author_email="wimpomp@gmail.com",
description="Explore a tiff structure.",

View File

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

View File

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