ML-Motivators commited on
Commit
c5c8fc9
·
verified ·
1 Parent(s): 3278c29

uploads files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. gradio_demo/__pycache__/apply_net.cpython-310.pyc +0 -0
  2. gradio_demo/__pycache__/utils_mask.cpython-310.pyc +0 -0
  3. gradio_demo/app.py +315 -0
  4. gradio_demo/apply_net.py +359 -0
  5. gradio_demo/densepose/__init__.py +20 -0
  6. gradio_demo/densepose/__pycache__/__init__.cpython-310.pyc +0 -0
  7. gradio_demo/densepose/__pycache__/config.cpython-310.pyc +0 -0
  8. gradio_demo/densepose/config.py +277 -0
  9. gradio_demo/densepose/converters/__init__.py +15 -0
  10. gradio_demo/densepose/converters/__pycache__/__init__.cpython-310.pyc +0 -0
  11. gradio_demo/densepose/converters/__pycache__/base.cpython-310.pyc +0 -0
  12. gradio_demo/densepose/converters/__pycache__/builtin.cpython-310.pyc +0 -0
  13. gradio_demo/densepose/converters/__pycache__/chart_output_hflip.cpython-310.pyc +0 -0
  14. gradio_demo/densepose/converters/__pycache__/chart_output_to_chart_result.cpython-310.pyc +0 -0
  15. gradio_demo/densepose/converters/__pycache__/hflip.cpython-310.pyc +0 -0
  16. gradio_demo/densepose/converters/__pycache__/segm_to_mask.cpython-310.pyc +0 -0
  17. gradio_demo/densepose/converters/__pycache__/to_chart_result.cpython-310.pyc +0 -0
  18. gradio_demo/densepose/converters/__pycache__/to_mask.cpython-310.pyc +0 -0
  19. gradio_demo/densepose/converters/base.py +93 -0
  20. gradio_demo/densepose/converters/builtin.py +31 -0
  21. gradio_demo/densepose/converters/chart_output_hflip.py +71 -0
  22. gradio_demo/densepose/converters/chart_output_to_chart_result.py +188 -0
  23. gradio_demo/densepose/converters/hflip.py +34 -0
  24. gradio_demo/densepose/converters/segm_to_mask.py +150 -0
  25. gradio_demo/densepose/converters/to_chart_result.py +70 -0
  26. gradio_demo/densepose/converters/to_mask.py +49 -0
  27. gradio_demo/densepose/data/__init__.py +25 -0
  28. gradio_demo/densepose/data/__pycache__/__init__.cpython-310.pyc +0 -0
  29. gradio_demo/densepose/data/__pycache__/build.cpython-310.pyc +0 -0
  30. gradio_demo/densepose/data/__pycache__/combined_loader.cpython-310.pyc +0 -0
  31. gradio_demo/densepose/data/__pycache__/dataset_mapper.cpython-310.pyc +0 -0
  32. gradio_demo/densepose/data/__pycache__/image_list_dataset.cpython-310.pyc +0 -0
  33. gradio_demo/densepose/data/__pycache__/inference_based_loader.cpython-310.pyc +0 -0
  34. gradio_demo/densepose/data/__pycache__/utils.cpython-310.pyc +0 -0
  35. gradio_demo/densepose/data/build.py +736 -0
  36. gradio_demo/densepose/data/combined_loader.py +44 -0
  37. gradio_demo/densepose/data/dataset_mapper.py +168 -0
  38. gradio_demo/densepose/data/datasets/__init__.py +5 -0
  39. gradio_demo/densepose/data/datasets/__pycache__/__init__.cpython-310.pyc +0 -0
  40. gradio_demo/densepose/data/datasets/__pycache__/builtin.cpython-310.pyc +0 -0
  41. gradio_demo/densepose/data/datasets/__pycache__/chimpnsee.cpython-310.pyc +0 -0
  42. gradio_demo/densepose/data/datasets/__pycache__/coco.cpython-310.pyc +0 -0
  43. gradio_demo/densepose/data/datasets/__pycache__/dataset_type.cpython-310.pyc +0 -0
  44. gradio_demo/densepose/data/datasets/__pycache__/lvis.cpython-310.pyc +0 -0
  45. gradio_demo/densepose/data/datasets/builtin.py +16 -0
  46. gradio_demo/densepose/data/datasets/chimpnsee.py +29 -0
  47. gradio_demo/densepose/data/datasets/coco.py +432 -0
  48. gradio_demo/densepose/data/datasets/dataset_type.py +11 -0
  49. gradio_demo/densepose/data/datasets/lvis.py +257 -0
  50. gradio_demo/densepose/data/image_list_dataset.py +72 -0
gradio_demo/__pycache__/apply_net.cpython-310.pyc ADDED
Binary file (11.5 kB). View file
 
gradio_demo/__pycache__/utils_mask.cpython-310.pyc ADDED
Binary file (4.88 kB). View file
 
gradio_demo/app.py ADDED
@@ -0,0 +1,315 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import sys
3
+
4
+ sys.path.append('./')
5
+ from PIL import Image
6
+ import gradio as gr
7
+ from src.tryon_pipeline import StableDiffusionXLInpaintPipeline as TryonPipeline
8
+ from src.unet_hacked_garmnet import UNet2DConditionModel as UNet2DConditionModel_ref
9
+ from src.unet_hacked_tryon import UNet2DConditionModel
10
+ from transformers import (
11
+ CLIPImageProcessor,
12
+ CLIPVisionModelWithProjection,
13
+ CLIPTextModel,
14
+ CLIPTextModelWithProjection,
15
+ )
16
+ from diffusers import DDPMScheduler,AutoencoderKL
17
+ from typing import List
18
+
19
+ import torch
20
+ import os
21
+ from transformers import AutoTokenizer
22
+ import numpy as np
23
+ from utils_mask import get_mask_location
24
+ from torchvision import transforms
25
+ import apply_net
26
+ from preprocess.humanparsing.run_parsing import Parsing
27
+ from preprocess.openpose.run_openpose import OpenPose
28
+ from detectron2.data.detection_utils import convert_PIL_to_numpy,_apply_exif_orientation
29
+ from torchvision.transforms.functional import to_pil_image
30
+
31
+ device = 'cuda:0' if torch.cuda.is_available() else 'cpu'
32
+
33
+ def pil_to_binary_mask(pil_image, threshold=0):
34
+ np_image = np.array(pil_image)
35
+ grayscale_image = Image.fromarray(np_image).convert("L")
36
+ binary_mask = np.array(grayscale_image) > threshold
37
+ mask = np.zeros(binary_mask.shape, dtype=np.uint8)
38
+ for i in range(binary_mask.shape[0]):
39
+ for j in range(binary_mask.shape[1]):
40
+ if binary_mask[i,j] == True :
41
+ mask[i,j] = 1
42
+ mask = (mask*255).astype(np.uint8)
43
+ output_mask = Image.fromarray(mask)
44
+ return output_mask
45
+
46
+
47
+ base_path = 'yisol/IDM-VTON'
48
+ example_path = os.path.join(os.path.dirname(__file__), 'example')
49
+
50
+ unet = UNet2DConditionModel.from_pretrained(
51
+ base_path,
52
+ subfolder="unet",
53
+ torch_dtype=torch.float16,
54
+ )
55
+ unet.requires_grad_(False)
56
+ tokenizer_one = AutoTokenizer.from_pretrained(
57
+ base_path,
58
+ subfolder="tokenizer",
59
+ revision=None,
60
+ use_fast=False,
61
+ )
62
+ tokenizer_two = AutoTokenizer.from_pretrained(
63
+ base_path,
64
+ subfolder="tokenizer_2",
65
+ revision=None,
66
+ use_fast=False,
67
+ )
68
+ noise_scheduler = DDPMScheduler.from_pretrained(base_path, subfolder="scheduler")
69
+
70
+ text_encoder_one = CLIPTextModel.from_pretrained(
71
+ base_path,
72
+ subfolder="text_encoder",
73
+ torch_dtype=torch.float16,
74
+ )
75
+ text_encoder_two = CLIPTextModelWithProjection.from_pretrained(
76
+ base_path,
77
+ subfolder="text_encoder_2",
78
+ torch_dtype=torch.float16,
79
+ )
80
+ image_encoder = CLIPVisionModelWithProjection.from_pretrained(
81
+ base_path,
82
+ subfolder="image_encoder",
83
+ torch_dtype=torch.float16,
84
+ )
85
+ vae = AutoencoderKL.from_pretrained(base_path,
86
+ subfolder="vae",
87
+ torch_dtype=torch.float16,
88
+ )
89
+
90
+ # "stabilityai/stable-diffusion-xl-base-1.0",
91
+ UNet_Encoder = UNet2DConditionModel_ref.from_pretrained(
92
+ base_path,
93
+ subfolder="unet_encoder",
94
+ torch_dtype=torch.float16,
95
+ )
96
+
97
+ parsing_model = Parsing(0)
98
+ openpose_model = OpenPose(0)
99
+
100
+ UNet_Encoder.requires_grad_(False)
101
+ image_encoder.requires_grad_(False)
102
+ vae.requires_grad_(False)
103
+ unet.requires_grad_(False)
104
+ text_encoder_one.requires_grad_(False)
105
+ text_encoder_two.requires_grad_(False)
106
+ tensor_transfrom = transforms.Compose(
107
+ [
108
+ transforms.ToTensor(),
109
+ transforms.Normalize([0.5], [0.5]),
110
+ ]
111
+ )
112
+
113
+ pipe = TryonPipeline.from_pretrained(
114
+ base_path,
115
+ unet=unet,
116
+ vae=vae,
117
+ feature_extractor= CLIPImageProcessor(),
118
+ text_encoder = text_encoder_one,
119
+ text_encoder_2 = text_encoder_two,
120
+ tokenizer = tokenizer_one,
121
+ tokenizer_2 = tokenizer_two,
122
+ scheduler = noise_scheduler,
123
+ image_encoder=image_encoder,
124
+ torch_dtype=torch.float16,
125
+ )
126
+ pipe.unet_encoder = UNet_Encoder
127
+
128
+ def start_tryon(dict,garm_img,garment_des,is_checked,is_checked_crop,denoise_steps,seed):
129
+
130
+ openpose_model.preprocessor.body_estimation.model.to(device)
131
+ pipe.to(device)
132
+ pipe.unet_encoder.to(device)
133
+
134
+ garm_img= garm_img.convert("RGB").resize((768,1024))
135
+ human_img_orig = dict["background"].convert("RGB")
136
+
137
+ if is_checked_crop:
138
+ width, height = human_img_orig.size
139
+ target_width = int(min(width, height * (3 / 4)))
140
+ target_height = int(min(height, width * (4 / 3)))
141
+ left = (width - target_width) / 2
142
+ top = (height - target_height) / 2
143
+ right = (width + target_width) / 2
144
+ bottom = (height + target_height) / 2
145
+ cropped_img = human_img_orig.crop((left, top, right, bottom))
146
+ crop_size = cropped_img.size
147
+ human_img = cropped_img.resize((768,1024))
148
+ else:
149
+ human_img = human_img_orig.resize((768,1024))
150
+
151
+
152
+ if is_checked:
153
+ keypoints = openpose_model(human_img.resize((384,512)))
154
+ model_parse, _ = parsing_model(human_img.resize((384,512)))
155
+ mask, mask_gray = get_mask_location('hd', "upper_body", model_parse, keypoints)
156
+ mask = mask.resize((768,1024))
157
+ else:
158
+ mask = pil_to_binary_mask(dict['layers'][0].convert("RGB").resize((768, 1024)))
159
+ # mask = transforms.ToTensor()(mask)
160
+ # mask = mask.unsqueeze(0)
161
+ mask_gray = (1-transforms.ToTensor()(mask)) * tensor_transfrom(human_img)
162
+ mask_gray = to_pil_image((mask_gray+1.0)/2.0)
163
+
164
+
165
+ human_img_arg = _apply_exif_orientation(human_img.resize((384,512)))
166
+ human_img_arg = convert_PIL_to_numpy(human_img_arg, format="BGR")
167
+
168
+
169
+
170
+ args = apply_net.create_argument_parser().parse_args(('show', './configs/densepose_rcnn_R_50_FPN_s1x.yaml', './ckpt/densepose/model_final_162be9.pkl', 'dp_segm', '-v', '--opts', 'MODEL.DEVICE', 'cuda'))
171
+ # verbosity = getattr(args, "verbosity", None)
172
+ pose_img = args.func(args,human_img_arg)
173
+ pose_img = pose_img[:,:,::-1]
174
+ pose_img = Image.fromarray(pose_img).resize((768,1024))
175
+
176
+ with torch.no_grad():
177
+ # Extract the images
178
+ with torch.cuda.amp.autocast():
179
+ with torch.no_grad():
180
+ prompt = "model is wearing " + garment_des
181
+ negative_prompt = "monochrome, lowres, bad anatomy, worst quality, low quality"
182
+ with torch.inference_mode():
183
+ (
184
+ prompt_embeds,
185
+ negative_prompt_embeds,
186
+ pooled_prompt_embeds,
187
+ negative_pooled_prompt_embeds,
188
+ ) = pipe.encode_prompt(
189
+ prompt,
190
+ num_images_per_prompt=1,
191
+ do_classifier_free_guidance=True,
192
+ negative_prompt=negative_prompt,
193
+ )
194
+
195
+ prompt = "a photo of " + garment_des
196
+ negative_prompt = "monochrome, lowres, bad anatomy, worst quality, low quality"
197
+ if not isinstance(prompt, List):
198
+ prompt = [prompt] * 1
199
+ if not isinstance(negative_prompt, List):
200
+ negative_prompt = [negative_prompt] * 1
201
+ with torch.inference_mode():
202
+ (
203
+ prompt_embeds_c,
204
+ _,
205
+ _,
206
+ _,
207
+ ) = pipe.encode_prompt(
208
+ prompt,
209
+ num_images_per_prompt=1,
210
+ do_classifier_free_guidance=False,
211
+ negative_prompt=negative_prompt,
212
+ )
213
+
214
+
215
+
216
+ pose_img = tensor_transfrom(pose_img).unsqueeze(0).to(device,torch.float16)
217
+ garm_tensor = tensor_transfrom(garm_img).unsqueeze(0).to(device,torch.float16)
218
+ generator = torch.Generator(device).manual_seed(seed) if seed is not None else None
219
+ images = pipe(
220
+ prompt_embeds=prompt_embeds.to(device,torch.float16),
221
+ negative_prompt_embeds=negative_prompt_embeds.to(device,torch.float16),
222
+ pooled_prompt_embeds=pooled_prompt_embeds.to(device,torch.float16),
223
+ negative_pooled_prompt_embeds=negative_pooled_prompt_embeds.to(device,torch.float16),
224
+ num_inference_steps=denoise_steps,
225
+ generator=generator,
226
+ strength = 1.0,
227
+ pose_img = pose_img.to(device,torch.float16),
228
+ text_embeds_cloth=prompt_embeds_c.to(device,torch.float16),
229
+ cloth = garm_tensor.to(device,torch.float16),
230
+ mask_image=mask,
231
+ image=human_img,
232
+ height=1024,
233
+ width=768,
234
+ ip_adapter_image = garm_img.resize((768,1024)),
235
+ guidance_scale=2.0,
236
+ )[0]
237
+
238
+ if is_checked_crop:
239
+ out_img = images[0].resize(crop_size)
240
+ human_img_orig.paste(out_img, (int(left), int(top)))
241
+ return human_img_orig, mask_gray
242
+ else:
243
+ return images[0], mask_gray
244
+ # return images[0], mask_gray
245
+
246
+ garm_list = os.listdir(os.path.join(example_path,"cloth"))
247
+ garm_list_path = [os.path.join(example_path,"cloth",garm) for garm in garm_list]
248
+
249
+ human_list = os.listdir(os.path.join(example_path,"human"))
250
+ human_list_path = [os.path.join(example_path,"human",human) for human in human_list]
251
+
252
+ human_ex_list = []
253
+ for ex_human in human_list_path:
254
+ ex_dict= {}
255
+ ex_dict['background'] = ex_human
256
+ ex_dict['layers'] = None
257
+ ex_dict['composite'] = None
258
+ human_ex_list.append(ex_dict)
259
+
260
+ ##default human
261
+
262
+
263
+ image_blocks = gr.Blocks().queue()
264
+ with image_blocks as demo:
265
+ gr.Markdown("## IDM-VTON 👕👔👚")
266
+ gr.Markdown("Virtual Try-on with your image and garment image. Check out the [source codes](https://github.com/yisol/IDM-VTON) and the [model](https://huggingface.co/yisol/IDM-VTON)")
267
+ with gr.Row():
268
+ with gr.Column():
269
+ imgs = gr.ImageEditor(sources='upload', type="pil", label='Human. Mask with pen or use auto-masking', interactive=True)
270
+ with gr.Row():
271
+ is_checked = gr.Checkbox(label="Yes", info="Use auto-generated mask (Takes 5 seconds)",value=True)
272
+ with gr.Row():
273
+ is_checked_crop = gr.Checkbox(label="Yes", info="Use auto-crop & resizing",value=False)
274
+
275
+ example = gr.Examples(
276
+ inputs=imgs,
277
+ examples_per_page=10,
278
+ examples=human_ex_list
279
+ )
280
+
281
+ with gr.Column():
282
+ garm_img = gr.Image(label="Garment", sources='upload', type="pil")
283
+ with gr.Row(elem_id="prompt-container"):
284
+ with gr.Row():
285
+ prompt = gr.Textbox(placeholder="Description of garment ex) Short Sleeve Round Neck T-shirts", show_label=False, elem_id="prompt")
286
+ example = gr.Examples(
287
+ inputs=garm_img,
288
+ examples_per_page=8,
289
+ examples=garm_list_path)
290
+ with gr.Column():
291
+ # image_out = gr.Image(label="Output", elem_id="output-img", height=400)
292
+ masked_img = gr.Image(label="Masked image output", elem_id="masked-img",show_share_button=False)
293
+ with gr.Column():
294
+ # image_out = gr.Image(label="Output", elem_id="output-img", height=400)
295
+ image_out = gr.Image(label="Output", elem_id="output-img",show_share_button=False)
296
+
297
+
298
+
299
+
300
+ with gr.Column():
301
+ try_button = gr.Button(value="Try-on")
302
+ with gr.Accordion(label="Advanced Settings", open=False):
303
+ with gr.Row():
304
+ denoise_steps = gr.Number(label="Denoising Steps", minimum=20, maximum=40, value=30, step=1)
305
+ seed = gr.Number(label="Seed", minimum=-1, maximum=2147483647, step=1, value=42)
306
+
307
+
308
+
309
+ try_button.click(fn=start_tryon, inputs=[imgs, garm_img, prompt, is_checked,is_checked_crop, denoise_steps, seed], outputs=[image_out,masked_img], api_name='tryon')
310
+
311
+
312
+
313
+
314
+ image_blocks.launch()
315
+
gradio_demo/apply_net.py ADDED
@@ -0,0 +1,359 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # Copyright (c) Facebook, Inc. and its affiliates.
3
+
4
+ import argparse
5
+ import glob
6
+ import logging
7
+ import os
8
+ import sys
9
+ from typing import Any, ClassVar, Dict, List
10
+ import torch
11
+
12
+ from detectron2.config import CfgNode, get_cfg
13
+ from detectron2.data.detection_utils import read_image
14
+ from detectron2.engine.defaults import DefaultPredictor
15
+ from detectron2.structures.instances import Instances
16
+ from detectron2.utils.logger import setup_logger
17
+
18
+ from densepose import add_densepose_config
19
+ from densepose.structures import DensePoseChartPredictorOutput, DensePoseEmbeddingPredictorOutput
20
+ from densepose.utils.logger import verbosity_to_level
21
+ from densepose.vis.base import CompoundVisualizer
22
+ from densepose.vis.bounding_box import ScoredBoundingBoxVisualizer
23
+ from densepose.vis.densepose_outputs_vertex import (
24
+ DensePoseOutputsTextureVisualizer,
25
+ DensePoseOutputsVertexVisualizer,
26
+ get_texture_atlases,
27
+ )
28
+ from densepose.vis.densepose_results import (
29
+ DensePoseResultsContourVisualizer,
30
+ DensePoseResultsFineSegmentationVisualizer,
31
+ DensePoseResultsUVisualizer,
32
+ DensePoseResultsVVisualizer,
33
+ )
34
+ from densepose.vis.densepose_results_textures import (
35
+ DensePoseResultsVisualizerWithTexture,
36
+ get_texture_atlas,
37
+ )
38
+ from densepose.vis.extractor import (
39
+ CompoundExtractor,
40
+ DensePoseOutputsExtractor,
41
+ DensePoseResultExtractor,
42
+ create_extractor,
43
+ )
44
+
45
+ DOC = """Apply Net - a tool to print / visualize DensePose results
46
+ """
47
+
48
+ LOGGER_NAME = "apply_net"
49
+ logger = logging.getLogger(LOGGER_NAME)
50
+
51
+ _ACTION_REGISTRY: Dict[str, "Action"] = {}
52
+
53
+
54
+ class Action:
55
+ @classmethod
56
+ def add_arguments(cls: type, parser: argparse.ArgumentParser):
57
+ parser.add_argument(
58
+ "-v",
59
+ "--verbosity",
60
+ action="count",
61
+ help="Verbose mode. Multiple -v options increase the verbosity.",
62
+ )
63
+
64
+
65
+ def register_action(cls: type):
66
+ """
67
+ Decorator for action classes to automate action registration
68
+ """
69
+ global _ACTION_REGISTRY
70
+ _ACTION_REGISTRY[cls.COMMAND] = cls
71
+ return cls
72
+
73
+
74
+ class InferenceAction(Action):
75
+ @classmethod
76
+ def add_arguments(cls: type, parser: argparse.ArgumentParser):
77
+ super(InferenceAction, cls).add_arguments(parser)
78
+ parser.add_argument("cfg", metavar="<config>", help="Config file")
79
+ parser.add_argument("model", metavar="<model>", help="Model file")
80
+ parser.add_argument(
81
+ "--opts",
82
+ help="Modify config options using the command-line 'KEY VALUE' pairs",
83
+ default=[],
84
+ nargs=argparse.REMAINDER,
85
+ )
86
+
87
+ @classmethod
88
+ def execute(cls: type, args: argparse.Namespace, human_img):
89
+ logger.info(f"Loading config from {args.cfg}")
90
+ opts = []
91
+ cfg = cls.setup_config(args.cfg, args.model, args, opts)
92
+ logger.info(f"Loading model from {args.model}")
93
+ predictor = DefaultPredictor(cfg)
94
+ # logger.info(f"Loading data from {args.input}")
95
+ # file_list = cls._get_input_file_list(args.input)
96
+ # if len(file_list) == 0:
97
+ # logger.warning(f"No input images for {args.input}")
98
+ # return
99
+ context = cls.create_context(args, cfg)
100
+ # for file_name in file_list:
101
+ # img = read_image(file_name, format="BGR") # predictor expects BGR image.
102
+ with torch.no_grad():
103
+ outputs = predictor(human_img)["instances"]
104
+ out_pose = cls.execute_on_outputs(context, {"image": human_img}, outputs)
105
+ cls.postexecute(context)
106
+ return out_pose
107
+
108
+ @classmethod
109
+ def setup_config(
110
+ cls: type, config_fpath: str, model_fpath: str, args: argparse.Namespace, opts: List[str]
111
+ ):
112
+ cfg = get_cfg()
113
+ add_densepose_config(cfg)
114
+ cfg.merge_from_file(config_fpath)
115
+ cfg.merge_from_list(args.opts)
116
+ if opts:
117
+ cfg.merge_from_list(opts)
118
+ cfg.MODEL.WEIGHTS = model_fpath
119
+ cfg.freeze()
120
+ return cfg
121
+
122
+ @classmethod
123
+ def _get_input_file_list(cls: type, input_spec: str):
124
+ if os.path.isdir(input_spec):
125
+ file_list = [
126
+ os.path.join(input_spec, fname)
127
+ for fname in os.listdir(input_spec)
128
+ if os.path.isfile(os.path.join(input_spec, fname))
129
+ ]
130
+ elif os.path.isfile(input_spec):
131
+ file_list = [input_spec]
132
+ else:
133
+ file_list = glob.glob(input_spec)
134
+ return file_list
135
+
136
+
137
+ @register_action
138
+ class DumpAction(InferenceAction):
139
+ """
140
+ Dump action that outputs results to a pickle file
141
+ """
142
+
143
+ COMMAND: ClassVar[str] = "dump"
144
+
145
+ @classmethod
146
+ def add_parser(cls: type, subparsers: argparse._SubParsersAction):
147
+ parser = subparsers.add_parser(cls.COMMAND, help="Dump model outputs to a file.")
148
+ cls.add_arguments(parser)
149
+ parser.set_defaults(func=cls.execute)
150
+
151
+ @classmethod
152
+ def add_arguments(cls: type, parser: argparse.ArgumentParser):
153
+ super(DumpAction, cls).add_arguments(parser)
154
+ parser.add_argument(
155
+ "--output",
156
+ metavar="<dump_file>",
157
+ default="results.pkl",
158
+ help="File name to save dump to",
159
+ )
160
+
161
+ @classmethod
162
+ def execute_on_outputs(
163
+ cls: type, context: Dict[str, Any], entry: Dict[str, Any], outputs: Instances
164
+ ):
165
+ image_fpath = entry["file_name"]
166
+ logger.info(f"Processing {image_fpath}")
167
+ result = {"file_name": image_fpath}
168
+ if outputs.has("scores"):
169
+ result["scores"] = outputs.get("scores").cpu()
170
+ if outputs.has("pred_boxes"):
171
+ result["pred_boxes_XYXY"] = outputs.get("pred_boxes").tensor.cpu()
172
+ if outputs.has("pred_densepose"):
173
+ if isinstance(outputs.pred_densepose, DensePoseChartPredictorOutput):
174
+ extractor = DensePoseResultExtractor()
175
+ elif isinstance(outputs.pred_densepose, DensePoseEmbeddingPredictorOutput):
176
+ extractor = DensePoseOutputsExtractor()
177
+ result["pred_densepose"] = extractor(outputs)[0]
178
+ context["results"].append(result)
179
+
180
+ @classmethod
181
+ def create_context(cls: type, args: argparse.Namespace, cfg: CfgNode):
182
+ context = {"results": [], "out_fname": args.output}
183
+ return context
184
+
185
+ @classmethod
186
+ def postexecute(cls: type, context: Dict[str, Any]):
187
+ out_fname = context["out_fname"]
188
+ out_dir = os.path.dirname(out_fname)
189
+ if len(out_dir) > 0 and not os.path.exists(out_dir):
190
+ os.makedirs(out_dir)
191
+ with open(out_fname, "wb") as hFile:
192
+ torch.save(context["results"], hFile)
193
+ logger.info(f"Output saved to {out_fname}")
194
+
195
+
196
+ @register_action
197
+ class ShowAction(InferenceAction):
198
+ """
199
+ Show action that visualizes selected entries on an image
200
+ """
201
+
202
+ COMMAND: ClassVar[str] = "show"
203
+ VISUALIZERS: ClassVar[Dict[str, object]] = {
204
+ "dp_contour": DensePoseResultsContourVisualizer,
205
+ "dp_segm": DensePoseResultsFineSegmentationVisualizer,
206
+ "dp_u": DensePoseResultsUVisualizer,
207
+ "dp_v": DensePoseResultsVVisualizer,
208
+ "dp_iuv_texture": DensePoseResultsVisualizerWithTexture,
209
+ "dp_cse_texture": DensePoseOutputsTextureVisualizer,
210
+ "dp_vertex": DensePoseOutputsVertexVisualizer,
211
+ "bbox": ScoredBoundingBoxVisualizer,
212
+ }
213
+
214
+ @classmethod
215
+ def add_parser(cls: type, subparsers: argparse._SubParsersAction):
216
+ parser = subparsers.add_parser(cls.COMMAND, help="Visualize selected entries")
217
+ cls.add_arguments(parser)
218
+ parser.set_defaults(func=cls.execute)
219
+
220
+ @classmethod
221
+ def add_arguments(cls: type, parser: argparse.ArgumentParser):
222
+ super(ShowAction, cls).add_arguments(parser)
223
+ parser.add_argument(
224
+ "visualizations",
225
+ metavar="<visualizations>",
226
+ help="Comma separated list of visualizations, possible values: "
227
+ "[{}]".format(",".join(sorted(cls.VISUALIZERS.keys()))),
228
+ )
229
+ parser.add_argument(
230
+ "--min_score",
231
+ metavar="<score>",
232
+ default=0.8,
233
+ type=float,
234
+ help="Minimum detection score to visualize",
235
+ )
236
+ parser.add_argument(
237
+ "--nms_thresh", metavar="<threshold>", default=None, type=float, help="NMS threshold"
238
+ )
239
+ parser.add_argument(
240
+ "--texture_atlas",
241
+ metavar="<texture_atlas>",
242
+ default=None,
243
+ help="Texture atlas file (for IUV texture transfer)",
244
+ )
245
+ parser.add_argument(
246
+ "--texture_atlases_map",
247
+ metavar="<texture_atlases_map>",
248
+ default=None,
249
+ help="JSON string of a dict containing texture atlas files for each mesh",
250
+ )
251
+ parser.add_argument(
252
+ "--output",
253
+ metavar="<image_file>",
254
+ default="outputres.png",
255
+ help="File name to save output to",
256
+ )
257
+
258
+ @classmethod
259
+ def setup_config(
260
+ cls: type, config_fpath: str, model_fpath: str, args: argparse.Namespace, opts: List[str]
261
+ ):
262
+ opts.append("MODEL.ROI_HEADS.SCORE_THRESH_TEST")
263
+ opts.append(str(args.min_score))
264
+ if args.nms_thresh is not None:
265
+ opts.append("MODEL.ROI_HEADS.NMS_THRESH_TEST")
266
+ opts.append(str(args.nms_thresh))
267
+ cfg = super(ShowAction, cls).setup_config(config_fpath, model_fpath, args, opts)
268
+ return cfg
269
+
270
+ @classmethod
271
+ def execute_on_outputs(
272
+ cls: type, context: Dict[str, Any], entry: Dict[str, Any], outputs: Instances
273
+ ):
274
+ import cv2
275
+ import numpy as np
276
+ visualizer = context["visualizer"]
277
+ extractor = context["extractor"]
278
+ # image_fpath = entry["file_name"]
279
+ # logger.info(f"Processing {image_fpath}")
280
+ image = cv2.cvtColor(entry["image"], cv2.COLOR_BGR2GRAY)
281
+ image = np.tile(image[:, :, np.newaxis], [1, 1, 3])
282
+ data = extractor(outputs)
283
+ image_vis = visualizer.visualize(image, data)
284
+
285
+ return image_vis
286
+ entry_idx = context["entry_idx"] + 1
287
+ out_fname = './image-densepose/' + image_fpath.split('/')[-1]
288
+ out_dir = './image-densepose'
289
+ out_dir = os.path.dirname(out_fname)
290
+ if len(out_dir) > 0 and not os.path.exists(out_dir):
291
+ os.makedirs(out_dir)
292
+ cv2.imwrite(out_fname, image_vis)
293
+ logger.info(f"Output saved to {out_fname}")
294
+ context["entry_idx"] += 1
295
+
296
+ @classmethod
297
+ def postexecute(cls: type, context: Dict[str, Any]):
298
+ pass
299
+ # python ./apply_net.py show ./configs/densepose_rcnn_R_50_FPN_s1x.yaml https://dl.fbaipublicfiles.com/densepose/densepose_rcnn_R_50_FPN_s1x/165712039/model_final_162be9.pkl /home/alin0222/DressCode/upper_body/images dp_segm -v --opts MODEL.DEVICE cpu
300
+
301
+ @classmethod
302
+ def _get_out_fname(cls: type, entry_idx: int, fname_base: str):
303
+ base, ext = os.path.splitext(fname_base)
304
+ return base + ".{0:04d}".format(entry_idx) + ext
305
+
306
+ @classmethod
307
+ def create_context(cls: type, args: argparse.Namespace, cfg: CfgNode) -> Dict[str, Any]:
308
+ vis_specs = args.visualizations.split(",")
309
+ visualizers = []
310
+ extractors = []
311
+ for vis_spec in vis_specs:
312
+ texture_atlas = get_texture_atlas(args.texture_atlas)
313
+ texture_atlases_dict = get_texture_atlases(args.texture_atlases_map)
314
+ vis = cls.VISUALIZERS[vis_spec](
315
+ cfg=cfg,
316
+ texture_atlas=texture_atlas,
317
+ texture_atlases_dict=texture_atlases_dict,
318
+ )
319
+ visualizers.append(vis)
320
+ extractor = create_extractor(vis)
321
+ extractors.append(extractor)
322
+ visualizer = CompoundVisualizer(visualizers)
323
+ extractor = CompoundExtractor(extractors)
324
+ context = {
325
+ "extractor": extractor,
326
+ "visualizer": visualizer,
327
+ "out_fname": args.output,
328
+ "entry_idx": 0,
329
+ }
330
+ return context
331
+
332
+
333
+ def create_argument_parser() -> argparse.ArgumentParser:
334
+ parser = argparse.ArgumentParser(
335
+ description=DOC,
336
+ formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=120),
337
+ )
338
+ parser.set_defaults(func=lambda _: parser.print_help(sys.stdout))
339
+ subparsers = parser.add_subparsers(title="Actions")
340
+ for _, action in _ACTION_REGISTRY.items():
341
+ action.add_parser(subparsers)
342
+ return parser
343
+
344
+
345
+ def main():
346
+ parser = create_argument_parser()
347
+ args = parser.parse_args()
348
+ verbosity = getattr(args, "verbosity", None)
349
+ global logger
350
+ logger = setup_logger(name=LOGGER_NAME)
351
+ logger.setLevel(verbosity_to_level(verbosity))
352
+ args.func(args)
353
+
354
+
355
+ if __name__ == "__main__":
356
+ main()
357
+
358
+
359
+ # python ./apply_net.py show ./configs/densepose_rcnn_R_50_FPN_s1x.yaml https://dl.fbaipublicfiles.com/densepose/densepose_rcnn_R_50_FPN_s1x/165712039/model_final_162be9.pkl /home/alin0222/Dresscode/dresses/humanonly dp_segm -v --opts MODEL.DEVICE cuda
gradio_demo/densepose/__init__.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ from .data.datasets import builtin # just to register data
3
+ from .converters import builtin as builtin_converters # register converters
4
+ from .config import (
5
+ add_densepose_config,
6
+ add_densepose_head_config,
7
+ add_hrnet_config,
8
+ add_dataset_category_config,
9
+ add_bootstrap_config,
10
+ load_bootstrap_config,
11
+ )
12
+ from .structures import DensePoseDataRelative, DensePoseList, DensePoseTransformData
13
+ from .evaluation import DensePoseCOCOEvaluator
14
+ from .modeling.roi_heads import DensePoseROIHeads
15
+ from .modeling.test_time_augmentation import (
16
+ DensePoseGeneralizedRCNNWithTTA,
17
+ DensePoseDatasetMapperTTA,
18
+ )
19
+ from .utils.transform import load_from_cfg
20
+ from .modeling.hrfpn import build_hrfpn_backbone
gradio_demo/densepose/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (922 Bytes). View file
 
