Skip to content

Commit

Permalink
Merge pull request #1144 from girder/tile_source_dtype
Browse files Browse the repository at this point in the history
Add tile source _dtype attribute
  • Loading branch information
annehaley authored May 26, 2023
2 parents 6eb6f08 + 36d2438 commit 90368a4
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 3 deletions.
36 changes: 34 additions & 2 deletions large_image/tilesource/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ def __init__(self, encoding='JPEG', jpegQuality=95, jpegSubsampling=0,
self.sizeX = None
self.sizeY = None
self._styleLock = threading.RLock()
self._dtype = None

if encoding not in TileOutputMimeTypes:
raise ValueError('Invalid encoding "%s"' % encoding)
Expand Down Expand Up @@ -202,6 +203,24 @@ def _setStyle(self, style):
def style(self):
return self._style

@property
def dtype(self):
if not self._dtype:
with self._styleLock:
if not hasattr(self, '_skipStyle'):
self._setSkipStyle(True)
try:
sample, format = self.getRegion(
region=dict(left=0, top=0, width=1, height=1),
format=TILE_FORMAT_NUMPY)
self._dtype = sample.dtype
finally:
self._setSkipStyle(False)
else:
return None

return self._dtype

@staticmethod
def getLRUHash(*args, **kwargs):
"""
Expand Down Expand Up @@ -1317,6 +1336,8 @@ def _applyICCProfile(self, sc, frame):
return sc.iccimage

def _setSkipStyle(self, setSkip=False):
if not hasattr(self, '_classkey'):
self._classkey = self.getState()
if setSkip:
self._unlocked_classkey = self._classkey
if hasattr(self, 'cache_lock'):
Expand Down Expand Up @@ -1532,6 +1553,15 @@ def _outputTile(self, tile, tileEncoding, x, y, z, pilImageAllowed=False,
numpyAllowed != 'always' and tileEncoding == self.encoding and
not isEdge and (not applyStyle or not hasStyle)):
return tile

if self._dtype is None:
if tileEncoding == TILE_FORMAT_NUMPY:
self._dtype = tile.dtype
elif tileEncoding == TILE_FORMAT_PIL:
self._dtype = numpy.uint8 if ';16' not in tile.mode else numpy.uint16
else:
self._dtype = _imageToNumpy(tile)[0].dtype

mode = None
if (numpyAllowed == 'always' or tileEncoding == TILE_FORMAT_NUMPY or
(applyStyle and hasStyle) or isEdge):
Expand Down Expand Up @@ -1595,6 +1625,7 @@ def getMetadata(self):
:magnification: if known, the magnificaiton of the image.
:mm_x: if known, the width of a pixel in millimeters.
:mm_y: if known, the height of a pixel in millimeters.
:dtype: if known, the type of values in this image.
In addition to the keys that listed above, tile sources that expose
multiple frames will also contain
Expand Down Expand Up @@ -1625,7 +1656,7 @@ def getMetadata(self):
:channelmap: optional. If known, a dictionary of channel names
with their offset into the channel list.
Note that this does nto include band information, though some tile
Note that this does not include band information, though some tile
sources may do so.
"""
mag = self.getNativeMagnification()
Expand All @@ -1638,6 +1669,7 @@ def getMetadata(self):
'magnification': mag['magnification'],
'mm_x': mag['mm_x'],
'mm_y': mag['mm_y'],
'dtype': self.dtype,
})

@property
Expand Down Expand Up @@ -2415,7 +2447,7 @@ def getLevelForMagnification(self, magnification=None, exact=False,
# Perform some slight rounding to handle numerical precision issues
ratios = [round(ratio, 4) for ratio in ratios]
if not len(ratios):
return mag['level']
return mag.get('level', 0)
if exact:
if any(int(ratio) != ratio or ratio != ratios[0]
for ratio in ratios):
Expand Down
5 changes: 4 additions & 1 deletion large_image/tilesource/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,10 @@ def _imageToNumpy(image):
if image.mode not in ('L', 'LA', 'RGB', 'RGBA'):
image = image.convert('RGBA')
mode = image.mode
image = numpy.asarray(image)
if not image.width or not image.height:
image = numpy.zeros((image.height, image.width, len(mode)))
else:
image = numpy.asarray(image)
else:
if len(image.shape) == 3:
mode = ['L', 'LA', 'RGB', 'RGBA'][(image.shape[2] - 1) if image.shape[2] <= 4 else 3]
Expand Down
12 changes: 12 additions & 0 deletions sources/tiff/large_image_source_tiff/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,12 @@ def __init__(self, path, **kwargs): # noqa
raise TileSourceError(
'Tiff image must have at least two levels.')

sampleformat = highest._tiffInfo.get('sampleformat')
bitspersample = highest._tiffInfo.get('bitspersample')
self._dtype = numpy.dtype('%s%d' % (
tifftools.constants.SampleFormat[sampleformat or 1].name,
bitspersample
))
# Sort the directories so that the highest resolution is the last one;
# if a level is missing, put a None value in its place.
self._tiffDirectories = [directories.get(key) for key in
Expand Down Expand Up @@ -286,6 +292,12 @@ def _initWithTiffTools(self): # noqa
self.levels = max(1, int(math.ceil(math.log(max(
dir0.imageWidth / dir0.tileWidth,
dir0.imageHeight / dir0.tileHeight)) / math.log(2))) + 1)
sampleformat = dir0._tiffInfo.get('sampleformat')
bitspersample = dir0._tiffInfo.get('bitspersample')
self._dtype = numpy.dtype('%s%d' % (
tifftools.constants.SampleFormat[sampleformat or 1].name,
bitspersample
))
info = _cached_read_tiff(self._largeImagePath)
frames = []
associated = [] # for now, a list of directories
Expand Down

0 comments on commit 90368a4

Please sign in to comment.