| |
| import numpy as np |
| from typing import List, Optional, Tuple |
| import torch |
|
|
| from detectron2.data.detection_utils import read_image |
|
|
| from ..structures import DensePoseChartResult |
| from .base import Boxes, Image |
| from .densepose_results import DensePoseResultsVisualizer |
|
|
|
|
| def get_texture_atlas(path: Optional[str]) -> Optional[np.ndarray]: |
| if path is None: |
| return None |
|
|
| |
| |
| |
| |
| bgr_image = read_image(path) |
| rgb_image = np.copy(bgr_image) |
| rgb_image[:, :, :3] = rgb_image[:, :, 2::-1] |
| return rgb_image |
|
|
|
|
| class DensePoseResultsVisualizerWithTexture(DensePoseResultsVisualizer): |
| """ |
| texture_atlas: An image, size 6N * 4N, with N * N squares for each of the 24 body parts. |
| It must follow the grid found at https://github.com/facebookresearch/DensePose/blob/master/DensePoseData/demo_data/texture_atlas_200.png # noqa |
| For each body part, U is proportional to the x coordinate, and (1 - V) to y |
| """ |
|
|
| def __init__(self, texture_atlas, **kwargs): |
| self.texture_atlas = texture_atlas |
| self.body_part_size = texture_atlas.shape[0] // 6 |
| assert self.body_part_size == texture_atlas.shape[1] // 4 |
|
|
| def visualize( |
| self, |
| image_bgr: Image, |
| results_and_boxes_xywh: Tuple[Optional[List[DensePoseChartResult]], Optional[Boxes]], |
| ) -> Image: |
| densepose_result, boxes_xywh = results_and_boxes_xywh |
| if densepose_result is None or boxes_xywh is None: |
| return image_bgr |
|
|
| boxes_xywh = boxes_xywh.int().cpu().numpy() |
| texture_image, alpha = self.get_texture() |
| for i, result in enumerate(densepose_result): |
| iuv_array = torch.cat((result.labels[None], result.uv.clamp(0, 1))) |
| x, y, w, h = boxes_xywh[i] |
| bbox_image = image_bgr[y : y + h, x : x + w] |
| image_bgr[y : y + h, x : x + w] = self.generate_image_with_texture( |
| texture_image, alpha, bbox_image, iuv_array.cpu().numpy() |
| ) |
| return image_bgr |
|
|
| def get_texture(self): |
| N = self.body_part_size |
| texture_image = np.zeros([24, N, N, self.texture_atlas.shape[-1]]) |
| for i in range(4): |
| for j in range(6): |
| texture_image[(6 * i + j), :, :, :] = self.texture_atlas[ |
| N * j : N * (j + 1), N * i : N * (i + 1), : |
| ] |
|
|
| if texture_image.shape[-1] == 4: |
| alpha = texture_image[:, :, :, -1] / 255.0 |
| texture_image = texture_image[:, :, :, :3] |
| else: |
| alpha = texture_image.sum(axis=-1) > 0 |
|
|
| return texture_image, alpha |
|
|
| def generate_image_with_texture(self, texture_image, alpha, bbox_image_bgr, iuv_array): |
|
|
| I, U, V = iuv_array |
| generated_image_bgr = bbox_image_bgr.copy() |
|
|
| for PartInd in range(1, 25): |
| x, y = np.where(I == PartInd) |
| x_index = (U[x, y] * (self.body_part_size - 1)).astype(int) |
| y_index = ((1 - V[x, y]) * (self.body_part_size - 1)).astype(int) |
| part_alpha = np.expand_dims(alpha[PartInd - 1, y_index, x_index], -1) |
| generated_image_bgr[I == PartInd] = ( |
| generated_image_bgr[I == PartInd] * (1 - part_alpha) |
| + texture_image[PartInd - 1, y_index, x_index] * part_alpha |
| ) |
|
|
| return generated_image_bgr.astype(np.uint8) |
|
|