gradio_demo/densepose/__pycache__/config.cpython-310.pyc ADDED
Binary file (5.83 kB). View file
 
gradio_demo/densepose/config.py ADDED
@@ -0,0 +1,277 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding = utf-8 -*-
2
+ # Copyright (c) Facebook, Inc. and its affiliates.
3
+ # pyre-ignore-all-errors
4
+
5
+ from detectron2.config import CfgNode as CN
6
+
7
+
8
+ def add_dataset_category_config(cfg: CN) -> None:
9
+ """
10
+ Add config for additional category-related dataset options
11
+ - category whitelisting
12
+ - category mapping
13
+ """
14
+ _C = cfg
15
+ _C.DATASETS.CATEGORY_MAPS = CN(new_allowed=True)
16
+ _C.DATASETS.WHITELISTED_CATEGORIES = CN(new_allowed=True)
17
+ # class to mesh mapping
18
+ _C.DATASETS.CLASS_TO_MESH_NAME_MAPPING = CN(new_allowed=True)
19
+
20
+
21
+ def add_evaluation_config(cfg: CN) -> None:
22
+ _C = cfg
23
+ _C.DENSEPOSE_EVALUATION = CN()
24
+ # evaluator type, possible values:
25
+ # - "iou": evaluator for models that produce iou data
26
+ # - "cse": evaluator for models that produce cse data
27
+ _C.DENSEPOSE_EVALUATION.TYPE = "iou"
28
+ # storage for DensePose results, possible values:
29
+ # - "none": no explicit storage, all the results are stored in the
30
+ # dictionary with predictions, memory intensive;
31
+ # historically the default storage type
32
+ # - "ram": RAM storage, uses per-process RAM storage, which is
33
+ # reduced to a single process storage on later stages,
34
+ # less memory intensive
35
+ # - "file": file storage, uses per-process file-based storage,
36
+ # the least memory intensive, but may create bottlenecks
37
+ # on file system accesses
38
+ _C.DENSEPOSE_EVALUATION.STORAGE = "none"
39
+ # minimum threshold for IOU values: the lower its values is,
40
+ # the more matches are produced (and the higher the AP score)
41
+ _C.DENSEPOSE_EVALUATION.MIN_IOU_THRESHOLD = 0.5
42
+ # Non-distributed inference is slower (at inference time) but can avoid RAM OOM
43
+ _C.DENSEPOSE_EVALUATION.DISTRIBUTED_INFERENCE = True
44
+ # evaluate mesh alignment based on vertex embeddings, only makes sense in CSE context
45
+ _C.DENSEPOSE_EVALUATION.EVALUATE_MESH_ALIGNMENT = False
46
+ # meshes to compute mesh alignment for
47
+ _C.DENSEPOSE_EVALUATION.MESH_ALIGNMENT_MESH_NAMES = []
48
+
49
+
50
+ def add_bootstrap_config(cfg: CN) -> None:
51
+ """ """
52
+ _C = cfg
53
+ _C.BOOTSTRAP_DATASETS = []
54
+ _C.BOOTSTRAP_MODEL = CN()
55
+ _C.BOOTSTRAP_MODEL.WEIGHTS = ""
56
+ _C.BOOTSTRAP_MODEL.DEVICE = "cuda"
57
+
58
+
59
+ def get_bootstrap_dataset_config() -> CN:
60
+ _C = CN()
61
+ _C.DATASET = ""
62
+ # ratio used to mix data loaders
63
+ _C.RATIO = 0.1
64
+ # image loader
65
+ _C.IMAGE_LOADER = CN(new_allowed=True)
66
+ _C.IMAGE_LOADER.TYPE = ""
67
+ _C.IMAGE_LOADER.BATCH_SIZE = 4
68
+ _C.IMAGE_LOADER.NUM_WORKERS = 4
69
+ _C.IMAGE_LOADER.CATEGORIES = []
70
+ _C.IMAGE_LOADER.MAX_COUNT_PER_CATEGORY = 1_000_000
71
+ _C.IMAGE_LOADER.CATEGORY_TO_CLASS_MAPPING = CN(new_allowed=True)
72
+ # inference
73
+ _C.INFERENCE = CN()
74
+ # batch size for model inputs
75
+ _C.INFERENCE.INPUT_BATCH_SIZE = 4
76
+ # batch size to group model outputs
77
+ _C.INFERENCE.OUTPUT_BATCH_SIZE = 2
78
+ # sampled data
79
+ _C.DATA_SAMPLER = CN(new_allowed=True)
80
+ _C.DATA_SAMPLER.TYPE = ""
81
+ _C.DATA_SAMPLER.USE_GROUND_TRUTH_CATEGORIES = False
82
+ # filter
83
+ _C.FILTER = CN(new_allowed=True)
84
+ _C.FILTER.TYPE = ""
85
+ return _C
86
+
87
+
88
+ def load_bootstrap_config(cfg: CN) -> None:
89
+ """
90
+ Bootstrap datasets are given as a list of `dict` that are not automatically
91
+ converted into CfgNode. This method processes all bootstrap dataset entries
92
+ and ensures that they are in CfgNode format and comply with the specification
93
+ """
94
+ if not cfg.BOOTSTRAP_DATASETS:
95
+ return
96
+
97
+ bootstrap_datasets_cfgnodes = []
98
+ for dataset_cfg in cfg.BOOTSTRAP_DATASETS:
99
+ _C = get_bootstrap_dataset_config().clone()
100
+ _C.merge_from_other_cfg(CN(dataset_cfg))
101
+ bootstrap_datasets_cfgnodes.append(_C)
102
+ cfg.BOOTSTRAP_DATASETS = bootstrap_datasets_cfgnodes
103
+
104
+
105
+ def add_densepose_head_cse_config(cfg: CN) -> None:
106
+ """
107
+ Add configuration options for Continuous Surface Embeddings (CSE)
108
+ """
109
+ _C = cfg
110
+ _C.MODEL.ROI_DENSEPOSE_HEAD.CSE = CN()
111
+ # Dimensionality D of the embedding space
112
+ _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.EMBED_SIZE = 16
113
+ # Embedder specifications for various mesh IDs
114
+ _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.EMBEDDERS = CN(new_allowed=True)
115
+ # normalization coefficient for embedding distances
116
+ _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.EMBEDDING_DIST_GAUSS_SIGMA = 0.01
117
+ # normalization coefficient for geodesic distances
118
+ _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.GEODESIC_DIST_GAUSS_SIGMA = 0.01
119
+ # embedding loss weight
120
+ _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.EMBED_LOSS_WEIGHT = 0.6
121
+ # embedding loss name, currently the following options are supported:
122
+ # - EmbeddingLoss: cross-entropy on vertex labels
123
+ # - SoftEmbeddingLoss: cross-entropy on vertex label combined with
124
+ # Gaussian penalty on distance between vertices
125
+ _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.EMBED_LOSS_NAME = "EmbeddingLoss"
126
+ # optimizer hyperparameters
127
+ _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.FEATURES_LR_FACTOR = 1.0
128
+ _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.EMBEDDING_LR_FACTOR = 1.0
129
+ # Shape to shape cycle consistency loss parameters:
130
+ _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.SHAPE_TO_SHAPE_CYCLE_LOSS = CN({"ENABLED": False})
131
+ # shape to shape cycle consistency loss weight
132
+ _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.SHAPE_TO_SHAPE_CYCLE_LOSS.WEIGHT = 0.025
133
+ # norm type used for loss computation
134
+ _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.SHAPE_TO_SHAPE_CYCLE_LOSS.NORM_P = 2
135
+ # normalization term for embedding similarity matrices
136
+ _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.SHAPE_TO_SHAPE_CYCLE_LOSS.TEMPERATURE = 0.05
137
+ # maximum number of vertices to include into shape to shape cycle loss
138
+ # if negative or zero, all vertices are considered
139
+ # if positive, random subset of vertices of given size is considered
140
+ _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.SHAPE_TO_SHAPE_CYCLE_LOSS.MAX_NUM_VERTICES = 4936
141
+ # Pixel to shape cycle consistency loss parameters:
142
+ _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.PIX_TO_SHAPE_CYCLE_LOSS = CN({"ENABLED": False})
143
+ # pixel to shape cycle consistency loss weight
144
+ _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.PIX_TO_SHAPE_CYCLE_LOSS.WEIGHT = 0.0001
145
+ # norm type used for loss computation
146
+ _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.PIX_TO_SHAPE_CYCLE_LOSS.NORM_P = 2
147
+ # map images to all meshes and back (if false, use only gt meshes from the batch)
148
+ _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.PIX_TO_SHAPE_CYCLE_LOSS.USE_ALL_MESHES_NOT_GT_ONLY = False
149
+ # Randomly select at most this number of pixels from every instance
150
+ # if negative or zero, all vertices are considered
151
+ _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.PIX_TO_SHAPE_CYCLE_LOSS.NUM_PIXELS_TO_SAMPLE = 100
152
+ # normalization factor for pixel to pixel distances (higher value = smoother distribution)
153
+ _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.PIX_TO_SHAPE_CYCLE_LOSS.PIXEL_SIGMA = 5.0
154
+ _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.PIX_TO_SHAPE_CYCLE_LOSS.TEMPERATURE_PIXEL_TO_VERTEX = 0.05
155
+ _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.PIX_TO_SHAPE_CYCLE_LOSS.TEMPERATURE_VERTEX_TO_PIXEL = 0.05
156
+
157
+
158
+ def add_densepose_head_config(cfg: CN) -> None:
159
+ """
160
+ Add config for densepose head.
161
+ """
162
+ _C = cfg
163
+
164
+ _C.MODEL.DENSEPOSE_ON = True
165
+
166
+ _C.MODEL.ROI_DENSEPOSE_HEAD = CN()
167
+ _C.MODEL.ROI_DENSEPOSE_HEAD.NAME = ""
168
+ _C.MODEL.ROI_DENSEPOSE_HEAD.NUM_STACKED_CONVS = 8
169
+ # Number of parts used for point labels
170
+ _C.MODEL.ROI_DENSEPOSE_HEAD.NUM_PATCHES = 24
171
+ _C.MODEL.ROI_DENSEPOSE_HEAD.DECONV_KERNEL = 4
172
+ _C.MODEL.ROI_DENSEPOSE_HEAD.CONV_HEAD_DIM = 512
173
+ _C.MODEL.ROI_DENSEPOSE_HEAD.CONV_HEAD_KERNEL = 3
174
+ _C.MODEL.ROI_DENSEPOSE_HEAD.UP_SCALE = 2
175
+ _C.MODEL.ROI_DENSEPOSE_HEAD.HEATMAP_SIZE = 112
176
+ _C.MODEL.ROI_DENSEPOSE_HEAD.POOLER_TYPE = "ROIAlignV2"
177
+ _C.MODEL.ROI_DENSEPOSE_HEAD.POOLER_RESOLUTION = 28
178
+ _C.MODEL.ROI_DENSEPOSE_HEAD.POOLER_SAMPLING_RATIO = 2
179
+ _C.MODEL.ROI_DENSEPOSE_HEAD.NUM_COARSE_SEGM_CHANNELS = 2 # 15 or 2
180
+ # Overlap threshold for an RoI to be considered foreground (if >= FG_IOU_THRESHOLD)
181
+ _C.MODEL.ROI_DENSEPOSE_HEAD.FG_IOU_THRESHOLD = 0.7
182
+ # Loss weights for annotation masks.(14 Parts)
183
+ _C.MODEL.ROI_DENSEPOSE_HEAD.INDEX_WEIGHTS = 5.0
184
+ # Loss weights for surface parts. (24 Parts)
185
+ _C.MODEL.ROI_DENSEPOSE_HEAD.PART_WEIGHTS = 1.0
186
+ # Loss weights for UV regression.
187
+ _C.MODEL.ROI_DENSEPOSE_HEAD.POINT_REGRESSION_WEIGHTS = 0.01
188
+ # Coarse segmentation is trained using instance segmentation task data
189
+ _C.MODEL.ROI_DENSEPOSE_HEAD.COARSE_SEGM_TRAINED_BY_MASKS = False
190
+ # For Decoder
191
+ _C.MODEL.ROI_DENSEPOSE_HEAD.DECODER_ON = True
192
+ _C.MODEL.ROI_DENSEPOSE_HEAD.DECODER_NUM_CLASSES = 256
193
+ _C.MODEL.ROI_DENSEPOSE_HEAD.DECODER_CONV_DIMS = 256
194
+ _C.MODEL.ROI_DENSEPOSE_HEAD.DECODER_NORM = ""
195
+ _C.MODEL.ROI_DENSEPOSE_HEAD.DECODER_COMMON_STRIDE = 4
196
+ # For DeepLab head
197
+ _C.MODEL.ROI_DENSEPOSE_HEAD.DEEPLAB = CN()
198
+ _C.MODEL.ROI_DENSEPOSE_HEAD.DEEPLAB.NORM = "GN"
199
+ _C.MODEL.ROI_DENSEPOSE_HEAD.DEEPLAB.NONLOCAL_ON = 0
200
+ # Predictor class name, must be registered in DENSEPOSE_PREDICTOR_REGISTRY
201
+ # Some registered predictors:
202
+ # "DensePoseChartPredictor": predicts segmentation and UV coordinates for predefined charts
203
+ # "DensePoseChartWithConfidencePredictor": predicts segmentation, UV coordinates
204
+ # and associated confidences for predefined charts (default)
205
+ # "DensePoseEmbeddingWithConfidencePredictor": predicts segmentation, embeddings
206
+ # and associated confidences for CSE
207
+ _C.MODEL.ROI_DENSEPOSE_HEAD.PREDICTOR_NAME = "DensePoseChartWithConfidencePredictor"
208
+ # Loss class name, must be registered in DENSEPOSE_LOSS_REGISTRY
209
+ # Some registered losses:
210
+ # "DensePoseChartLoss": loss for chart-based models that estimate
211
+ # segmentation and UV coordinates
212
+ # "DensePoseChartWithConfidenceLoss": loss for chart-based models that estimate
213
+ # segmentation, UV coordinates and the corresponding confidences (default)
214
+ _C.MODEL.ROI_DENSEPOSE_HEAD.LOSS_NAME = "DensePoseChartWithConfidenceLoss"
215
+ # Confidences
216
+ # Enable learning UV confidences (variances) along with the actual values
217
+ _C.MODEL.ROI_DENSEPOSE_HEAD.UV_CONFIDENCE = CN({"ENABLED": False})
218
+ # UV confidence lower bound
219
+ _C.MODEL.ROI_DENSEPOSE_HEAD.UV_CONFIDENCE.EPSILON = 0.01
220
+ # Enable learning segmentation confidences (variances) along with the actual values
221
+ _C.MODEL.ROI_DENSEPOSE_HEAD.SEGM_CONFIDENCE = CN({"ENABLED": False})
222
+ # Segmentation confidence lower bound
223
+ _C.MODEL.ROI_DENSEPOSE_HEAD.SEGM_CONFIDENCE.EPSILON = 0.01
224
+ # Statistical model type for confidence learning, possible values:
225
+ # - "iid_iso": statistically independent identically distributed residuals
226
+ # with isotropic covariance
227
+ # - "indep_aniso": statistically independent residuals with anisotropic
228
+ # covariances
229
+ _C.MODEL.ROI_DENSEPOSE_HEAD.UV_CONFIDENCE.TYPE = "iid_iso"
230
+ # List of angles for rotation in data augmentation during training
231
+ _C.INPUT.ROTATION_ANGLES = [0]
232
+ _C.TEST.AUG.ROTATION_ANGLES = () # Rotation TTA
233
+
234
+ add_densepose_head_cse_config(cfg)
235
+
236
+
237
+ def add_hrnet_config(cfg: CN) -> None:
238
+ """
239
+ Add config for HRNet backbone.
240
+ """
241
+ _C = cfg
242
+
243
+ # For HigherHRNet w32
244
+ _C.MODEL.HRNET = CN()
245
+ _C.MODEL.HRNET.STEM_INPLANES = 64
246
+ _C.MODEL.HRNET.STAGE2 = CN()
247
+ _C.MODEL.HRNET.STAGE2.NUM_MODULES = 1
248
+ _C.MODEL.HRNET.STAGE2.NUM_BRANCHES = 2
249
+ _C.MODEL.HRNET.STAGE2.BLOCK = "BASIC"
250
+ _C.MODEL.HRNET.STAGE2.NUM_BLOCKS = [4, 4]
251
+ _C.MODEL.HRNET.STAGE2.NUM_CHANNELS = [32, 64]
252
+ _C.MODEL.HRNET.STAGE2.FUSE_METHOD = "SUM"
253
+ _C.MODEL.HRNET.STAGE3 = CN()
254
+ _C.MODEL.HRNET.STAGE3.NUM_MODULES = 4
255
+ _C.MODEL.HRNET.STAGE3.NUM_BRANCHES = 3
256
+ _C.MODEL.HRNET.STAGE3.BLOCK = "BASIC"
257
+ _C.MODEL.HRNET.STAGE3.NUM_BLOCKS = [4, 4, 4]
258
+ _C.MODEL.HRNET.STAGE3.NUM_CHANNELS = [32, 64, 128]
259
+ _C.MODEL.HRNET.STAGE3.FUSE_METHOD = "SUM"
260
+ _C.MODEL.HRNET.STAGE4 = CN()
261
+ _C.MODEL.HRNET.STAGE4.NUM_MODULES = 3
262
+ _C.MODEL.HRNET.STAGE4.NUM_BRANCHES = 4
263
+ _C.MODEL.HRNET.STAGE4.BLOCK = "BASIC"
264
+ _C.MODEL.HRNET.STAGE4.NUM_BLOCKS = [4, 4, 4, 4]
265
+ _C.MODEL.HRNET.STAGE4.NUM_CHANNELS = [32, 64, 128, 256]
266
+ _C.MODEL.HRNET.STAGE4.FUSE_METHOD = "SUM"
267
+
268
+ _C.MODEL.HRNET.HRFPN = CN()
269
+ _C.MODEL.HRNET.HRFPN.OUT_CHANNELS = 256
270
+
271
+
272
+ def add_densepose_config(cfg: CN) -> None:
273
+ add_densepose_head_config(cfg)
274
+ add_hrnet_config(cfg)
275
+ add_bootstrap_config(cfg)
276
+ add_dataset_category_config(cfg)
277
+ add_evaluation_config(cfg)
gradio_demo/densepose/converters/__init__.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+
3
+ from .hflip import HFlipConverter
4
+ from .to_mask import ToMaskConverter
5
+ from .to_chart_result import ToChartResultConverter, ToChartResultConverterWithConfidences
6
+ from .segm_to_mask import (
7
+ predictor_output_with_fine_and_coarse_segm_to_mask,
8
+ predictor_output_with_coarse_segm_to_mask,
9
+ resample_fine_and_coarse_segm_to_bbox,
10
+ )
11
+ from .chart_output_to_chart_result import (
12
+ densepose_chart_predictor_output_to_result,
13
+ densepose_chart_predictor_output_to_result_with_confidences,
14
+ )
15
+ from .chart_output_hflip import densepose_chart_predictor_output_hflip
gradio_demo/densepose/converters/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (796 Bytes). View file
 
