File size: 6,539 Bytes
10076ea
28c5d43
 
 
 
10076ea
 
28c5d43
 
 
 
 
 
10076ea
 
28c5d43
 
10076ea
28c5d43
 
a39331b
28c5d43
10076ea
28c5d43
3ceff75
28c5d43
 
26b0060
 
 
 
 
 
28c5d43
26b0060
28c5d43
 
26b0060
 
28c5d43
10076ea
26b0060
28c5d43
 
 
 
 
 
 
 
 
26b0060
 
28c5d43
 
f04a56f
28c5d43
 
 
 
 
 
 
 
10076ea
e099dae
 
 
 
 
28c5d43
10076ea
e099dae
28c5d43
10076ea
e099dae
 
 
 
3ceff75
e099dae
 
 
3ceff75
 
 
 
 
e099dae
3ceff75
e099dae
28c5d43
10076ea
3ceff75
 
e099dae
28c5d43
10076ea
 
 
 
 
 
 
e099dae
 
 
 
 
 
 
 
 
 
28c5d43
e099dae
 
28c5d43
e099dae
 
28c5d43
e099dae
28c5d43
e099dae
 
28c5d43
e099dae
 
28c5d43
e099dae
a39331b
28c5d43
e099dae
 
28c5d43
e099dae
28c5d43
e099dae
 
 
 
28c5d43
e099dae
28c5d43
10076ea
e099dae
 
 
 
 
28c5d43
10076ea
e099dae
 
 
 
 
 
 
28c5d43
10076ea
e099dae
 
3ceff75
28c5d43
10076ea
28c5d43
f04a56f
 
 
 
 
a39331b
3ceff75
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
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