|
"""Visualization utility functions.""" |
|
|
|
import colorsys |
|
import random |
|
from typing import List, Optional, Sequence, Tuple |
|
|
|
import numpy as np |
|
|
|
|
|
|
|
def get_colors(num_colors: int) -> List[Tuple[int, int, int]]: |
|
"""Gets colormap for points.""" |
|
colors = [] |
|
for i in np.arange(0.0, 360.0, 360.0 / num_colors): |
|
hue = i / 360.0 |
|
lightness = (50 + np.random.rand() * 10) / 100.0 |
|
saturation = (90 + np.random.rand() * 10) / 100.0 |
|
color = colorsys.hls_to_rgb(hue, lightness, saturation) |
|
colors.append( |
|
(int(color[0] * 255), int(color[1] * 255), int(color[2] * 255)) |
|
) |
|
random.shuffle(colors) |
|
return colors |
|
|
|
|
|
def paint_point_track( |
|
frames: np.ndarray, |
|
point_tracks: np.ndarray, |
|
visibles: np.ndarray, |
|
colormap: Optional[List[Tuple[int, int, int]]] = None, |
|
) -> np.ndarray: |
|
"""Converts a sequence of points to color code video. |
|
|
|
Args: |
|
frames: [num_frames, height, width, 3], np.uint8, [0, 255] |
|
point_tracks: [num_points, num_frames, 2], np.float32, [0, width / height] |
|
visibles: [num_points, num_frames], bool |
|
colormap: colormap for points, each point has a different RGB color. |
|
|
|
Returns: |
|
video: [num_frames, height, width, 3], np.uint8, [0, 255] |
|
""" |
|
num_points, num_frames = point_tracks.shape[0:2] |
|
if colormap is None: |
|
colormap = get_colors(num_colors=num_points) |
|
height, width = frames.shape[1:3] |
|
dot_size_as_fraction_of_min_edge = 0.015 |
|
radius = int(round(min(height, width) * dot_size_as_fraction_of_min_edge)) |
|
diam = radius * 2 + 1 |
|
quadratic_y = np.square(np.arange(diam)[:, np.newaxis] - radius - 1) |
|
quadratic_x = np.square(np.arange(diam)[np.newaxis, :] - radius - 1) |
|
icon = (quadratic_y + quadratic_x) - (radius**2) / 2.0 |
|
sharpness = 0.15 |
|
icon = np.clip(icon / (radius * 2 * sharpness), 0, 1) |
|
icon = 1 - icon[:, :, np.newaxis] |
|
icon1 = np.pad(icon, [(0, 1), (0, 1), (0, 0)]) |
|
icon2 = np.pad(icon, [(1, 0), (0, 1), (0, 0)]) |
|
icon3 = np.pad(icon, [(0, 1), (1, 0), (0, 0)]) |
|
icon4 = np.pad(icon, [(1, 0), (1, 0), (0, 0)]) |
|
|
|
video = frames.copy() |
|
for t in range(num_frames): |
|
|
|
image = np.pad( |
|
video[t], |
|
[ |
|
(radius + 1, radius + 1), |
|
(radius + 1, radius + 1), |
|
(0, 0), |
|
], |
|
) |
|
for i in range(num_points): |
|
|
|
|
|
|
|
|
|
|
|
x, y = point_tracks[i, t, :] + 0.5 |
|
x = min(max(x, 0.0), width) |
|
y = min(max(y, 0.0), height) |
|
|
|
if visibles[i, t]: |
|
x1, y1 = np.floor(x).astype(np.int32), np.floor(y).astype(np.int32) |
|
x2, y2 = x1 + 1, y1 + 1 |
|
|
|
|
|
patch = ( |
|
icon1 * (x2 - x) * (y2 - y) |
|
+ icon2 * (x2 - x) * (y - y1) |
|
+ icon3 * (x - x1) * (y2 - y) |
|
+ icon4 * (x - x1) * (y - y1) |
|
) |
|
x_ub = x1 + 2 * radius + 2 |
|
y_ub = y1 + 2 * radius + 2 |
|
image[y1:y_ub, x1:x_ub, :] = (1 - patch) * image[ |
|
y1:y_ub, x1:x_ub, : |
|
] + patch * np.array(colormap[i])[np.newaxis, np.newaxis, :] |
|
|
|
|
|
video[t] = image[ |
|
radius + 1 : -radius - 1, radius + 1 : -radius - 1 |
|
].astype(np.uint8) |
|
return video |
|
|
|
|