| | |
| | from typing import Dict, List, Optional, Tuple, Union |
| |
|
| | import cv2 |
| | import mmcv |
| | import numpy as np |
| |
|
| | try: |
| | import seaborn as sns |
| | except ImportError: |
| | sns = None |
| | import torch |
| | from mmengine.dist import master_only |
| | from mmengine.structures import InstanceData, PixelData |
| | from mmengine.visualization import Visualizer |
| |
|
| | from ..evaluation import INSTANCE_OFFSET |
| | from ..registry import VISUALIZERS |
| | from ..structures import DetDataSample |
| | from ..structures.mask import BitmapMasks, PolygonMasks, bitmap_to_polygon |
| | from .palette import _get_adaptive_scales, get_palette, jitter_color |
| |
|
| |
|
| | @VISUALIZERS.register_module() |
| | class DetLocalVisualizer(Visualizer): |
| | """MMDetection Local Visualizer. |
| | |
| | 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. |
| | bbox_color (str, tuple(int), optional): Color of bbox lines. |
| | The tuple of color should be in BGR order. Defaults to None. |
| | text_color (str, tuple(int), optional): Color of texts. |
| | The tuple of color should be in BGR order. |
| | Defaults to (200, 200, 200). |
| | mask_color (str, tuple(int), optional): Color of masks. |
| | The tuple of color should be in BGR order. |
| | Defaults to None. |
| | line_width (int, float): The linewidth of lines. |
| | Defaults to 3. |
| | alpha (int, float): The transparency of bboxes or mask. |
| | Defaults to 0.8. |
| | |
| | Examples: |
| | >>> import numpy as np |
| | >>> import torch |
| | >>> from mmengine.structures import InstanceData |
| | >>> from mmdet.structures import DetDataSample |
| | >>> from mmdet.visualization import DetLocalVisualizer |
| | |
| | >>> det_local_visualizer = DetLocalVisualizer() |
| | >>> image = np.random.randint(0, 256, |
| | ... size=(10, 12, 3)).astype('uint8') |
| | >>> gt_instances = InstanceData() |
| | >>> gt_instances.bboxes = torch.Tensor([[1, 2, 2, 5]]) |
| | >>> gt_instances.labels = torch.randint(0, 2, (1,)) |
| | >>> gt_det_data_sample = DetDataSample() |
| | >>> gt_det_data_sample.gt_instances = gt_instances |
| | >>> det_local_visualizer.add_datasample('image', image, |
| | ... gt_det_data_sample) |
| | >>> det_local_visualizer.add_datasample( |
| | ... 'image', image, gt_det_data_sample, |
| | ... out_file='out_file.jpg') |
| | >>> det_local_visualizer.add_datasample( |
| | ... 'image', image, gt_det_data_sample, |
| | ... show=True) |
| | >>> pred_instances = InstanceData() |
| | >>> pred_instances.bboxes = torch.Tensor([[2, 4, 4, 8]]) |
| | >>> pred_instances.labels = torch.randint(0, 2, (1,)) |
| | >>> pred_det_data_sample = DetDataSample() |
| | >>> pred_det_data_sample.pred_instances = pred_instances |
| | >>> det_local_visualizer.add_datasample('image', image, |
| | ... gt_det_data_sample, |
| | ... pred_det_data_sample) |
| | """ |
| |
|
| | def __init__(self, |
| | name: str = 'visualizer', |
| | image: Optional[np.ndarray] = None, |
| | vis_backends: Optional[Dict] = None, |
| | save_dir: Optional[str] = None, |
| | bbox_color: Optional[Union[str, Tuple[int]]] = None, |
| | text_color: Optional[Union[str, |
| | Tuple[int]]] = (200, 200, 200), |
| | mask_color: Optional[Union[str, Tuple[int]]] = None, |
| | line_width: Union[int, float] = 3, |
| | alpha: float = 0.8) -> None: |
| | super().__init__( |
| | name=name, |
| | image=image, |
| | vis_backends=vis_backends, |
| | save_dir=save_dir) |
| | self.bbox_color = bbox_color |
| | self.text_color = text_color |
| | self.mask_color = mask_color |
| | self.line_width = line_width |
| | self.alpha = alpha |
| | |
| | |
| | |
| | self.dataset_meta = {} |
| |
|
| | def _draw_instances(self, image: np.ndarray, instances: ['InstanceData'], |
| | classes: Optional[List[str]], |
| | palette: Optional[List[tuple]]) -> 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. |
| | classes (List[str], optional): Category information. |
| | palette (List[tuple], optional): Palette information |
| | corresponding to the category. |
| | |
| | Returns: |
| | np.ndarray: the drawn image which channel is RGB. |
| | """ |
| | self.set_image(image) |
| |
|
| | if 'bboxes' in instances and instances.bboxes.sum() > 0: |
| | bboxes = instances.bboxes |
| | labels = instances.labels |
| |
|
| | max_label = int(max(labels) if len(labels) > 0 else 0) |
| | text_palette = get_palette(self.text_color, max_label + 1) |
| | text_colors = [text_palette[label] for label in labels] |
| |
|
| | bbox_color = palette if self.bbox_color is None \ |
| | else self.bbox_color |
| | bbox_palette = get_palette(bbox_color, max_label + 1) |
| | colors = [bbox_palette[label] for label in labels] |
| | self.draw_bboxes( |
| | bboxes, |
| | edge_colors=colors, |
| | alpha=self.alpha, |
| | line_widths=self.line_width) |
| |
|
| | positions = bboxes[:, :2] + self.line_width |
| | areas = (bboxes[:, 3] - bboxes[:, 1]) * ( |
| | bboxes[:, 2] - bboxes[:, 0]) |
| | scales = _get_adaptive_scales(areas) |
| |
|
| | for i, (pos, label) in enumerate(zip(positions, labels)): |
| | if 'label_names' in instances: |
| | label_text = instances.label_names[i] |
| | else: |
| | label_text = classes[ |
| | label] if classes is not None else f'class {label}' |
| | if 'scores' in instances: |
| | score = round(float(instances.scores[i]) * 100, 1) |
| | label_text += f': {score}' |
| |
|
| | self.draw_texts( |
| | label_text, |
| | pos, |
| | colors=text_colors[i], |
| | font_sizes=int(13 * scales[i]), |
| | bboxes=[{ |
| | 'facecolor': 'black', |
| | 'alpha': 0.8, |
| | 'pad': 0.7, |
| | 'edgecolor': 'none' |
| | }]) |
| |
|
| | if 'masks' in instances: |
| | labels = instances.labels |
| | masks = instances.masks |
| | if isinstance(masks, torch.Tensor): |
| | masks = masks.numpy() |
| | elif isinstance(masks, (PolygonMasks, BitmapMasks)): |
| | masks = masks.to_ndarray() |
| |
|
| | masks = masks.astype(bool) |
| |
|
| | max_label = int(max(labels) if len(labels) > 0 else 0) |
| | mask_color = palette if self.mask_color is None \ |
| | else self.mask_color |
| | mask_palette = get_palette(mask_color, max_label + 1) |
| | colors = [jitter_color(mask_palette[label]) for label in labels] |
| | text_palette = get_palette(self.text_color, max_label + 1) |
| | text_colors = [text_palette[label] for label in labels] |
| |
|
| | polygons = [] |
| | for i, mask in enumerate(masks): |
| | contours, _ = bitmap_to_polygon(mask) |
| | polygons.extend(contours) |
| | self.draw_polygons(polygons, edge_colors='w', alpha=self.alpha) |
| | self.draw_binary_masks(masks, colors=colors, alphas=self.alpha) |
| |
|
| | if len(labels) > 0 and \ |
| | ('bboxes' not in instances or |
| | instances.bboxes.sum() == 0): |
| | |
| | |
| | areas = [] |
| | positions = [] |
| | for mask in masks: |
| | _, _, stats, centroids = cv2.connectedComponentsWithStats( |
| | mask.astype(np.uint8), connectivity=8) |
| | if stats.shape[0] > 1: |
| | largest_id = np.argmax(stats[1:, -1]) + 1 |
| | positions.append(centroids[largest_id]) |
| | areas.append(stats[largest_id, -1]) |
| | areas = np.stack(areas, axis=0) |
| | scales = _get_adaptive_scales(areas) |
| |
|
| | for i, (pos, label) in enumerate(zip(positions, labels)): |
| | if 'label_names' in instances: |
| | label_text = instances.label_names[i] |
| | else: |
| | label_text = classes[ |
| | label] if classes is not None else f'class {label}' |
| | if 'scores' in instances: |
| | score = round(float(instances.scores[i]) * 100, 1) |
| | label_text += f': {score}' |
| |
|
| | self.draw_texts( |
| | label_text, |
| | pos, |
| | colors=text_colors[i], |
| | font_sizes=int(13 * scales[i]), |
| | horizontal_alignments='center', |
| | bboxes=[{ |
| | 'facecolor': 'black', |
| | 'alpha': 0.8, |
| | 'pad': 0.7, |
| | 'edgecolor': 'none' |
| | }]) |
| | return self.get_image() |
| |
|
| | def _draw_panoptic_seg(self, image: np.ndarray, |
| | panoptic_seg: ['PixelData'], |
| | classes: Optional[List[str]], |
| | palette: Optional[List]) -> np.ndarray: |
| | """Draw panoptic seg of GT or prediction. |
| | |
| | Args: |
| | image (np.ndarray): The image to draw. |
| | panoptic_seg (:obj:`PixelData`): Data structure for |
| | pixel-level annotations or predictions. |
| | classes (List[str], optional): Category information. |
| | |
| | Returns: |
| | np.ndarray: the drawn image which channel is RGB. |
| | """ |
| | |
| | num_classes = len(classes) |
| |
|
| | panoptic_seg_data = panoptic_seg.sem_seg[0] |
| |
|
| | ids = np.unique(panoptic_seg_data)[::-1] |
| |
|
| | if 'label_names' in panoptic_seg: |
| | |
| | classes = panoptic_seg.metainfo['label_names'] |
| | ignore_index = panoptic_seg.metainfo.get('ignore_index', |
| | len(classes)) |
| | ids = ids[ids != ignore_index] |
| | else: |
| | |
| | ids = ids[ids != num_classes] |
| |
|
| | labels = np.array([id % INSTANCE_OFFSET for id in ids], dtype=np.int64) |
| | segms = (panoptic_seg_data[None] == ids[:, None, None]) |
| |
|
| | max_label = int(max(labels) if len(labels) > 0 else 0) |
| |
|
| | mask_color = palette if self.mask_color is None \ |
| | else self.mask_color |
| | mask_palette = get_palette(mask_color, max_label + 1) |
| | colors = [mask_palette[label] for label in labels] |
| |
|
| | self.set_image(image) |
| |
|
| | |
| | polygons = [] |
| | for i, mask in enumerate(segms): |
| | contours, _ = bitmap_to_polygon(mask) |
| | polygons.extend(contours) |
| | self.draw_polygons(polygons, edge_colors='w', alpha=self.alpha) |
| | self.draw_binary_masks(segms, colors=colors, alphas=self.alpha) |
| |
|
| | |
| | areas = [] |
| | positions = [] |
| | for mask in segms: |
| | _, _, stats, centroids = cv2.connectedComponentsWithStats( |
| | mask.astype(np.uint8), connectivity=8) |
| | max_id = np.argmax(stats[1:, -1]) + 1 |
| | positions.append(centroids[max_id]) |
| | areas.append(stats[max_id, -1]) |
| | areas = np.stack(areas, axis=0) |
| | scales = _get_adaptive_scales(areas) |
| |
|
| | text_palette = get_palette(self.text_color, max_label + 1) |
| | text_colors = [text_palette[label] for label in labels] |
| |
|
| | for i, (pos, label) in enumerate(zip(positions, labels)): |
| | label_text = classes[label] |
| |
|
| | self.draw_texts( |
| | label_text, |
| | pos, |
| | colors=text_colors[i], |
| | font_sizes=int(13 * scales[i]), |
| | bboxes=[{ |
| | 'facecolor': 'black', |
| | 'alpha': 0.8, |
| | 'pad': 0.7, |
| | 'edgecolor': 'none' |
| | }], |
| | horizontal_alignments='center') |
| | return self.get_image() |
| |
|
| | def _draw_sem_seg(self, image: np.ndarray, sem_seg: PixelData, |
| | classes: Optional[List], |
| | palette: Optional[List]) -> np.ndarray: |
| | """Draw semantic seg of GT or prediction. |
| | |
| | Args: |
| | image (np.ndarray): The image to draw. |
| | sem_seg (:obj:`PixelData`): Data structure for pixel-level |
| | annotations or predictions. |
| | classes (list, optional): Input classes for result rendering, as |
| | the prediction of segmentation model is a segment map with |
| | label indices, `classes` is a list which includes items |
| | responding to the label indices. If classes is not defined, |
| | visualizer will take `cityscapes` classes by default. |
| | Defaults to None. |
| | palette (list, optional): Input palette for result rendering, which |
| | is a list of color palette responding to the classes. |
| | Defaults to None. |
| | |
| | Returns: |
| | np.ndarray: the drawn image which channel is RGB. |
| | """ |
| | sem_seg_data = sem_seg.sem_seg |
| | if isinstance(sem_seg_data, torch.Tensor): |
| | sem_seg_data = sem_seg_data.numpy() |
| |
|
| | |
| | ids = np.unique(sem_seg_data) |
| | ignore_index = sem_seg.metainfo.get('ignore_index', 255) |
| | ids = ids[ids != ignore_index] |
| |
|
| | if 'label_names' in sem_seg: |
| | |
| | label_names = sem_seg.metainfo['label_names'] |
| | else: |
| | label_names = classes |
| |
|
| | labels = np.array(ids, dtype=np.int64) |
| | colors = [palette[label] for label in labels] |
| |
|
| | self.set_image(image) |
| |
|
| | |
| | for i, (label, color) in enumerate(zip(labels, colors)): |
| | masks = sem_seg_data == label |
| | self.draw_binary_masks(masks, colors=[color], alphas=self.alpha) |
| | label_text = label_names[label] |
| | _, _, stats, centroids = cv2.connectedComponentsWithStats( |
| | masks[0].astype(np.uint8), connectivity=8) |
| | if stats.shape[0] > 1: |
| | largest_id = np.argmax(stats[1:, -1]) + 1 |
| | centroids = centroids[largest_id] |
| |
|
| | areas = stats[largest_id, -1] |
| | scales = _get_adaptive_scales(areas) |
| |
|
| | self.draw_texts( |
| | label_text, |
| | centroids, |
| | colors=(255, 255, 255), |
| | font_sizes=int(13 * scales), |
| | horizontal_alignments='center', |
| | bboxes=[{ |
| | 'facecolor': 'black', |
| | 'alpha': 0.8, |
| | 'pad': 0.7, |
| | 'edgecolor': 'none' |
| | }]) |
| |
|
| | return self.get_image() |
| |
|
| | @master_only |
| | def add_datasample( |
| | self, |
| | name: str, |
| | image: np.ndarray, |
| | data_sample: Optional['DetDataSample'] = None, |
| | draw_gt: bool = True, |
| | draw_pred: bool = True, |
| | show: bool = False, |
| | wait_time: float = 0, |
| | |
| | out_file: Optional[str] = None, |
| | pred_score_thr: float = 0.3, |
| | step: int = 0) -> 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 (:obj:`DetDataSample`, optional): A data |
| | sample that contain annotations and predictions. |
| | Defaults to None. |
| | draw_gt (bool): Whether to draw GT DetDataSample. Default to True. |
| | draw_pred (bool): Whether to draw Prediction DetDataSample. |
| | Defaults to True. |
| | show (bool): Whether to display the drawn image. Default to False. |
| | wait_time (float): 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. |
| | """ |
| | image = image.clip(0, 255).astype(np.uint8) |
| | classes = self.dataset_meta.get('classes', None) |
| | palette = self.dataset_meta.get('palette', None) |
| |
|
| | 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: |
| | gt_img_data = image |
| | if 'gt_instances' in data_sample: |
| | gt_img_data = self._draw_instances(image, |
| | data_sample.gt_instances, |
| | classes, palette) |
| | if 'gt_sem_seg' in data_sample: |
| | gt_img_data = self._draw_sem_seg(gt_img_data, |
| | data_sample.gt_sem_seg, |
| | classes, palette) |
| |
|
| | if 'gt_panoptic_seg' in data_sample: |
| | assert classes is not None, 'class information is ' \ |
| | 'not provided when ' \ |
| | 'visualizing panoptic ' \ |
| | 'segmentation results.' |
| | gt_img_data = self._draw_panoptic_seg( |
| | gt_img_data, data_sample.gt_panoptic_seg, classes, palette) |
| |
|
| | if draw_pred and data_sample is not None: |
| | pred_img_data = image |
| | if 'pred_instances' in data_sample: |
| | pred_instances = data_sample.pred_instances |
| | pred_instances = pred_instances[ |
| | pred_instances.scores > pred_score_thr] |
| | pred_img_data = self._draw_instances(image, pred_instances, |
| | classes, palette) |
| |
|
| | if 'pred_sem_seg' in data_sample: |
| | pred_img_data = self._draw_sem_seg(pred_img_data, |
| | data_sample.pred_sem_seg, |
| | classes, palette) |
| |
|
| | if 'pred_panoptic_seg' in data_sample: |
| | assert classes is not None, 'class information is ' \ |
| | 'not provided when ' \ |
| | 'visualizing panoptic ' \ |
| | 'segmentation results.' |
| | pred_img_data = self._draw_panoptic_seg( |
| | pred_img_data, data_sample.pred_panoptic_seg.numpy(), |
| | classes, palette) |
| |
|
| | 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 |
| | elif pred_img_data is not None: |
| | drawn_img = pred_img_data |
| | else: |
| | |
| | drawn_img = image |
| |
|
| | |
| | |
| | |
| | self.set_image(drawn_img) |
| |
|
| | 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) |
| |
|
| |
|
| | 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() |
| | color = colors[np.random.choice(range(len(colors)))] |
| | color = tuple([int(255 * c) for c in color]) |
| | return color |
| |
|
| |
|
| | @VISUALIZERS.register_module() |
| | class TrackLocalVisualizer(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] = 3, |
| | alpha: float = 0.8) -> None: |
| | super().__init__(name, image, vis_backends, save_dir) |
| | self.line_width = line_width |
| | self.alpha = alpha |
| | |
| | |
| | |
| | self.dataset_meta = {} |
| |
|
| | 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) |
| |
|
| | |
| | |
| | colors = [random_color(_id) for _id in instances.instances_id] |
| | categories = [ |
| | classes[label] if classes is not None else f'cls{label}' |
| | for label in instances.labels |
| | ] |
| | if 'scores' in instances: |
| | texts = [ |
| | f'{category_name}\n{instance_id} | {score:.2f}' |
| | for category_name, instance_id, score in zip( |
| | categories, instances.instances_id, instances.scores) |
| | ] |
| | else: |
| | texts = [ |
| | f'{category_name}\n{instance_id}' for category_name, |
| | instance_id in zip(categories, instances.instances_id) |
| | ] |
| |
|
| | |
| | if 'bboxes' in instances: |
| | |
| | bboxes = instances.bboxes.clone() |
| | self.draw_bboxes( |
| | bboxes, |
| | edge_colors=colors, |
| | alpha=self.alpha, |
| | line_widths=self.line_width) |
| | |
| | if texts is not None: |
| | 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 in enumerate(positions): |
| | self.draw_texts( |
| | texts[i], |
| | 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' |
| | }]) |
| |
|
| | |
| | if 'masks' in instances: |
| | masks = instances.masks |
| | polygons = [] |
| | for i, mask in enumerate(masks): |
| | contours, _ = bitmap_to_polygon(mask) |
| | polygons.extend(contours) |
| | self.draw_polygons(polygons, edge_colors='w', alpha=self.alpha) |
| | self.draw_binary_masks(masks, colors=colors, alphas=self.alpha) |
| |
|
| | return self.get_image() |
| |
|
| | @master_only |
| | 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, |
| | |
| | out_file: Optional[str] = None, |
| | pred_score_thr: float = 0.3, |
| | step: int = 0) -> 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 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) |
| |
|