|
|
|
import logging
|
|
import numpy as np
|
|
import cv2
|
|
import torch
|
|
|
|
Image = np.ndarray
|
|
Boxes = torch.Tensor
|
|
|
|
|
|
class MatrixVisualizer:
|
|
"""
|
|
Base visualizer for matrix data
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
inplace=True,
|
|
cmap=cv2.COLORMAP_PARULA,
|
|
val_scale=1.0,
|
|
alpha=0.7,
|
|
interp_method_matrix=cv2.INTER_LINEAR,
|
|
interp_method_mask=cv2.INTER_NEAREST,
|
|
):
|
|
self.inplace = inplace
|
|
self.cmap = cmap
|
|
self.val_scale = val_scale
|
|
self.alpha = alpha
|
|
self.interp_method_matrix = interp_method_matrix
|
|
self.interp_method_mask = interp_method_mask
|
|
|
|
def visualize(self, image_bgr, mask, matrix, bbox_xywh):
|
|
self._check_image(image_bgr)
|
|
self._check_mask_matrix(mask, matrix)
|
|
if self.inplace:
|
|
image_target_bgr = image_bgr
|
|
else:
|
|
image_target_bgr = image_bgr
|
|
image_target_bgr *= 0
|
|
x, y, w, h = [int(v) for v in bbox_xywh]
|
|
if w <= 0 or h <= 0:
|
|
return image_bgr
|
|
mask, matrix = self._resize(mask, matrix, w, h)
|
|
mask_bg = np.tile((mask == 0)[:, :, np.newaxis], [1, 1, 3])
|
|
matrix_scaled = matrix.astype(np.float32) * self.val_scale
|
|
_EPSILON = 1e-6
|
|
if np.any(matrix_scaled > 255 + _EPSILON):
|
|
logger = logging.getLogger(__name__)
|
|
logger.warning(
|
|
f"Matrix has values > {255 + _EPSILON} after " f"scaling, clipping to [0..255]"
|
|
)
|
|
matrix_scaled_8u = matrix_scaled.clip(0, 255).astype(np.uint8)
|
|
matrix_vis = cv2.applyColorMap(matrix_scaled_8u, self.cmap)
|
|
matrix_vis[mask_bg] = image_target_bgr[y : y + h, x : x + w, :][mask_bg]
|
|
image_target_bgr[y : y + h, x : x + w, :] = (
|
|
image_target_bgr[y : y + h, x : x + w, :] * (1.0 - self.alpha) + matrix_vis * self.alpha
|
|
)
|
|
return image_target_bgr.astype(np.uint8)
|
|
|
|
def _resize(self, mask, matrix, w, h):
|
|
if (w != mask.shape[1]) or (h != mask.shape[0]):
|
|
mask = cv2.resize(mask, (w, h), self.interp_method_mask)
|
|
if (w != matrix.shape[1]) or (h != matrix.shape[0]):
|
|
matrix = cv2.resize(matrix, (w, h), self.interp_method_matrix)
|
|
return mask, matrix
|
|
|
|
def _check_image(self, image_rgb):
|
|
assert len(image_rgb.shape) == 3
|
|
assert image_rgb.shape[2] == 3
|
|
assert image_rgb.dtype == np.uint8
|
|
|
|
def _check_mask_matrix(self, mask, matrix):
|
|
assert len(matrix.shape) == 2
|
|
assert len(mask.shape) == 2
|
|
assert mask.dtype == np.uint8
|
|
|
|
|
|
class RectangleVisualizer:
|
|
|
|
_COLOR_GREEN = (18, 127, 15)
|
|
|
|
def __init__(self, color=_COLOR_GREEN, thickness=1):
|
|
self.color = color
|
|
self.thickness = thickness
|
|
|
|
def visualize(self, image_bgr, bbox_xywh, color=None, thickness=None):
|
|
x, y, w, h = bbox_xywh
|
|
color = color or self.color
|
|
thickness = thickness or self.thickness
|
|
cv2.rectangle(image_bgr, (int(x), int(y)), (int(x + w), int(y + h)), color, thickness)
|
|
return image_bgr
|
|
|
|
|
|
class PointsVisualizer:
|
|
|
|
_COLOR_GREEN = (18, 127, 15)
|
|
|
|
def __init__(self, color_bgr=_COLOR_GREEN, r=5):
|
|
self.color_bgr = color_bgr
|
|
self.r = r
|
|
|
|
def visualize(self, image_bgr, pts_xy, colors_bgr=None, rs=None):
|
|
for j, pt_xy in enumerate(pts_xy):
|
|
x, y = pt_xy
|
|
color_bgr = colors_bgr[j] if colors_bgr is not None else self.color_bgr
|
|
r = rs[j] if rs is not None else self.r
|
|
cv2.circle(image_bgr, (x, y), r, color_bgr, -1)
|
|
return image_bgr
|
|
|
|
|
|
class TextVisualizer:
|
|
|
|
_COLOR_GRAY = (218, 227, 218)
|
|
_COLOR_WHITE = (255, 255, 255)
|
|
|
|
def __init__(
|
|
self,
|
|
font_face=cv2.FONT_HERSHEY_SIMPLEX,
|
|
font_color_bgr=_COLOR_GRAY,
|
|
font_scale=0.35,
|
|
font_line_type=cv2.LINE_AA,
|
|
font_line_thickness=1,
|
|
fill_color_bgr=_COLOR_WHITE,
|
|
fill_color_transparency=1.0,
|
|
frame_color_bgr=_COLOR_WHITE,
|
|
frame_color_transparency=1.0,
|
|
frame_thickness=1,
|
|
):
|
|
self.font_face = font_face
|
|
self.font_color_bgr = font_color_bgr
|
|
self.font_scale = font_scale
|
|
self.font_line_type = font_line_type
|
|
self.font_line_thickness = font_line_thickness
|
|
self.fill_color_bgr = fill_color_bgr
|
|
self.fill_color_transparency = fill_color_transparency
|
|
self.frame_color_bgr = frame_color_bgr
|
|
self.frame_color_transparency = frame_color_transparency
|
|
self.frame_thickness = frame_thickness
|
|
|
|
def visualize(self, image_bgr, txt, topleft_xy):
|
|
txt_w, txt_h = self.get_text_size_wh(txt)
|
|
topleft_xy = tuple(map(int, topleft_xy))
|
|
x, y = topleft_xy
|
|
if self.frame_color_transparency < 1.0:
|
|
t = self.frame_thickness
|
|
image_bgr[y - t : y + txt_h + t, x - t : x + txt_w + t, :] = (
|
|
image_bgr[y - t : y + txt_h + t, x - t : x + txt_w + t, :]
|
|
* self.frame_color_transparency
|
|
+ np.array(self.frame_color_bgr) * (1.0 - self.frame_color_transparency)
|
|
).astype(float)
|
|
if self.fill_color_transparency < 1.0:
|
|
image_bgr[y : y + txt_h, x : x + txt_w, :] = (
|
|
image_bgr[y : y + txt_h, x : x + txt_w, :] * self.fill_color_transparency
|
|
+ np.array(self.fill_color_bgr) * (1.0 - self.fill_color_transparency)
|
|
).astype(float)
|
|
cv2.putText(
|
|
image_bgr,
|
|
txt,
|
|
topleft_xy,
|
|
self.font_face,
|
|
self.font_scale,
|
|
self.font_color_bgr,
|
|
self.font_line_thickness,
|
|
self.font_line_type,
|
|
)
|
|
return image_bgr
|
|
|
|
def get_text_size_wh(self, txt):
|
|
((txt_w, txt_h), _) = cv2.getTextSize(
|
|
txt, self.font_face, self.font_scale, self.font_line_thickness
|
|
)
|
|
return txt_w, txt_h
|
|
|
|
|
|
class CompoundVisualizer:
|
|
def __init__(self, visualizers):
|
|
self.visualizers = visualizers
|
|
|
|
def visualize(self, image_bgr, data):
|
|
assert len(data) == len(
|
|
self.visualizers
|
|
), "The number of datas {} should match the number of visualizers" " {}".format(
|
|
len(data), len(self.visualizers)
|
|
)
|
|
image = image_bgr
|
|
for i, visualizer in enumerate(self.visualizers):
|
|
image = visualizer.visualize(image, data[i])
|
|
return image
|
|
|
|
def __str__(self):
|
|
visualizer_str = ", ".join([str(v) for v in self.visualizers])
|
|
return "Compound Visualizer [{}]".format(visualizer_str)
|
|
|