Mask visualization

This page explains how to visualize the output of masks with JavaScript and Python code, and overlay them onto original and restored images.

polygon_mask

An example of code converting a vectorized mask with "mask_type": "polygon_mask" to a raster image in Python and to an SVG in JavaScript

import numpy as np
from typing import Tuple, Union
from skimage.draw import polygon
from matplotlib.colors import hex2color


def view_box_to_shape(view_box: str) -> Tuple[int, int]:
    """Supplementary method to convert viewBox to image shape.

    :param view_box: str, view_box
    :return: tuple, image shape, (height, width)
    """
    view_box = [int(elem) for elem in view_box.split(" ")]
    return view_box[3] - view_box[1], view_box[2] - view_box[0]


def draw_multipolygons(vector_mask):
    """Function to rasterize vector polygon mask

    :return: np.ndarray, np.float32 2D mask with values in range [0, 1]
    """
    img_shape = view_box_to_shape(vector_mask["view_box"])
    canvas = np.zeros(img_shape, dtype=np.uint8)
    for feature in vector_mask["features"]:
        intensity = (
            np.uint8(feature["properties"]["intensity"] * 255)
            if feature["properties"]["intensity"]
            else 255
        )
        for polygon_object in feature["geometry"]["coordinates"]:
            external_contours = np.array(polygon_object[0])
            rr, cc = polygon(
                external_contours[:, 1], external_contours[:, 0], canvas.shape
            )
            canvas[rr, cc] += intensity
            if len(polygon_object) > 1:
                for contour in polygon_object[1:]:
                    internal_contour = np.array(contour)
                    rr, cc = polygon(
                        internal_contour[:, 1], internal_contour[:, 0], canvas.shape
                    )
                    canvas[rr, cc] -= intensity
        canvas = np.clip(canvas, 0, 255)
    return canvas

An example of code overlaying a vectorized mask with an input image

import matplotlib.pyplot as plt
import cv2

def color_hex2rgb(hex_color: str) -> tuple:
    """Function to convert color from HEX to RGB"""
    return tuple([int(255 * value) for value in hex2color(hex_color)])

def overlay_mask(
    image: np.ndarray, vector_mask: dict, fill=None, opacity=0.8
) -> np.ndarray:

    """Method to overlay vector mask on given image

    :param image: np.ndarray, RGB / RGB+A image, np.uint8
    :param vector_mask: dict containing vector masks
    :param fill: fill color in HEX format.
        Example: '#ffffff' for white color. If left `None`,
        default color #2f63d9 is used. Default is `None`
    :param opacity: opacity of mask
    :return: overlaid_image, np.ndarray, RGB / RGB+A image, np.uint8
    """

    image_rgb = image[:, :, :3]

    if vector_mask['mask_type'] in ['polygon_mask', 'heatmap_mask']:
        mask = draw_multipolygons(vector_mask)
    else:
        mask = draw_multipoints(vector_mask)

    if fill is None:
        if vector_mask["fill"] is None:
            fill = "#2f63d9"
        else:
            fill = vector_mask["fill"]

    rgb_color = color_hex2rgb(fill)

    canvas = image.copy().astype(np.float32)
    for channel in range(len(rgb_color)):
        channel_intensity = rgb_color[channel]
        canvas[:, :, channel] -= canvas[:, :, channel] * opacity * mask
        canvas[:, :, channel] += mask * opacity * channel_intensity
    return canvas.clip(0,255).astype(np.uint8)

img = cv2.imread('test_img.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.imshow(overlay_mask(img,vector_mask))
plt.show()

Input image Overlaid image

A file with an example mask is attached:

point_mask

Code to convert a vectorized mask with "mask_type": "point_mask" to a raster image

def draw_multipoints(vector_mask: dict) -> np.ndarray:
    """Function to rasterize vector points mask

    :param vector_mask: dict describing a mask
    :return: np.ndarray, np.float32 2D mask with values in range [0, 1]
    """
    img_shape = view_box_to_shape(vector_mask["view_box"])
    canvas = np.zeros(img_shape, dtype=np.uint8)
    for feature in vector_mask['features']:
        intensity = (
            int(feature['properties']['intensity'] * 255)
            if feature['properties']['intensity']
            else 255
        )
        for point_object in feature['geometry']['coordinates']:
            canvas = cv2.circle(
                canvas,
                tuple(point_object[:2]),
                point_object[2],
                color=intensity,
                thickness=-1,
            )
    return canvas.astype(np.uint8)
    
img = cv2.imread('test_img.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.imshow(overlay_mask(img,vector_mask))
plt.show()

polyline_mask

Code to convert a vectorized mask with "mask_type": "polyline_mask" to a raster image

Please pay attention to the lines; they should have a defined line width to be visible, so there is one additional parameter for the function: line_thickness_pixels

  • For Python, this parameter is expected to be an integer number >= 1, and the value would be treated as a line width in pixels (as it returns a raster image)

  • For JavaScript htis parameter is expected to be a ...

def draw_multipolylines(  # noqa
    vector_mask: VectorMask, line_thickness_pixels: int = 1
) -> np.ndarray:
    """Function to rasterize vector polylines mask

    :param vector_mask: VectorMask, see `haut_ai.dev.utils.ds.algorithm_base`
    :param line_thickness_pixels: int, default value for visualization of lines
    :return: np.ndarray, np.float32 2D mask with values in range [0, 1]
    """
    line_thickness_pixels = int(max(1, line_thickness_pixels))

    img_shape = view_box_to_shape(vector_mask.view_box)
    canvas = np.zeros(img_shape, dtype=np.uint8)
    for feature in vector_mask.features:
        intensity = (
            int(feature.properties.intensity * 255)
            if feature.properties.intensity
            else 255
        )
        for polyline_object in feature.geometry.coordinates:
            for point_1, point_2 in zip(polyline_object[:-1], polyline_object[1:]):
                canvas = cv2.line(
                    canvas,
                    tuple(point_1),
                    tuple(point_2),
                    intensity,
                    thickness=line_thickness_pixels,
                )

    return canvas.astype(np.uint8)

img = cv2.imread('test_img.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.imshow(overlay_mask(img,vector_mask))
plt.show()

heatmap_mask

The visualization is the same as for a polygon_mask

Last updated