gradio_demo/densepose/converters/__pycache__/base.cpython-310.pyc ADDED
Binary file (3.68 kB). View file
 
gradio_demo/densepose/converters/__pycache__/builtin.cpython-310.pyc ADDED
Binary file (801 Bytes). View file
 
gradio_demo/densepose/converters/__pycache__/chart_output_hflip.cpython-310.pyc ADDED
Binary file (1.94 kB). View file
 
gradio_demo/densepose/converters/__pycache__/chart_output_to_chart_result.cpython-310.pyc ADDED
Binary file (6.08 kB). View file
 
gradio_demo/densepose/converters/__pycache__/hflip.cpython-310.pyc ADDED
Binary file (1.35 kB). View file
 
gradio_demo/densepose/converters/__pycache__/segm_to_mask.cpython-310.pyc ADDED
Binary file (5.79 kB). View file
 
gradio_demo/densepose/converters/__pycache__/to_chart_result.cpython-310.pyc ADDED
Binary file (2.67 kB). View file
 
gradio_demo/densepose/converters/__pycache__/to_mask.cpython-310.pyc ADDED
Binary file (1.77 kB). View file
 
gradio_demo/densepose/converters/base.py ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+
3
+ from typing import Any, Tuple, Type
4
+ import torch
5
+
6
+
7
+ class BaseConverter:
8
+ """
9
+ Converter base class to be reused by various converters.
10
+ Converter allows one to convert data from various source types to a particular
11
+ destination type. Each source type needs to register its converter. The
12
+ registration for each source type is valid for all descendants of that type.
13
+ """
14
+
15
+ @classmethod
16
+ def register(cls, from_type: Type, converter: Any = None):
17
+ """
18
+ Registers a converter for the specified type.
19
+ Can be used as a decorator (if converter is None), or called as a method.
20
+
21
+ Args:
22
+ from_type (type): type to register the converter for;
23
+ all instances of this type will use the same converter
24
+ converter (callable): converter to be registered for the given
25
+ type; if None, this method is assumed to be a decorator for the converter
26
+ """
27
+
28
+ if converter is not None:
29
+ cls._do_register(from_type, converter)
30
+
31
+ def wrapper(converter: Any) -> Any:
32
+ cls._do_register(from_type, converter)
33
+ return converter
34
+
35
+ return wrapper
36
+
37
+ @classmethod
38
+ def _do_register(cls, from_type: Type, converter: Any):
39
+ cls.registry[from_type] = converter # pyre-ignore[16]
40
+
41
+ @classmethod
42
+ def _lookup_converter(cls, from_type: Type) -> Any:
43
+ """
44
+ Perform recursive lookup for the given type
45
+ to find registered converter. If a converter was found for some base
46
+ class, it gets registered for this class to save on further lookups.
47
+
48
+ Args:
49
+ from_type: type for which to find a converter
50
+ Return:
51
+ callable or None - registered converter or None
52
+ if no suitable entry was found in the registry
53
+ """
54
+ if from_type in cls.registry: # pyre-ignore[16]
55
+ return cls.registry[from_type]
56
+ for base in from_type.__bases__:
57
+ converter = cls._lookup_converter(base)
58
+ if converter is not None:
59
+ cls._do_register(from_type, converter)
60
+ return converter
61
+ return None
62
+
63
+ @classmethod
64
+ def convert(cls, instance: Any, *args, **kwargs):
65
+ """
66
+ Convert an instance to the destination type using some registered
67
+ converter. Does recursive lookup for base classes, so there's no need
68
+ for explicit registration for derived classes.
69
+
70
+ Args:
71
+ instance: source instance to convert to the destination type
72
+ Return:
73
+ An instance of the destination type obtained from the source instance
74
+ Raises KeyError, if no suitable converter found
75
+ """
76
+ instance_type = type(instance)
77
+ converter = cls._lookup_converter(instance_type)
78
+ if converter is None:
79
+ if cls.dst_type is None: # pyre-ignore[16]
80
+ output_type_str = "itself"
81
+ else:
82
+ output_type_str = cls.dst_type
83
+ raise KeyError(f"Could not find converter from {instance_type} to {output_type_str}")
84
+ return converter(instance, *args, **kwargs)
85
+
86
+
87
+ IntTupleBox = Tuple[int, int, int, int]
88
+
89
+
90
+ def make_int_box(box: torch.Tensor) -> IntTupleBox:
91
+ int_box = [0, 0, 0, 0]
92
+ int_box[0], int_box[1], int_box[2], int_box[3] = tuple(box.long().tolist())
93
+ return int_box[0], int_box[1], int_box[2], int_box[3]
gradio_demo/densepose/converters/builtin.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+
3
+ from ..structures import DensePoseChartPredictorOutput, DensePoseEmbeddingPredictorOutput
4
+ from . import (
5
+ HFlipConverter,
6
+ ToChartResultConverter,
7
+ ToChartResultConverterWithConfidences,
8
+ ToMaskConverter,
9
+ densepose_chart_predictor_output_hflip,
10
+ densepose_chart_predictor_output_to_result,
11
+ densepose_chart_predictor_output_to_result_with_confidences,
12
+ predictor_output_with_coarse_segm_to_mask,
13
+ predictor_output_with_fine_and_coarse_segm_to_mask,
14
+ )
15
+
16
+ ToMaskConverter.register(
17
+ DensePoseChartPredictorOutput, predictor_output_with_fine_and_coarse_segm_to_mask
18
+ )
19
+ ToMaskConverter.register(
20
+ DensePoseEmbeddingPredictorOutput, predictor_output_with_coarse_segm_to_mask
21
+ )
22
+
23
+ ToChartResultConverter.register(
24
+ DensePoseChartPredictorOutput, densepose_chart_predictor_output_to_result
25
+ )
26
+
27
+ ToChartResultConverterWithConfidences.register(
28
+ DensePoseChartPredictorOutput, densepose_chart_predictor_output_to_result_with_confidences
29
+ )
30
+
31
+ HFlipConverter.register(DensePoseChartPredictorOutput, densepose_chart_predictor_output_hflip)
gradio_demo/densepose/converters/chart_output_hflip.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ from dataclasses import fields
3
+ import torch
4
+
5
+ from densepose.structures import DensePoseChartPredictorOutput, DensePoseTransformData
6
+
7
+
8
+ def densepose_chart_predictor_output_hflip(
9
+ densepose_predictor_output: DensePoseChartPredictorOutput,
10
+ transform_data: DensePoseTransformData,
11
+ ) -> DensePoseChartPredictorOutput:
12
+ """
13
+ Change to take into account a Horizontal flip.
14
+ """
15
+ if len(densepose_predictor_output) > 0:
16
+
17
+ PredictorOutput = type(densepose_predictor_output)
18
+ output_dict = {}
19
+
20
+ for field in fields(densepose_predictor_output):
21
+ field_value = getattr(densepose_predictor_output, field.name)
22
+ # flip tensors
23
+ if isinstance(field_value, torch.Tensor):
24
+ setattr(densepose_predictor_output, field.name, torch.flip(field_value, [3]))
25
+
26
+ densepose_predictor_output = _flip_iuv_semantics_tensor(
27
+ densepose_predictor_output, transform_data
28
+ )
29
+ densepose_predictor_output = _flip_segm_semantics_tensor(
30
+ densepose_predictor_output, transform_data
31
+ )
32
+
33
+ for field in fields(densepose_predictor_output):
34
+ output_dict[field.name] = getattr(densepose_predictor_output, field.name)
35
+
36
+ return PredictorOutput(**output_dict)
37
+ else:
38
+ return densepose_predictor_output
39
+
40
+
41
+ def _flip_iuv_semantics_tensor(
42
+ densepose_predictor_output: DensePoseChartPredictorOutput,
43
+ dp_transform_data: DensePoseTransformData,
44
+ ) -> DensePoseChartPredictorOutput:
45
+ point_label_symmetries = dp_transform_data.point_label_symmetries
46
+ uv_symmetries = dp_transform_data.uv_symmetries
47
+
48
+ N, C, H, W = densepose_predictor_output.u.shape
49
+ u_loc = (densepose_predictor_output.u[:, 1:, :, :].clamp(0, 1) * 255).long()
50
+ v_loc = (densepose_predictor_output.v[:, 1:, :, :].clamp(0, 1) * 255).long()
51
+ Iindex = torch.arange(C - 1, device=densepose_predictor_output.u.device)[
52
+ None, :, None, None
53
+ ].expand(N, C - 1, H, W)
54
+ densepose_predictor_output.u[:, 1:, :, :] = uv_symmetries["U_transforms"][Iindex, v_loc, u_loc]
55
+ densepose_predictor_output.v[:, 1:, :, :] = uv_symmetries["V_transforms"][Iindex, v_loc, u_loc]
56
+
57
+ for el in ["fine_segm", "u", "v"]:
58
+ densepose_predictor_output.__dict__[el] = densepose_predictor_output.__dict__[el][
59
+ :, point_label_symmetries, :, :
60
+ ]
61
+ return densepose_predictor_output
62
+
63
+
64
+ def _flip_segm_semantics_tensor(
65
+ densepose_predictor_output: DensePoseChartPredictorOutput, dp_transform_data
66
+ ):
67
+ if densepose_predictor_output.coarse_segm.shape[1] > 2:
68
+ densepose_predictor_output.coarse_segm = densepose_predictor_output.coarse_segm[
69
+ :, dp_transform_data.mask_label_symmetries, :, :
70
+ ]
71
+ return densepose_predictor_output
gradio_demo/densepose/converters/chart_output_to_chart_result.py ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+
3
+ from typing import Dict
4
+ import torch
5
+ from torch.nn import functional as F
6
+
7
+ from detectron2.structures.boxes import Boxes, BoxMode
8
+
9
+ from ..structures import (
10
+ DensePoseChartPredictorOutput,
11
+ DensePoseChartResult,
12
+ DensePoseChartResultWithConfidences,
13
+ )
14
+ from . import resample_fine_and_coarse_segm_to_bbox
15
+ from .base import IntTupleBox, make_int_box
16
+
17
+
18
+ def resample_uv_tensors_to_bbox(
19
+ u: torch.Tensor,
20
+ v: torch.Tensor,
21
+ labels: torch.Tensor,
22
+ box_xywh_abs: IntTupleBox,
23
+ ) -> torch.Tensor:
24
+ """
25
+ Resamples U and V coordinate estimates for the given bounding box
26
+
27
+ Args:
28
+ u (tensor [1, C, H, W] of float): U coordinates
29
+ v (tensor [1, C, H, W] of float): V coordinates
30
+ labels (tensor [H, W] of long): labels obtained by resampling segmentation
31
+ outputs for the given bounding box
32
+ box_xywh_abs (tuple of 4 int): bounding box that corresponds to predictor outputs
33
+ Return:
34
+ Resampled U and V coordinates - a tensor [2, H, W] of float
35
+ """
36
+ x, y, w, h = box_xywh_abs
37
+ w = max(int(w), 1)
38
+ h = max(int(h), 1)
39
+ u_bbox = F.interpolate(u, (h, w), mode="bilinear", align_corners=False)
40
+ v_bbox = F.interpolate(v, (h, w), mode="bilinear", align_corners=False)
41
+ uv = torch.zeros([2, h, w], dtype=torch.float32, device=u.device)
42
+ for part_id in range(1, u_bbox.size(1)):
43
+ uv[0][labels == part_id] = u_bbox[0, part_id][labels == part_id]
44
+ uv[1][labels == part_id] = v_bbox[0, part_id][labels == part_id]
45
+ return uv
46
+
47
+
48
+ def resample_uv_to_bbox(
49
+ predictor_output: DensePoseChartPredictorOutput,
50
+ labels: torch.Tensor,
51
+ box_xywh_abs: IntTupleBox,
52
+ ) -> torch.Tensor:
53
+ """
54
+ Resamples U and V coordinate estimates for the given bounding box
55
+
56
+ Args:
57
+ predictor_output (DensePoseChartPredictorOutput): DensePose predictor
58
+ output to be resampled
59
+ labels (tensor [H, W] of long): labels obtained by resampling segmentation
60
+ outputs for the given bounding box
61
+ box_xywh_abs (tuple of 4 int): bounding box that corresponds to predictor outputs
62
+ Return:
63
+ Resampled U and V coordinates - a tensor [2, H, W] of float
64
+ """
65
+ return resample_uv_tensors_to_bbox(
66
+ predictor_output.u,
67
+ predictor_output.v,
68
+ labels,
69
+ box_xywh_abs,
70
+ )
71
+
72
+
73
+ def densepose_chart_predictor_output_to_result(
74
+ predictor_output: DensePoseChartPredictorOutput, boxes: Boxes
75
+ ) -> DensePoseChartResult:
76
+ """
77
+ Convert densepose chart predictor outputs to results
78
+
79
+ Args:
80
+ predictor_output (DensePoseChartPredictorOutput): DensePose predictor
81
+ output to be converted to results, must contain only 1 output
82
+ boxes (Boxes): bounding box that corresponds to the predictor output,
83
+ must contain only 1 bounding box
84
+ Return:
85
+ DensePose chart-based result (DensePoseChartResult)
86
+ """
87
+ assert len(predictor_output) == 1 and len(boxes) == 1, (
88
+ f"Predictor output to result conversion can operate only single outputs"
89
+ f", got {len(predictor_output)} predictor outputs and {len(boxes)} boxes"
90
+ )
91
+
92
+ boxes_xyxy_abs = boxes.tensor.clone()
93
+ boxes_xywh_abs = BoxMode.convert(boxes_xyxy_abs, BoxMode.XYXY_ABS, BoxMode.XYWH_ABS)
94
+ box_xywh = make_int_box(boxes_xywh_abs[0])
95
+
96
+ labels = resample_fine_and_coarse_segm_to_bbox(predictor_output, box_xywh).squeeze(0)
97
+ uv = resample_uv_to_bbox(predictor_output, labels, box_xywh)
98
+ return DensePoseChartResult(labels=labels, uv=uv)
99
+
100
+
101
+ def resample_confidences_to_bbox(
102
+ predictor_output: DensePoseChartPredictorOutput,
103
+ labels: torch.Tensor,
104
+ box_xywh_abs: IntTupleBox,
105
+ ) -> Dict[str, torch.Tensor]:
106
+ """
107
+ Resamples confidences for the given bounding box
108
+
109
+ Args:
110
+ predictor_output (DensePoseChartPredictorOutput): DensePose predictor
111
+ output to be resampled
112
+ labels (tensor [H, W] of long): labels obtained by resampling segmentation
113
+ outputs for the given bounding box
114
+ box_xywh_abs (tuple of 4 int): bounding box that corresponds to predictor outputs
115
+ Return:
116
+ Resampled confidences - a dict of [H, W] tensors of float
117
+ """
118
+
119
+ x, y, w, h = box_xywh_abs
120
+ w = max(int(w), 1)
121
+ h = max(int(h), 1)
122
+
123
+ confidence_names = [
124
+ "sigma_1",
125
+ "sigma_2",
126
+ "kappa_u",
127
+ "kappa_v",
128
+ "fine_segm_confidence",
129
+ "coarse_segm_confidence",
130
+ ]
131
+ confidence_results = {key: None for key in confidence_names}
132
+ confidence_names = [
133
+ key for key in confidence_names if getattr(predictor_output, key) is not None
134
+ ]
135
+ confidence_base = torch.zeros([h, w], dtype=torch.float32, device=predictor_output.u.device)
136
+
137
+ # assign data from channels that correspond to the labels
138
+ for key in confidence_names:
139
+ resampled_confidence = F.interpolate(
140
+ getattr(predictor_output, key),
141
+ (h, w),
142
+ mode="bilinear",
143
+ align_corners=False,
144
+ )
145
+ result = confidence_base.clone()
146
+ for part_id in range(1, predictor_output.u.size(1)):
147
+ if resampled_confidence.size(1) != predictor_output.u.size(1):
148
+ # confidence is not part-based, don't try to fill it part by part
149
+ continue
150
+ result[labels == part_id] = resampled_confidence[0, part_id][labels == part_id]
151
+
152
+ if resampled_confidence.size(1) != predictor_output.u.size(1):
153
+ # confidence is not part-based, fill the data with the first channel
154
+ # (targeted for segmentation confidences that have only 1 channel)
155
+ result = resampled_confidence[0, 0]
156
+
157
+ confidence_results[key] = result
158
+
159
+ return confidence_results # pyre-ignore[7]
160
+
161
+
162
+ def densepose_chart_predictor_output_to_result_with_confidences(
163
+ predictor_output: DensePoseChartPredictorOutput, boxes: Boxes
164
+ ) -> DensePoseChartResultWithConfidences:
165
+ """
166
+ Convert densepose chart predictor outputs to results
167
+
168
+ Args:
169
+ predictor_output (DensePoseChartPredictorOutput): DensePose predictor
170
+ output with confidences to be converted to results, must contain only 1 output
171
+ boxes (Boxes): bounding box that corresponds to the predictor output,
172
+ must contain only 1 bounding box
173
+ Return:
174
+ DensePose chart-based result with confidences (DensePoseChartResultWithConfidences)
175
+ """
176
+ assert len(predictor_output) == 1 and len(boxes) == 1, (
177
+ f"Predictor output to result conversion can operate only single outputs"
178
+ f", got {len(predictor_output)} predictor outputs and {len(boxes)} boxes"
179
+ )
180
+
181
+ boxes_xyxy_abs = boxes.tensor.clone()
182
+ boxes_xywh_abs = BoxMode.convert(boxes_xyxy_abs, BoxMode.XYXY_ABS, BoxMode.XYWH_ABS)
183
+ box_xywh = make_int_box(boxes_xywh_abs[0])
184
+
185
+ labels = resample_fine_and_coarse_segm_to_bbox(predictor_output, box_xywh).squeeze(0)
186
+ uv = resample_uv_to_bbox(predictor_output, labels, box_xywh)
187
+ confidences = resample_confidences_to_bbox(predictor_output, labels, box_xywh)
188
+ return DensePoseChartResultWithConfidences(labels=labels, uv=uv, **confidences)
gradio_demo/densepose/converters/hflip.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+
3
+ from typing import Any
4
+
5
+ from .base import BaseConverter
6
+
7
+
8
+ class HFlipConverter(BaseConverter):
9
+ """
10
+ Converts various DensePose predictor outputs to DensePose results.
11
+ Each DensePose predictor output type has to register its convertion strategy.
12
+ """
13
+
14
+ registry = {}
15
+ dst_type = None
16
+
17
+ @classmethod
18
+ # pyre-fixme[14]: `convert` overrides method defined in `BaseConverter`
19
+ # inconsistently.
20
+ def convert(cls, predictor_outputs: Any, transform_data: Any, *args, **kwargs):
21
+ """
22
+ Performs an horizontal flip on DensePose predictor outputs.
23
+ Does recursive lookup for base classes, so there's no need
24
+ for explicit registration for derived classes.
25
+
26
+ Args:
27
+ predictor_outputs: DensePose predictor output to be converted to BitMasks
28
+ transform_data: Anything useful for the flip
29
+ Return:
30
+ An instance of the same type as predictor_outputs
31
+ """
32
+ return super(HFlipConverter, cls).convert(
33
+ predictor_outputs, transform_data, *args, **kwargs
34
+ )
gradio_demo/densepose/converters/segm_to_mask.py ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+
3
+ from typing import Any
4
+ import torch
5
+ from torch.nn import functional as F
6
+
7
+ from detectron2.structures import BitMasks, Boxes, BoxMode
8
+
9
+ from .base import IntTupleBox, make_int_box
10
+ from .to_mask import ImageSizeType
11
+
12
+
13
+ def resample_coarse_segm_tensor_to_bbox(coarse_segm: torch.Tensor, box_xywh_abs: IntTupleBox):
14
+ """
15
+ Resample coarse segmentation tensor to the given
16
+ bounding box and derive labels for each pixel of the bounding box
17
+
18
+ Args:
19
+ coarse_segm: float tensor of shape [1, K, Hout, Wout]
20
+ box_xywh_abs (tuple of 4 int): bounding box given by its upper-left
21
+ corner coordinates, width (W) and height (H)
22
+ Return:
23
+ Labels for each pixel of the bounding box, a long tensor of size [1, H, W]
24
+ """
25
+ x, y, w, h = box_xywh_abs
26
+ w = max(int(w), 1)
27
+ h = max(int(h), 1)
28
+ labels = F.interpolate(coarse_segm, (h, w), mode="bilinear", align_corners=False).argmax(dim=1)
29
+ return labels
30
+
31
+
32
+ def resample_fine_and_coarse_segm_tensors_to_bbox(
33
+ fine_segm: torch.Tensor, coarse_segm: torch.Tensor, box_xywh_abs: IntTupleBox
34
+ ):
35
+ """
36
+ Resample fine and coarse segmentation tensors to the given
37
+ bounding box and derive labels for each pixel of the bounding box
38
+
39
+ Args:
40
+ fine_segm: float tensor of shape [1, C, Hout, Wout]
41
+ coarse_segm: float tensor of shape [1, K, Hout, Wout]
42
+ box_xywh_abs (tuple of 4 int): bounding box given by its upper-left
43
+ corner coordinates, width (W) and height (H)
44
+ Return:
45
+ Labels for each pixel of the bounding box, a long tensor of size [1, H, W]
46
+ """
47
+ x, y, w, h = box_xywh_abs
48
+ w = max(int(w), 1)
49
+ h = max(int(h), 1)
50
+ # coarse segmentation
51
+ coarse_segm_bbox = F.interpolate(
52
+ coarse_segm,
53
+ (h, w),
54
+ mode="bilinear",
55
+ align_corners=False,
56
+ ).argmax(dim=1)
57
+ # combined coarse and fine segmentation
58
+ labels = (
59
+ F.interpolate(fine_segm, (h, w), mode="bilinear", align_corners=False).argmax(dim=1)
60
+ * (coarse_segm_bbox > 0).long()
61
+ )
62
+ return labels
63
+
64
+
65
+ def resample_fine_and_coarse_segm_to_bbox(predictor_output: Any, box_xywh_abs: IntTupleBox):
66
+ """
67
+ Resample fine and coarse segmentation outputs from a predictor to the given
68
+ bounding box and derive labels for each pixel of the bounding box
69
+
70
+ Args:
71
+ predictor_output: DensePose predictor output that contains segmentation
72
+ results to be resampled
73
+ box_xywh_abs (tuple of 4 int): bounding box given by its upper-left
74
+ corner coordinates, width (W) and height (H)
75
+ Return:
76
+ Labels for each pixel of the bounding box, a long tensor of size [1, H, W]
77
+ """
78
+ return resample_fine_and_coarse_segm_tensors_to_bbox(
79
+ predictor_output.fine_segm,
80
+ predictor_output.coarse_segm,
81
+ box_xywh_abs,
82
+ )
83
+
84
+
85
+ def predictor_output_with_coarse_segm_to_mask(
86
+ predictor_output: Any, boxes: Boxes, image_size_hw: ImageSizeType
87
+ ) -> BitMasks:
88
+ """
89
+ Convert predictor output with coarse and fine segmentation to a mask.
90
+ Assumes that predictor output has the following attributes:
91
+ - coarse_segm (tensor of size [N, D, H, W]): coarse segmentation
92
+ unnormalized scores for N instances; D is the number of coarse
93
+ segmentation labels, H and W is the resolution of the estimate
94
+
95
+ Args:
96
+ predictor_output: DensePose predictor output to be converted to mask
97
+ boxes (Boxes): bounding boxes that correspond to the DensePose
98
+ predictor outputs
99
+ image_size_hw (tuple [int, int]): image height Himg and width Wimg
100
+ Return:
101
+ BitMasks that contain a bool tensor of size [N, Himg, Wimg] with
102
+ a mask of the size of the image for each instance
103
+ """
104
+ H, W = image_size_hw
105
+ boxes_xyxy_abs = boxes.tensor.clone()
106
+ boxes_xywh_abs = BoxMode.convert(boxes_xyxy_abs, BoxMode.XYXY_ABS, BoxMode.XYWH_ABS)
107
+ N = len(boxes_xywh_abs)
108
+ masks = torch.zeros((N, H, W), dtype=torch.bool, device=boxes.tensor.device)
109
+ for i in range(len(boxes_xywh_abs)):
110
+ box_xywh = make_int_box(boxes_xywh_abs[i])
111
+ box_mask = resample_coarse_segm_tensor_to_bbox(predictor_output[i].coarse_segm, box_xywh)
112
+ x, y, w, h = box_xywh
113
+ masks[i, y : y + h, x : x + w] = box_mask
114
+
115
+ return BitMasks(masks)
116
+
117
+
118
+ def predictor_output_with_fine_and_coarse_segm_to_mask(
119
+ predictor_output: Any, boxes: Boxes, image_size_hw: ImageSizeType
120
+ ) -> BitMasks:
121
+ """
122
+ Convert predictor output with coarse and fine segmentation to a mask.
123
+ Assumes that predictor output has the following attributes:
124
+ - coarse_segm (tensor of size [N, D, H, W]): coarse segmentation
125
+ unnormalized scores for N instances; D is the number of coarse
126
+ segmentation labels, H and W is the resolution of the estimate
127
+ - fine_segm (tensor of size [N, C, H, W]): fine segmentation
128
+ unnormalized scores for N instances; C is the number of fine
129
+ segmentation labels, H and W is the resolution of the estimate
130
+
131
+ Args:
132
+ predictor_output: DensePose predictor output to be converted to mask
133
+ boxes (Boxes): bounding boxes that correspond to the DensePose
134
+ predictor outputs
135
+ image_size_hw (tuple [int, int]): image height Himg and width Wimg
136
+ Return:
137
+ BitMasks that contain a bool tensor of size [N, Himg, Wimg] with
138
+ a mask of the size of the image for each instance
139
+ """
140
+ H, W = image_size_hw
141
+ boxes_xyxy_abs = boxes.tensor.clone()
142
+ boxes_xywh_abs = BoxMode.convert(boxes_xyxy_abs, BoxMode.XYXY_ABS, BoxMode.XYWH_ABS)
143
+ N = len(boxes_xywh_abs)
144
+ masks = torch.zeros((N, H, W), dtype=torch.bool, device=boxes.tensor.device)
145
+ for i in range(len(boxes_xywh_abs)):
146
+ box_xywh = make_int_box(boxes_xywh_abs[i])
147
+ labels_i = resample_fine_and_coarse_segm_to_bbox(predictor_output[i], box_xywh)
148
+ x, y, w, h = box_xywh
149
+ masks[i, y : y + h, x : x + w] = labels_i > 0
150
+ return BitMasks(masks)
gradio_demo/densepose/converters/to_chart_result.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+
3
+ from typing import Any
4
+
5
+ from detectron2.structures import Boxes
6
+
7
+ from ..structures import DensePoseChartResult, DensePoseChartResultWithConfidences
8
+ from .base import BaseConverter
9
+
10
+
11
+ class ToChartResultConverter(BaseConverter):
12
+ """
13
+ Converts various DensePose predictor outputs to DensePose results.
14
+ Each DensePose predictor output type has to register its convertion strategy.
15
+ """
16
+
17
+ registry = {}
18
+ dst_type = DensePoseChartResult
19
+
20
+ @classmethod
21
+ # pyre-fixme[14]: `convert` overrides method defined in `BaseConverter`
22
+ # inconsistently.
23
+ def convert(cls, predictor_outputs: Any, boxes: Boxes, *args, **kwargs) -> DensePoseChartResult:
24
+ """
25
+ Convert DensePose predictor outputs to DensePoseResult using some registered
26
+ converter. Does recursive lookup for base classes, so there's no need
27
+ for explicit registration for derived classes.
28
+
29
+ Args:
30
+ densepose_predictor_outputs: DensePose predictor output to be
31
+ converted to BitMasks
32
+ boxes (Boxes): bounding boxes that correspond to the DensePose
33
+ predictor outputs
34
+ Return:
35
+ An instance of DensePoseResult. If no suitable converter was found, raises KeyError
36
+ """
37
+ return super(ToChartResultConverter, cls).convert(predictor_outputs, boxes, *args, **kwargs)
38
+
39
+
40
+ class ToChartResultConverterWithConfidences(BaseConverter):
41
+ """
42
+ Converts various DensePose predictor outputs to DensePose results.
43
+ Each DensePose predictor output type has to register its convertion strategy.
44
+ """
45
+
46
+ registry = {}
47
+ dst_type = DensePoseChartResultWithConfidences
48
+
49
+ @classmethod
50
+ # pyre-fixme[14]: `convert` overrides method defined in `BaseConverter`
51
+ # inconsistently.
52
+ def convert(
53
+ cls, predictor_outputs: Any, boxes: Boxes, *args, **kwargs
54
+ ) -> DensePoseChartResultWithConfidences:
55
+ """
56
+ Convert DensePose predictor outputs to DensePoseResult with confidences
57
+ using some registered converter. Does recursive lookup for base classes,
58
+ so there's no need for explicit registration for derived classes.
59
+
60
+ Args:
61
+ densepose_predictor_outputs: DensePose predictor output with confidences
62
+ to be converted to BitMasks
63
+ boxes (Boxes): bounding boxes that correspond to the DensePose
64
+ predictor outputs
65
+ Return:
66
+ An instance of DensePoseResult. If no suitable converter was found, raises KeyError
67
+ """
68
+ return super(ToChartResultConverterWithConfidences, cls).convert(
69
+ predictor_outputs, boxes, *args, **kwargs
70
+ )
gradio_demo/densepose/converters/to_mask.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+
3
+ from typing import Any, Tuple
4
+
5
+ from detectron2.structures import BitMasks, Boxes
6
+
7
+ from .base import BaseConverter
8
+
9
+ ImageSizeType = Tuple[int, int]
10
+
11
+
12
+ class ToMaskConverter(BaseConverter):
13
+ """
14
+ Converts various DensePose predictor outputs to masks
15
+ in bit mask format (see `BitMasks`). Each DensePose predictor output type
16
+ has to register its convertion strategy.
17
+ """
18
+
19
+ registry = {}
20
+ dst_type = BitMasks
21
+
22
+ @classmethod
23
+ # pyre-fixme[14]: `convert` overrides method defined in `BaseConverter`
24
+ # inconsistently.
25
+ def convert(
26
+ cls,
27
+ densepose_predictor_outputs: Any,
28
+ boxes: Boxes,
29
+ image_size_hw: ImageSizeType,
30
+ *args,
31
+ **kwargs
32
+ ) -> BitMasks:
33
+ """
34
+ Convert DensePose predictor outputs to BitMasks using some registered
35
+ converter. Does recursive lookup for base classes, so there's no need
36
+ for explicit registration for derived classes.
37
+
38
+ Args:
39
+ densepose_predictor_outputs: DensePose predictor output to be
40
+ converted to BitMasks
41
+ boxes (Boxes): bounding boxes that correspond to the DensePose
42
+ predictor outputs
43
+ image_size_hw (tuple [int, int]): image height and width
44
+ Return:
45
+ An instance of `BitMasks`. If no suitable converter was found, raises KeyError
46
+ """
47
+ return super(ToMaskConverter, cls).convert(
48
+ densepose_predictor_outputs, boxes, image_size_hw, *args, **kwargs
49
+ )
gradio_demo/densepose/data/__init__.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+
3
+ from .meshes import builtin
4
+ from .build import (
5
+ build_detection_test_loader,
6
+ build_detection_train_loader,
7
+ build_combined_loader,
8
+ build_frame_selector,
9
+ build_inference_based_loaders,
10
+ has_inference_based_loaders,
11
+ BootstrapDatasetFactoryCatalog,
12
+ )
13
+ from .combined_loader import CombinedDataLoader
14
+ from .dataset_mapper import DatasetMapper
15
+ from .inference_based_loader import InferenceBasedLoader, ScoreBasedFilter
16
+ from .image_list_dataset import ImageListDataset
17
+ from .utils import is_relative_local_path, maybe_prepend_base_path
18
+
19
+ # ensure the builtin datasets are registered
20
+ from . import datasets
21
+
22
+ # ensure the bootstrap datasets builders are registered
23
+ from . import build
24
+
25
+ __all__ = [k for k in globals().keys() if not k.startswith("_")]
gradio_demo/densepose/data/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (1.05 kB). View file
 
