|  |  | 
					
						
						|  | import json | 
					
						
						|  | import numpy as np | 
					
						
						|  | from functools import lru_cache | 
					
						
						|  | from typing import Dict, List, Optional, Tuple | 
					
						
						|  | import cv2 | 
					
						
						|  | import torch | 
					
						
						|  |  | 
					
						
						|  | from detectron2.utils.file_io import PathManager | 
					
						
						|  |  | 
					
						
						|  | from densepose.modeling import build_densepose_embedder | 
					
						
						|  | from densepose.modeling.cse.utils import get_closest_vertices_mask_from_ES | 
					
						
						|  |  | 
					
						
						|  | from ..data.utils import get_class_to_mesh_name_mapping | 
					
						
						|  | from ..structures import DensePoseEmbeddingPredictorOutput | 
					
						
						|  | from ..structures.mesh import create_mesh | 
					
						
						|  | from .base import Boxes, Image, MatrixVisualizer | 
					
						
						|  | from .densepose_results_textures import get_texture_atlas | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | @lru_cache() | 
					
						
						|  | def get_xyz_vertex_embedding(mesh_name: str, device: torch.device): | 
					
						
						|  | if mesh_name == "smpl_27554": | 
					
						
						|  | embed_path = PathManager.get_local_path( | 
					
						
						|  | "https://dl.fbaipublicfiles.com/densepose/data/cse/mds_d=256.npy" | 
					
						
						|  | ) | 
					
						
						|  | embed_map, _ = np.load(embed_path, allow_pickle=True) | 
					
						
						|  | embed_map = torch.tensor(embed_map).float()[:, 0] | 
					
						
						|  | embed_map -= embed_map.min() | 
					
						
						|  | embed_map /= embed_map.max() | 
					
						
						|  | else: | 
					
						
						|  | mesh = create_mesh(mesh_name, device) | 
					
						
						|  | embed_map = mesh.vertices.sum(dim=1) | 
					
						
						|  | embed_map -= embed_map.min() | 
					
						
						|  | embed_map /= embed_map.max() | 
					
						
						|  | embed_map = embed_map**2 | 
					
						
						|  | return embed_map | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | class DensePoseOutputsVertexVisualizer: | 
					
						
						|  | def __init__( | 
					
						
						|  | self, | 
					
						
						|  | cfg, | 
					
						
						|  | inplace=True, | 
					
						
						|  | cmap=cv2.COLORMAP_JET, | 
					
						
						|  | alpha=0.7, | 
					
						
						|  | device="cuda", | 
					
						
						|  | default_class=0, | 
					
						
						|  | **kwargs, | 
					
						
						|  | ): | 
					
						
						|  | self.mask_visualizer = MatrixVisualizer( | 
					
						
						|  | inplace=inplace, cmap=cmap, val_scale=1.0, alpha=alpha | 
					
						
						|  | ) | 
					
						
						|  | self.class_to_mesh_name = get_class_to_mesh_name_mapping(cfg) | 
					
						
						|  | self.embedder = build_densepose_embedder(cfg) | 
					
						
						|  | self.device = torch.device(device) | 
					
						
						|  | self.default_class = default_class | 
					
						
						|  |  | 
					
						
						|  | self.mesh_vertex_embeddings = { | 
					
						
						|  | mesh_name: self.embedder(mesh_name).to(self.device) | 
					
						
						|  | for mesh_name in self.class_to_mesh_name.values() | 
					
						
						|  | if self.embedder.has_embeddings(mesh_name) | 
					
						
						|  | } | 
					
						
						|  |  | 
					
						
						|  | def visualize( | 
					
						
						|  | self, | 
					
						
						|  | image_bgr: Image, | 
					
						
						|  | outputs_boxes_xywh_classes: Tuple[ | 
					
						
						|  | Optional[DensePoseEmbeddingPredictorOutput], Optional[Boxes], Optional[List[int]] | 
					
						
						|  | ], | 
					
						
						|  | ) -> Image: | 
					
						
						|  | if outputs_boxes_xywh_classes[0] is None: | 
					
						
						|  | return image_bgr | 
					
						
						|  |  | 
					
						
						|  | S, E, N, bboxes_xywh, pred_classes = self.extract_and_check_outputs_and_boxes( | 
					
						
						|  | outputs_boxes_xywh_classes | 
					
						
						|  | ) | 
					
						
						|  |  | 
					
						
						|  | for n in range(N): | 
					
						
						|  | x, y, w, h = bboxes_xywh[n].int().tolist() | 
					
						
						|  | mesh_name = self.class_to_mesh_name[pred_classes[n]] | 
					
						
						|  | closest_vertices, mask = get_closest_vertices_mask_from_ES( | 
					
						
						|  | E[[n]], | 
					
						
						|  | S[[n]], | 
					
						
						|  | h, | 
					
						
						|  | w, | 
					
						
						|  | self.mesh_vertex_embeddings[mesh_name], | 
					
						
						|  | self.device, | 
					
						
						|  | ) | 
					
						
						|  | embed_map = get_xyz_vertex_embedding(mesh_name, self.device) | 
					
						
						|  | vis = (embed_map[closest_vertices].clip(0, 1) * 255.0).cpu().numpy() | 
					
						
						|  | mask_numpy = mask.cpu().numpy().astype(dtype=np.uint8) | 
					
						
						|  | image_bgr = self.mask_visualizer.visualize(image_bgr, mask_numpy, vis, [x, y, w, h]) | 
					
						
						|  |  | 
					
						
						|  | return image_bgr | 
					
						
						|  |  | 
					
						
						|  | def extract_and_check_outputs_and_boxes(self, outputs_boxes_xywh_classes): | 
					
						
						|  |  | 
					
						
						|  | densepose_output, bboxes_xywh, pred_classes = outputs_boxes_xywh_classes | 
					
						
						|  |  | 
					
						
						|  | if pred_classes is None: | 
					
						
						|  | pred_classes = [self.default_class] * len(bboxes_xywh) | 
					
						
						|  |  | 
					
						
						|  | assert isinstance( | 
					
						
						|  | densepose_output, DensePoseEmbeddingPredictorOutput | 
					
						
						|  | ), "DensePoseEmbeddingPredictorOutput expected, {} encountered".format( | 
					
						
						|  | type(densepose_output) | 
					
						
						|  | ) | 
					
						
						|  |  | 
					
						
						|  | S = densepose_output.coarse_segm | 
					
						
						|  | E = densepose_output.embedding | 
					
						
						|  | N = S.size(0) | 
					
						
						|  | assert N == E.size( | 
					
						
						|  | 0 | 
					
						
						|  | ), "CSE coarse_segm {} and embeddings {}" " should have equal first dim size".format( | 
					
						
						|  | S.size(), E.size() | 
					
						
						|  | ) | 
					
						
						|  | assert N == len( | 
					
						
						|  | bboxes_xywh | 
					
						
						|  | ), "number of bounding boxes {}" " should be equal to first dim size of outputs {}".format( | 
					
						
						|  | len(bboxes_xywh), N | 
					
						
						|  | ) | 
					
						
						|  | assert N == len(pred_classes), ( | 
					
						
						|  | "number of predicted classes {}" | 
					
						
						|  | " should be equal to first dim size of outputs {}".format(len(bboxes_xywh), N) | 
					
						
						|  | ) | 
					
						
						|  |  | 
					
						
						|  | return S, E, N, bboxes_xywh, pred_classes | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | def get_texture_atlases(json_str: Optional[str]) -> Optional[Dict[str, Optional[np.ndarray]]]: | 
					
						
						|  | """ | 
					
						
						|  | json_str is a JSON string representing a mesh_name -> texture_atlas_path dictionary | 
					
						
						|  | """ | 
					
						
						|  | if json_str is None: | 
					
						
						|  | return None | 
					
						
						|  |  | 
					
						
						|  | paths = json.loads(json_str) | 
					
						
						|  | return {mesh_name: get_texture_atlas(path) for mesh_name, path in paths.items()} | 
					
						
						|  |  | 
					
						
						|  |  | 
					
						
						|  | class DensePoseOutputsTextureVisualizer(DensePoseOutputsVertexVisualizer): | 
					
						
						|  | def __init__( | 
					
						
						|  | self, | 
					
						
						|  | cfg, | 
					
						
						|  | texture_atlases_dict, | 
					
						
						|  | device="cuda", | 
					
						
						|  | default_class=0, | 
					
						
						|  | **kwargs, | 
					
						
						|  | ): | 
					
						
						|  | self.embedder = build_densepose_embedder(cfg) | 
					
						
						|  |  | 
					
						
						|  | self.texture_image_dict = {} | 
					
						
						|  | self.alpha_dict = {} | 
					
						
						|  |  | 
					
						
						|  | for mesh_name in texture_atlases_dict.keys(): | 
					
						
						|  | if texture_atlases_dict[mesh_name].shape[-1] == 4: | 
					
						
						|  | self.alpha_dict[mesh_name] = texture_atlases_dict[mesh_name][:, :, -1] / 255.0 | 
					
						
						|  | self.texture_image_dict[mesh_name] = texture_atlases_dict[mesh_name][:, :, :3] | 
					
						
						|  | else: | 
					
						
						|  | self.alpha_dict[mesh_name] = texture_atlases_dict[mesh_name].sum(axis=-1) > 0 | 
					
						
						|  | self.texture_image_dict[mesh_name] = texture_atlases_dict[mesh_name] | 
					
						
						|  |  | 
					
						
						|  | self.device = torch.device(device) | 
					
						
						|  | self.class_to_mesh_name = get_class_to_mesh_name_mapping(cfg) | 
					
						
						|  | self.default_class = default_class | 
					
						
						|  |  | 
					
						
						|  | self.mesh_vertex_embeddings = { | 
					
						
						|  | mesh_name: self.embedder(mesh_name).to(self.device) | 
					
						
						|  | for mesh_name in self.class_to_mesh_name.values() | 
					
						
						|  | } | 
					
						
						|  |  | 
					
						
						|  | def visualize( | 
					
						
						|  | self, | 
					
						
						|  | image_bgr: Image, | 
					
						
						|  | outputs_boxes_xywh_classes: Tuple[ | 
					
						
						|  | Optional[DensePoseEmbeddingPredictorOutput], Optional[Boxes], Optional[List[int]] | 
					
						
						|  | ], | 
					
						
						|  | ) -> Image: | 
					
						
						|  | image_target_bgr = image_bgr.copy() | 
					
						
						|  | if outputs_boxes_xywh_classes[0] is None: | 
					
						
						|  | return image_target_bgr | 
					
						
						|  |  | 
					
						
						|  | S, E, N, bboxes_xywh, pred_classes = self.extract_and_check_outputs_and_boxes( | 
					
						
						|  | outputs_boxes_xywh_classes | 
					
						
						|  | ) | 
					
						
						|  |  | 
					
						
						|  | meshes = { | 
					
						
						|  | p: create_mesh(self.class_to_mesh_name[p], self.device) for p in np.unique(pred_classes) | 
					
						
						|  | } | 
					
						
						|  |  | 
					
						
						|  | for n in range(N): | 
					
						
						|  | x, y, w, h = bboxes_xywh[n].int().cpu().numpy() | 
					
						
						|  | mesh_name = self.class_to_mesh_name[pred_classes[n]] | 
					
						
						|  | closest_vertices, mask = get_closest_vertices_mask_from_ES( | 
					
						
						|  | E[[n]], | 
					
						
						|  | S[[n]], | 
					
						
						|  | h, | 
					
						
						|  | w, | 
					
						
						|  | self.mesh_vertex_embeddings[mesh_name], | 
					
						
						|  | self.device, | 
					
						
						|  | ) | 
					
						
						|  | uv_array = meshes[pred_classes[n]].texcoords[closest_vertices].permute((2, 0, 1)) | 
					
						
						|  | uv_array = uv_array.cpu().numpy().clip(0, 1) | 
					
						
						|  | textured_image = self.generate_image_with_texture( | 
					
						
						|  | image_target_bgr[y : y + h, x : x + w], | 
					
						
						|  | uv_array, | 
					
						
						|  | mask.cpu().numpy(), | 
					
						
						|  | self.class_to_mesh_name[pred_classes[n]], | 
					
						
						|  | ) | 
					
						
						|  | if textured_image is None: | 
					
						
						|  | continue | 
					
						
						|  | image_target_bgr[y : y + h, x : x + w] = textured_image | 
					
						
						|  |  | 
					
						
						|  | return image_target_bgr | 
					
						
						|  |  | 
					
						
						|  | def generate_image_with_texture(self, bbox_image_bgr, uv_array, mask, mesh_name): | 
					
						
						|  | alpha = self.alpha_dict.get(mesh_name) | 
					
						
						|  | texture_image = self.texture_image_dict.get(mesh_name) | 
					
						
						|  | if alpha is None or texture_image is None: | 
					
						
						|  | return None | 
					
						
						|  | U, V = uv_array | 
					
						
						|  | x_index = (U * texture_image.shape[1]).astype(int) | 
					
						
						|  | y_index = (V * texture_image.shape[0]).astype(int) | 
					
						
						|  | local_texture = texture_image[y_index, x_index][mask] | 
					
						
						|  | local_alpha = np.expand_dims(alpha[y_index, x_index][mask], -1) | 
					
						
						|  | output_image = bbox_image_bgr.copy() | 
					
						
						|  | output_image[mask] = output_image[mask] * (1 - local_alpha) + local_texture * local_alpha | 
					
						
						|  | return output_image.astype(np.uint8) | 
					
						
						|  |  |