Spaces:
Runtime error
Runtime error
# Copyright (c) OpenMMLab. All rights reserved. | |
from typing import Dict, List, Optional, Tuple, Union | |
import mmcv | |
import numpy as np | |
try: | |
import seaborn as sns | |
except ImportError: | |
sns = None | |
from mmdet.registry import VISUALIZERS | |
from mmdet.structures import DetDataSample | |
from mmdet.structures.mask import bitmap_to_polygon | |
from mmdet.visualization.palette import _get_adaptive_scales | |
from mmengine.dist import master_only | |
from mmengine.structures import InstanceData, PixelData | |
from mmengine.visualization import Visualizer | |
def random_color(seed): | |
"""Random a color according to the input seed.""" | |
if sns is None: | |
raise RuntimeError( | |
"motmetrics is not installed,\ | |
please install it by: pip install seaborn" | |
) | |
np.random.seed(seed) | |
colors = sns.color_palette("tab20") | |
color = colors[np.random.choice(range(len(colors)))] | |
color = tuple([int(255 * c) for c in color]) | |
return color | |
class MasaTrackLocalVisualizer(Visualizer): | |
"""Tracking Local Visualizer for the MOT, VIS tasks. | |
Args: | |
name (str): Name of the instance. Defaults to 'visualizer'. | |
image (np.ndarray, optional): the origin image to draw. The format | |
should be RGB. Defaults to None. | |
vis_backends (list, optional): Visual backend config list. | |
Defaults to None. | |
save_dir (str, optional): Save file dir for all storage backends. | |
If it is None, the backend storage will not save any data. | |
line_width (int, float): The linewidth of lines. | |
Defaults to 3. | |
alpha (int, float): The transparency of bboxes or mask. | |
Defaults to 0.8. | |
""" | |
def __init__( | |
self, | |
name: str = "visualizer", | |
image: Optional[np.ndarray] = None, | |
vis_backends: Optional[Dict] = None, | |
save_dir: Optional[str] = None, | |
line_width: Union[int, float] = 5, | |
alpha: float = 0.8, | |
show_polygon: bool = False, | |
texts=None, | |
) -> None: | |
super().__init__(name, image, vis_backends, save_dir) | |
self.line_width = line_width | |
self.alpha = alpha | |
self.show_polygon = show_polygon | |
# Set default value. When calling | |
# `TrackLocalVisualizer().dataset_meta=xxx`, | |
# it will override the default value. | |
self.dataset_meta = {} | |
if texts is not None: | |
if isinstance(texts, str): | |
if not texts.endswith("."): | |
original_caption = texts + " . " | |
original_caption = original_caption.split(" . ") | |
class_names = list(filter(lambda x: len(x) > 0, original_caption)) | |
else: | |
class_names = list(texts) | |
self.label_names = class_names | |
else: | |
self.label_names = None | |
def _draw_instances( | |
self, image: np.ndarray, instances: ["InstanceData"] | |
) -> np.ndarray: | |
"""Draw instances of GT or prediction. | |
Args: | |
image (np.ndarray): The image to draw. | |
instances (:obj:`InstanceData`): Data structure for | |
instance-level annotations or predictions. | |
Returns: | |
np.ndarray: the drawn image which channel is RGB. | |
""" | |
self.set_image(image) | |
classes = self.dataset_meta.get("classes", None) | |
# get colors and texts | |
# for the MOT and VIS tasks | |
colors = [random_color(_id) for _id in instances.instances_id] | |
# draw bboxes and texts | |
if "bboxes" in instances: | |
# draw bboxes | |
bboxes = instances.bboxes.clone() | |
labels = instances.labels.clone() | |
self.draw_bboxes( | |
bboxes, | |
edge_colors=colors, | |
alpha=self.alpha, | |
line_widths=self.line_width, | |
) | |
# draw texts | |
positions = bboxes[:, :2] - self.line_width | |
areas = (bboxes[:, 3] - bboxes[:, 1]) * (bboxes[:, 2] - bboxes[:, 0]) | |
scales = _get_adaptive_scales(areas.cpu().numpy()) | |
for i, (pos, label) in enumerate(zip(positions, labels)): | |
if self.label_names is not None: | |
label_text = self.label_names[label] | |
else: | |
label_text = ( | |
classes[label] if classes is not None else f"class {label}" | |
) | |
if "instances_id" in instances: | |
label_text += f" | {instances.instances_id[i]}" | |
if "scores" in instances: | |
score = round(float(instances.scores[i]) * 100, 1) | |
label_text += f": {score}" | |
self.draw_texts( | |
label_text, | |
pos, | |
colors="black", | |
font_sizes=int(13 * scales[i]), | |
bboxes=[ | |
{ | |
"facecolor": [c / 255 for c in colors[i]], | |
"alpha": 0.8, | |
"pad": 0.7, | |
"edgecolor": "none", | |
} | |
], | |
) | |
# draw masks | |
if "masks" in instances: | |
masks = instances.masks | |
polygons = [] | |
for i, mask in enumerate(masks): | |
contours, _ = bitmap_to_polygon(mask) | |
polygons.extend(contours) | |
if self.show_polygon: | |
self.draw_polygons(polygons, edge_colors="w", alpha=self.alpha) | |
self.draw_binary_masks(masks, colors=colors, alphas=self.alpha) | |
return self.get_image() | |
def add_datasample( | |
self, | |
name: str, | |
image: np.ndarray, | |
data_sample: DetDataSample = None, | |
draw_gt: bool = True, | |
draw_pred: bool = True, | |
show: bool = False, | |
wait_time: int = 0, | |
# TODO: Supported in mmengine's Viusalizer. | |
out_file: Optional[str] = None, | |
pred_score_thr: float = 0.3, | |
vis_score=False, | |
step: int = 0, | |
fps=None, | |
) -> None: | |
"""Draw datasample and save to all backends. | |
- If GT and prediction are plotted at the same time, they are | |
displayed in a stitched image where the left image is the | |
ground truth and the right image is the prediction. | |
- If ``show`` is True, all storage backends are ignored, and | |
the images will be displayed in a local window. | |
- If ``out_file`` is specified, the drawn image will be | |
saved to ``out_file``. t is usually used when the display | |
is not available. | |
Args: | |
name (str): The image identifier. | |
image (np.ndarray): The image to draw. | |
data_sample (OptTrackSampleList): A data | |
sample that contain annotations and predictions. | |
Defaults to None. | |
draw_gt (bool): Whether to draw GT TrackDataSample. | |
Default to True. | |
draw_pred (bool): Whether to draw Prediction TrackDataSample. | |
Defaults to True. | |
show (bool): Whether to display the drawn image. Default to False. | |
wait_time (int): The interval of show (s). Defaults to 0. | |
out_file (str): Path to output file. Defaults to None. | |
pred_score_thr (float): The threshold to visualize the bboxes | |
and masks. Defaults to 0.3. | |
step (int): Global step value to record. Defaults to 0. | |
""" | |
gt_img_data = None | |
pred_img_data = None | |
if data_sample is not None: | |
data_sample = data_sample.cpu() | |
if draw_gt and data_sample is not None: | |
assert "gt_instances" in data_sample | |
gt_img_data = self._draw_instances(image, data_sample.gt_instances) | |
if draw_pred and data_sample is not None: | |
assert "pred_track_instances" in data_sample | |
pred_instances = data_sample.pred_track_instances | |
if "scores" in pred_instances: | |
pred_instances = pred_instances[ | |
pred_instances.scores > pred_score_thr | |
].cpu() | |
pred_img_data = self._draw_instances(image, pred_instances) | |
if fps is not None: | |
self.draw_texts( | |
f"FPS: {fps: .1f}", | |
np.array([10, 10]), | |
colors="black", | |
font_sizes=15, | |
bboxes=[ | |
{"facecolor": "w", "alpha": 0.8, "pad": 0.7, "edgecolor": "none"} | |
], | |
) | |
if gt_img_data is not None and pred_img_data is not None: | |
drawn_img = np.concatenate((gt_img_data, pred_img_data), axis=1) | |
elif gt_img_data is not None: | |
drawn_img = gt_img_data | |
else: | |
drawn_img = pred_img_data | |
if show: | |
self.show(drawn_img, win_name=name, wait_time=wait_time) | |
if out_file is not None: | |
mmcv.imwrite(drawn_img[..., ::-1], out_file) | |
else: | |
self.add_image(name, drawn_img, step) | |