🟡Visualize Masks on Server [Python]
This page explains how to visualize the output of masks with JavaScript and Python code, and overlay them onto original and restored images.
This page will soon be updated.
polygon_mask
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
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<!--styles-->
<style>
#holder-img {
position: relative;
}
#holder-img img{
width: 100%;
height: 100%;
max-height: 100vh;
object-fit: contain;
}
#holder-img svg{
position: absolute;
left: -1000px;
right: -1000px;
top: -1000px;
bottom: -1000px;
margin: auto;
height: 100%;
width: auto;
}
</style>
</head>
<body>
<!--image holder-->
<div id="holder-img">
<img src="./image.png" alt="">
</div>
<!--example json data -->
<script type="text/javascript" src="./acne_multipolygons__1_.json"></script>
<script type="text/javascript" src="./pores_multipoints__1_.json"></script>
<script type="text/javascript">
//Create SVG
const svg = document.querySelector('#holder-img').appendChild(document.createElementNS("http://www.w3.org/2000/svg", "svg"));
svg.setAttribute("width",'100%');
svg.setAttribute("height",'100%');
acne=JSON.parse(acne);
pores=JSON.parse(pores);
//Create geometry
generate(acne, svg);
generate(pores, svg);
function generate(DATA, svg) {
//Set viewBox
if (DATA.view_box) {
svg.setAttribute("viewBox", DATA.view_box);
}
DATA.features.forEach(function(d){
d.geometry.coordinates.forEach(function(c){
let object = null;
//Polygon
if (d.geometry.type == 'MultiPolygon') {
object = svg.appendChild(document.createElementNS("http://www.w3.org/2000/svg", "polygon"));
object.setAttribute("points",c[0]);
}
//Point
if (d.geometry.type == 'MultiPoint') {
object = svg.appendChild(document.createElementNS("http://www.w3.org/2000/svg", "circle"));
object.setAttribute("cx",c[0]);
object.setAttribute("cy",c[1]);
object.setAttribute("r",c[2]);
}
//Multiline
if (d.geometry.type == 'MultiLineString') {
object = svg.appendChild(document.createElementNS("http://www.w3.org/2000/svg", "points"));
object.setAttribute("points",c);
}
//intensity
if (d.geometry.intensity) {
object.style.opacity = d.geometry.intensity;
}
//fill
if (DATA.fill) {
object.style.fill = DATA.fill;
}
});
});
}
</script>
</body>
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
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
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()
// Some code
heatmap_mask
heatmap_mask
The visualization is the same as for a polygon_mask
Last updated