gradio_demo/densepose/data/__pycache__/build.cpython-310.pyc ADDED
Binary file (23.8 kB). View file
 
gradio_demo/densepose/data/__pycache__/combined_loader.cpython-310.pyc ADDED
Binary file (1.84 kB). View file
 
gradio_demo/densepose/data/__pycache__/dataset_mapper.cpython-310.pyc ADDED
Binary file (5.43 kB). View file
 
gradio_demo/densepose/data/__pycache__/image_list_dataset.cpython-310.pyc ADDED
Binary file (2.67 kB). View file
 
gradio_demo/densepose/data/__pycache__/inference_based_loader.cpython-310.pyc ADDED
Binary file (5.86 kB). View file
 
gradio_demo/densepose/data/__pycache__/utils.cpython-310.pyc ADDED
Binary file (1.61 kB). View file
 
gradio_demo/densepose/data/build.py ADDED
@@ -0,0 +1,736 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+
3
+ import itertools
4
+ import logging
5
+ import numpy as np
6
+ from collections import UserDict, defaultdict
7
+ from dataclasses import dataclass
8
+ from typing import Any, Callable, Collection, Dict, Iterable, List, Optional, Sequence, Tuple
9
+ import torch
10
+ from torch.utils.data.dataset import Dataset
11
+
12
+ from detectron2.config import CfgNode
13
+ from detectron2.data.build import build_detection_test_loader as d2_build_detection_test_loader
14
+ from detectron2.data.build import build_detection_train_loader as d2_build_detection_train_loader
15
+ from detectron2.data.build import (
16
+ load_proposals_into_dataset,
17
+ print_instances_class_histogram,
18
+ trivial_batch_collator,
19
+ worker_init_reset_seed,
20
+ )
21
+ from detectron2.data.catalog import DatasetCatalog, Metadata, MetadataCatalog
22
+ from detectron2.data.samplers import TrainingSampler
23
+ from detectron2.utils.comm import get_world_size
24
+
25
+ from densepose.config import get_bootstrap_dataset_config
26
+ from densepose.modeling import build_densepose_embedder
27
+
28
+ from .combined_loader import CombinedDataLoader, Loader
29
+ from .dataset_mapper import DatasetMapper
30
+ from .datasets.coco import DENSEPOSE_CSE_KEYS_WITHOUT_MASK, DENSEPOSE_IUV_KEYS_WITHOUT_MASK
31
+ from .datasets.dataset_type import DatasetType
32
+ from .inference_based_loader import InferenceBasedLoader, ScoreBasedFilter
33
+ from .samplers import (
34
+ DensePoseConfidenceBasedSampler,
35
+ DensePoseCSEConfidenceBasedSampler,
36
+ DensePoseCSEUniformSampler,
37
+ DensePoseUniformSampler,
38
+ MaskFromDensePoseSampler,
39
+ PredictionToGroundTruthSampler,
40
+ )
41
+ from .transform import ImageResizeTransform
42
+ from .utils import get_category_to_class_mapping, get_class_to_mesh_name_mapping
43
+ from .video import (
44
+ FirstKFramesSelector,
45
+ FrameSelectionStrategy,
46
+ LastKFramesSelector,
47
+ RandomKFramesSelector,
48
+ VideoKeyframeDataset,
49
+ video_list_from_file,
50
+ )
51
+
52
+ __all__ = ["build_detection_train_loader", "build_detection_test_loader"]
53
+
54
+
55
+ Instance = Dict[str, Any]
56
+ InstancePredicate = Callable[[Instance], bool]
57
+
58
+
59
+ def _compute_num_images_per_worker(cfg: CfgNode) -> int:
60
+ num_workers = get_world_size()
61
+ images_per_batch = cfg.SOLVER.IMS_PER_BATCH
62
+ assert (
63
+ images_per_batch % num_workers == 0
64
+ ), "SOLVER.IMS_PER_BATCH ({}) must be divisible by the number of workers ({}).".format(
65
+ images_per_batch, num_workers
66
+ )
67
+ assert (
68
+ images_per_batch >= num_workers
69
+ ), "SOLVER.IMS_PER_BATCH ({}) must be larger than the number of workers ({}).".format(
70
+ images_per_batch, num_workers
71
+ )
72
+ images_per_worker = images_per_batch // num_workers
73
+ return images_per_worker
74
+
75
+
76
+ def _map_category_id_to_contiguous_id(dataset_name: str, dataset_dicts: Iterable[Instance]) -> None:
77
+ meta = MetadataCatalog.get(dataset_name)
78
+ for dataset_dict in dataset_dicts:
79
+ for ann in dataset_dict["annotations"]:
80
+ ann["category_id"] = meta.thing_dataset_id_to_contiguous_id[ann["category_id"]]
81
+
82
+
83
+ @dataclass
84
+ class _DatasetCategory:
85
+ """
86
+ Class representing category data in a dataset:
87
+ - id: category ID, as specified in the dataset annotations file
88
+ - name: category name, as specified in the dataset annotations file
89
+ - mapped_id: category ID after applying category maps (DATASETS.CATEGORY_MAPS config option)
90
+ - mapped_name: category name after applying category maps
91
+ - dataset_name: dataset in which the category is defined
92
+
93
+ For example, when training models in a class-agnostic manner, one could take LVIS 1.0
94
+ dataset and map the animal categories to the same category as human data from COCO:
95
+ id = 225
96
+ name = "cat"
97
+ mapped_id = 1
98
+ mapped_name = "person"
99
+ dataset_name = "lvis_v1_animals_dp_train"
100
+ """
101
+
102
+ id: int
103
+ name: str
104
+ mapped_id: int
105
+ mapped_name: str
106
+ dataset_name: str
107
+
108
+
109
+ _MergedCategoriesT = Dict[int, List[_DatasetCategory]]
110
+
111
+
112
+ def _add_category_id_to_contiguous_id_maps_to_metadata(
113
+ merged_categories: _MergedCategoriesT,
114
+ ) -> None:
115
+ merged_categories_per_dataset = {}
116
+ for contiguous_cat_id, cat_id in enumerate(sorted(merged_categories.keys())):
117
+ for cat in merged_categories[cat_id]:
118
+ if cat.dataset_name not in merged_categories_per_dataset:
119
+ merged_categories_per_dataset[cat.dataset_name] = defaultdict(list)
120
+ merged_categories_per_dataset[cat.dataset_name][cat_id].append(
121
+ (
122
+ contiguous_cat_id,
123
+ cat,
124
+ )
125
+ )
126
+
127
+ logger = logging.getLogger(__name__)
128
+ for dataset_name, merged_categories in merged_categories_per_dataset.items():
129
+ meta = MetadataCatalog.get(dataset_name)
130
+ if not hasattr(meta, "thing_classes"):
131
+ meta.thing_classes = []
132
+ meta.thing_dataset_id_to_contiguous_id = {}
133
+ meta.thing_dataset_id_to_merged_id = {}
134
+ else:
135
+ meta.thing_classes.clear()
136
+ meta.thing_dataset_id_to_contiguous_id.clear()
137
+ meta.thing_dataset_id_to_merged_id.clear()
138
+ logger.info(f"Dataset {dataset_name}: category ID to contiguous ID mapping:")
139
+ for _cat_id, categories in sorted(merged_categories.items()):
140
+ added_to_thing_classes = False
141
+ for contiguous_cat_id, cat in categories:
142
+ if not added_to_thing_classes:
143
+ meta.thing_classes.append(cat.mapped_name)
144
+ added_to_thing_classes = True
145
+ meta.thing_dataset_id_to_contiguous_id[cat.id] = contiguous_cat_id
146
+ meta.thing_dataset_id_to_merged_id[cat.id] = cat.mapped_id
147
+ logger.info(f"{cat.id} ({cat.name}) -> {contiguous_cat_id}")
148
+
149
+
150
+ def _maybe_create_general_keep_instance_predicate(cfg: CfgNode) -> Optional[InstancePredicate]:
151
+ def has_annotations(instance: Instance) -> bool:
152
+ return "annotations" in instance
153
+
154
+ def has_only_crowd_anotations(instance: Instance) -> bool:
155
+ for ann in instance["annotations"]:
156
+ if ann.get("is_crowd", 0) == 0:
157
+ return False
158
+ return True
159
+
160
+ def general_keep_instance_predicate(instance: Instance) -> bool:
161
+ return has_annotations(instance) and not has_only_crowd_anotations(instance)
162
+
163
+ if not cfg.DATALOADER.FILTER_EMPTY_ANNOTATIONS:
164
+ return None
165
+ return general_keep_instance_predicate
166
+
167
+
168
+ def _maybe_create_keypoints_keep_instance_predicate(cfg: CfgNode) -> Optional[InstancePredicate]:
169
+
170
+ min_num_keypoints = cfg.MODEL.ROI_KEYPOINT_HEAD.MIN_KEYPOINTS_PER_IMAGE
171
+
172
+ def has_sufficient_num_keypoints(instance: Instance) -> bool:
173
+ num_kpts = sum(
174
+ (np.array(ann["keypoints"][2::3]) > 0).sum()
175
+ for ann in instance["annotations"]
176
+ if "keypoints" in ann
177
+ )
178
+ return num_kpts >= min_num_keypoints
179
+
180
+ if cfg.MODEL.KEYPOINT_ON and (min_num_keypoints > 0):
181
+ return has_sufficient_num_keypoints
182
+ return None
183
+
184
+
185
+ def _maybe_create_mask_keep_instance_predicate(cfg: CfgNode) -> Optional[InstancePredicate]:
186
+ if not cfg.MODEL.MASK_ON:
187
+ return None
188
+
189
+ def has_mask_annotations(instance: Instance) -> bool:
190
+ return any("segmentation" in ann for ann in instance["annotations"])
191
+
192
+ return has_mask_annotations
193
+
194
+
195
+ def _maybe_create_densepose_keep_instance_predicate(cfg: CfgNode) -> Optional[InstancePredicate]:
196
+ if not cfg.MODEL.DENSEPOSE_ON:
197
+ return None
198
+
199
+ use_masks = cfg.MODEL.ROI_DENSEPOSE_HEAD.COARSE_SEGM_TRAINED_BY_MASKS
200
+
201
+ def has_densepose_annotations(instance: Instance) -> bool:
202
+ for ann in instance["annotations"]:
203
+ if all(key in ann for key in DENSEPOSE_IUV_KEYS_WITHOUT_MASK) or all(
204
+ key in ann for key in DENSEPOSE_CSE_KEYS_WITHOUT_MASK
205
+ ):
206
+ return True
207
+ if use_masks and "segmentation" in ann:
208
+ return True
209
+ return False
210
+
211
+ return has_densepose_annotations
212
+
213
+
214
+ def _maybe_create_specific_keep_instance_predicate(cfg: CfgNode) -> Optional[InstancePredicate]:
215
+ specific_predicate_creators = [
216
+ _maybe_create_keypoints_keep_instance_predicate,
217
+ _maybe_create_mask_keep_instance_predicate,
218
+ _maybe_create_densepose_keep_instance_predicate,
219
+ ]
220
+ predicates = [creator(cfg) for creator in specific_predicate_creators]
221
+ predicates = [p for p in predicates if p is not None]
222
+ if not predicates:
223
+ return None
224
+
225
+ def combined_predicate(instance: Instance) -> bool:
226
+ return any(p(instance) for p in predicates)
227
+
228
+ return combined_predicate
229
+
230
+
231
+ def _get_train_keep_instance_predicate(cfg: CfgNode):
232
+ general_keep_predicate = _maybe_create_general_keep_instance_predicate(cfg)
233
+ combined_specific_keep_predicate = _maybe_create_specific_keep_instance_predicate(cfg)
234
+
235
+ def combined_general_specific_keep_predicate(instance: Instance) -> bool:
236
+ return general_keep_predicate(instance) and combined_specific_keep_predicate(instance)
237
+
238
+ if (general_keep_predicate is None) and (combined_specific_keep_predicate is None):
239
+ return None
240
+ if general_keep_predicate is None:
241
+ return combined_specific_keep_predicate
242
+ if combined_specific_keep_predicate is None:
243
+ return general_keep_predicate
244
+ return combined_general_specific_keep_predicate
245
+
246
+
247
+ def _get_test_keep_instance_predicate(cfg: CfgNode):
248
+ general_keep_predicate = _maybe_create_general_keep_instance_predicate(cfg)
249
+ return general_keep_predicate
250
+
251
+
252
+ def _maybe_filter_and_map_categories(
253
+ dataset_name: str, dataset_dicts: List[Instance]
254
+ ) -> List[Instance]:
255
+ meta = MetadataCatalog.get(dataset_name)
256
+ category_id_map = meta.thing_dataset_id_to_contiguous_id
257
+ filtered_dataset_dicts = []
258
+ for dataset_dict in dataset_dicts:
259
+ anns = []
260
+ for ann in dataset_dict["annotations"]:
261
+ cat_id = ann["category_id"]
262
+ if cat_id not in category_id_map:
263
+ continue
264
+ ann["category_id"] = category_id_map[cat_id]
265
+ anns.append(ann)
266
+ dataset_dict["annotations"] = anns
267
+ filtered_dataset_dicts.append(dataset_dict)
268
+ return filtered_dataset_dicts
269
+
270
+
271
+ def _add_category_whitelists_to_metadata(cfg: CfgNode) -> None:
272
+ for dataset_name, whitelisted_cat_ids in cfg.DATASETS.WHITELISTED_CATEGORIES.items():
273
+ meta = MetadataCatalog.get(dataset_name)
274
+ meta.whitelisted_categories = whitelisted_cat_ids
275
+ logger = logging.getLogger(__name__)
276
+ logger.info(
277
+ "Whitelisted categories for dataset {}: {}".format(
278
+ dataset_name, meta.whitelisted_categories
279
+ )
280
+ )
281
+
282
+
283
+ def _add_category_maps_to_metadata(cfg: CfgNode) -> None:
284
+ for dataset_name, category_map in cfg.DATASETS.CATEGORY_MAPS.items():
285
+ category_map = {
286
+ int(cat_id_src): int(cat_id_dst) for cat_id_src, cat_id_dst in category_map.items()
287
+ }
288
+ meta = MetadataCatalog.get(dataset_name)
289
+ meta.category_map = category_map
290
+ logger = logging.getLogger(__name__)
291
+ logger.info("Category maps for dataset {}: {}".format(dataset_name, meta.category_map))
292
+
293
+
294
+ def _add_category_info_to_bootstrapping_metadata(dataset_name: str, dataset_cfg: CfgNode) -> None:
295
+ meta = MetadataCatalog.get(dataset_name)
296
+ meta.category_to_class_mapping = get_category_to_class_mapping(dataset_cfg)
297
+ meta.categories = dataset_cfg.CATEGORIES
298
+ meta.max_count_per_category = dataset_cfg.MAX_COUNT_PER_CATEGORY
299
+ logger = logging.getLogger(__name__)
300
+ logger.info(
301
+ "Category to class mapping for dataset {}: {}".format(
302
+ dataset_name, meta.category_to_class_mapping
303
+ )
304
+ )
305
+
306
+
307
+ def _maybe_add_class_to_mesh_name_map_to_metadata(dataset_names: List[str], cfg: CfgNode) -> None:
308
+ for dataset_name in dataset_names:
309
+ meta = MetadataCatalog.get(dataset_name)
310
+ if not hasattr(meta, "class_to_mesh_name"):
311
+ meta.class_to_mesh_name = get_class_to_mesh_name_mapping(cfg)
312
+
313
+
314
+ def _merge_categories(dataset_names: Collection[str]) -> _MergedCategoriesT:
315
+ merged_categories = defaultdict(list)
316
+ category_names = {}
317
+ for dataset_name in dataset_names:
318
+ meta = MetadataCatalog.get(dataset_name)
319
+ whitelisted_categories = meta.get("whitelisted_categories")
320
+ category_map = meta.get("category_map", {})
321
+ cat_ids = (
322
+ whitelisted_categories if whitelisted_categories is not None else meta.categories.keys()
323
+ )
324
+ for cat_id in cat_ids:
325
+ cat_name = meta.categories[cat_id]
326
+ cat_id_mapped = category_map.get(cat_id, cat_id)
327
+ if cat_id_mapped == cat_id or cat_id_mapped in cat_ids:
328
+ category_names[cat_id] = cat_name
329
+ else:
330
+ category_names[cat_id] = str(cat_id_mapped)
331
+ # assign temporary mapped category name, this name can be changed
332
+ # during the second pass, since mapped ID can correspond to a category
333
+ # from a different dataset
334
+ cat_name_mapped = meta.categories[cat_id_mapped]
335
+ merged_categories[cat_id_mapped].append(
336
+ _DatasetCategory(
337
+ id=cat_id,
338
+ name=cat_name,
339
+ mapped_id=cat_id_mapped,
340
+ mapped_name=cat_name_mapped,
341
+ dataset_name=dataset_name,
342
+ )
343
+ )
344
+ # second pass to assign proper mapped category names
345
+ for cat_id, categories in merged_categories.items():
346
+ for cat in categories:
347
+ if cat_id in category_names and cat.mapped_name != category_names[cat_id]:
348
+ cat.mapped_name = category_names[cat_id]
349
+
350
+ return merged_categories
351
+
352
+
353
+ def _warn_if_merged_different_categories(merged_categories: _MergedCategoriesT) -> None:
354
+ logger = logging.getLogger(__name__)
355
+ for cat_id in merged_categories:
356
+ merged_categories_i = merged_categories[cat_id]
357
+ first_cat_name = merged_categories_i[0].name
358
+ if len(merged_categories_i) > 1 and not all(
359
+ cat.name == first_cat_name for cat in merged_categories_i[1:]
360
+ ):
361
+ cat_summary_str = ", ".join(
362
+ [f"{cat.id} ({cat.name}) from {cat.dataset_name}" for cat in merged_categories_i]
363
+ )
364
+ logger.warning(
365
+ f"Merged category {cat_id} corresponds to the following categories: "
366
+ f"{cat_summary_str}"
367
+ )
368
+
369
+
370
+ def combine_detection_dataset_dicts(
371
+ dataset_names: Collection[str],
372
+ keep_instance_predicate: Optional[InstancePredicate] = None,
373
+ proposal_files: Optional[Collection[str]] = None,
374
+ ) -> List[Instance]:
375
+ """
376
+ Load and prepare dataset dicts for training / testing
377
+
378
+ Args:
379
+ dataset_names (Collection[str]): a list of dataset names
380
+ keep_instance_predicate (Callable: Dict[str, Any] -> bool): predicate
381
+ applied to instance dicts which defines whether to keep the instance
382
+ proposal_files (Collection[str]): if given, a list of object proposal files
383
+ that match each dataset in `dataset_names`.
384
+ """
385
+ assert len(dataset_names)
386
+ if proposal_files is None:
387
+ proposal_files = [None] * len(dataset_names)
388
+ assert len(dataset_names) == len(proposal_files)
389
+ # load datasets and metadata
390
+ dataset_name_to_dicts = {}
391
+ for dataset_name in dataset_names:
392
+ dataset_name_to_dicts[dataset_name] = DatasetCatalog.get(dataset_name)
393
+ assert len(dataset_name_to_dicts), f"Dataset '{dataset_name}' is empty!"
394
+ # merge categories, requires category metadata to be loaded
395
+ # cat_id -> [(orig_cat_id, cat_name, dataset_name)]
396
+ merged_categories = _merge_categories(dataset_names)
397
+ _warn_if_merged_different_categories(merged_categories)
398
+ merged_category_names = [
399
+ merged_categories[cat_id][0].mapped_name for cat_id in sorted(merged_categories)
400
+ ]
401
+ # map to contiguous category IDs
402
+ _add_category_id_to_contiguous_id_maps_to_metadata(merged_categories)
403
+ # load annotations and dataset metadata
404
+ for dataset_name, proposal_file in zip(dataset_names, proposal_files):
405
+ dataset_dicts = dataset_name_to_dicts[dataset_name]
406
+ assert len(dataset_dicts), f"Dataset '{dataset_name}' is empty!"
407
+ if proposal_file is not None:
408
+ dataset_dicts = load_proposals_into_dataset(dataset_dicts, proposal_file)
409
+ dataset_dicts = _maybe_filter_and_map_categories(dataset_name, dataset_dicts)
410
+ print_instances_class_histogram(dataset_dicts, merged_category_names)
411
+ dataset_name_to_dicts[dataset_name] = dataset_dicts
412
+
413
+ if keep_instance_predicate is not None:
414
+ all_datasets_dicts_plain = [
415
+ d
416
+ for d in itertools.chain.from_iterable(dataset_name_to_dicts.values())
417
+ if keep_instance_predicate(d)
418
+ ]
419
+ else:
420
+ all_datasets_dicts_plain = list(
421
+ itertools.chain.from_iterable(dataset_name_to_dicts.values())
422
+ )
423
+ return all_datasets_dicts_plain
424
+
425
+
426
+ def build_detection_train_loader(cfg: CfgNode, mapper=None):
427
+ """
428
+ A data loader is created in a way similar to that of Detectron2.
429
+ The main differences are:
430
+ - it allows to combine datasets with different but compatible object category sets
431
+
432
+ The data loader is created by the following steps:
433
+ 1. Use the dataset names in config to query :class:`DatasetCatalog`, and obtain a list of dicts.
434
+ 2. Start workers to work on the dicts. Each worker will:
435
+ * Map each metadata dict into another format to be consumed by the model.
436
+ * Batch them by simply putting dicts into a list.
437
+ The batched ``list[mapped_dict]`` is what this dataloader will return.
438
+
439
+ Args:
440
+ cfg (CfgNode): the config
441
+ mapper (callable): a callable which takes a sample (dict) from dataset and
442
+ returns the format to be consumed by the model.
443
+ By default it will be `DatasetMapper(cfg, True)`.
444
+
445
+ Returns:
446
+ an infinite iterator of training data
447
+ """
448
+
449
+ _add_category_whitelists_to_metadata(cfg)
450
+ _add_category_maps_to_metadata(cfg)
451
+ _maybe_add_class_to_mesh_name_map_to_metadata(cfg.DATASETS.TRAIN, cfg)
452
+ dataset_dicts = combine_detection_dataset_dicts(
453
+ cfg.DATASETS.TRAIN,
454
+ keep_instance_predicate=_get_train_keep_instance_predicate(cfg),
455
+ proposal_files=cfg.DATASETS.PROPOSAL_FILES_TRAIN if cfg.MODEL.LOAD_PROPOSALS else None,
456
+ )
457
+ if mapper is None:
458
+ mapper = DatasetMapper(cfg, True)
459
+ return d2_build_detection_train_loader(cfg, dataset=dataset_dicts, mapper=mapper)
460
+
461
+
462
+ def build_detection_test_loader(cfg, dataset_name, mapper=None):
463
+ """
464
+ Similar to `build_detection_train_loader`.
465
+ But this function uses the given `dataset_name` argument (instead of the names in cfg),
466
+ and uses batch size 1.
467
+
468
+ Args:
469
+ cfg: a detectron2 CfgNode
470
+ dataset_name (str): a name of the dataset that's available in the DatasetCatalog
471
+ mapper (callable): a callable which takes a sample (dict) from dataset
472
+ and returns the format to be consumed by the model.
473
+ By default it will be `DatasetMapper(cfg, False)`.
474
+
475
+ Returns:
476
+ DataLoader: a torch DataLoader, that loads the given detection
477
+ dataset, with test-time transformation and batching.
478
+ """
479
+ _add_category_whitelists_to_metadata(cfg)
480
+ _add_category_maps_to_metadata(cfg)
481
+ _maybe_add_class_to_mesh_name_map_to_metadata([dataset_name], cfg)
482
+ dataset_dicts = combine_detection_dataset_dicts(
483
+ [dataset_name],
484
+ keep_instance_predicate=_get_test_keep_instance_predicate(cfg),
485
+ proposal_files=[
486
+ cfg.DATASETS.PROPOSAL_FILES_TEST[list(cfg.DATASETS.TEST).index(dataset_name)]
487
+ ]
488
+ if cfg.MODEL.LOAD_PROPOSALS
489
+ else None,
490
+ )
491
+ sampler = None
492
+ if not cfg.DENSEPOSE_EVALUATION.DISTRIBUTED_INFERENCE:
493
+ sampler = torch.utils.data.SequentialSampler(dataset_dicts)
494
+ if mapper is None:
495
+ mapper = DatasetMapper(cfg, False)
496
+ return d2_build_detection_test_loader(
497
+ dataset_dicts, mapper=mapper, num_workers=cfg.DATALOADER.NUM_WORKERS, sampler=sampler
498
+ )
499
+
500
+
501
+ def build_frame_selector(cfg: CfgNode):
502
+ strategy = FrameSelectionStrategy(cfg.STRATEGY)
503
+ if strategy == FrameSelectionStrategy.RANDOM_K:
504
+ frame_selector = RandomKFramesSelector(cfg.NUM_IMAGES)
505
+ elif strategy == FrameSelectionStrategy.FIRST_K:
506
+ frame_selector = FirstKFramesSelector(cfg.NUM_IMAGES)
507
+ elif strategy == FrameSelectionStrategy.LAST_K:
508
+ frame_selector = LastKFramesSelector(cfg.NUM_IMAGES)
509
+ elif strategy == FrameSelectionStrategy.ALL:
510
+ frame_selector = None
511
+ # pyre-fixme[61]: `frame_selector` may not be initialized here.
512
+ return frame_selector
513
+
514
+
515
+ def build_transform(cfg: CfgNode, data_type: str):
516
+ if cfg.TYPE == "resize":
517
+ if data_type == "image":
518
+ return ImageResizeTransform(cfg.MIN_SIZE, cfg.MAX_SIZE)
519
+ raise ValueError(f"Unknown transform {cfg.TYPE} for data type {data_type}")
520
+
521
+
522
+ def build_combined_loader(cfg: CfgNode, loaders: Collection[Loader], ratios: Sequence[float]):
523
+ images_per_worker = _compute_num_images_per_worker(cfg)
524
+ return CombinedDataLoader(loaders, images_per_worker, ratios)
525
+
526
+
527
+ def build_bootstrap_dataset(dataset_name: str, cfg: CfgNode) -> Sequence[torch.Tensor]:
528
+ """
529
+ Build dataset that provides data to bootstrap on
530
+
531
+ Args:
532
+ dataset_name (str): Name of the dataset, needs to have associated metadata
533
+ to load the data
534
+ cfg (CfgNode): bootstrapping config
535
+ Returns:
536
+ Sequence[Tensor] - dataset that provides image batches, Tensors of size
537
+ [N, C, H, W] of type float32
538
+ """
539
+ logger = logging.getLogger(__name__)
540
+ _add_category_info_to_bootstrapping_metadata(dataset_name, cfg)
541
+ meta = MetadataCatalog.get(dataset_name)
542
+ factory = BootstrapDatasetFactoryCatalog.get(meta.dataset_type)
543
+ dataset = None
544
+ if factory is not None:
545
+ dataset = factory(meta, cfg)
546
+ if dataset is None:
547
+ logger.warning(f"Failed to create dataset {dataset_name} of type {meta.dataset_type}")
548
+ return dataset
549
+
550
+
551
+ def build_data_sampler(cfg: CfgNode, sampler_cfg: CfgNode, embedder: Optional[torch.nn.Module]):
552
+ if sampler_cfg.TYPE == "densepose_uniform":
553
+ data_sampler = PredictionToGroundTruthSampler()
554
+ # transform densepose pred -> gt
555
+ data_sampler.register_sampler(
556
+ "pred_densepose",
557
+ "gt_densepose",
558
+ DensePoseUniformSampler(count_per_class=sampler_cfg.COUNT_PER_CLASS),
559
+ )
560
+ data_sampler.register_sampler("pred_densepose", "gt_masks", MaskFromDensePoseSampler())
561
+ return data_sampler
562
+ elif sampler_cfg.TYPE == "densepose_UV_confidence":
563
+ data_sampler = PredictionToGroundTruthSampler()
564
+ # transform densepose pred -> gt
565
+ data_sampler.register_sampler(
566
+ "pred_densepose",
567
+ "gt_densepose",
568
+ DensePoseConfidenceBasedSampler(
569
+ confidence_channel="sigma_2",
570
+ count_per_class=sampler_cfg.COUNT_PER_CLASS,
571
+ search_proportion=0.5,
572
+ ),
573
+ )
574
+ data_sampler.register_sampler("pred_densepose", "gt_masks", MaskFromDensePoseSampler())
575
+ return data_sampler
576
+ elif sampler_cfg.TYPE == "densepose_fine_segm_confidence":
577
+ data_sampler = PredictionToGroundTruthSampler()
578
+ # transform densepose pred -> gt
579
+ data_sampler.register_sampler(
580
+ "pred_densepose",
581
+ "gt_densepose",
582
+ DensePoseConfidenceBasedSampler(
583
+ confidence_channel="fine_segm_confidence",
584
+ count_per_class=sampler_cfg.COUNT_PER_CLASS,
585
+ search_proportion=0.5,
586
+ ),
587
+ )
588
+ data_sampler.register_sampler("pred_densepose", "gt_masks", MaskFromDensePoseSampler())
589
+ return data_sampler
590
+ elif sampler_cfg.TYPE == "densepose_coarse_segm_confidence":
591
+ data_sampler = PredictionToGroundTruthSampler()
592
+ # transform densepose pred -> gt
593
+ data_sampler.register_sampler(
594
+ "pred_densepose",
595
+ "gt_densepose",
596
+ DensePoseConfidenceBasedSampler(
597
+ confidence_channel="coarse_segm_confidence",
598
+ count_per_class=sampler_cfg.COUNT_PER_CLASS,
599
+ search_proportion=0.5,
600
+ ),
601
+ )
602
+ data_sampler.register_sampler("pred_densepose", "gt_masks", MaskFromDensePoseSampler())
603
+ return data_sampler
604
+ elif sampler_cfg.TYPE == "densepose_cse_uniform":
605
+ assert embedder is not None
606
+ data_sampler = PredictionToGroundTruthSampler()
607
+ # transform densepose pred -> gt
608
+ data_sampler.register_sampler(
609
+ "pred_densepose",
610
+ "gt_densepose",
611
+ DensePoseCSEUniformSampler(
612
+ cfg=cfg,
613
+ use_gt_categories=sampler_cfg.USE_GROUND_TRUTH_CATEGORIES,
614
+ embedder=embedder,
615
+ count_per_class=sampler_cfg.COUNT_PER_CLASS,
616
+ ),
617
+ )
618
+ data_sampler.register_sampler("pred_densepose", "gt_masks", MaskFromDensePoseSampler())
619
+ return data_sampler
620
+ elif sampler_cfg.TYPE == "densepose_cse_coarse_segm_confidence":
621
+ assert embedder is not None
622
+ data_sampler = PredictionToGroundTruthSampler()
623
+ # transform densepose pred -> gt
624
+ data_sampler.register_sampler(
625
+ "pred_densepose",
626
+ "gt_densepose",
627
+ DensePoseCSEConfidenceBasedSampler(
628
+ cfg=cfg,
629
+ use_gt_categories=sampler_cfg.USE_GROUND_TRUTH_CATEGORIES,
630
+ embedder=embedder,
631
+ confidence_channel="coarse_segm_confidence",
632
+ count_per_class=sampler_cfg.COUNT_PER_CLASS,
633
+ search_proportion=0.5,
634
+ ),
635
+ )
636
+ data_sampler.register_sampler("pred_densepose", "gt_masks", MaskFromDensePoseSampler())
637
+ return data_sampler
638
+
639
+ raise ValueError(f"Unknown data sampler type {sampler_cfg.TYPE}")
640
+
641
+
642
+ def build_data_filter(cfg: CfgNode):
643
+ if cfg.TYPE == "detection_score":
644
+ min_score = cfg.MIN_VALUE
645
+ return ScoreBasedFilter(min_score=min_score)
646
+ raise ValueError(f"Unknown data filter type {cfg.TYPE}")
647
+
648
+
649
+ def build_inference_based_loader(
650
+ cfg: CfgNode,
651
+ dataset_cfg: CfgNode,
652
+ model: torch.nn.Module,
653
+ embedder: Optional[torch.nn.Module] = None,
654
+ ) -> InferenceBasedLoader:
655
+ """
656
+ Constructs data loader based on inference results of a model.
657
+ """
658
+ dataset = build_bootstrap_dataset(dataset_cfg.DATASET, dataset_cfg.IMAGE_LOADER)
659
+ meta = MetadataCatalog.get(dataset_cfg.DATASET)
660
+ training_sampler = TrainingSampler(len(dataset))
661
+ data_loader = torch.utils.data.DataLoader(
662
+ dataset, # pyre-ignore[6]
663
+ batch_size=dataset_cfg.IMAGE_LOADER.BATCH_SIZE,
664
+ sampler=training_sampler,
665
+ num_workers=dataset_cfg.IMAGE_LOADER.NUM_WORKERS,
666
+ collate_fn=trivial_batch_collator,
667
+ worker_init_fn=worker_init_reset_seed,
668
+ )
669
+ return InferenceBasedLoader(
670
+ model,
671
+ data_loader=data_loader,
672
+ data_sampler=build_data_sampler(cfg, dataset_cfg.DATA_SAMPLER, embedder),
673
+ data_filter=build_data_filter(dataset_cfg.FILTER),
674
+ shuffle=True,
675
+ batch_size=dataset_cfg.INFERENCE.OUTPUT_BATCH_SIZE,
676
+ inference_batch_size=dataset_cfg.INFERENCE.INPUT_BATCH_SIZE,
677
+ category_to_class_mapping=meta.category_to_class_mapping,
678
+ )
679
+
680
+
681
+ def has_inference_based_loaders(cfg: CfgNode) -> bool:
682
+ """
683
+ Returns True, if at least one inferense-based loader must
684
+ be instantiated for training
685
+ """
686
+ return len(cfg.BOOTSTRAP_DATASETS) > 0
687
+
688
+
689
+ def build_inference_based_loaders(
690
+ cfg: CfgNode, model: torch.nn.Module
691
+ ) -> Tuple[List[InferenceBasedLoader], List[float]]:
692
+ loaders = []
693
+ ratios = []
694
+ embedder = build_densepose_embedder(cfg).to(device=model.device) # pyre-ignore[16]
695
+ for dataset_spec in cfg.BOOTSTRAP_DATASETS:
696
+ dataset_cfg = get_bootstrap_dataset_config().clone()
697
+ dataset_cfg.merge_from_other_cfg(CfgNode(dataset_spec))
698
+ loader = build_inference_based_loader(cfg, dataset_cfg, model, embedder)
699
+ loaders.append(loader)
700
+ ratios.append(dataset_cfg.RATIO)
701
+ return loaders, ratios
702
+
703
+
704
+ def build_video_list_dataset(meta: Metadata, cfg: CfgNode):
705
+ video_list_fpath = meta.video_list_fpath
706
+ video_base_path = meta.video_base_path
707
+ category = meta.category
708
+ if cfg.TYPE == "video_keyframe":
709
+ frame_selector = build_frame_selector(cfg.SELECT)
710
+ transform = build_transform(cfg.TRANSFORM, data_type="image")
711
+ video_list = video_list_from_file(video_list_fpath, video_base_path)
712
+ keyframe_helper_fpath = getattr(cfg, "KEYFRAME_HELPER", None)
713
+ return VideoKeyframeDataset(
714
+ video_list, category, frame_selector, transform, keyframe_helper_fpath
715
+ )
716
+
717
+
718
+ class _BootstrapDatasetFactoryCatalog(UserDict):
719
+ """
720
+ A global dictionary that stores information about bootstrapped datasets creation functions
721
+ from metadata and config, for diverse DatasetType
722
+ """
723
+
724
+ def register(self, dataset_type: DatasetType, factory: Callable[[Metadata, CfgNode], Dataset]):
725
+ """
726
+ Args:
727
+ dataset_type (DatasetType): a DatasetType e.g. DatasetType.VIDEO_LIST
728
+ factory (Callable[Metadata, CfgNode]): a callable which takes Metadata and cfg
729
+ arguments and returns a dataset object.
730
+ """
731
+ assert dataset_type not in self, "Dataset '{}' is already registered!".format(dataset_type)
732
+ self[dataset_type] = factory
733
+
734
+
735
+ BootstrapDatasetFactoryCatalog = _BootstrapDatasetFactoryCatalog()
736
+ BootstrapDatasetFactoryCatalog.register(DatasetType.VIDEO_LIST, build_video_list_dataset)
gradio_demo/densepose/data/combined_loader.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+
3
+ import random
4
+ from collections import deque
5
+ from typing import Any, Collection, Deque, Iterable, Iterator, List, Sequence
6
+
7
+ Loader = Iterable[Any]
8
+
9
+
10
+ def _pooled_next(iterator: Iterator[Any], pool: Deque[Any]):
11
+ if not pool:
12
+ pool.extend(next(iterator))
13
+ return pool.popleft()
14
+
15
+
16
+ class CombinedDataLoader:
17
+ """
18
+ Combines data loaders using the provided sampling ratios
19
+ """
20
+
21
+ BATCH_COUNT = 100
22
+
23
+ def __init__(self, loaders: Collection[Loader], batch_size: int, ratios: Sequence[float]):
24
+ self.loaders = loaders
25
+ self.batch_size = batch_size
26
+ self.ratios = ratios
27
+
28
+ def __iter__(self) -> Iterator[List[Any]]:
29
+ iters = [iter(loader) for loader in self.loaders]
30
+ indices = []
31
+ pool = [deque()] * len(iters)
32
+ # infinite iterator, as in D2
33
+ while True:
34
+ if not indices:
35
+ # just a buffer of indices, its size doesn't matter
36
+ # as long as it's a multiple of batch_size
37
+ k = self.batch_size * self.BATCH_COUNT
38
+ indices = random.choices(range(len(self.loaders)), self.ratios, k=k)
39
+ try:
40
+ batch = [_pooled_next(iters[i], pool[i]) for i in indices[: self.batch_size]]
41
+ except StopIteration:
42
+ break
43
+ indices = indices[self.batch_size :]
44
+ yield batch
gradio_demo/densepose/data/dataset_mapper.py ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright (c) Facebook, Inc. and its affiliates.
3
+
4
+ import copy
5
+ import logging
6
+ from typing import Any, Dict, List, Tuple
7
+ import torch
8
+
9
+ from detectron2.data import MetadataCatalog
10
+ from detectron2.data import detection_utils as utils
11
+ from detectron2.data import transforms as T
12
+ from detectron2.layers import ROIAlign
13
+ from detectron2.structures import BoxMode
14
+ from detectron2.utils.file_io import PathManager
15
+
16
+ from densepose.structures import DensePoseDataRelative, DensePoseList, DensePoseTransformData
17
+
18
+
19
+ def build_augmentation(cfg, is_train):
20
+ logger = logging.getLogger(__name__)
21
+ result = utils.build_augmentation(cfg, is_train)
22
+ if is_train:
23
+ random_rotation = T.RandomRotation(
24
+ cfg.INPUT.ROTATION_ANGLES, expand=False, sample_style="choice"
25
+ )
26
+ result.append(random_rotation)
27
+ logger.info("DensePose-specific augmentation used in training: " + str(random_rotation))
28
+ return result
29
+
30
+
31
+ class DatasetMapper:
32
+ """
33
+ A customized version of `detectron2.data.DatasetMapper`
34
+ """
35
+
36
+ def __init__(self, cfg, is_train=True):
37
+ self.augmentation = build_augmentation(cfg, is_train)
38
+
39
+ # fmt: off
40
+ self.img_format = cfg.INPUT.FORMAT
41
+ self.mask_on = (
42
+ cfg.MODEL.MASK_ON or (
43
+ cfg.MODEL.DENSEPOSE_ON
44
+ and cfg.MODEL.ROI_DENSEPOSE_HEAD.COARSE_SEGM_TRAINED_BY_MASKS)
45
+ )
46
+ self.keypoint_on = cfg.MODEL.KEYPOINT_ON
47
+ self.densepose_on = cfg.MODEL.DENSEPOSE_ON
48
+ assert not cfg.MODEL.LOAD_PROPOSALS, "not supported yet"
49
+ # fmt: on
50
+ if self.keypoint_on and is_train:
51
+ # Flip only makes sense in training
52
+ self.keypoint_hflip_indices = utils.create_keypoint_hflip_indices(cfg.DATASETS.TRAIN)
53
+ else:
54
+ self.keypoint_hflip_indices = None
55
+
56
+ if self.densepose_on:
57
+ densepose_transform_srcs = [
58
+ MetadataCatalog.get(ds).densepose_transform_src
59
+ for ds in cfg.DATASETS.TRAIN + cfg.DATASETS.TEST
60
+ ]
61
+ assert len(densepose_transform_srcs) > 0
62
+ # TODO: check that DensePose transformation data is the same for
63
+ # all the datasets. Otherwise one would have to pass DB ID with
64
+ # each entry to select proper transformation data. For now, since
65
+ # all DensePose annotated data uses the same data semantics, we
66
+ # omit this check.
67
+ densepose_transform_data_fpath = PathManager.get_local_path(densepose_transform_srcs[0])
68
+ self.densepose_transform_data = DensePoseTransformData.load(
69
+ densepose_transform_data_fpath
70
+ )
71
+
72
+ self.is_train = is_train
73
+
74
+ def __call__(self, dataset_dict):
75
+ """
76
+ Args:
77
+ dataset_dict (dict): Metadata of one image, in Detectron2 Dataset format.
78
+
79
+ Returns:
80
+ dict: a format that builtin models in detectron2 accept
81
+ """
82
+ dataset_dict = copy.deepcopy(dataset_dict) # it will be modified by code below
83
+ image = utils.read_image(dataset_dict["file_name"], format=self.img_format)
84
+ utils.check_image_size(dataset_dict, image)
85
+
86
+ image, transforms = T.apply_transform_gens(self.augmentation, image)
87
+ image_shape = image.shape[:2] # h, w
88
+ dataset_dict["image"] = torch.as_tensor(image.transpose(2, 0, 1).astype("float32"))
89
+
90
+ if not self.is_train:
91
+ dataset_dict.pop("annotations", None)
92
+ return dataset_dict
93
+
94
+ for anno in dataset_dict["annotations"]:
95
+ if not self.mask_on:
96
+ anno.pop("segmentation", None)
97
+ if not self.keypoint_on:
98
+ anno.pop("keypoints", None)
99
+
100
+ # USER: Implement additional transformations if you have other types of data
101
+ # USER: Don't call transpose_densepose if you don't need
102
+ annos = [
103
+ self._transform_densepose(
104
+ utils.transform_instance_annotations(
105
+ obj, transforms, image_shape, keypoint_hflip_indices=self.keypoint_hflip_indices
106
+ ),
107
+ transforms,
108
+ )
109
+ for obj in dataset_dict.pop("annotations")
110
+ if obj.get("iscrowd", 0) == 0
111
+ ]
112
+
113
+ if self.mask_on:
114
+ self._add_densepose_masks_as_segmentation(annos, image_shape)
115
+
116
+ instances = utils.annotations_to_instances(annos, image_shape, mask_format="bitmask")
117
+ densepose_annotations = [obj.get("densepose") for obj in annos]
118
+ if densepose_annotations and not all(v is None for v in densepose_annotations):
119
+ instances.gt_densepose = DensePoseList(
120
+ densepose_annotations, instances.gt_boxes, image_shape
121
+ )
122
+
123
+ dataset_dict["instances"] = instances[instances.gt_boxes.nonempty()]
124
+ return dataset_dict
125
+
126
+ def _transform_densepose(self, annotation, transforms):
127
+ if not self.densepose_on:
128
+ return annotation
129
+
130
+ # Handle densepose annotations
131
+ is_valid, reason_not_valid = DensePoseDataRelative.validate_annotation(annotation)
132
+ if is_valid:
133
+ densepose_data = DensePoseDataRelative(annotation, cleanup=True)
134
+ densepose_data.apply_transform(transforms, self.densepose_transform_data)
135
+ annotation["densepose"] = densepose_data
136
+ else:
137
+ # logger = logging.getLogger(__name__)
138
+ # logger.debug("Could not load DensePose annotation: {}".format(reason_not_valid))
139
+ DensePoseDataRelative.cleanup_annotation(annotation)
140
+ # NOTE: annotations for certain instances may be unavailable.
141
+ # 'None' is accepted by the DensePostList data structure.
142
+ annotation["densepose"] = None
143
+ return annotation
144
+
145
+ def _add_densepose_masks_as_segmentation(
146
+ self, annotations: List[Dict[str, Any]], image_shape_hw: Tuple[int, int]
147
+ ):
148
+ for obj in annotations:
149
+ if ("densepose" not in obj) or ("segmentation" in obj):
150
+ continue
151
+ # DP segmentation: torch.Tensor [S, S] of float32, S=256
152
+ segm_dp = torch.zeros_like(obj["densepose"].segm)
153
+ segm_dp[obj["densepose"].segm > 0] = 1
154
+ segm_h, segm_w = segm_dp.shape
155
+ bbox_segm_dp = torch.tensor((0, 0, segm_h - 1, segm_w - 1), dtype=torch.float32)
156
+ # image bbox
157
+ x0, y0, x1, y1 = (
158
+ v.item() for v in BoxMode.convert(obj["bbox"], obj["bbox_mode"], BoxMode.XYXY_ABS)
159
+ )
160
+ segm_aligned = (
161
+ ROIAlign((y1 - y0, x1 - x0), 1.0, 0, aligned=True)
162
+ .forward(segm_dp.view(1, 1, *segm_dp.shape), bbox_segm_dp)
163
+ .squeeze()
164
+ )
165
+ image_mask = torch.zeros(*image_shape_hw, dtype=torch.float32)
166
+ image_mask[y0:y1, x0:x1] = segm_aligned
167
+ # segmentation for BitMask: np.array [H, W] of bool
168
+ obj["segmentation"] = image_mask >= 0.5
gradio_demo/densepose/data/datasets/__init__.py ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+
3
+ from . import builtin # ensure the builtin datasets are registered
4
+
5
+ __all__ = [k for k in globals().keys() if "builtin" not in k and not k.startswith("_")]
gradio_demo/densepose/data/datasets/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (383 Bytes). View file
 
