Skip to content

Commit

Permalink
Speed up styling by doing less.
Browse files Browse the repository at this point in the history
  • Loading branch information
manthey committed Jul 22, 2022
1 parent a9f1bd6 commit 747279c
Show file tree
Hide file tree
Showing 10 changed files with 39 additions and 18 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- Store the id of job results for easier post-job work ([887](../../pull/887))
- Harden style compositing of partial tiles ([889](../../pull/889))
- Cache read_tiff calls to speed up restyling ([891](../../pull/891))
- Speed up styling by doing less ([892](../../pull/892))

### Changes
- Be more consistent in source class name attribute assignment ([884](../../pull/884))
Expand Down
36 changes: 28 additions & 8 deletions large_image/tilesource/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1128,7 +1128,7 @@ def _applyStyle(self, image, style, x, y, z, frame=None): # noqa
output = numpy.zeros((image.shape[0], image.shape[1], 4), float)
mainImage = image
mainFrame = frame
for entry in style:
for eidx, entry in enumerate(style):
dtype = dtype if dtype is not None else entry.get('dtype')
axis = axis if axis is not None else entry.get('axis')
bandidx = 0 if image.shape[2] <= 2 else 1
Expand Down Expand Up @@ -1213,13 +1213,17 @@ def _applyStyle(self, image, style, x, y, z, frame=None): # noqa
clrs = palette[numpy.floor(band * len(palette)).astype(int).clip(
0, len(palette) - 1), channel]
if composite == 'multiply':
output[:keep.shape[0], :keep.shape[1], channel] = numpy.multiply(
output[:keep.shape[0], :keep.shape[1], channel],
numpy.where(keep, clrs / 255, 1))
if eidx:
output[:keep.shape[0], :keep.shape[1], channel] = numpy.multiply(
output[:keep.shape[0], :keep.shape[1], channel],
numpy.where(keep, clrs / 255, 1))
else:
output[:keep.shape[0], :keep.shape[1], channel] = numpy.maximum(
output[:keep.shape[0], :keep.shape[1], channel],
numpy.where(keep, clrs, 0))
if not eidx:
output[:keep.shape[0], :keep.shape[1], channel] = numpy.where(keep, clrs, 0)
else:
output[:keep.shape[0], :keep.shape[1], channel] = numpy.maximum(
output[:keep.shape[0], :keep.shape[1], channel],
numpy.where(keep, clrs, 0))
if dtype == 'uint16':
output = (output * 65535 / 255).astype(numpy.uint16)
elif dtype == 'float':
Expand Down Expand Up @@ -1287,7 +1291,8 @@ def _outputTile(self, tile, tileEncoding, x, y, z, pilImageAllowed=False,
mode = None
if (numpyAllowed == 'always' or tileEncoding == TILE_FORMAT_NUMPY or
(applyStyle and getattr(self, 'style', None)) or isEdge):
tile, mode = self._outputTileNumpyStyle(tile, applyStyle, x, y, z, kwargs.get('frame'))
tile, mode = self._outputTileNumpyStyle(
tile, applyStyle, x, y, z, self._getFrame(**kwargs))
if isEdge:
contentWidth = min(self.tileWidth,
sizeX - (maxX - self.tileWidth))
Expand Down Expand Up @@ -1491,6 +1496,21 @@ def getBandInformation(self, statistics=False, **kwargs):
self._bandInfo = bandInfo
return self._bandInfo

def _getFrame(self, frame=None, **kwargs):
"""
Get the current frame number. If a style is used that completely
specified the frame, use that value instead.
:param frame: an integer or string with the frame number.
:returns: an integer frame number.
"""
frame = int(frame or 0)
if (hasattr(self, 'style') and 'bands' in self.style and
len(self.style['bands']) and
all(entry.get('frame') is not None for entry in self.style['bands'])):
frame = int(self.style['bands'][0]['frame'])
return frame

def _xyzInRange(self, x, y, z, frame=None, numFrames=None):
"""
Check if a tile at x, y, z is in range based on self.levels,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,7 @@ def getTile(self, x, y, z, pilImageAllowed=False, numpyAllowed=False, **kwargs):
ft = fc = fz = 0
fseries = self._metadata['frameSeries'][0]
if kwargs.get('frame') is not None:
frame = int(kwargs.get('frame'))
frame = self._getFrame(**kwargs)
fc = frame % self._metadata['sizeC']
fz = (frame // self._metadata['sizeC']) % self._metadata['sizeZ']
ft = (frame // self._metadata['sizeC'] //
Expand Down
2 changes: 1 addition & 1 deletion sources/multi/large_image_source_multi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -906,7 +906,7 @@ def _addSourceToTile(self, tile, sourceEntry, corners, scale):

@methodcache()
def getTile(self, x, y, z, pilImageAllowed=False, numpyAllowed=False, **kwargs):
frame = int(kwargs.get('frame') or 0)
frame = self._getFrame(**kwargs)
self._xyzInRange(x, y, z, frame, len(self._frames) if hasattr(self, '_frames') else None)
scale = 2 ** (self.levels - 1 - z)
corners = [[
Expand Down
2 changes: 1 addition & 1 deletion sources/nd2/large_image_source_nd2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ def getInternalMetadata(self, **kwargs):

@methodcache()
def getTile(self, x, y, z, pilImageAllowed=False, numpyAllowed=False, **kwargs):
frame = int(kwargs.get('frame') or 0)
frame = self._getFrame(**kwargs)
self._xyzInRange(x, y, z, frame, self._framecount)
x0, y0, x1, y1, step = self._xyzToCorners(x, y, z)
tileframe = self._nd2array
Expand Down
2 changes: 1 addition & 1 deletion sources/ometiff/large_image_source_ometiff/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ def getTile(self, x, y, z, pilImageAllowed=False, numpyAllowed=False,
x, y, z, pilImageAllowed=pilImageAllowed,
numpyAllowed=numpyAllowed, sparseFallback=sparseFallback,
**kwargs)
frame = int(kwargs.get('frame') or 0)
frame = self._getFrame(**kwargs)
if frame < 0 or frame >= len(self._omebase['TiffData']):
raise TileSourceError('Frame does not exist')
subdir = None
Expand Down
2 changes: 1 addition & 1 deletion sources/test/large_image_source_test/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ def getInternalMetadata(self, **kwargs):

@methodcache()
def getTile(self, x, y, z, *args, **kwargs):
frame = int(kwargs.get('frame') or 0)
frame = self._getFrame(**kwargs)
self._xyzInRange(x, y, z, frame, len(self._frames) if hasattr(self, '_frames') else None)

if not (self.minLevel <= z <= self.maxLevel):
Expand Down
6 changes: 3 additions & 3 deletions sources/tiff/large_image_source_tiff/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,7 @@ def getInternalMetadata(self, **kwargs):
@methodcache()
def getTile(self, x, y, z, pilImageAllowed=False, numpyAllowed=False,
sparseFallback=False, **kwargs):
frame = int(kwargs.get('frame') or 0)
frame = self._getFrame(**kwargs)
self._xyzInRange(x, y, z, frame, len(self._frames) if hasattr(self, '_frames') else None)
if frame > 0:
if self._frames[frame]['dirs'][z] is not None:
Expand Down Expand Up @@ -641,7 +641,7 @@ def getTileFromEmptyDirectory(self, x, y, z, **kwargs):
basez = z
scale = 1
dirlist = self._tiffDirectories
frame = int(kwargs.get('frame') or 0)
frame = self._getFrame(**kwargs)
if frame > 0:
dirlist = self._frames[frame]['dirs']
while dirlist[z] is None:
Expand All @@ -662,7 +662,7 @@ def getTileFromEmptyDirectory(self, x, y, z, **kwargs):
subtile = self.getTile(
x * scale + newX, y * scale + newY, z,
pilImageAllowed=True, numpyAllowed=False,
sparseFallback=True, edge=False, frame=kwargs.get('frame'))
sparseFallback=True, edge=False, frame=frame)
if not isinstance(subtile, PIL.Image.Image):
subtile = PIL.Image.open(io.BytesIO(subtile))
tile.paste(subtile, (newX * self.tileWidth,
Expand Down
2 changes: 1 addition & 1 deletion sources/tifffile/large_image_source_tifffile/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ def _getAssociatedImage(self, imageKey):

@methodcache()
def getTile(self, x, y, z, pilImageAllowed=False, numpyAllowed=False, **kwargs):
frame = int(kwargs.get('frame') or 0)
frame = self._getFrame(**kwargs)
self._xyzInRange(x, y, z, frame, self._framecount)
x0, y0, x1, y1, step = self._xyzToCorners(x, y, z)
if len(self._series) > 1:
Expand Down
2 changes: 1 addition & 1 deletion sources/vips/large_image_source_vips/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ def getNativeMagnification(self):

@methodcache()
def getTile(self, x, y, z, pilImageAllowed=False, numpyAllowed=False, **kwargs):
frame = int(kwargs.get('frame') or 0)
frame = self._getFrame(**kwargs)
self._xyzInRange(x, y, z, frame, len(self._frames))
img = self._getFrameImage(frame)
x0, y0, x1, y1, step = self._xyzToCorners(x, y, z)
Expand Down

0 comments on commit 747279c

Please sign in to comment.