Spaces:
Running
on
Zero
Running
on
Zero
| # Copyright (c) OpenMMLab. All rights reserved. | |
| from typing import Optional, Union | |
| import cv2 as cv | |
| import numpy as np | |
| import torch | |
| from torchvision.transforms import ToPILImage | |
| class SimCCVisualizer: | |
| def draw_instance_xy_heatmap(self, | |
| heatmap: torch.Tensor, | |
| overlaid_image: Optional[np.ndarray], | |
| n: int = 20, | |
| mix: bool = True, | |
| weight: float = 0.5): | |
| """Draw heatmaps of GT or prediction. | |
| Args: | |
| heatmap (torch.Tensor): Tensor of heatmap. | |
| overlaid_image (np.ndarray): The image to draw. | |
| n (int): Number of keypoint, up to 20. | |
| mix (bool):Whether to merge heatmap and original image. | |
| weight (float): Weight of original image during fusion. | |
| Returns: | |
| np.ndarray: the drawn image which channel is RGB. | |
| """ | |
| heatmap2d = heatmap.data.max(0, keepdim=True)[0] | |
| xy_heatmap, K = self.split_simcc_xy(heatmap) | |
| K = K if K <= n else n | |
| blank_size = tuple(heatmap.size()[1:]) | |
| maps = {'x': [], 'y': []} | |
| for i in xy_heatmap: | |
| x, y = self.draw_1d_heatmaps(i['x']), self.draw_1d_heatmaps(i['y']) | |
| maps['x'].append(x) | |
| maps['y'].append(y) | |
| white = self.creat_blank(blank_size, K) | |
| map2d = self.draw_2d_heatmaps(heatmap2d) | |
| if mix: | |
| map2d = cv.addWeighted(overlaid_image, 1 - weight, map2d, weight, | |
| 0) | |
| self.image_cover(white, map2d, int(blank_size[1] * 0.1), | |
| int(blank_size[0] * 0.1)) | |
| white = self.add_1d_heatmaps(maps, white, blank_size, K) | |
| return white | |
| def split_simcc_xy(self, heatmap: Union[np.ndarray, torch.Tensor]): | |
| """Extract one-dimensional heatmap from two-dimensional heatmap and | |
| calculate the number of keypoint.""" | |
| size = heatmap.size() | |
| k = size[0] if size[0] <= 20 else 20 | |
| maps = [] | |
| for _ in range(k): | |
| xy_dict = {} | |
| single_heatmap = heatmap[_] | |
| xy_dict['x'], xy_dict['y'] = self.merge_maps(single_heatmap) | |
| maps.append(xy_dict) | |
| return maps, k | |
| def merge_maps(self, map_2d): | |
| """Synthesis of one-dimensional heatmap.""" | |
| x = map_2d.data.max(0, keepdim=True)[0] | |
| y = map_2d.data.max(1, keepdim=True)[0] | |
| return x, y | |
| def draw_1d_heatmaps(self, heatmap_1d): | |
| """Draw one-dimensional heatmap.""" | |
| size = heatmap_1d.size() | |
| length = max(size) | |
| np_heatmap = ToPILImage()(heatmap_1d).convert('RGB') | |
| cv_img = cv.cvtColor(np.asarray(np_heatmap), cv.COLOR_RGB2BGR) | |
| if size[0] < size[1]: | |
| cv_img = cv.resize(cv_img, (length, 15)) | |
| else: | |
| cv_img = cv.resize(cv_img, (15, length)) | |
| single_map = cv.applyColorMap(cv_img, cv.COLORMAP_JET) | |
| return single_map | |
| def creat_blank(self, | |
| size: Union[list, tuple], | |
| K: int = 20, | |
| interval: int = 10): | |
| """Create the background.""" | |
| blank_height = int( | |
| max(size[0] * 2, size[0] * 1.1 + (K + 1) * (15 + interval))) | |
| blank_width = int( | |
| max(size[1] * 2, size[1] * 1.1 + (K + 1) * (15 + interval))) | |
| blank = np.zeros((blank_height, blank_width, 3), np.uint8) | |
| blank.fill(255) | |
| return blank | |
| def draw_2d_heatmaps(self, heatmap_2d): | |
| """Draw a two-dimensional heatmap fused with the original image.""" | |
| np_heatmap = ToPILImage()(heatmap_2d).convert('RGB') | |
| cv_img = cv.cvtColor(np.asarray(np_heatmap), cv.COLOR_RGB2BGR) | |
| map_2d = cv.applyColorMap(cv_img, cv.COLORMAP_JET) | |
| return map_2d | |
| def image_cover(self, background: np.ndarray, foreground: np.ndarray, | |
| x: int, y: int): | |
| """Paste the foreground on the background.""" | |
| fore_size = foreground.shape | |
| background[y:y + fore_size[0], x:x + fore_size[1]] = foreground | |
| return background | |
| def add_1d_heatmaps(self, | |
| maps: dict, | |
| background: np.ndarray, | |
| map2d_size: Union[tuple, list], | |
| K: int, | |
| interval: int = 10): | |
| """Paste one-dimensional heatmaps onto the background in turn.""" | |
| y_startpoint, x_startpoint = [int(1.1*map2d_size[1]), | |
| int(0.1*map2d_size[0])],\ | |
| [int(0.1*map2d_size[1]), | |
| int(1.1*map2d_size[0])] | |
| x_startpoint[1] += interval * 2 | |
| y_startpoint[0] += interval * 2 | |
| add = interval + 10 | |
| for i in range(K): | |
| self.image_cover(background, maps['x'][i], x_startpoint[0], | |
| x_startpoint[1]) | |
| cv.putText(background, str(i), | |
| (x_startpoint[0] - 30, x_startpoint[1] + 10), | |
| cv.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2) | |
| self.image_cover(background, maps['y'][i], y_startpoint[0], | |
| y_startpoint[1]) | |
| cv.putText(background, str(i), | |
| (y_startpoint[0], y_startpoint[1] - 5), | |
| cv.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2) | |
| x_startpoint[1] += add | |
| y_startpoint[0] += add | |
| return background[:x_startpoint[1] + y_startpoint[1] + | |
| 1, :y_startpoint[0] + x_startpoint[0] + 1] | |