import os.path as osp import os import matplotlib.pyplot as plt import torch import cv2 import math import numpy as np import tqdm from cv2 import findContours from dl_ext.primitive import safe_zip from dl_ext.timer import EvalTime def plot_confidence(confidence): n = len(confidence) plt.plot(np.arange(n), confidence) plt.show() def image_grid( images, rows=None, cols=None, fill: bool = True, show_axes: bool = False, rgb=None, show=True, label=None, **kwargs ): """ A util function for plotting a grid of images. Args: images: (N, H, W, 4) array of RGBA images rows: number of rows in the grid cols: number of columns in the grid fill: boolean indicating if the space between images should be filled show_axes: boolean indicating if the axes of the plots should be visible rgb: boolean, If True, only RGB channels are plotted. If False, only the alpha channel is plotted. Returns: None """ evaltime = EvalTime(disable=True) evaltime('') if isinstance(images, torch.Tensor): images = images.detach().cpu() if len(images[0].shape) == 2: rgb = False if images[0].shape[-1] == 2: # flow images = [flow_to_image(im) for im in images] if (rows is None) != (cols is None): raise ValueError("Specify either both rows and cols or neither.") if rows is None: rows = int(len(images) ** 0.5) cols = math.ceil(len(images) / rows) gridspec_kw = {"wspace": 0.0, "hspace": 0.0} if fill else {} if len(images) < 50: figsize = (10, 10) else: figsize = (15, 15) evaltime('0.5') plt.figure(figsize=figsize) # fig, axarr = plt.subplots(rows, cols, gridspec_kw=gridspec_kw, figsize=figsize) if label: # fig.suptitle(label, fontsize=30) plt.suptitle(label, fontsize=30) # bleed = 0 # fig.subplots_adjust(left=bleed, bottom=bleed, right=(1 - bleed), top=(1 - bleed)) evaltime('subplots') # for i, (ax, im) in enumerate(tqdm.tqdm(zip(axarr.ravel(), images), leave=True, total=len(images))): for i in range(len(images)): # evaltime(f'{i} begin') plt.subplot(rows, cols, i + 1) if rgb: # only render RGB channels plt.imshow(images[i][..., :3], **kwargs) # ax.imshow(im[..., :3], **kwargs) else: # only render Alpha channel plt.imshow(images[i], **kwargs) # ax.imshow(im, **kwargs) if not show_axes: plt.axis('off') # ax.set_axis_off() # ax.set_title(f'{i}') plt.title(f'{i}') # evaltime(f'{i} end') evaltime('2') if show: plt.show() # return fig def depth_grid( depths, rows=None, cols=None, fill: bool = True, show_axes: bool = False, ): """ A util function for plotting a grid of images. Args: images: (N, H, W, 4) array of RGBA images rows: number of rows in the grid cols: number of columns in the grid fill: boolean indicating if the space between images should be filled show_axes: boolean indicating if the axes of the plots should be visible rgb: boolean, If True, only RGB channels are plotted. If False, only the alpha channel is plotted. Returns: None """ if (rows is None) != (cols is None): raise ValueError("Specify either both rows and cols or neither.") if rows is None: rows = len(depths) cols = 1 gridspec_kw = {"wspace": 0.0, "hspace": 0.0} if fill else {} fig, axarr = plt.subplots(rows, cols, gridspec_kw=gridspec_kw, figsize=(15, 9)) bleed = 0 fig.subplots_adjust(left=bleed, bottom=bleed, right=(1 - bleed), top=(1 - bleed)) for ax, im in zip(axarr.ravel(), depths): ax.imshow(im) if not show_axes: ax.set_axis_off() plt.show() def hover_masks_on_imgs(images, masks): masks = np.array(masks) new_imgs = [] tids = list(range(1, masks.max() + 1)) colors = colormap(rgb=True, lighten=True) for im, mask in tqdm.tqdm(safe_zip(images, masks), total=len(images)): for tid in tids: im = vis_mask( im, (mask == tid).astype(np.uint8), color=colors[tid], alpha=0.5, border_alpha=0.5, border_color=[255, 255, 255], border_thick=3) new_imgs.append(im) return new_imgs def vis_mask(img, mask, color=[255, 255, 255], alpha=0.4, show_border=True, border_alpha=0.5, border_thick=1, border_color=None): """Visualizes a single binary mask.""" if isinstance(mask, torch.Tensor): from anypose.utils.pn_utils import to_array mask = to_array(mask > 0).astype(np.uint8) img = img.astype(np.float32) idx = np.nonzero(mask) img[idx[0], idx[1], :] *= 1.0 - alpha img[idx[0], idx[1], :] += [alpha * x for x in color] if show_border: contours, _ = findContours( mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) # contours = [c for c in contours if c.shape[0] > 10] if border_color is None: border_color = color if not isinstance(border_color, list): border_color = border_color.tolist() if border_alpha < 1: with_border = img.copy() cv2.drawContours(with_border, contours, -1, border_color, border_thick, cv2.LINE_AA) img = (1 - border_alpha) * img + border_alpha * with_border else: cv2.drawContours(img, contours, -1, border_color, border_thick, cv2.LINE_AA) return img.astype(np.uint8) def colormap(rgb=False, lighten=True): """Copied from Detectron codebase.""" color_list = np.array( [ 0.000, 0.447, 0.741, 0.850, 0.325, 0.098, 0.929, 0.694, 0.125, 0.494, 0.184, 0.556, 0.466, 0.674, 0.188, 0.301, 0.745, 0.933, 0.635, 0.078, 0.184, 0.300, 0.300, 0.300, 0.600, 0.600, 0.600, 1.000, 0.000, 0.000, 1.000, 0.500, 0.000, 0.749, 0.749, 0.000, 0.000, 1.000, 0.000, 0.000, 0.000, 1.000, 0.667, 0.000, 1.000, 0.333, 0.333, 0.000, 0.333, 0.667, 0.000, 0.333, 1.000, 0.000, 0.667, 0.333, 0.000, 0.667, 0.667, 0.000, 0.667, 1.000, 0.000, 1.000, 0.333, 0.000, 1.000, 0.667, 0.000, 1.000, 1.000, 0.000, 0.000, 0.333, 0.500, 0.000, 0.667, 0.500, 0.000, 1.000, 0.500, 0.333, 0.000, 0.500, 0.333, 0.333, 0.500, 0.333, 0.667, 0.500, 0.333, 1.000, 0.500, 0.667, 0.000, 0.500, 0.667, 0.333, 0.500, 0.667, 0.667, 0.500, 0.667, 1.000, 0.500, 1.000, 0.000, 0.500, 1.000, 0.333, 0.500, 1.000, 0.667, 0.500, 1.000, 1.000, 0.500, 0.000, 0.333, 1.000, 0.000, 0.667, 1.000, 0.000, 1.000, 1.000, 0.333, 0.000, 1.000, 0.333, 0.333, 1.000, 0.333, 0.667, 1.000, 0.333, 1.000, 1.000, 0.667, 0.000, 1.000, 0.667, 0.333, 1.000, 0.667, 0.667, 1.000, 0.667, 1.000, 1.000, 1.000, 0.000, 1.000, 1.000, 0.333, 1.000, 1.000, 0.667, 1.000, 0.167, 0.000, 0.000, 0.333, 0.000, 0.000, 0.500, 0.000, 0.000, 0.667, 0.000, 0.000, 0.833, 0.000, 0.000, 1.000, 0.000, 0.000, 0.000, 0.167, 0.000, 0.000, 0.333, 0.000, 0.000, 0.500, 0.000, 0.000, 0.667, 0.000, 0.000, 0.833, 0.000, 0.000, 1.000, 0.000, 0.000, 0.000, 0.167, 0.000, 0.000, 0.333, 0.000, 0.000, 0.500, 0.000, 0.000, 0.667, 0.000, 0.000, 0.833, 0.000, 0.000, 1.000, 0.000, 0.000, 0.000, 0.143, 0.143, 0.143, 0.286, 0.286, 0.286, 0.429, 0.429, 0.429, 0.571, 0.571, 0.571, 0.714, 0.714, 0.714, 0.857, 0.857, 0.857, 1.000, 1.000, 1.000 ] ).astype(np.float32) color_list = color_list.reshape((-1, 3)) if not rgb: color_list = color_list[:, ::-1] if lighten: # Make all the colors a little lighter / whiter. This is copied # from the detectron visualization code (search for 'w_ratio'). w_ratio = 0.4 color_list = (color_list * (1 - w_ratio) + w_ratio) return color_list * 255 def vis_layer_mask(masks, save_path=None): masks = torch.as_tensor(masks) tids = masks.unique().tolist() tids.remove(0) for tid in tqdm.tqdm(tids): show = save_path is None image_grid(masks == tid, label=f'{tid}', show=show) if save_path: os.makedirs(osp.dirname(save_path), exist_ok=True) plt.savefig(save_path % tid) plt.close('all') def show(x, **kwargs): if isinstance(x, torch.Tensor): x = x.detach().cpu() plt.imshow(x, **kwargs) plt.show() def vis_title(rgb, text, shift_y=30): tmp = rgb.copy() shift_x = rgb.shape[1] // 2 cv2.putText(tmp, text, (shift_x, shift_y), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), thickness=2, lineType=cv2.LINE_AA) return tmp