- 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(
|
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.",
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
Reference in New Issue
Block a user