Spaces:
Runtime error
Runtime error
import io | |
import cv2 | |
import imageio | |
import numpy as np | |
import torch | |
from typing import Dict, List | |
from fvcore.common.config import CfgNode | |
from detectron2.config import get_cfg | |
from detectron2.engine.defaults import DefaultPredictor | |
from detectron2.structures.instances import Instances | |
from densepose import add_densepose_config | |
from densepose.vis.base import CompoundVisualizer | |
from densepose.vis.densepose_outputs_vertex import get_texture_atlases | |
from densepose.vis.densepose_results_textures import DensePoseResultsVisualizerWithTexture as dp_iuv_texture | |
from densepose.vis.extractor import CompoundExtractor, create_extractor, DensePoseResultExtractor | |
class TextureProcessor: | |
def __init__(self, config: str, weights: str) -> None: | |
self.config = self.get_config(config, weights) | |
self.predictor = DefaultPredictor(self.config) | |
self.extractor = DensePoseResultExtractor() | |
def process_texture(self, image: np.ndarray) -> np.ndarray: | |
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) | |
output = self.execute(image) | |
if 'pred_densepose' in output: | |
texture = self.create_iuv(output, image) | |
atlas_texture_bytes = io.BytesIO() | |
imageio.imwrite(atlas_texture_bytes, texture, format='PNG') | |
texture_atlas_array = np.frombuffer(atlas_texture_bytes.getvalue(), dtype=np.uint8) | |
texture_atlas = cv2.imdecode(texture_atlas_array, cv2.IMREAD_COLOR) | |
texture_atlas = cv2.cvtColor(texture_atlas, cv2.COLOR_BGR2RGB) | |
return texture_atlas | |
else: | |
raise Exception('Clothes not found') | |
def extract(self, person_img, model_img): | |
texture_atlas = self.process_texture(model_img) | |
return self.overlay_texture(texture_atlas, person_img) | |
def overlay_texture(self, texture_atlas: np.ndarray, original_image: np.ndarray) -> np.ndarray: | |
texture_atlas[:, :, :3] = texture_atlas[:, :, 2::-1] | |
texture_atlases_dict = get_texture_atlases(None) | |
vis = dp_iuv_texture( | |
cfg=self.config, | |
texture_atlas=texture_atlas, | |
texture_atlases_dict=texture_atlases_dict | |
) | |
extractor = create_extractor(vis) | |
visualizer = CompoundVisualizer([vis]) | |
extractor = CompoundExtractor([extractor]) | |
with torch.no_grad(): | |
outputs = self.predictor(original_image)['instances'] | |
image = cv2.cvtColor(original_image, cv2.COLOR_BGR2GRAY) | |
image = np.tile(image[:, :, np.newaxis], [1, 1, 3]) | |
data = extractor(outputs) | |
image_vis = visualizer.visualize(image, data) | |
return image_vis | |
def parse_iuv(self, result: Dict) -> np.ndarray: | |
i = result['pred_densepose'][0].labels.cpu().numpy().astype(float) | |
uv = (result['pred_densepose'][0].uv.cpu().numpy() * 255.0).astype(float) | |
iuv = np.stack((uv[1, :, :], uv[0, :, :], i)) | |
iuv = np.transpose(iuv, (1, 2, 0)) | |
return iuv | |
def parse_bbox(self, result: Dict) -> np.ndarray: | |
return result['pred_boxes_XYXY'][0].cpu().numpy() | |
def interpolate_tex(self, tex: np.ndarray) -> np.ndarray: | |
valid_mask = np.array((tex.sum(0) != 0) * 1, dtype='uint8') | |
radius_increase = 10 | |
kernel = np.ones((radius_increase, radius_increase), np.uint8) | |
dilated_mask = cv2.dilate(valid_mask, kernel, iterations=1) | |
invalid_region = 1 - valid_mask | |
actual_part_max = tex.max() | |
actual_part_min = tex.min() | |
actual_part_uint = np.array((tex - actual_part_min) / (actual_part_max - actual_part_min) * 255, dtype='uint8') | |
actual_part_uint = cv2.inpaint(actual_part_uint.transpose((1, 2, 0)), invalid_region, 1, cv2.INPAINT_TELEA).transpose((2, 0, 1)) | |
actual_part = (actual_part_uint / 255.0) * (actual_part_max - actual_part_min) + actual_part_min | |
actual_part = actual_part * dilated_mask | |
return actual_part | |
def concat_textures(self, array: List[np.ndarray]) -> np.ndarray: | |
texture_rows = [np.concatenate(array[i:i+6], axis=1) for i in range(0, 24, 6)] | |
texture = np.concatenate(texture_rows, axis=0) | |
return texture | |
def get_texture( | |
self, | |
im: np.ndarray, | |
iuv: np.ndarray, | |
bbox: List[int], | |
tex_part_size: int = 200) -> np.ndarray: | |
im = im.transpose(2, 1, 0) / 255 | |
image_w, image_h = im.shape[1], im.shape[2] | |
bbox[2] = bbox[2] - bbox[0] | |
bbox[3] = bbox[3] - bbox[1] | |
x, y, w, h = [int(v) for v in bbox] | |
bg = np.zeros((image_h, image_w, 3)) | |
bg[y:y + h, x:x + w, :] = iuv | |
iuv = bg | |
iuv = iuv.transpose((2, 1, 0)) | |
i, u, v = iuv[2], iuv[1], iuv[0] | |
n_parts = 22 | |
texture = np.zeros((n_parts, 3, tex_part_size, tex_part_size)) | |
for part_id in range(1, n_parts + 1): | |
generated = np.zeros((3, tex_part_size, tex_part_size)) | |
x, y = u[i == part_id], v[i == part_id] | |
tex_u_coo = (x * (tex_part_size - 1) / 255).astype(int) | |
tex_v_coo = (y * (tex_part_size - 1) / 255).astype(int) | |
tex_u_coo = np.clip(tex_u_coo, 0, tex_part_size - 1) | |
tex_v_coo = np.clip(tex_v_coo, 0, tex_part_size - 1) | |
for channel in range(3): | |
generated[channel][tex_v_coo, tex_u_coo] = im[channel][i == part_id] | |
if np.sum(generated) > 0: | |
generated = self.interpolate_tex(generated) | |
texture[part_id - 1] = generated[:, ::-1, :] | |
tex_concat = np.zeros((24, tex_part_size, tex_part_size, 3)) | |
for i in range(texture.shape[0]): | |
tex_concat[i] = texture[i].transpose(2, 1, 0) | |
tex = self.concat_textures(tex_concat) | |
return tex | |
def create_iuv(self, results: Dict, image: np.ndarray) -> np.ndarray: | |
iuv = self.parse_iuv(results) | |
bbox = self.parse_bbox(results) | |
uv_texture = self.get_texture(image, iuv, bbox) | |
uv_texture = uv_texture.transpose([1, 0, 2]) | |
return uv_texture | |
def get_config(self, config_fpath: str, model_fpath: str) -> CfgNode: | |
cfg = get_cfg() | |
add_densepose_config(cfg) | |
cfg.merge_from_file(config_fpath) | |
cfg.MODEL.WEIGHTS = model_fpath | |
cfg.MODEL.DEVICE = 'cpu' | |
cfg.freeze() | |
return cfg | |
def execute(self, image: np.ndarray) -> Dict: | |
with torch.no_grad(): | |
outputs = self.predictor(image)['instances'] | |
return self.execute_on_outputs(outputs) | |
def execute_on_outputs(self, outputs: Instances) -> Dict: | |
result = {} | |
if outputs.has('scores'): | |
result['scores'] = outputs.get('scores').cpu() | |
if outputs.has('pred_boxes'): | |
result['pred_boxes_XYXY'] = outputs.get('pred_boxes').tensor.cpu() | |
if outputs.has('pred_densepose'): | |
result['pred_densepose'] = self.extractor(outputs)[0] | |
return result |