From 08281a11470095da5825b9f0f08e6774b6dfc22f Mon Sep 17 00:00:00 2001 From: David Manthey Date: Fri, 16 Dec 2022 09:25:58 -0500 Subject: [PATCH] Speed up loading or parsing some sources. For nd2 files, this caches metadata computation. For multi sources, this prefers a fast json parser if the file permits it. --- CHANGELOG.md | 1 + .../large_image_source_multi/__init__.py | 7 +++- .../nd2/large_image_source_nd2/__init__.py | 38 ++++++++++--------- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f1ba9213..72caa6930 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ### Improvements - Better release file handles ([#1007](../../pull/1007)) - Support tiny images from the test source ([#1013](../../pull/1013)) +- Speed up loading or parsing some multi sources ([#1015](../../pull/1015)) ### Changes - Don't report filename in internal PIL metadata ([#1006](../../pull/1006)) diff --git a/sources/multi/large_image_source_multi/__init__.py b/sources/multi/large_image_source_multi/__init__.py index c9cd2a4fa..55245d3d2 100644 --- a/sources/multi/large_image_source_multi/__init__.py +++ b/sources/multi/large_image_source_multi/__init__.py @@ -403,7 +403,12 @@ def __init__(self, path, **kwargs): if start[:1] not in ('{', '#', '-') and (start[:1] < 'a' or start[:1] > 'z'): raise TileSourceError('File cannot be opened via multi-source reader.') fptr.seek(0) - self._info = yaml.safe_load(fptr) + try: + import orjson + self._info = orjson.loads(fptr.read()) + except Exception: + fptr.seek(0) + self._info = yaml.safe_load(fptr) except (json.JSONDecodeError, yaml.YAMLError, UnicodeDecodeError): raise TileSourceError('File cannot be opened via multi-source reader.') self._validator.validate(self._info) diff --git a/sources/nd2/large_image_source_nd2/__init__.py b/sources/nd2/large_image_source_nd2/__init__.py index 2574fa429..cff5edc17 100644 --- a/sources/nd2/large_image_source_nd2/__init__.py +++ b/sources/nd2/large_image_source_nd2/__init__.py @@ -218,24 +218,26 @@ def getMetadata(self): :returns: metadata dictionary. """ - result = super().getMetadata() - - sizes = self._nd2.sizes - axes = self._nd2order[:self._nd2order.index('Y')][::-1] - sizes = self._nd2.sizes - result['frames'] = frames = [] - for idx in range(self._frameCount): - frame = {'Frame': idx} - basis = 1 - ref = {} - for axis in axes: - ref[axis] = (idx // basis) % sizes[axis] - frame['Index' + (axis.upper() if axis.upper() != 'P' else 'XY')] = ( - idx // basis) % sizes[axis] - basis *= sizes.get(axis, 1) - frames.append(frame) - self._addMetadataFrameInformation(result, self._channels) - return result + if not hasattr(self, '_computedMetadata'): + result = super().getMetadata() + + sizes = self._nd2.sizes + axes = self._nd2order[:self._nd2order.index('Y')][::-1] + sizes = self._nd2.sizes + result['frames'] = frames = [] + for idx in range(self._frameCount): + frame = {'Frame': idx} + basis = 1 + ref = {} + for axis in axes: + ref[axis] = (idx // basis) % sizes[axis] + frame['Index' + (axis.upper() if axis.upper() != 'P' else 'XY')] = ( + idx // basis) % sizes[axis] + basis *= sizes.get(axis, 1) + frames.append(frame) + self._addMetadataFrameInformation(result, self._channels) + self._computedMetadata = result + return self._computedMetadata def getInternalMetadata(self, **kwargs): """