gradio_demo/densepose/data/datasets/__pycache__/builtin.cpython-310.pyc ADDED
Binary file (572 Bytes). View file
 
gradio_demo/densepose/data/datasets/__pycache__/chimpnsee.cpython-310.pyc ADDED
Binary file (1.03 kB). View file
 
gradio_demo/densepose/data/datasets/__pycache__/coco.cpython-310.pyc ADDED
Binary file (11.7 kB). View file
 
gradio_demo/densepose/data/datasets/__pycache__/dataset_type.cpython-310.pyc ADDED
Binary file (498 Bytes). View file
 
gradio_demo/densepose/data/datasets/__pycache__/lvis.cpython-310.pyc ADDED
Binary file (7.86 kB). View file
 
gradio_demo/densepose/data/datasets/builtin.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ from .chimpnsee import register_dataset as register_chimpnsee_dataset
3
+ from .coco import BASE_DATASETS as BASE_COCO_DATASETS
4
+ from .coco import DATASETS as COCO_DATASETS
5
+ from .coco import register_datasets as register_coco_datasets
6
+ from .lvis import DATASETS as LVIS_DATASETS
7
+ from .lvis import register_datasets as register_lvis_datasets
8
+
9
+ DEFAULT_DATASETS_ROOT = "datasets"
10
+
11
+
12
+ register_coco_datasets(COCO_DATASETS, DEFAULT_DATASETS_ROOT)
13
+ register_coco_datasets(BASE_COCO_DATASETS, DEFAULT_DATASETS_ROOT)
14
+ register_lvis_datasets(LVIS_DATASETS, DEFAULT_DATASETS_ROOT)
15
+
16
+ register_chimpnsee_dataset(DEFAULT_DATASETS_ROOT) # pyre-ignore[19]
gradio_demo/densepose/data/datasets/chimpnsee.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+
3
+ from typing import Optional
4
+
5
+ from detectron2.data import DatasetCatalog, MetadataCatalog
6
+
7
+ from ..utils import maybe_prepend_base_path
8
+ from .dataset_type import DatasetType
9
+
10
+ CHIMPNSEE_DATASET_NAME = "chimpnsee"
11
+
12
+
13
+ def register_dataset(datasets_root: Optional[str] = None) -> None:
14
+ def empty_load_callback():
15
+ pass
16
+
17
+ video_list_fpath = maybe_prepend_base_path(
18
+ datasets_root,
19
+ "chimpnsee/cdna.eva.mpg.de/video_list.txt",
20
+ )
21
+ video_base_path = maybe_prepend_base_path(datasets_root, "chimpnsee/cdna.eva.mpg.de")
22
+
23
+ DatasetCatalog.register(CHIMPNSEE_DATASET_NAME, empty_load_callback)
24
+ MetadataCatalog.get(CHIMPNSEE_DATASET_NAME).set(
25
+ dataset_type=DatasetType.VIDEO_LIST,
26
+ video_list_fpath=video_list_fpath,
27
+ video_base_path=video_base_path,
28
+ category="chimpanzee",
29
+ )
gradio_demo/densepose/data/datasets/coco.py ADDED
@@ -0,0 +1,432 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ import contextlib
3
+ import io
4
+ import logging
5
+ import os
6
+ from collections import defaultdict
7
+ from dataclasses import dataclass
8
+ from typing import Any, Dict, Iterable, List, Optional
9
+ from fvcore.common.timer import Timer
10
+
11
+ from detectron2.data import DatasetCatalog, MetadataCatalog
12
+ from detectron2.structures import BoxMode
13
+ from detectron2.utils.file_io import PathManager
14
+
15
+ from ..utils import maybe_prepend_base_path
16
+
17
+ DENSEPOSE_MASK_KEY = "dp_masks"
18
+ DENSEPOSE_IUV_KEYS_WITHOUT_MASK = ["dp_x", "dp_y", "dp_I", "dp_U", "dp_V"]
19
+ DENSEPOSE_CSE_KEYS_WITHOUT_MASK = ["dp_x", "dp_y", "dp_vertex", "ref_model"]
20
+ DENSEPOSE_ALL_POSSIBLE_KEYS = set(
21
+ DENSEPOSE_IUV_KEYS_WITHOUT_MASK + DENSEPOSE_CSE_KEYS_WITHOUT_MASK + [DENSEPOSE_MASK_KEY]
22
+ )
23
+ DENSEPOSE_METADATA_URL_PREFIX = "https://dl.fbaipublicfiles.com/densepose/data/"
24
+
25
+
26
+ @dataclass
27
+ class CocoDatasetInfo:
28
+ name: str
29
+ images_root: str
30
+ annotations_fpath: str
31
+
32
+
33
+ DATASETS = [
34
+ CocoDatasetInfo(
35
+ name="densepose_coco_2014_train",
36
+ images_root="coco/train2014",
37
+ annotations_fpath="coco/annotations/densepose_train2014.json",
38
+ ),
39
+ CocoDatasetInfo(
40
+ name="densepose_coco_2014_minival",
41
+ images_root="coco/val2014",
42
+ annotations_fpath="coco/annotations/densepose_minival2014.json",
43
+ ),
44
+ CocoDatasetInfo(
45
+ name="densepose_coco_2014_minival_100",
46
+ images_root="coco/val2014",
47
+ annotations_fpath="coco/annotations/densepose_minival2014_100.json",
48
+ ),
49
+ CocoDatasetInfo(
50
+ name="densepose_coco_2014_valminusminival",
51
+ images_root="coco/val2014",
52
+ annotations_fpath="coco/annotations/densepose_valminusminival2014.json",
53
+ ),
54
+ CocoDatasetInfo(
55
+ name="densepose_coco_2014_train_cse",
56
+ images_root="coco/train2014",
57
+ annotations_fpath="coco_cse/densepose_train2014_cse.json",
58
+ ),
59
+ CocoDatasetInfo(
60
+ name="densepose_coco_2014_minival_cse",
61
+ images_root="coco/val2014",
62
+ annotations_fpath="coco_cse/densepose_minival2014_cse.json",
63
+ ),
64
+ CocoDatasetInfo(
65
+ name="densepose_coco_2014_minival_100_cse",
66
+ images_root="coco/val2014",
67
+ annotations_fpath="coco_cse/densepose_minival2014_100_cse.json",
68
+ ),
69
+ CocoDatasetInfo(
70
+ name="densepose_coco_2014_valminusminival_cse",
71
+ images_root="coco/val2014",
72
+ annotations_fpath="coco_cse/densepose_valminusminival2014_cse.json",
73
+ ),
74
+ CocoDatasetInfo(
75
+ name="densepose_chimps",
76
+ images_root="densepose_chimps/images",
77
+ annotations_fpath="densepose_chimps/densepose_chimps_densepose.json",
78
+ ),
79
+ CocoDatasetInfo(
80
+ name="densepose_chimps_cse_train",
81
+ images_root="densepose_chimps/images",
82
+ annotations_fpath="densepose_chimps/densepose_chimps_cse_train.json",
83
+ ),
84
+ CocoDatasetInfo(
85
+ name="densepose_chimps_cse_val",
86
+ images_root="densepose_chimps/images",
87
+ annotations_fpath="densepose_chimps/densepose_chimps_cse_val.json",
88
+ ),
89
+ CocoDatasetInfo(
90
+ name="posetrack2017_train",
91
+ images_root="posetrack2017/posetrack_data_2017",
92
+ annotations_fpath="posetrack2017/densepose_posetrack_train2017.json",
93
+ ),
94
+ CocoDatasetInfo(
95
+ name="posetrack2017_val",
96
+ images_root="posetrack2017/posetrack_data_2017",
97
+ annotations_fpath="posetrack2017/densepose_posetrack_val2017.json",
98
+ ),
99
+ CocoDatasetInfo(
100
+ name="lvis_v05_train",
101
+ images_root="coco/train2017",
102
+ annotations_fpath="lvis/lvis_v0.5_plus_dp_train.json",
103
+ ),
104
+ CocoDatasetInfo(
105
+ name="lvis_v05_val",
106
+ images_root="coco/val2017",
107
+ annotations_fpath="lvis/lvis_v0.5_plus_dp_val.json",
108
+ ),
109
+ ]
110
+
111
+
112
+ BASE_DATASETS = [
113
+ CocoDatasetInfo(
114
+ name="base_coco_2017_train",
115
+ images_root="coco/train2017",
116
+ annotations_fpath="coco/annotations/instances_train2017.json",
117
+ ),
118
+ CocoDatasetInfo(
119
+ name="base_coco_2017_val",
120
+ images_root="coco/val2017",
121
+ annotations_fpath="coco/annotations/instances_val2017.json",
122
+ ),
123
+ CocoDatasetInfo(
124
+ name="base_coco_2017_val_100",
125
+ images_root="coco/val2017",
126
+ annotations_fpath="coco/annotations/instances_val2017_100.json",
127
+ ),
128
+ ]
129
+
130
+
131
+ def get_metadata(base_path: Optional[str]) -> Dict[str, Any]:
132
+ """
133
+ Returns metadata associated with COCO DensePose datasets
134
+
135
+ Args:
136
+ base_path: Optional[str]
137
+ Base path used to load metadata from
138
+
139
+ Returns:
140
+ Dict[str, Any]
141
+ Metadata in the form of a dictionary
142
+ """
143
+ meta = {
144
+ "densepose_transform_src": maybe_prepend_base_path(base_path, "UV_symmetry_transforms.mat"),
145
+ "densepose_smpl_subdiv": maybe_prepend_base_path(base_path, "SMPL_subdiv.mat"),
146
+ "densepose_smpl_subdiv_transform": maybe_prepend_base_path(
147
+ base_path,
148
+ "SMPL_SUBDIV_TRANSFORM.mat",
149
+ ),
150
+ }
151
+ return meta
152
+
153
+
154
+ def _load_coco_annotations(json_file: str):
155
+ """
156
+ Load COCO annotations from a JSON file
157
+
158
+ Args:
159
+ json_file: str
160
+ Path to the file to load annotations from
161
+ Returns:
162
+ Instance of `pycocotools.coco.COCO` that provides access to annotations
163
+ data
164
+ """
165
+ from pycocotools.coco import COCO
166
+
167
+ logger = logging.getLogger(__name__)
168
+ timer = Timer()
169
+ with contextlib.redirect_stdout(io.StringIO()):
170
+ coco_api = COCO(json_file)
171
+ if timer.seconds() > 1:
172
+ logger.info("Loading {} takes {:.2f} seconds.".format(json_file, timer.seconds()))
173
+ return coco_api
174
+
175
+
176
+ def _add_categories_metadata(dataset_name: str, categories: List[Dict[str, Any]]):
177
+ meta = MetadataCatalog.get(dataset_name)
178
+ meta.categories = {c["id"]: c["name"] for c in categories}
179
+ logger = logging.getLogger(__name__)
180
+ logger.info("Dataset {} categories: {}".format(dataset_name, meta.categories))
181
+
182
+
183
+ def _verify_annotations_have_unique_ids(json_file: str, anns: List[List[Dict[str, Any]]]):
184
+ if "minival" in json_file:
185
+ # Skip validation on COCO2014 valminusminival and minival annotations
186
+ # The ratio of buggy annotations there is tiny and does not affect accuracy
187
+ # Therefore we explicitly white-list them
188
+ return
189
+ ann_ids = [ann["id"] for anns_per_image in anns for ann in anns_per_image]
190
+ assert len(set(ann_ids)) == len(ann_ids), "Annotation ids in '{}' are not unique!".format(
191
+ json_file
192
+ )
193
+
194
+
195
+ def _maybe_add_bbox(obj: Dict[str, Any], ann_dict: Dict[str, Any]):
196
+ if "bbox" not in ann_dict:
197
+ return
198
+ obj["bbox"] = ann_dict["bbox"]
199
+ obj["bbox_mode"] = BoxMode.XYWH_ABS
200
+
201
+
202
+ def _maybe_add_segm(obj: Dict[str, Any], ann_dict: Dict[str, Any]):
203
+ if "segmentation" not in ann_dict:
204
+ return
205
+ segm = ann_dict["segmentation"]
206
+ if not isinstance(segm, dict):
207
+ # filter out invalid polygons (< 3 points)
208
+ segm = [poly for poly in segm if len(poly) % 2 == 0 and len(poly) >= 6]
209
+ if len(segm) == 0:
210
+ return
211
+ obj["segmentation"] = segm
212
+
213
+
214
+ def _maybe_add_keypoints(obj: Dict[str, Any], ann_dict: Dict[str, Any]):
215
+ if "keypoints" not in ann_dict:
216
+ return
217
+ keypts = ann_dict["keypoints"] # list[int]
218
+ for idx, v in enumerate(keypts):
219
+ if idx % 3 != 2:
220
+ # COCO's segmentation coordinates are floating points in [0, H or W],
221
+ # but keypoint coordinates are integers in [0, H-1 or W-1]
222
+ # Therefore we assume the coordinates are "pixel indices" and
223
+ # add 0.5 to convert to floating point coordinates.
224
+ keypts[idx] = v + 0.5
225
+ obj["keypoints"] = keypts
226
+
227
+
228
+ def _maybe_add_densepose(obj: Dict[str, Any], ann_dict: Dict[str, Any]):
229
+ for key in DENSEPOSE_ALL_POSSIBLE_KEYS:
230
+ if key in ann_dict:
231
+ obj[key] = ann_dict[key]
232
+
233
+
234
+ def _combine_images_with_annotations(
235
+ dataset_name: str,
236
+ image_root: str,
237
+ img_datas: Iterable[Dict[str, Any]],
238
+ ann_datas: Iterable[Iterable[Dict[str, Any]]],
239
+ ):
240
+
241
+ ann_keys = ["iscrowd", "category_id"]
242
+ dataset_dicts = []
243
+ contains_video_frame_info = False
244
+
245
+ for img_dict, ann_dicts in zip(img_datas, ann_datas):
246
+ record = {}
247
+ record["file_name"] = os.path.join(image_root, img_dict["file_name"])
248
+ record["height"] = img_dict["height"]
249
+ record["width"] = img_dict["width"]
250
+ record["image_id"] = img_dict["id"]
251
+ record["dataset"] = dataset_name
252
+ if "frame_id" in img_dict:
253
+ record["frame_id"] = img_dict["frame_id"]
254
+ record["video_id"] = img_dict.get("vid_id", None)
255
+ contains_video_frame_info = True
256
+ objs = []
257
+ for ann_dict in ann_dicts:
258
+ assert ann_dict["image_id"] == record["image_id"]
259
+ assert ann_dict.get("ignore", 0) == 0
260
+ obj = {key: ann_dict[key] for key in ann_keys if key in ann_dict}
261
+ _maybe_add_bbox(obj, ann_dict)
262
+ _maybe_add_segm(obj, ann_dict)
263
+ _maybe_add_keypoints(obj, ann_dict)
264
+ _maybe_add_densepose(obj, ann_dict)
265
+ objs.append(obj)
266
+ record["annotations"] = objs
267
+ dataset_dicts.append(record)
268
+ if contains_video_frame_info:
269
+ create_video_frame_mapping(dataset_name, dataset_dicts)
270
+ return dataset_dicts
271
+
272
+
273
+ def get_contiguous_id_to_category_id_map(metadata):
274
+ cat_id_2_cont_id = metadata.thing_dataset_id_to_contiguous_id
275
+ cont_id_2_cat_id = {}
276
+ for cat_id, cont_id in cat_id_2_cont_id.items():
277
+ if cont_id in cont_id_2_cat_id:
278
+ continue
279
+ cont_id_2_cat_id[cont_id] = cat_id
280
+ return cont_id_2_cat_id
281
+
282
+
283
+ def maybe_filter_categories_cocoapi(dataset_name, coco_api):
284
+ meta = MetadataCatalog.get(dataset_name)
285
+ cont_id_2_cat_id = get_contiguous_id_to_category_id_map(meta)
286
+ cat_id_2_cont_id = meta.thing_dataset_id_to_contiguous_id
287
+ # filter categories
288
+ cats = []
289
+ for cat in coco_api.dataset["categories"]:
290
+ cat_id = cat["id"]
291
+ if cat_id not in cat_id_2_cont_id:
292
+ continue
293
+ cont_id = cat_id_2_cont_id[cat_id]
294
+ if (cont_id in cont_id_2_cat_id) and (cont_id_2_cat_id[cont_id] == cat_id):
295
+ cats.append(cat)
296
+ coco_api.dataset["categories"] = cats
297
+ # filter annotations, if multiple categories are mapped to a single
298
+ # contiguous ID, use only one category ID and map all annotations to that category ID
299
+ anns = []
300
+ for ann in coco_api.dataset["annotations"]:
301
+ cat_id = ann["category_id"]
302
+ if cat_id not in cat_id_2_cont_id:
303
+ continue
304
+ cont_id = cat_id_2_cont_id[cat_id]
305
+ ann["category_id"] = cont_id_2_cat_id[cont_id]
306
+ anns.append(ann)
307
+ coco_api.dataset["annotations"] = anns
308
+ # recreate index
309
+ coco_api.createIndex()
310
+
311
+
312
+ def maybe_filter_and_map_categories_cocoapi(dataset_name, coco_api):
313
+ meta = MetadataCatalog.get(dataset_name)
314
+ category_id_map = meta.thing_dataset_id_to_contiguous_id
315
+ # map categories
316
+ cats = []
317
+ for cat in coco_api.dataset["categories"]:
318
+ cat_id = cat["id"]
319
+ if cat_id not in category_id_map:
320
+ continue
321
+ cat["id"] = category_id_map[cat_id]
322
+ cats.append(cat)
323
+ coco_api.dataset["categories"] = cats
324
+ # map annotation categories
325
+ anns = []
326
+ for ann in coco_api.dataset["annotations"]:
327
+ cat_id = ann["category_id"]
328
+ if cat_id not in category_id_map:
329
+ continue
330
+ ann["category_id"] = category_id_map[cat_id]
331
+ anns.append(ann)
332
+ coco_api.dataset["annotations"] = anns
333
+ # recreate index
334
+ coco_api.createIndex()
335
+
336
+
337
+ def create_video_frame_mapping(dataset_name, dataset_dicts):
338
+ mapping = defaultdict(dict)
339
+ for d in dataset_dicts:
340
+ video_id = d.get("video_id")
341
+ if video_id is None:
342
+ continue
343
+ mapping[video_id].update({d["frame_id"]: d["file_name"]})
344
+ MetadataCatalog.get(dataset_name).set(video_frame_mapping=mapping)
345
+
346
+
347
+ def load_coco_json(annotations_json_file: str, image_root: str, dataset_name: str):
348
+ """
349
+ Loads a JSON file with annotations in COCO instances format.
350
+ Replaces `detectron2.data.datasets.coco.load_coco_json` to handle metadata
351
+ in a more flexible way. Postpones category mapping to a later stage to be
352
+ able to combine several datasets with different (but coherent) sets of
353
+ categories.
354
+
355
+ Args:
356
+
357
+ annotations_json_file: str
358
+ Path to the JSON file with annotations in COCO instances format.
359
+ image_root: str
360
+ directory that contains all the images
361
+ dataset_name: str
362
+ the name that identifies a dataset, e.g. "densepose_coco_2014_train"
363
+ extra_annotation_keys: Optional[List[str]]
364
+ If provided, these keys are used to extract additional data from
365
+ the annotations.
366
+ """
367
+ coco_api = _load_coco_annotations(PathManager.get_local_path(annotations_json_file))
368
+ _add_categories_metadata(dataset_name, coco_api.loadCats(coco_api.getCatIds()))
369
+ # sort indices for reproducible results
370
+ img_ids = sorted(coco_api.imgs.keys())
371
+ # imgs is a list of dicts, each looks something like:
372
+ # {'license': 4,
373
+ # 'url': 'http://farm6.staticflickr.com/5454/9413846304_881d5e5c3b_z.jpg',
374
+ # 'file_name': 'COCO_val2014_000000001268.jpg',
375
+ # 'height': 427,
376
+ # 'width': 640,
377
+ # 'date_captured': '2013-11-17 05:57:24',
378
+ # 'id': 1268}
379
+ imgs = coco_api.loadImgs(img_ids)
380
+ logger = logging.getLogger(__name__)
381
+ logger.info("Loaded {} images in COCO format from {}".format(len(imgs), annotations_json_file))
382
+ # anns is a list[list[dict]], where each dict is an annotation
383
+ # record for an object. The inner list enumerates the objects in an image
384
+ # and the outer list enumerates over images.
385
+ anns = [coco_api.imgToAnns[img_id] for img_id in img_ids]
386
+ _verify_annotations_have_unique_ids(annotations_json_file, anns)
387
+ dataset_records = _combine_images_with_annotations(dataset_name, image_root, imgs, anns)
388
+ return dataset_records
389
+
390
+
391
+ def register_dataset(dataset_data: CocoDatasetInfo, datasets_root: Optional[str] = None):
392
+ """
393
+ Registers provided COCO DensePose dataset
394
+
395
+ Args:
396
+ dataset_data: CocoDatasetInfo
397
+ Dataset data
398
+ datasets_root: Optional[str]
399
+ Datasets root folder (default: None)
400
+ """
401
+ annotations_fpath = maybe_prepend_base_path(datasets_root, dataset_data.annotations_fpath)
402
+ images_root = maybe_prepend_base_path(datasets_root, dataset_data.images_root)
403
+
404
+ def load_annotations():
405
+ return load_coco_json(
406
+ annotations_json_file=annotations_fpath,
407
+ image_root=images_root,
408
+ dataset_name=dataset_data.name,
409
+ )
410
+
411
+ DatasetCatalog.register(dataset_data.name, load_annotations)
412
+ MetadataCatalog.get(dataset_data.name).set(
413
+ json_file=annotations_fpath,
414
+ image_root=images_root,
415
+ **get_metadata(DENSEPOSE_METADATA_URL_PREFIX)
416
+ )
417
+
418
+
419
+ def register_datasets(
420
+ datasets_data: Iterable[CocoDatasetInfo], datasets_root: Optional[str] = None
421
+ ):
422
+ """
423
+ Registers provided COCO DensePose datasets
424
+
425
+ Args:
426
+ datasets_data: Iterable[CocoDatasetInfo]
427
+ An iterable of dataset datas
428
+ datasets_root: Optional[str]
429
+ Datasets root folder (default: None)
430
+ """
431
+ for dataset_data in datasets_data:
432
+ register_dataset(dataset_data, datasets_root)
gradio_demo/densepose/data/datasets/dataset_type.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+
3
+ from enum import Enum
4
+
5
+
6
+ class DatasetType(Enum):
7
+ """
8
+ Dataset type, mostly used for datasets that contain data to bootstrap models on
9
+ """
10
+
11
+ VIDEO_LIST = "video_list"
gradio_demo/densepose/data/datasets/lvis.py ADDED
@@ -0,0 +1,257 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ import logging
3
+ import os
4
+ from typing import Any, Dict, Iterable, List, Optional
5
+ from fvcore.common.timer import Timer
6
+
7
+ from detectron2.data import DatasetCatalog, MetadataCatalog
8
+ from detectron2.data.datasets.lvis import get_lvis_instances_meta
9
+ from detectron2.structures import BoxMode
10
+ from detectron2.utils.file_io import PathManager
11
+
12
+ from ..utils import maybe_prepend_base_path
13
+ from .coco import (
14
+ DENSEPOSE_ALL_POSSIBLE_KEYS,
15
+ DENSEPOSE_METADATA_URL_PREFIX,
16
+ CocoDatasetInfo,
17
+ get_metadata,
18
+ )
19
+
20
+ DATASETS = [
21
+ CocoDatasetInfo(
22
+ name="densepose_lvis_v1_ds1_train_v1",
23
+ images_root="coco_",
24
+ annotations_fpath="lvis/densepose_lvis_v1_ds1_train_v1.json",
25
+ ),
26
+ CocoDatasetInfo(
27
+ name="densepose_lvis_v1_ds1_val_v1",
28
+ images_root="coco_",
29
+ annotations_fpath="lvis/densepose_lvis_v1_ds1_val_v1.json",
30
+ ),
31
+ CocoDatasetInfo(
32
+ name="densepose_lvis_v1_ds2_train_v1",
33
+ images_root="coco_",
34
+ annotations_fpath="lvis/densepose_lvis_v1_ds2_train_v1.json",
35
+ ),
36
+ CocoDatasetInfo(
37
+ name="densepose_lvis_v1_ds2_val_v1",
38
+ images_root="coco_",
39
+ annotations_fpath="lvis/densepose_lvis_v1_ds2_val_v1.json",
40
+ ),
41
+ CocoDatasetInfo(
42
+ name="densepose_lvis_v1_ds1_val_animals_100",
43
+ images_root="coco_",
44
+ annotations_fpath="lvis/densepose_lvis_v1_val_animals_100_v2.json",
45
+ ),
46
+ ]
47
+
48
+
49
+ def _load_lvis_annotations(json_file: str):
50
+ """
51
+ Load COCO annotations from a JSON file
52
+
53
+ Args:
54
+ json_file: str
55
+ Path to the file to load annotations from
56
+ Returns:
57
+ Instance of `pycocotools.coco.COCO` that provides access to annotations
58
+ data
59
+ """
60
+ from lvis import LVIS
61
+
62
+ json_file = PathManager.get_local_path(json_file)
63
+ logger = logging.getLogger(__name__)
64
+ timer = Timer()
65
+ lvis_api = LVIS(json_file)
66
+ if timer.seconds() > 1:
67
+ logger.info("Loading {} takes {:.2f} seconds.".format(json_file, timer.seconds()))
68
+ return lvis_api
69
+
70
+
71
+ def _add_categories_metadata(dataset_name: str) -> None:
72
+ metadict = get_lvis_instances_meta(dataset_name)
73
+ categories = metadict["thing_classes"]
74
+ metadata = MetadataCatalog.get(dataset_name)
75
+ metadata.categories = {i + 1: categories[i] for i in range(len(categories))}
76
+ logger = logging.getLogger(__name__)
77
+ logger.info(f"Dataset {dataset_name} has {len(categories)} categories")
78
+
79
+
80
+ def _verify_annotations_have_unique_ids(json_file: str, anns: List[List[Dict[str, Any]]]) -> None:
81
+ ann_ids = [ann["id"] for anns_per_image in anns for ann in anns_per_image]
82
+ assert len(set(ann_ids)) == len(ann_ids), "Annotation ids in '{}' are not unique!".format(
83
+ json_file
84
+ )
85
+
86
+
87
+ def _maybe_add_bbox(obj: Dict[str, Any], ann_dict: Dict[str, Any]) -> None:
88
+ if "bbox" not in ann_dict:
89
+ return
90
+ obj["bbox"] = ann_dict["bbox"]
91
+ obj["bbox_mode"] = BoxMode.XYWH_ABS
92
+
93
+
94
+ def _maybe_add_segm(obj: Dict[str, Any], ann_dict: Dict[str, Any]) -> None:
95
+ if "segmentation" not in ann_dict:
96
+ return
97
+ segm = ann_dict["segmentation"]
98
+ if not isinstance(segm, dict):
99
+ # filter out invalid polygons (< 3 points)
100
+ segm = [poly for poly in segm if len(poly) % 2 == 0 and len(poly) >= 6]
101
+ if len(segm) == 0:
102
+ return
103
+ obj["segmentation"] = segm
104
+
105
+
106
+ def _maybe_add_keypoints(obj: Dict[str, Any], ann_dict: Dict[str, Any]) -> None:
107
+ if "keypoints" not in ann_dict:
108
+ return
109
+ keypts = ann_dict["keypoints"] # list[int]
110
+ for idx, v in enumerate(keypts):
111
+ if idx % 3 != 2:
112
+ # COCO's segmentation coordinates are floating points in [0, H or W],
113
+ # but keypoint coordinates are integers in [0, H-1 or W-1]
114
+ # Therefore we assume the coordinates are "pixel indices" and
115
+ # add 0.5 to convert to floating point coordinates.
116
+ keypts[idx] = v + 0.5
117
+ obj["keypoints"] = keypts
118
+
119
+
120
+ def _maybe_add_densepose(obj: Dict[str, Any], ann_dict: Dict[str, Any]) -> None:
121
+ for key in DENSEPOSE_ALL_POSSIBLE_KEYS:
122
+ if key in ann_dict:
123
+ obj[key] = ann_dict[key]
124
+
125
+
126
+ def _combine_images_with_annotations(
127
+ dataset_name: str,
128
+ image_root: str,
129
+ img_datas: Iterable[Dict[str, Any]],
130
+ ann_datas: Iterable[Iterable[Dict[str, Any]]],
131
+ ):
132
+
133
+ dataset_dicts = []
134
+
135
+ def get_file_name(img_root, img_dict):
136
+ # Determine the path including the split folder ("train2017", "val2017", "test2017") from
137
+ # the coco_url field. Example:
138
+ # 'coco_url': 'http://images.cocodataset.org/train2017/000000155379.jpg'
139
+ split_folder, file_name = img_dict["coco_url"].split("/")[-2:]
140
+ return os.path.join(img_root + split_folder, file_name)
141
+
142
+ for img_dict, ann_dicts in zip(img_datas, ann_datas):
143
+ record = {}
144
+ record["file_name"] = get_file_name(image_root, img_dict)
145
+ record["height"] = img_dict["height"]
146
+ record["width"] = img_dict["width"]
147
+ record["not_exhaustive_category_ids"] = img_dict.get("not_exhaustive_category_ids", [])
148
+ record["neg_category_ids"] = img_dict.get("neg_category_ids", [])
149
+ record["image_id"] = img_dict["id"]
150
+ record["dataset"] = dataset_name
151
+
152
+ objs = []
153
+ for ann_dict in ann_dicts:
154
+ assert ann_dict["image_id"] == record["image_id"]
155
+ obj = {}
156
+ _maybe_add_bbox(obj, ann_dict)
157
+ obj["iscrowd"] = ann_dict.get("iscrowd", 0)
158
+ obj["category_id"] = ann_dict["category_id"]
159
+ _maybe_add_segm(obj, ann_dict)
160
+ _maybe_add_keypoints(obj, ann_dict)
161
+ _maybe_add_densepose(obj, ann_dict)
162
+ objs.append(obj)
163
+ record["annotations"] = objs
164
+ dataset_dicts.append(record)
165
+ return dataset_dicts
166
+
167
+
168
+ def load_lvis_json(annotations_json_file: str, image_root: str, dataset_name: str):
169
+ """
170
+ Loads a JSON file with annotations in LVIS instances format.
171
+ Replaces `detectron2.data.datasets.coco.load_lvis_json` to handle metadata
172
+ in a more flexible way. Postpones category mapping to a later stage to be
173
+ able to combine several datasets with different (but coherent) sets of
174
+ categories.
175
+
176
+ Args:
177
+
178
+ annotations_json_file: str
179
+ Path to the JSON file with annotations in COCO instances format.
180
+ image_root: str
181
+ directory that contains all the images
182
+ dataset_name: str
183
+ the name that identifies a dataset, e.g. "densepose_coco_2014_train"
184
+ extra_annotation_keys: Optional[List[str]]
185
+ If provided, these keys are used to extract additional data from
186
+ the annotations.
187
+ """
188
+ lvis_api = _load_lvis_annotations(PathManager.get_local_path(annotations_json_file))
189
+
190
+ _add_categories_metadata(dataset_name)
191
+
192
+ # sort indices for reproducible results
193
+ img_ids = sorted(lvis_api.imgs.keys())
194
+ # imgs is a list of dicts, each looks something like:
195
+ # {'license': 4,
196
+ # 'url': 'http://farm6.staticflickr.com/5454/9413846304_881d5e5c3b_z.jpg',
197
+ # 'file_name': 'COCO_val2014_000000001268.jpg',
198
+ # 'height': 427,
199
+ # 'width': 640,
200
+ # 'date_captured': '2013-11-17 05:57:24',
201
+ # 'id': 1268}
202
+ imgs = lvis_api.load_imgs(img_ids)
203
+ logger = logging.getLogger(__name__)
204
+ logger.info("Loaded {} images in LVIS format from {}".format(len(imgs), annotations_json_file))
205
+ # anns is a list[list[dict]], where each dict is an annotation
206
+ # record for an object. The inner list enumerates the objects in an image
207
+ # and the outer list enumerates over images.
208
+ anns = [lvis_api.img_ann_map[img_id] for img_id in img_ids]
209
+
210
+ _verify_annotations_have_unique_ids(annotations_json_file, anns)
211
+ dataset_records = _combine_images_with_annotations(dataset_name, image_root, imgs, anns)
212
+ return dataset_records
213
+
214
+
215
+ def register_dataset(dataset_data: CocoDatasetInfo, datasets_root: Optional[str] = None) -> None:
216
+ """
217
+ Registers provided LVIS DensePose dataset
218
+
219
+ Args:
220
+ dataset_data: CocoDatasetInfo
221
+ Dataset data
222
+ datasets_root: Optional[str]
223
+ Datasets root folder (default: None)
224
+ """
225
+ annotations_fpath = maybe_prepend_base_path(datasets_root, dataset_data.annotations_fpath)
226
+ images_root = maybe_prepend_base_path(datasets_root, dataset_data.images_root)
227
+
228
+ def load_annotations():
229
+ return load_lvis_json(
230
+ annotations_json_file=annotations_fpath,
231
+ image_root=images_root,
232
+ dataset_name=dataset_data.name,
233
+ )
234
+
235
+ DatasetCatalog.register(dataset_data.name, load_annotations)
236
+ MetadataCatalog.get(dataset_data.name).set(
237
+ json_file=annotations_fpath,
238
+ image_root=images_root,
239
+ evaluator_type="lvis",
240
+ **get_metadata(DENSEPOSE_METADATA_URL_PREFIX),
241
+ )
242
+
243
+
244
+ def register_datasets(
245
+ datasets_data: Iterable[CocoDatasetInfo], datasets_root: Optional[str] = None
246
+ ) -> None:
247
+ """
248
+ Registers provided LVIS DensePose datasets
249
+
250
+ Args:
251
+ datasets_data: Iterable[CocoDatasetInfo]
252
+ An iterable of dataset datas
253
+ datasets_root: Optional[str]
254
+ Datasets root folder (default: None)
255
+ """
256
+ for dataset_data in datasets_data:
257
+ register_dataset(dataset_data, datasets_root)
gradio_demo/densepose/data/image_list_dataset.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright (c) Facebook, Inc. and its affiliates.
3
+
4
+ import logging
5
+ import numpy as np
6
+ from typing import Any, Callable, Dict, List, Optional, Union
7
+ import torch
8
+ from torch.utils.data.dataset import Dataset
9
+
10
+ from detectron2.data.detection_utils import read_image
11
+
12
+ ImageTransform = Callable[[torch.Tensor], torch.Tensor]
13
+
14
+
15
+ class ImageListDataset(Dataset):
16
+ """
17
+ Dataset that provides images from a list.
18
+ """
19
+
20
+ _EMPTY_IMAGE = torch.empty((0, 3, 1, 1))
21
+
22
+ def __init__(
23
+ self,
24
+ image_list: List[str],
25
+ category_list: Union[str, List[str], None] = None,
26
+ transform: Optional[ImageTransform] = None,
27
+ ):
28
+ """
29
+ Args:
30
+ image_list (List[str]): list of paths to image files
31
+ category_list (Union[str, List[str], None]): list of animal categories for
32
+ each image. If it is a string, or None, this applies to all images
33
+ """
34
+ if type(category_list) == list:
35
+ self.category_list = category_list
36
+ else:
37
+ self.category_list = [category_list] * len(image_list)
38
+ assert len(image_list) == len(
39
+ self.category_list
40
+ ), "length of image and category lists must be equal"
41
+ self.image_list = image_list
42
+ self.transform = transform
43
+
44
+ def __getitem__(self, idx: int) -> Dict[str, Any]:
45
+ """
46
+ Gets selected images from the list
47
+
48
+ Args:
49
+ idx (int): video index in the video list file
50
+ Returns:
51
+ A dictionary containing two keys:
52
+ images (torch.Tensor): tensor of size [N, 3, H, W] (N = 1, or 0 for _EMPTY_IMAGE)
53
+ categories (List[str]): categories of the frames
54
+ """
55
+ categories = [self.category_list[idx]]
56
+ fpath = self.image_list[idx]
57
+ transform = self.transform
58
+
59
+ try:
60
+ image = torch.from_numpy(np.ascontiguousarray(read_image(fpath, format="BGR")))
61
+ image = image.permute(2, 0, 1).unsqueeze(0).float() # HWC -> NCHW
62
+ if transform is not None:
63
+ image = transform(image)
64
+ return {"images": image, "categories": categories}
65
+ except (OSError, RuntimeError) as e:
66
+ logger = logging.getLogger(__name__)
67
+ logger.warning(f"Error opening image file container {fpath}: {e}")
68
+
69
+ return {"images": self._EMPTY_IMAGE, "categories": []}
70
+
71
+ def __len__(self):
72
+ return len(self.image_list)