- 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:
2
setup.py
2
setup.py
@@ -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.",
|
||||
|
||||
@@ -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])
|
||||
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)
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
self.parent.setImage()
|
||||
self.parent.properties.setText('\n'.join(text))
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user