Source code for mantidimaging.gui.widgets.bad_data_overlay.bad_data_overlay

# Copyright (C) 2024 ISIS Rutherford Appleton Laboratory UKRI
# SPDX - License - Identifier: GPL-3.0-or-later
from __future__ import annotations

from typing import Callable, List, Optional, Tuple

import numpy as np
from pyqtgraph import ColorMap, ImageItem, ViewBox

from mantidimaging.gui.widgets.indicator_icon.view import IndicatorIconView
from mantidimaging.core.utility import finder

OVERLAY_COLOUR_NAN = [255, 0, 0, 255]
OVERLAY_COLOUR_NONPOSITVE = [255, 192, 0, 255]
OVERLAY_COLOUR_MESSAGE = [255, 0, 0, 255]


[docs] class BadDataCheck: check_function: Callable[[np.ndarray], np.ndarray] indicator: IndicatorIconView def __init__(self, check_function, indicator, overlay, color): self.check_function = check_function self.indicator = indicator self.overlay = overlay self.color = color self.setup_overlay() self.indicator.connected_overlay = self.overlay
[docs] def do_check(self, data): bad_data = self.check_function(data) any_bad = bad_data.any() # cast any_bad to python bool to prevent DeprecationWarning self.indicator.setVisible(bool(any_bad)) self.overlay.setImage(bad_data, autoLevels=False)
[docs] def setup_overlay(self): color = np.array([[0, 0, 0, 0], self.color], dtype=np.ubyte) color_map = ColorMap([0, 1], color) self.overlay.setVisible(False) lut = color_map.getLookupTable(0, 1, 2) self.overlay.setLookupTable(lut) self.overlay.setZValue(11) self.overlay.setLevels([0, 1])
[docs] def remove(self): self.overlay.getViewBox().removeItem(self.indicator) self.overlay.getViewBox().removeItem(self.overlay) self.overlay.clear()
[docs] def clear(self): self.indicator.setVisible(False) self.overlay.clear()
[docs] class BadDataOverlay: """ Mixin class to be used with MIImageView and MIMiniImageView """ def __init__(self) -> None: super().__init__() self.enabled_checks: dict[str, BadDataCheck] = {} self.message_indicator: IndicatorIconView | None = None if hasattr(self, "sigTimeChanged"): self.sigTimeChanged.connect(self.check_for_bad_data) @property def image_item(self) -> ImageItem: raise NotImplementedError @property def viewbox(self) -> ViewBox: raise NotImplementedError
[docs] def enable_nan_check(self, enable: bool = True, actions: Optional[List[Tuple[str, Callable]]] = None): if enable: self.enable_check("nan", OVERLAY_COLOUR_NAN, 0, np.isnan, "Invalid values: Not a number", actions) else: self.disable_check("nan")
[docs] def enable_nonpositive_check(self, enable: bool = True, actions: Optional[List[Tuple[str, Callable]]] = None): if enable: def is_non_positive(data): return data <= 0 self.enable_check("nonpos", OVERLAY_COLOUR_NONPOSITVE, 1, is_non_positive, "Non-positive values", actions) else: self.disable_check("nonpos")
[docs] def enable_check(self, name: str, color: List[int], pos: int, func: Callable, message: str, actions: Optional[List[Tuple[str, Callable]]]): if name not in self.enabled_checks: icon_path = finder.ROOT_PATH + "/gui/ui/images/exclamation-triangle-red.png" indicator = IndicatorIconView(self.viewbox, icon_path, pos, color, message) if actions is not None: indicator.add_actions(actions) overlay = ImageItem() self.viewbox.addItem(overlay) check = BadDataCheck(func, indicator, overlay, color) self.enabled_checks[name] = check self.check_for_bad_data()
[docs] def disable_check(self, name: str): if name in self.enabled_checks: self.enabled_checks[name].remove() self.enabled_checks.pop(name, None)
def _get_current_slice(self) -> Optional[np.ndarray]: data = self.image_item.image return data
[docs] def check_for_bad_data(self): current_slice = self._get_current_slice() if current_slice is not None: for test in self.enabled_checks.values(): test.do_check(current_slice)
[docs] def clear_overlays(self): for check in self.enabled_checks.values(): check.clear()
[docs] def enable_message(self, enable: bool = True): if enable: icon_path = finder.ROOT_PATH + "/gui/ui/images/exclamation-triangle-red.png" self.message_indicator = IndicatorIconView(self.viewbox, icon_path, 0, OVERLAY_COLOUR_MESSAGE, "") self.message_indicator.setVisible(False) else: self.message_indicator = None
[docs] def show_message(self, message: str | None) -> None: if self.message_indicator is None: return if message: self.message_indicator.set_message(message) self.message_indicator.setVisible(True) else: self.message_indicator.setVisible(False)