From 548addc697a133621c9e4a285095e9506b64712a Mon Sep 17 00:00:00 2001 From: William Moore Date: Fri, 23 Feb 2024 14:23:42 +0000 Subject: [PATCH 01/14] imgData JSON with no stateful services --- omeroweb/webgateway/marshal.py | 124 ++++++++++++++++++++++++++------- 1 file changed, 99 insertions(+), 25 deletions(-) diff --git a/omeroweb/webgateway/marshal.py b/omeroweb/webgateway/marshal.py index 80008ea3c2..55b1b4b705 100644 --- a/omeroweb/webgateway/marshal.py +++ b/omeroweb/webgateway/marshal.py @@ -36,6 +36,32 @@ # OME model point list regular expression OME_MODEL_POINT_LIST_RE = re.compile(r"([\d.]+),([\d.]+)") +from omero.gateway import ChannelWrapper + + +def getChannelsNoRe(image): + """ + Returns a list of Channels. + + This differs from image.getChannels(noRE=True) in that we load statsInfo + here which is used by channel.getWindowMin() and channel.getWindowMax() + + :return: Channels + :rtype: List of :class:`ChannelWrapper` + """ + + conn = image._conn + pid = image.getPixelsId() + params = omero.sys.ParametersI() + params.addId(pid) + query = """select p from Pixels p join fetch p.channels as c + join fetch c.logicalChannel as lc + join fetch c.statsInfo + where p.id=:id""" + pixels = conn.getQueryService().findByQuery(query, params, conn.SERVICE_OPTS) + return [ChannelWrapper(conn, c, idx=n, img=image) + for n, c in enumerate(pixels.iterateChannels())] + def eventContextMarshal(event_context): """ @@ -73,6 +99,38 @@ def eventContextMarshal(event_context): return ctx +def rdefMarshal(rdef, image): + + channels = [] + + for rdef_ch, channel in zip(rdef['c'], getChannelsNoRe(image)): + + chan = { + "emissionWave": channel.getEmissionWave(), + "label": channel.getLabel(), + "color": rdef_ch["color"], + # 'reverseIntensity' is deprecated. Use 'inverted' + "inverted": rdef_ch["inverted"], + "reverseIntensity": rdef_ch["inverted"], + "family": rdef_ch["family"], + "coefficient": rdef_ch["coefficient"], + "active": rdef_ch["active"], + } + + chan["window"] = { + "min": channel.getWindowMin(), + "max": channel.getWindowMax(), + "start": rdef_ch["start"], + "end": rdef_ch["end"], + } + + lut = channel.getLut() + if lut and len(lut) > 0: + chan["lut"] = lut + channels.append(chan) + return channels + + def channelMarshal(channel): """ return a dict with all there is to know about a channel @@ -113,7 +171,13 @@ def imageMarshal(image, key=None, request=None): @param key: key of specific attributes to select @return: Dict """ + get_resolution_levels = True + if request is not None: + get_res = request.GET.get("get_resolution_levels", "") + if get_res.lower() == "false": + get_resolution_levels = False + # projection / invertAxis etc from annotation image.loadRenderOptions() pr = image.getProject() ds = None @@ -165,31 +229,40 @@ def imageMarshal(image, key=None, request=None): "canLink": image.canLink(), }, } - try: - reOK = image._prepareRenderingEngine() - if not reOK: - logger.debug("Failed to prepare Rendering Engine for imageMarshal") + + exp_id = image._conn.getUserId() + rdefs = image.getAllRenderingDefs(exp_id) + print("rdefs", rdefs) + # Assume there is only 1 rdef + # TODO: handle case where user doesn't own any rendering settings (load image owners) + rdef = rdefs[0] + + if get_resolution_levels: + try: + reOK = image._prepareRenderingEngine() + if not reOK: + logger.debug("Failed to prepare Rendering Engine for imageMarshal") + return rv + except omero.ConcurrencyException as ce: + backOff = ce.backOff + rv = {"ConcurrencyException": {"backOff": backOff}} return rv - except omero.ConcurrencyException as ce: - backOff = ce.backOff - rv = {"ConcurrencyException": {"backOff": backOff}} - return rv - except Exception as ex: # Handle everything else. - rv["Exception"] = ex.message - logger.error(traceback.format_exc()) - return rv # Return what we have already, in case it's useful - - # big images - levels = image._re.getResolutionLevels() - tiles = levels > 1 - rv["tiles"] = tiles - if tiles: - width, height = image._re.getTileSize() - zoomLevelScaling = image.getZoomLevelScaling() - - rv.update({"tile_size": {"width": width, "height": height}, "levels": levels}) - if zoomLevelScaling is not None: - rv["zoomLevelScaling"] = zoomLevelScaling + except Exception as ex: # Handle everything else. + rv["Exception"] = ex.message + logger.error(traceback.format_exc()) + return rv # Return what we have already, in case it's useful + + # big images + levels = image._re.getResolutionLevels() + tiles = levels > 1 + rv["tiles"] = tiles + if tiles: + width, height = image._re.getTileSize() + zoomLevelScaling = image.getZoomLevelScaling() + + rv.update({"tile_size": {"width": width, "height": height}, "levels": levels}) + if zoomLevelScaling is not None: + rv["zoomLevelScaling"] = zoomLevelScaling nominalMagnification = ( image.getObjectiveSettings() is not None @@ -242,7 +315,8 @@ def pixel_size_in_microns(method): rv.update({"nominalMagnification": nominalMagnification}) try: rv["pixel_range"] = image.getPixelRange() - rv["channels"] = [channelMarshal(x) for x in image.getChannels()] + rv["channels"] = rdefMarshal(rdef, image) + # rv["channels"] = [channelMarshal(x) for x in image.getChannels()] rv["split_channel"] = image.splitChannelDims() rv["rdefs"] = { "model": (image.isGreyscaleRenderingModel() and "greyscale" or "color"), From f625fa891ea870ea021a3c5ad8638fc396e04aa3 Mon Sep 17 00:00:00 2001 From: William Moore Date: Fri, 23 Feb 2024 14:31:22 +0000 Subject: [PATCH 02/14] Use ?resolution_levels=false --- omeroweb/webgateway/marshal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/omeroweb/webgateway/marshal.py b/omeroweb/webgateway/marshal.py index 55b1b4b705..b9fa7df99a 100644 --- a/omeroweb/webgateway/marshal.py +++ b/omeroweb/webgateway/marshal.py @@ -173,7 +173,7 @@ def imageMarshal(image, key=None, request=None): """ get_resolution_levels = True if request is not None: - get_res = request.GET.get("get_resolution_levels", "") + get_res = request.GET.get("resolution_levels", "") if get_res.lower() == "false": get_resolution_levels = False From 944dd0396ccfae1f16003299c1c1ffe1e9748386 Mon Sep 17 00:00:00 2001 From: William Moore Date: Sat, 24 Feb 2024 06:01:56 +0000 Subject: [PATCH 03/14] load all imgData without state, except zoomLevels --- omeroweb/webgateway/marshal.py | 147 ++++++++++++++++++++------------- 1 file changed, 91 insertions(+), 56 deletions(-) diff --git a/omeroweb/webgateway/marshal.py b/omeroweb/webgateway/marshal.py index b9fa7df99a..5052580ab1 100644 --- a/omeroweb/webgateway/marshal.py +++ b/omeroweb/webgateway/marshal.py @@ -25,6 +25,11 @@ import traceback from future.utils import isbytes, bytes_to_native_str +from omero.model.enums import PixelsTypeint8, PixelsTypeuint8, PixelsTypeint16 +from omero.model.enums import PixelsTypeuint16, PixelsTypeint32 +from omero.model.enums import PixelsTypeuint32, PixelsTypefloat +from omero.model.enums import PixelsTypedouble + from omero.rtypes import unwrap from omero_marshal import get_encoder @@ -56,7 +61,7 @@ def getChannelsNoRe(image): params.addId(pid) query = """select p from Pixels p join fetch p.channels as c join fetch c.logicalChannel as lc - join fetch c.statsInfo + left outer join fetch c.statsInfo where p.id=:id""" pixels = conn.getQueryService().findByQuery(query, params, conn.SERVICE_OPTS) return [ChannelWrapper(conn, c, idx=n, img=image) @@ -99,7 +104,44 @@ def eventContextMarshal(event_context): return ctx -def rdefMarshal(rdef, image): +def getPixelRange(image): + minVals = { + PixelsTypeint8: -128, + PixelsTypeuint8: 0, + PixelsTypeint16: -32768, + PixelsTypeuint16: 0, + PixelsTypeint32: -2147483648, + PixelsTypeuint32: 0, + PixelsTypefloat: -2147483648, + PixelsTypedouble: -2147483648} + maxVals = { + PixelsTypeint8: 127, + PixelsTypeuint8: 255, + PixelsTypeint16: 32767, + PixelsTypeuint16: 65535, + PixelsTypeint32: 2147483647, + PixelsTypeuint32: 4294967295, + PixelsTypefloat: 2147483647, + PixelsTypedouble: 2147483647} + pixtype = image.getPrimaryPixels().getPixelsType().getValue() + return [minVals[pixtype], maxVals[pixtype]] + + +def getWindowMin(channel): + si = channel._obj.getStatsInfo() + if si is None: + return None + return si.getGlobalMin().val + + +def getWindowMax(channel): + si = channel._obj.getStatsInfo() + if si is None: + return None + return si.getGlobalMax().val + + +def rdefMarshal(rdef, image, pixel_range): channels = [] @@ -118,8 +160,8 @@ def rdefMarshal(rdef, image): } chan["window"] = { - "min": channel.getWindowMin(), - "max": channel.getWindowMax(), + "min": getWindowMin(channel) or pixel_range[0], + "max": getWindowMax(channel) or pixel_range[1], "start": rdef_ch["start"], "end": rdef_ch["end"], } @@ -171,11 +213,6 @@ def imageMarshal(image, key=None, request=None): @param key: key of specific attributes to select @return: Dict """ - get_resolution_levels = True - if request is not None: - get_res = request.GET.get("resolution_levels", "") - if get_res.lower() == "false": - get_resolution_levels = False # projection / invertAxis etc from annotation image.loadRenderOptions() @@ -230,54 +267,20 @@ def imageMarshal(image, key=None, request=None): }, } - exp_id = image._conn.getUserId() - rdefs = image.getAllRenderingDefs(exp_id) - print("rdefs", rdefs) - # Assume there is only 1 rdef - # TODO: handle case where user doesn't own any rendering settings (load image owners) - rdef = rdefs[0] - - if get_resolution_levels: - try: - reOK = image._prepareRenderingEngine() - if not reOK: - logger.debug("Failed to prepare Rendering Engine for imageMarshal") - return rv - except omero.ConcurrencyException as ce: - backOff = ce.backOff - rv = {"ConcurrencyException": {"backOff": backOff}} - return rv - except Exception as ex: # Handle everything else. - rv["Exception"] = ex.message - logger.error(traceback.format_exc()) - return rv # Return what we have already, in case it's useful - - # big images - levels = image._re.getResolutionLevels() - tiles = levels > 1 - rv["tiles"] = tiles - if tiles: - width, height = image._re.getTileSize() - zoomLevelScaling = image.getZoomLevelScaling() - - rv.update({"tile_size": {"width": width, "height": height}, "levels": levels}) - if zoomLevelScaling is not None: - rv["zoomLevelScaling"] = zoomLevelScaling - nominalMagnification = ( image.getObjectiveSettings() is not None and image.getObjectiveSettings().getObjective().getNominalMagnification() or None ) + if nominalMagnification is not None: + rv.update({"nominalMagnification": nominalMagnification}) + try: server_settings = request.session.get("server_settings", {}).get("viewer", {}) except Exception: server_settings = {} init_zoom = server_settings.get("initial_zoom_level", 0) - if init_zoom < 0: - init_zoom = levels + init_zoom - interpolate = server_settings.get("interpolate_pixels", True) try: @@ -309,20 +312,22 @@ def pixel_size_in_microns(method): }, } ) - if init_zoom is not None: - rv["init_zoom"] = init_zoom - if nominalMagnification is not None: - rv.update({"nominalMagnification": nominalMagnification}) + + exp_id = image._conn.getUserId() + rdefs = image.getAllRenderingDefs(exp_id) + # Assume there is only 1 rdef + # TODO: handle case where user doesn't own any rendering settings (load image owners) + rdef = rdefs[0] + try: - rv["pixel_range"] = image.getPixelRange() - rv["channels"] = rdefMarshal(rdef, image) - # rv["channels"] = [channelMarshal(x) for x in image.getChannels()] + rv["pixel_range"] = getPixelRange(image) + rv["channels"] = rdefMarshal(rdef, image, rv["pixel_range"]) rv["split_channel"] = image.splitChannelDims() rv["rdefs"] = { - "model": (image.isGreyscaleRenderingModel() and "greyscale" or "color"), + "model": (rdef["model"] == "greyscale" and "greyscale" or "color"), "projection": image.getProjection(), - "defaultZ": image._re.getDefaultZ(), - "defaultT": image._re.getDefaultT(), + "defaultZ": rdef["z"], + "defaultT": rdef["t"], "invertAxis": image.isInvertedAxis(), } except TypeError: @@ -341,6 +346,36 @@ def pixel_size_in_microns(method): except AttributeError: rv = None raise + + reOK = False + try: + reOK = image._prepareRenderingEngine() + if not reOK: + rv["Error"] = "Failed to prepare Rendering Engine for imageMarshal" + logger.debug("Failed to prepare Rendering Engine for imageMarshal") + except omero.ConcurrencyException as ce: + backOff = ce.backOff + rv["ConcurrencyException"] = {"backOff": backOff} + except Exception as ex: # Handle everything else. + rv["Exception"] = ex.message + logger.error(traceback.format_exc()) + + # big images + if reOK: + levels = image._re.getResolutionLevels() + tiles = levels > 1 + rv["tiles"] = tiles + if tiles: + width, height = image._re.getTileSize() + zoomLevelScaling = image.getZoomLevelScaling() + rv.update({"tile_size": {"width": width, "height": height}, "levels": levels}) + if zoomLevelScaling is not None: + rv["zoomLevelScaling"] = zoomLevelScaling + + if init_zoom < 0: + init_zoom = levels + init_zoom + rv["init_zoom"] = init_zoom + if key is not None and rv is not None: for k in key.split("."): rv = rv.get(k, {}) From cc3f741918e05dcf08fa2ae42fef7df6680b56e9 Mon Sep 17 00:00:00 2001 From: William Moore Date: Sat, 24 Feb 2024 06:51:36 +0000 Subject: [PATCH 04/14] Use owners or create rdef if None exist --- omeroweb/webgateway/marshal.py | 59 +++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/omeroweb/webgateway/marshal.py b/omeroweb/webgateway/marshal.py index 5052580ab1..bc1a8164db 100644 --- a/omeroweb/webgateway/marshal.py +++ b/omeroweb/webgateway/marshal.py @@ -204,6 +204,40 @@ def channelMarshal(channel): return chan +def load_re(image, rsp_dict=None): + rsp_dict = {} if rsp_dict is None else rsp_dict + reOK = False + try: + reOK = image._prepareRenderingEngine() + if not reOK: + rsp_dict["Error"] = "Failed to prepare Rendering Engine for imageMarshal" + logger.debug("Failed to prepare Rendering Engine for imageMarshal") + except omero.ConcurrencyException as ce: + backOff = ce.backOff + rsp_dict["ConcurrencyException"] = {"backOff": backOff} + except Exception as ex: # Handle everything else. + rsp_dict["Exception"] = ex.message + logger.error(traceback.format_exc()) + return reOK + + +def get_rendering_def(image, rsp_dict): + # Try to get user's rendering def + exp_id = image._conn.getUserId() + rdefs = image.getAllRenderingDefs(exp_id) + if len(rdefs) == 0 and image.canAnnotate(): + # try to create our own rendering settings + if load_re(image, rsp_dict): + rdefs = image.getAllRenderingDefs(exp_id) + # otherwise use owners + if len(rdefs) == 0: + owner_id = image.getDetails().getOwner().id + if owner_id != exp_id: + rdefs = image.getAllRenderingDefs(owner_id) + + return rdefs[0] if len(rdefs) > 0 else None + + def imageMarshal(image, key=None, request=None): """ return a dict with pretty much everything we know and care about an image, @@ -313,11 +347,9 @@ def pixel_size_in_microns(method): } ) - exp_id = image._conn.getUserId() - rdefs = image.getAllRenderingDefs(exp_id) - # Assume there is only 1 rdef - # TODO: handle case where user doesn't own any rendering settings (load image owners) - rdef = rdefs[0] + rdef = get_rendering_def(image, rv) + if not rdef: + return rv try: rv["pixel_range"] = getPixelRange(image) @@ -343,25 +375,14 @@ def pixel_size_in_microns(method): "defaultT": 0, "invertAxis": image.isInvertedAxis(), } + except AttributeError: + # Why do we do raise just for this exception?! rv = None raise - reOK = False - try: - reOK = image._prepareRenderingEngine() - if not reOK: - rv["Error"] = "Failed to prepare Rendering Engine for imageMarshal" - logger.debug("Failed to prepare Rendering Engine for imageMarshal") - except omero.ConcurrencyException as ce: - backOff = ce.backOff - rv["ConcurrencyException"] = {"backOff": backOff} - except Exception as ex: # Handle everything else. - rv["Exception"] = ex.message - logger.error(traceback.format_exc()) - # big images - if reOK: + if load_re(image, rv): levels = image._re.getResolutionLevels() tiles = levels > 1 rv["tiles"] = tiles From 0304697504dc501bc7dece6383222fe24f56d4a7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 24 Feb 2024 06:53:03 +0000 Subject: [PATCH 05/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- omeroweb/webgateway/marshal.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/omeroweb/webgateway/marshal.py b/omeroweb/webgateway/marshal.py index bc1a8164db..8ba2851715 100644 --- a/omeroweb/webgateway/marshal.py +++ b/omeroweb/webgateway/marshal.py @@ -64,8 +64,10 @@ def getChannelsNoRe(image): left outer join fetch c.statsInfo where p.id=:id""" pixels = conn.getQueryService().findByQuery(query, params, conn.SERVICE_OPTS) - return [ChannelWrapper(conn, c, idx=n, img=image) - for n, c in enumerate(pixels.iterateChannels())] + return [ + ChannelWrapper(conn, c, idx=n, img=image) + for n, c in enumerate(pixels.iterateChannels()) + ] def eventContextMarshal(event_context): @@ -113,7 +115,8 @@ def getPixelRange(image): PixelsTypeint32: -2147483648, PixelsTypeuint32: 0, PixelsTypefloat: -2147483648, - PixelsTypedouble: -2147483648} + PixelsTypedouble: -2147483648, + } maxVals = { PixelsTypeint8: 127, PixelsTypeuint8: 255, @@ -122,7 +125,8 @@ def getPixelRange(image): PixelsTypeint32: 2147483647, PixelsTypeuint32: 4294967295, PixelsTypefloat: 2147483647, - PixelsTypedouble: 2147483647} + PixelsTypedouble: 2147483647, + } pixtype = image.getPrimaryPixels().getPixelsType().getValue() return [minVals[pixtype], maxVals[pixtype]] @@ -145,7 +149,7 @@ def rdefMarshal(rdef, image, pixel_range): channels = [] - for rdef_ch, channel in zip(rdef['c'], getChannelsNoRe(image)): + for rdef_ch, channel in zip(rdef["c"], getChannelsNoRe(image)): chan = { "emissionWave": channel.getEmissionWave(), @@ -230,7 +234,7 @@ def get_rendering_def(image, rsp_dict): if load_re(image, rsp_dict): rdefs = image.getAllRenderingDefs(exp_id) # otherwise use owners - if len(rdefs) == 0: + if len(rdefs) == 0: owner_id = image.getDetails().getOwner().id if owner_id != exp_id: rdefs = image.getAllRenderingDefs(owner_id) @@ -389,7 +393,9 @@ def pixel_size_in_microns(method): if tiles: width, height = image._re.getTileSize() zoomLevelScaling = image.getZoomLevelScaling() - rv.update({"tile_size": {"width": width, "height": height}, "levels": levels}) + rv.update( + {"tile_size": {"width": width, "height": height}, "levels": levels} + ) if zoomLevelScaling is not None: rv["zoomLevelScaling"] = zoomLevelScaling From c6ebcaf0718d1f3c46a2670e96182bc45ff796e0 Mon Sep 17 00:00:00 2001 From: William Moore Date: Sat, 24 Feb 2024 17:36:28 +0000 Subject: [PATCH 06/14] Don't require canAnnotate() to try create rdef --- omeroweb/webgateway/marshal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/omeroweb/webgateway/marshal.py b/omeroweb/webgateway/marshal.py index 8ba2851715..8becf36bbf 100644 --- a/omeroweb/webgateway/marshal.py +++ b/omeroweb/webgateway/marshal.py @@ -229,7 +229,7 @@ def get_rendering_def(image, rsp_dict): # Try to get user's rendering def exp_id = image._conn.getUserId() rdefs = image.getAllRenderingDefs(exp_id) - if len(rdefs) == 0 and image.canAnnotate(): + if len(rdefs) == 0: # try to create our own rendering settings if load_re(image, rsp_dict): rdefs = image.getAllRenderingDefs(exp_id) From c0011976a8b5f8fda3f80cf13e2f0a2285fbc579 Mon Sep 17 00:00:00 2001 From: William Moore Date: Sat, 24 Feb 2024 18:02:58 +0000 Subject: [PATCH 07/14] flake8 fix --- omeroweb/webgateway/marshal.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/omeroweb/webgateway/marshal.py b/omeroweb/webgateway/marshal.py index 8becf36bbf..a7c0743f43 100644 --- a/omeroweb/webgateway/marshal.py +++ b/omeroweb/webgateway/marshal.py @@ -30,6 +30,7 @@ from omero.model.enums import PixelsTypeuint32, PixelsTypefloat from omero.model.enums import PixelsTypedouble +from omero.gateway import ChannelWrapper from omero.rtypes import unwrap from omero_marshal import get_encoder @@ -41,8 +42,6 @@ # OME model point list regular expression OME_MODEL_POINT_LIST_RE = re.compile(r"([\d.]+),([\d.]+)") -from omero.gateway import ChannelWrapper - def getChannelsNoRe(image): """ From 2030c49645cbd9504166312b6939ba70eafd42d1 Mon Sep 17 00:00:00 2001 From: William Moore Date: Sat, 24 Feb 2024 22:26:35 +0000 Subject: [PATCH 08/14] Don't load RE for preview panel rdef --- omeroweb/webclient/views.py | 4 +++- omeroweb/webgateway/marshal.py | 36 ++------------------------------ omeroweb/webgateway/util.py | 38 ++++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 35 deletions(-) diff --git a/omeroweb/webclient/views.py b/omeroweb/webclient/views.py index 017ca7e328..da3cee7e6e 100755 --- a/omeroweb/webclient/views.py +++ b/omeroweb/webclient/views.py @@ -45,6 +45,7 @@ from omeroweb.version import omeroweb_buildyear as build_year from omeroweb.version import omeroweb_version as omero_version +from omeroweb.webgateway.util import get_rendering_def import omero import omero.scripts @@ -1813,7 +1814,8 @@ def load_metadata_preview(request, c_type, c_id, conn=None, share_id=None, **kwa allRdefs = manager.image.getAllRenderingDefs() rdefs = {} - rdefId = manager.image.getRenderingDefId() + rdef = get_rendering_def(manager.image) + rdefId = rdef["id"] if rdef is not None else None # remove duplicates per user for r in allRdefs: ownerId = r["owner"]["id"] diff --git a/omeroweb/webgateway/marshal.py b/omeroweb/webgateway/marshal.py index a7c0743f43..47485de9a5 100644 --- a/omeroweb/webgateway/marshal.py +++ b/omeroweb/webgateway/marshal.py @@ -22,7 +22,6 @@ import time import re import logging -import traceback from future.utils import isbytes, bytes_to_native_str from omero.model.enums import PixelsTypeint8, PixelsTypeuint8, PixelsTypeint16 @@ -34,6 +33,8 @@ from omero.rtypes import unwrap from omero_marshal import get_encoder +from .util import get_rendering_def, load_re + logger = logging.getLogger(__name__) # OMERO.insight point list regular expression @@ -207,39 +208,6 @@ def channelMarshal(channel): return chan -def load_re(image, rsp_dict=None): - rsp_dict = {} if rsp_dict is None else rsp_dict - reOK = False - try: - reOK = image._prepareRenderingEngine() - if not reOK: - rsp_dict["Error"] = "Failed to prepare Rendering Engine for imageMarshal" - logger.debug("Failed to prepare Rendering Engine for imageMarshal") - except omero.ConcurrencyException as ce: - backOff = ce.backOff - rsp_dict["ConcurrencyException"] = {"backOff": backOff} - except Exception as ex: # Handle everything else. - rsp_dict["Exception"] = ex.message - logger.error(traceback.format_exc()) - return reOK - - -def get_rendering_def(image, rsp_dict): - # Try to get user's rendering def - exp_id = image._conn.getUserId() - rdefs = image.getAllRenderingDefs(exp_id) - if len(rdefs) == 0: - # try to create our own rendering settings - if load_re(image, rsp_dict): - rdefs = image.getAllRenderingDefs(exp_id) - # otherwise use owners - if len(rdefs) == 0: - owner_id = image.getDetails().getOwner().id - if owner_id != exp_id: - rdefs = image.getAllRenderingDefs(owner_id) - - return rdefs[0] if len(rdefs) > 0 else None - def imageMarshal(image, key=None, request=None): """ diff --git a/omeroweb/webgateway/util.py b/omeroweb/webgateway/util.py index 7a3a2468c6..5151b23c3d 100644 --- a/omeroweb/webgateway/util.py +++ b/omeroweb/webgateway/util.py @@ -23,6 +23,9 @@ import shutil import logging +import omero +import traceback + try: import long except ImportError: @@ -238,3 +241,38 @@ def points_string_to_XY_list(string): x, y = xy.split(",") xyList.append((float(x.strip()), float(y.strip()))) return xyList + + +def load_re(image, rsp_dict=None): + rsp_dict = {} if rsp_dict is None else rsp_dict + reOK = False + try: + reOK = image._prepareRenderingEngine() + if not reOK: + # rsp_dict["Error"] = "Failed to prepare Rendering Engine for imageMarshal" + logger.debug("Failed to prepare Rendering Engine for imageMarshal") + except omero.ConcurrencyException as ce: + backOff = ce.backOff + # rsp_dict["ConcurrencyException"] = {"backOff": backOff} + except Exception as ex: # Handle everything else. + # rsp_dict["Exception"] = ex.message + logger.error(traceback.format_exc()) + return reOK + + +def get_rendering_def(image, rsp_dict=None): + # rsp_dict allows errors to be added + # Try to get user's rendering def + exp_id = image._conn.getUserId() + rdefs = image.getAllRenderingDefs(exp_id) + if len(rdefs) == 0: + # try to create our own rendering settings + if load_re(image, rsp_dict): + rdefs = image.getAllRenderingDefs(exp_id) + # otherwise use owners + if len(rdefs) == 0: + owner_id = image.getDetails().getOwner().id + if owner_id != exp_id: + rdefs = image.getAllRenderingDefs(owner_id) + + return rdefs[0] if len(rdefs) > 0 else None From 1f190d3315b0e68135b0e4f791b5668766a55c26 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 24 Feb 2024 22:29:16 +0000 Subject: [PATCH 09/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- omeroweb/webgateway/marshal.py | 1 - 1 file changed, 1 deletion(-) diff --git a/omeroweb/webgateway/marshal.py b/omeroweb/webgateway/marshal.py index 47485de9a5..b341c827f3 100644 --- a/omeroweb/webgateway/marshal.py +++ b/omeroweb/webgateway/marshal.py @@ -208,7 +208,6 @@ def channelMarshal(channel): return chan - def imageMarshal(image, key=None, request=None): """ return a dict with pretty much everything we know and care about an image, From a3cb1875b52a34447e7091232963792d6bede1e9 Mon Sep 17 00:00:00 2001 From: William Moore Date: Sat, 24 Feb 2024 22:51:49 +0000 Subject: [PATCH 10/14] Don't lookup resolution levels for small images --- omeroweb/webgateway/marshal.py | 38 ++++++++++++++++++++-------------- omeroweb/webgateway/util.py | 6 +++--- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/omeroweb/webgateway/marshal.py b/omeroweb/webgateway/marshal.py index b341c827f3..6384746e2b 100644 --- a/omeroweb/webgateway/marshal.py +++ b/omeroweb/webgateway/marshal.py @@ -351,22 +351,28 @@ def pixel_size_in_microns(method): rv = None raise - # big images - if load_re(image, rv): - levels = image._re.getResolutionLevels() - tiles = levels > 1 - rv["tiles"] = tiles - if tiles: - width, height = image._re.getTileSize() - zoomLevelScaling = image.getZoomLevelScaling() - rv.update( - {"tile_size": {"width": width, "height": height}, "levels": levels} - ) - if zoomLevelScaling is not None: - rv["zoomLevelScaling"] = zoomLevelScaling - - if init_zoom < 0: - init_zoom = levels + init_zoom + # If image is big - need to load RE to get resolution levels + # NB: some small images like OME-Zarr can have pyramids + # but these will be ignored + + # TEMP - for A/B testing only use size test if ID is odd number! + if image.id % 1 == 0 and not image.requiresPixelsPyramid(): + rv["tiles"] = False + else: + if load_re(image, rv): + levels = image._re.getResolutionLevels() + tiles = levels > 1 + rv["tiles"] = tiles + if tiles: + width, height = image._re.getTileSize() + zoomLevelScaling = image.getZoomLevelScaling() + rv.update( + {"tile_size": {"width": width, "height": height}, "levels": levels} + ) + if zoomLevelScaling is not None: + rv["zoomLevelScaling"] = zoomLevelScaling + if init_zoom < 0: + init_zoom = levels + init_zoom rv["init_zoom"] = init_zoom if key is not None and rv is not None: diff --git a/omeroweb/webgateway/util.py b/omeroweb/webgateway/util.py index 5151b23c3d..291b89be58 100644 --- a/omeroweb/webgateway/util.py +++ b/omeroweb/webgateway/util.py @@ -249,13 +249,13 @@ def load_re(image, rsp_dict=None): try: reOK = image._prepareRenderingEngine() if not reOK: - # rsp_dict["Error"] = "Failed to prepare Rendering Engine for imageMarshal" + rsp_dict["Error"] = "Failed to prepare Rendering Engine for imageMarshal" logger.debug("Failed to prepare Rendering Engine for imageMarshal") except omero.ConcurrencyException as ce: backOff = ce.backOff - # rsp_dict["ConcurrencyException"] = {"backOff": backOff} + rsp_dict["ConcurrencyException"] = {"backOff": backOff} except Exception as ex: # Handle everything else. - # rsp_dict["Exception"] = ex.message + rsp_dict["Exception"] = ex.message logger.error(traceback.format_exc()) return reOK From 8309b4fb1d5d9d7a1bf0266ad4dab1a778734de4 Mon Sep 17 00:00:00 2001 From: William Moore Date: Mon, 26 Feb 2024 13:43:48 +0000 Subject: [PATCH 11/14] Remove TEMP A/B testing --- omeroweb/webgateway/marshal.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/omeroweb/webgateway/marshal.py b/omeroweb/webgateway/marshal.py index 6384746e2b..8eb7f7d3c0 100644 --- a/omeroweb/webgateway/marshal.py +++ b/omeroweb/webgateway/marshal.py @@ -354,9 +354,7 @@ def pixel_size_in_microns(method): # If image is big - need to load RE to get resolution levels # NB: some small images like OME-Zarr can have pyramids # but these will be ignored - - # TEMP - for A/B testing only use size test if ID is odd number! - if image.id % 1 == 0 and not image.requiresPixelsPyramid(): + if not image.requiresPixelsPyramid(): rv["tiles"] = False else: if load_re(image, rv): From ff4f516671f5f2f03ae2f121076bf8b46d5e4bcd Mon Sep 17 00:00:00 2001 From: William Moore Date: Thu, 21 Mar 2024 11:06:15 +0000 Subject: [PATCH 12/14] Revert changes to rendering engine usage for detecting pyramids --- omeroweb/webgateway/marshal.py | 59 ++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/omeroweb/webgateway/marshal.py b/omeroweb/webgateway/marshal.py index f00129e8f4..18f3e2c2e1 100644 --- a/omeroweb/webgateway/marshal.py +++ b/omeroweb/webgateway/marshal.py @@ -269,6 +269,31 @@ def imageMarshal(image, key=None, request=None): "canLink": image.canLink(), }, } + try: + reOK = image._prepareRenderingEngine() + if not reOK: + logger.debug("Failed to prepare Rendering Engine for imageMarshal") + return rv + except omero.ConcurrencyException as ce: + backOff = ce.backOff + rv = {"ConcurrencyException": {"backOff": backOff}} + return rv + except Exception as ex: # Handle everything else. + rv["Exception"] = ex.message + logger.error(traceback.format_exc()) + return rv # Return what we have already, in case it's useful + + # big images + levels = image._re.getResolutionLevels() + tiles = levels > 1 + rv["tiles"] = tiles + if tiles: + width, height = image._re.getTileSize() + zoomLevelScaling = image.getZoomLevelScaling() + + rv.update({"tile_size": {"width": width, "height": height}, "levels": levels}) + if zoomLevelScaling is not None: + rv["zoomLevelScaling"] = zoomLevelScaling nominalMagnification = ( image.getObjectiveSettings() is not None @@ -276,14 +301,14 @@ def imageMarshal(image, key=None, request=None): or None ) - if nominalMagnification is not None: - rv.update({"nominalMagnification": nominalMagnification}) - try: server_settings = request.session.get("server_settings", {}).get("viewer", {}) except Exception: server_settings = {} init_zoom = server_settings.get("initial_zoom_level", 0) + if init_zoom < 0: + init_zoom = levels + init_zoom + interpolate = server_settings.get("interpolate_pixels", True) try: @@ -315,6 +340,10 @@ def pixel_size_in_microns(method): }, } ) + if init_zoom is not None: + rv["init_zoom"] = init_zoom + if nominalMagnification is not None: + rv.update({"nominalMagnification": nominalMagnification}) rdef = get_rendering_def(image, rv) if not rdef: @@ -344,34 +373,10 @@ def pixel_size_in_microns(method): "defaultT": 0, "invertAxis": image.isInvertedAxis(), } - except AttributeError: # Why do we do raise just for this exception?! rv = None raise - - # If image is big - need to load RE to get resolution levels - # NB: some small images like OME-Zarr can have pyramids - # but these will be ignored - if not image.requiresPixelsPyramid(): - rv["tiles"] = False - else: - if load_re(image, rv): - levels = image._re.getResolutionLevels() - tiles = levels > 1 - rv["tiles"] = tiles - if tiles: - width, height = image._re.getTileSize() - zoomLevelScaling = image.getZoomLevelScaling() - rv.update( - {"tile_size": {"width": width, "height": height}, "levels": levels} - ) - if zoomLevelScaling is not None: - rv["zoomLevelScaling"] = zoomLevelScaling - if init_zoom < 0: - init_zoom = levels + init_zoom - rv["init_zoom"] = init_zoom - if key is not None and rv is not None: for k in key.split("."): rv = rv.get(k, {}) From 069e5c60afeaeca105e5209b14c37c87b49c2342 Mon Sep 17 00:00:00 2001 From: William Moore Date: Thu, 21 Mar 2024 12:38:36 +0000 Subject: [PATCH 13/14] Don't bail if re fails to load - still try to load rdef, marshal channels etc --- omeroweb/webgateway/marshal.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/omeroweb/webgateway/marshal.py b/omeroweb/webgateway/marshal.py index 18f3e2c2e1..2b15e69ee9 100644 --- a/omeroweb/webgateway/marshal.py +++ b/omeroweb/webgateway/marshal.py @@ -22,6 +22,7 @@ import time import re import logging +import traceback from omero.model.enums import PixelsTypeint8, PixelsTypeuint8, PixelsTypeint16 from omero.model.enums import PixelsTypeuint16, PixelsTypeint32 @@ -32,7 +33,7 @@ from omero.rtypes import unwrap from omero_marshal import get_encoder -from .util import get_rendering_def, load_re +from .util import get_rendering_def logger = logging.getLogger(__name__) @@ -269,11 +270,9 @@ def imageMarshal(image, key=None, request=None): "canLink": image.canLink(), }, } + reOK = False try: reOK = image._prepareRenderingEngine() - if not reOK: - logger.debug("Failed to prepare Rendering Engine for imageMarshal") - return rv except omero.ConcurrencyException as ce: backOff = ce.backOff rv = {"ConcurrencyException": {"backOff": backOff}} @@ -284,8 +283,10 @@ def imageMarshal(image, key=None, request=None): return rv # Return what we have already, in case it's useful # big images - levels = image._re.getResolutionLevels() - tiles = levels > 1 + tiles = False + if reOK: + levels = image._re.getResolutionLevels() + tiles = levels > 1 rv["tiles"] = tiles if tiles: width, height = image._re.getTileSize() From 412fa66a52477fb77319a35147313a6f3cf481a2 Mon Sep 17 00:00:00 2001 From: William Moore Date: Fri, 12 Apr 2024 15:23:43 +0100 Subject: [PATCH 14/14] Fix handling of windowMin/Max if 0 compared with None --- omeroweb/webgateway/marshal.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/omeroweb/webgateway/marshal.py b/omeroweb/webgateway/marshal.py index 2b15e69ee9..341ffb99d4 100644 --- a/omeroweb/webgateway/marshal.py +++ b/omeroweb/webgateway/marshal.py @@ -163,9 +163,12 @@ def rdefMarshal(rdef, image, pixel_range): "active": rdef_ch["active"], } + window_min = getWindowMin(channel) + window_max = getWindowMax(channel) + chan["window"] = { - "min": getWindowMin(channel) or pixel_range[0], - "max": getWindowMax(channel) or pixel_range[1], + "min": pixel_range[0] if window_min is None else window_min, + "max": pixel_range[1] if window_max is None else window_max, "start": rdef_ch["start"], "end": rdef_ch["end"], }