Skip to content

preview_widget

anomaly_match_ui.widgets.preview_widget

PreviewWidget: image viewer with transform controls for the detail screen.

PreviewWidget()

Image viewer with transform controls.

Used by :class:ImageDetailScreen to display a single image loaded from the prediction cache (via display_standalone).

Source code in anomaly_match_ui/widgets/preview_widget.py
def __init__(self):
    self.num_channels = BackendInterface.get_num_channels()

    self.filename_text = widgets.HTML(
        value="",
        layout=widgets.Layout(background_color="black"),
        style={"color": "white"},
    )

    self.image_widget = widgets.Image(
        value=b"",
        width=600,
        height=600,
        layout=widgets.Layout(background_color="black"),
    )

    self.widget = VBox(
        [self.filename_text, self.image_widget],
        layout=widgets.Layout(background_color="black"),
    )

    # Transform states
    self.invert = False
    self.brightness = 1.0
    self.contrast = 1.0
    self.unsharp_mask_applied = False

    # Channel visibility.  display_image_normalisation always collapses to a
    # 3-channel RGB image (picking rgb_mapping for >3-band sources), so the
    # R/G/B toggles act on those three displayed channels regardless of the
    # source's band count.
    self.show_r = True
    self.show_g = True
    self.show_b = True
    self.rgb_mapping = [0, 1, 2] if self.num_channels >= 3 else list(range(self.num_channels))

    # Image data
    self.original_image = None
    self.modified_image = None

display_standalone(image_array, filename, score)

Display an image directly without going through BackendInterface.

Used by the image detail screen to show a single image loaded from the prediction cache. Current display transforms (invert, brightness, contrast, unsharp, channel visibility) are re-applied so that a re-decode triggered by changing upstream normalisation doesn't desync from the toolbar's toggle state — without this, clicking Invert then changing the channel combination would drop the inversion while leaving the user with a button that has to be clicked twice to re-invert.

Parameters:

Name Type Description Default
image_array

Numpy array (HWC, uint8) of the image.

required
filename

The source filename for the label.

required
score

The anomaly score for the label.

required
Source code in anomaly_match_ui/widgets/preview_widget.py
def display_standalone(self, image_array, filename, score):
    """Display an image directly without going through BackendInterface.

    Used by the image detail screen to show a single image loaded
    from the prediction cache.  Current display transforms (invert,
    brightness, contrast, unsharp, channel visibility) are re-applied
    so that a re-decode triggered by changing upstream normalisation
    doesn't desync from the toolbar's toggle state — without this,
    clicking Invert then changing the channel combination would drop
    the inversion while leaving the user with a button that has to
    be clicked twice to re-invert.

    Args:
        image_array: Numpy array (HWC, uint8) of the image.
        filename: The source filename for the label.
        score: The anomaly score for the label.
    """
    rgb_map = self.rgb_mapping if self.num_channels > 3 else None
    self.original_image = display_image_normalisation(image_array, rgb_mapping=rgb_map)
    short = os.path.basename(filename) if filename else ""
    self.filename_text.value = (
        f'<span style="color:white;">Name: {short}</span>'
        f'<span style="float:right; color:white;">Score: {score:.4f}</span>'
    )
    self._apply_transforms_and_display()

restore()

Restore the current image to its original state.

Source code in anomaly_match_ui/widgets/preview_widget.py
def restore(self):
    """Restore the current image to its original state."""
    self.invert = False
    self.brightness = 1.0
    self.contrast = 1.0
    self.unsharp_mask_applied = False
    self.show_r = True
    self.show_g = True
    self.show_b = True
    self.modified_image = self.original_image
    # Skip the redraw on a fresh screen where no image has been
    # loaded yet — ``display_standalone`` will paint the real image
    # immediately afterwards.
    if self.original_image is not None:
        self._display_image(self.modified_image)

toggle_invert()

Toggle colour inversion.

Source code in anomaly_match_ui/widgets/preview_widget.py
def toggle_invert(self):
    """Toggle colour inversion."""
    self.invert = not self.invert
    self._apply_transforms_and_display()

toggle_unsharp_mask()

Toggle unsharp-mask sharpening.

Source code in anomaly_match_ui/widgets/preview_widget.py
def toggle_unsharp_mask(self):
    """Toggle unsharp-mask sharpening."""
    self.unsharp_mask_applied = not self.unsharp_mask_applied
    self._apply_transforms_and_display()

set_brightness(value)

Set brightness and update display.

Source code in anomaly_match_ui/widgets/preview_widget.py
def set_brightness(self, value):
    """Set brightness and update display."""
    self.brightness = value
    self._apply_transforms_and_display()

set_contrast(value)

Set contrast and update display.

Source code in anomaly_match_ui/widgets/preview_widget.py
def set_contrast(self, value):
    """Set contrast and update display."""
    self.contrast = value
    self._apply_transforms_and_display()

set_rgb_channels(r=None, g=None, b=None)

Set RGB channel visibility.

Source code in anomaly_match_ui/widgets/preview_widget.py
def set_rgb_channels(self, r=None, g=None, b=None):
    """Set RGB channel visibility."""
    if r is not None:
        self.show_r = r
    if g is not None:
        self.show_g = g
    if b is not None:
        self.show_b = b
    self._apply_transforms_and_display()