gayatripadmani commited on
Commit
5f4da52
1 Parent(s): c3d0b7c
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. preprocess/humanparsing/mhp_extension/detectron2/projects/TensorMask/tensormask/layers/csrc/vision.cpp +19 -0
  2. preprocess/humanparsing/mhp_extension/detectron2/projects/TensorMask/tensormask/layers/swap_align2nat.py +61 -0
  3. preprocess/humanparsing/mhp_extension/detectron2/projects/TensorMask/tests/__init__.py +1 -0
  4. preprocess/humanparsing/mhp_extension/detectron2/projects/TensorMask/tests/test_swap_align2nat.py +32 -0
  5. preprocess/humanparsing/mhp_extension/detectron2/projects/TensorMask/train_net.py +70 -0
  6. preprocess/humanparsing/mhp_extension/detectron2/projects/TridentNet/README.md +60 -0
  7. preprocess/humanparsing/mhp_extension/detectron2/projects/TridentNet/configs/Base-TridentNet-Fast-C4.yaml +29 -0
  8. preprocess/humanparsing/mhp_extension/detectron2/projects/TridentNet/configs/tridentnet_fast_R_101_C4_3x.yaml +9 -0
  9. preprocess/humanparsing/mhp_extension/detectron2/projects/TridentNet/configs/tridentnet_fast_R_50_C4_1x.yaml +6 -0
  10. preprocess/humanparsing/mhp_extension/detectron2/projects/TridentNet/configs/tridentnet_fast_R_50_C4_3x.yaml +9 -0
  11. preprocess/humanparsing/mhp_extension/detectron2/projects/TridentNet/train_net.py +67 -0
  12. preprocess/humanparsing/mhp_extension/detectron2/projects/TridentNet/tridentnet/__init__.py +9 -0
  13. preprocess/humanparsing/mhp_extension/detectron2/projects/TridentNet/tridentnet/config.py +26 -0
  14. preprocess/humanparsing/mhp_extension/detectron2/projects/TridentNet/tridentnet/trident_backbone.py +223 -0
  15. preprocess/humanparsing/mhp_extension/detectron2/projects/TridentNet/tridentnet/trident_conv.py +107 -0
  16. preprocess/humanparsing/mhp_extension/detectron2/projects/TridentNet/tridentnet/trident_rcnn.py +116 -0
  17. preprocess/humanparsing/mhp_extension/detectron2/projects/TridentNet/tridentnet/trident_rpn.py +32 -0
  18. preprocess/humanparsing/mhp_extension/detectron2/setup.cfg +26 -0
  19. preprocess/humanparsing/mhp_extension/detectron2/setup.py +156 -0
  20. preprocess/humanparsing/mhp_extension/detectron2/tests/README.md +9 -0
  21. preprocess/humanparsing/mhp_extension/detectron2/tests/__init__.py +1 -0
  22. preprocess/humanparsing/mhp_extension/detectron2/tests/data/__init__.py +0 -0
  23. preprocess/humanparsing/mhp_extension/detectron2/tests/data/test_coco.py +77 -0
  24. preprocess/humanparsing/mhp_extension/detectron2/tests/data/test_detection_utils.py +116 -0
  25. preprocess/humanparsing/mhp_extension/detectron2/tests/data/test_rotation_transform.py +62 -0
  26. preprocess/humanparsing/mhp_extension/detectron2/tests/data/test_sampler.py +23 -0
  27. preprocess/humanparsing/mhp_extension/detectron2/tests/data/test_transforms.py +134 -0
  28. preprocess/humanparsing/mhp_extension/detectron2/tests/layers/__init__.py +0 -0
  29. preprocess/humanparsing/mhp_extension/detectron2/tests/layers/test_mask_ops.py +190 -0
  30. preprocess/humanparsing/mhp_extension/detectron2/tests/layers/test_nms_rotated.py +188 -0
  31. preprocess/humanparsing/mhp_extension/detectron2/tests/layers/test_roi_align.py +152 -0
  32. preprocess/humanparsing/mhp_extension/detectron2/tests/layers/test_roi_align_rotated.py +176 -0
  33. preprocess/humanparsing/mhp_extension/detectron2/tests/modeling/__init__.py +0 -0
  34. preprocess/humanparsing/mhp_extension/detectron2/tests/modeling/test_anchor_generator.py +121 -0
  35. preprocess/humanparsing/mhp_extension/detectron2/tests/modeling/test_box2box_transform.py +64 -0
  36. preprocess/humanparsing/mhp_extension/detectron2/tests/modeling/test_fast_rcnn.py +106 -0
  37. preprocess/humanparsing/mhp_extension/detectron2/tests/modeling/test_model_e2e.py +154 -0
  38. preprocess/humanparsing/mhp_extension/detectron2/tests/modeling/test_roi_heads.py +108 -0
  39. preprocess/humanparsing/mhp_extension/detectron2/tests/modeling/test_roi_pooler.py +85 -0
  40. preprocess/humanparsing/mhp_extension/detectron2/tests/modeling/test_rpn.py +234 -0
  41. preprocess/humanparsing/mhp_extension/detectron2/tests/structures/__init__.py +0 -0
  42. preprocess/humanparsing/mhp_extension/detectron2/tests/structures/test_boxes.py +182 -0
  43. preprocess/humanparsing/mhp_extension/detectron2/tests/structures/test_imagelist.py +38 -0
  44. preprocess/humanparsing/mhp_extension/detectron2/tests/structures/test_instances.py +25 -0
  45. preprocess/humanparsing/mhp_extension/detectron2/tests/structures/test_rotated_boxes.py +357 -0
  46. preprocess/humanparsing/mhp_extension/detectron2/tests/test_checkpoint.py +48 -0
  47. preprocess/humanparsing/mhp_extension/detectron2/tests/test_config.py +240 -0
  48. preprocess/humanparsing/mhp_extension/detectron2/tests/test_export_caffe2.py +71 -0
  49. preprocess/humanparsing/mhp_extension/detectron2/tests/test_model_analysis.py +58 -0
  50. preprocess/humanparsing/mhp_extension/detectron2/tests/test_model_zoo.py +29 -0
preprocess/humanparsing/mhp_extension/detectron2/projects/TensorMask/tensormask/layers/csrc/vision.cpp ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+
3
+ #include <torch/extension.h>
4
+ #include "SwapAlign2Nat/SwapAlign2Nat.h"
5
+
6
+ namespace tensormask {
7
+
8
+ PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
9
+ m.def(
10
+ "swap_align2nat_forward",
11
+ &SwapAlign2Nat_forward,
12
+ "SwapAlign2Nat_forward");
13
+ m.def(
14
+ "swap_align2nat_backward",
15
+ &SwapAlign2Nat_backward,
16
+ "SwapAlign2Nat_backward");
17
+ }
18
+
19
+ } // namespace tensormask
preprocess/humanparsing/mhp_extension/detectron2/projects/TensorMask/tensormask/layers/swap_align2nat.py ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+ from torch import nn
3
+ from torch.autograd import Function
4
+ from torch.autograd.function import once_differentiable
5
+
6
+ from tensormask import _C
7
+
8
+
9
+ class _SwapAlign2Nat(Function):
10
+ @staticmethod
11
+ def forward(ctx, X, lambda_val, pad_val):
12
+ ctx.lambda_val = lambda_val
13
+ ctx.input_shape = X.size()
14
+
15
+ Y = _C.swap_align2nat_forward(X, lambda_val, pad_val)
16
+ return Y
17
+
18
+ @staticmethod
19
+ @once_differentiable
20
+ def backward(ctx, gY):
21
+ lambda_val = ctx.lambda_val
22
+ bs, ch, h, w = ctx.input_shape
23
+
24
+ gX = _C.swap_align2nat_backward(gY, lambda_val, bs, ch, h, w)
25
+
26
+ return gX, None, None
27
+
28
+
29
+ swap_align2nat = _SwapAlign2Nat.apply
30
+
31
+
32
+ class SwapAlign2Nat(nn.Module):
33
+ """
34
+ The op `SwapAlign2Nat` described in https://arxiv.org/abs/1903.12174.
35
+ Given an input tensor that predicts masks of shape (N, C=VxU, H, W),
36
+ apply the op, it will return masks of shape (N, V'xU', H', W') where
37
+ the unit lengths of (V, U) and (H, W) are swapped, and the mask representation
38
+ is transformed from aligned to natural.
39
+ Args:
40
+ lambda_val (int): the relative unit length ratio between (V, U) and (H, W),
41
+ as we always have larger unit lengths for (V, U) than (H, W),
42
+ lambda_val is always >= 1.
43
+ pad_val (float): padding value for the values falling outside of the input
44
+ tensor, default set to -6 as sigmoid(-6) is ~0, indicating
45
+ that is no masks outside of the tensor.
46
+ """
47
+
48
+ def __init__(self, lambda_val, pad_val=-6.0):
49
+ super(SwapAlign2Nat, self).__init__()
50
+ self.lambda_val = lambda_val
51
+ self.pad_val = pad_val
52
+
53
+ def forward(self, X):
54
+ return swap_align2nat(X, self.lambda_val, self.pad_val)
55
+
56
+ def __repr__(self):
57
+ tmpstr = self.__class__.__name__ + "("
58
+ tmpstr += "lambda_val=" + str(self.lambda_val)
59
+ tmpstr += ", pad_val=" + str(self.pad_val)
60
+ tmpstr += ")"
61
+ return tmpstr
preprocess/humanparsing/mhp_extension/detectron2/projects/TensorMask/tests/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
preprocess/humanparsing/mhp_extension/detectron2/projects/TensorMask/tests/test_swap_align2nat.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
3
+
4
+ import unittest
5
+ import torch
6
+ from torch.autograd import gradcheck
7
+
8
+ from tensormask.layers.swap_align2nat import SwapAlign2Nat
9
+
10
+
11
+ class SwapAlign2NatTest(unittest.TestCase):
12
+ @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available")
13
+ def test_swap_align2nat_gradcheck_cuda(self):
14
+ dtype = torch.float64
15
+ device = torch.device("cuda")
16
+ m = SwapAlign2Nat(2).to(dtype=dtype, device=device)
17
+ x = torch.rand(2, 4, 10, 10, dtype=dtype, device=device, requires_grad=True)
18
+
19
+ self.assertTrue(gradcheck(m, x), "gradcheck failed for SwapAlign2Nat CUDA")
20
+
21
+ def _swap_align2nat(self, tensor, lambda_val):
22
+ """
23
+ The basic setup for testing Swap_Align
24
+ """
25
+ op = SwapAlign2Nat(lambda_val, pad_val=0.0)
26
+ input = torch.from_numpy(tensor[None, :, :, :].astype("float32"))
27
+ output = op.forward(input.cuda()).cpu().numpy()
28
+ return output[0]
29
+
30
+
31
+ if __name__ == "__main__":
32
+ unittest.main()
preprocess/humanparsing/mhp_extension/detectron2/projects/TensorMask/train_net.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
3
+
4
+ """
5
+ TensorMask Training Script.
6
+
7
+ This script is a simplified version of the training script in detectron2/tools.
8
+ """
9
+
10
+ import os
11
+
12
+ import detectron2.utils.comm as comm
13
+ from detectron2.checkpoint import DetectionCheckpointer
14
+ from detectron2.config import get_cfg
15
+ from detectron2.engine import DefaultTrainer, default_argument_parser, default_setup, launch
16
+ from detectron2.evaluation import COCOEvaluator, verify_results
17
+
18
+ from tensormask import add_tensormask_config
19
+
20
+
21
+ class Trainer(DefaultTrainer):
22
+ @classmethod
23
+ def build_evaluator(cls, cfg, dataset_name, output_folder=None):
24
+ if output_folder is None:
25
+ output_folder = os.path.join(cfg.OUTPUT_DIR, "inference")
26
+ return COCOEvaluator(dataset_name, cfg, True, output_folder)
27
+
28
+
29
+ def setup(args):
30
+ """
31
+ Create configs and perform basic setups.
32
+ """
33
+ cfg = get_cfg()
34
+ add_tensormask_config(cfg)
35
+ cfg.merge_from_file(args.config_file)
36
+ cfg.merge_from_list(args.opts)
37
+ cfg.freeze()
38
+ default_setup(cfg, args)
39
+ return cfg
40
+
41
+
42
+ def main(args):
43
+ cfg = setup(args)
44
+
45
+ if args.eval_only:
46
+ model = Trainer.build_model(cfg)
47
+ DetectionCheckpointer(model, save_dir=cfg.OUTPUT_DIR).resume_or_load(
48
+ cfg.MODEL.WEIGHTS, resume=args.resume
49
+ )
50
+ res = Trainer.test(cfg, model)
51
+ if comm.is_main_process():
52
+ verify_results(cfg, res)
53
+ return res
54
+
55
+ trainer = Trainer(cfg)
56
+ trainer.resume_or_load(resume=args.resume)
57
+ return trainer.train()
58
+
59
+
60
+ if __name__ == "__main__":
61
+ args = default_argument_parser().parse_args()
62
+ print("Command Line Args:", args)
63
+ launch(
64
+ main,
65
+ args.num_gpus,
66
+ num_machines=args.num_machines,
67
+ machine_rank=args.machine_rank,
68
+ dist_url=args.dist_url,
69
+ args=(args,),
70
+ )
preprocess/humanparsing/mhp_extension/detectron2/projects/TridentNet/README.md ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ # TridentNet in Detectron2
3
+ **Scale-Aware Trident Networks for Object Detection**
4
+
5
+ Yanghao Li\*, Yuntao Chen\*, Naiyan Wang, Zhaoxiang Zhang
6
+
7
+ [[`TridentNet`](https://github.com/TuSimple/simpledet/tree/master/models/tridentnet)] [[`arXiv`](https://arxiv.org/abs/1901.01892)] [[`BibTeX`](#CitingTridentNet)]
8
+
9
+ <div align="center">
10
+ <img src="https://drive.google.com/uc?export=view&id=10THEPdIPmf3ooMyNzrfZbpWihEBvixwt" width="700px" />
11
+ </div>
12
+
13
+ In this repository, we implement TridentNet-Fast in Detectron2.
14
+ Trident Network (TridentNet) aims to generate scale-specific feature maps with a uniform representational power. We construct a parallel multi-branch architecture in which each branch shares the same transformation parameters but with different receptive fields. TridentNet-Fast is a fast approximation version of TridentNet that could achieve significant improvements without any additional parameters and computational cost.
15
+
16
+ ## Training
17
+
18
+ To train a model, run
19
+ ```bash
20
+ python /path/to/detectron2/projects/TridentNet/train_net.py --config-file <config.yaml>
21
+ ```
22
+
23
+ For example, to launch end-to-end TridentNet training with ResNet-50 backbone on 8 GPUs,
24
+ one should execute:
25
+ ```bash
26
+ python /path/to/detectron2/projects/TridentNet/train_net.py --config-file configs/tridentnet_fast_R_50_C4_1x.yaml --num-gpus 8
27
+ ```
28
+
29
+ ## Evaluation
30
+
31
+ Model evaluation can be done similarly:
32
+ ```bash
33
+ python /path/to/detectron2/projects/TridentNet/train_net.py --config-file configs/tridentnet_fast_R_50_C4_1x.yaml --eval-only MODEL.WEIGHTS model.pth
34
+ ```
35
+
36
+ ## Results on MS-COCO in Detectron2
37
+
38
+ |Model|Backbone|Head|lr sched|AP|AP50|AP75|APs|APm|APl|download|
39
+ |-----|--------|----|--------|--|----|----|---|---|---|--------|
40
+ |Faster|R50-C4|C5-512ROI|1X|35.7|56.1|38.0|19.2|40.9|48.7|<a href="https://dl.fbaipublicfiles.com/detectron2/COCO-Detection/faster_rcnn_R_50_C4_1x/137257644/model_final_721ade.pkl">model</a>&nbsp;\|&nbsp;<a href="https://dl.fbaipublicfiles.com/detectron2/COCO-Detection/faster_rcnn_R_50_C4_1x/137257644/metrics.json">metrics</a>|
41
+ |TridentFast|R50-C4|C5-128ROI|1X|38.0|58.1|40.8|19.5|42.2|54.6|<a href="https://dl.fbaipublicfiles.com/detectron2/TridentNet/tridentnet_fast_R_50_C4_1x/148572687/model_final_756cda.pkl">model</a>&nbsp;\|&nbsp;<a href="https://dl.fbaipublicfiles.com/detectron2/TridentNet/tridentnet_fast_R_50_C4_1x/148572687/metrics.json">metrics</a>|
42
+ |Faster|R50-C4|C5-512ROI|3X|38.4|58.7|41.3|20.7|42.7|53.1|<a href="https://dl.fbaipublicfiles.com/detectron2/COCO-Detection/faster_rcnn_R_50_C4_3x/137849393/model_final_f97cb7.pkl">model</a>&nbsp;\|&nbsp;<a href="https://dl.fbaipublicfiles.com/detectron2/COCO-Detection/faster_rcnn_R_50_C4_3x/137849393/metrics.json">metrics</a>|
43
+ |TridentFast|R50-C4|C5-128ROI|3X|40.6|60.8|43.6|23.4|44.7|57.1|<a href="https://dl.fbaipublicfiles.com/detectron2/TridentNet/tridentnet_fast_R_50_C4_3x/148572287/model_final_e1027c.pkl">model</a>&nbsp;\|&nbsp;<a href="https://dl.fbaipublicfiles.com/detectron2/TridentNet/tridentnet_fast_R_50_C4_3x/148572287/metrics.json">metrics</a>|
44
+ |Faster|R101-C4|C5-512ROI|3X|41.1|61.4|44.0|22.2|45.5|55.9|<a href="https://dl.fbaipublicfiles.com/detectron2/COCO-Detection/faster_rcnn_R_101_C4_3x/138204752/model_final_298dad.pkl">model</a>&nbsp;\|&nbsp;<a href="https://dl.fbaipublicfiles.com/detectron2/COCO-Detection/faster_rcnn_R_101_C4_3x/138204752/metrics.json">metrics</a>|
45
+ |TridentFast|R101-C4|C5-128ROI|3X|43.6|63.4|47.0|24.3|47.8|60.0|<a href="https://dl.fbaipublicfiles.com/detectron2/TridentNet/tridentnet_fast_R_101_C4_3x/148572198/model_final_164568.pkl">model</a>&nbsp;\|&nbsp;<a href="https://dl.fbaipublicfiles.com/detectron2/TridentNet/tridentnet_fast_R_101_C4_3x/148572198/metrics.json">metrics</a>|
46
+
47
+
48
+ ## <a name="CitingTridentNet"></a>Citing TridentNet
49
+
50
+ If you use TridentNet, please use the following BibTeX entry.
51
+
52
+ ```
53
+ @InProceedings{li2019scale,
54
+ title={Scale-Aware Trident Networks for Object Detection},
55
+ author={Li, Yanghao and Chen, Yuntao and Wang, Naiyan and Zhang, Zhaoxiang},
56
+ journal={The International Conference on Computer Vision (ICCV)},
57
+ year={2019}
58
+ }
59
+ ```
60
+
preprocess/humanparsing/mhp_extension/detectron2/projects/TridentNet/configs/Base-TridentNet-Fast-C4.yaml ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MODEL:
2
+ META_ARCHITECTURE: "GeneralizedRCNN"
3
+ BACKBONE:
4
+ NAME: "build_trident_resnet_backbone"
5
+ ROI_HEADS:
6
+ NAME: "TridentRes5ROIHeads"
7
+ POSITIVE_FRACTION: 0.5
8
+ BATCH_SIZE_PER_IMAGE: 128
9
+ PROPOSAL_APPEND_GT: False
10
+ PROPOSAL_GENERATOR:
11
+ NAME: "TridentRPN"
12
+ RPN:
13
+ POST_NMS_TOPK_TRAIN: 500
14
+ TRIDENT:
15
+ NUM_BRANCH: 3
16
+ BRANCH_DILATIONS: [1, 2, 3]
17
+ TEST_BRANCH_IDX: 1
18
+ TRIDENT_STAGE: "res4"
19
+ DATASETS:
20
+ TRAIN: ("coco_2017_train",)
21
+ TEST: ("coco_2017_val",)
22
+ SOLVER:
23
+ IMS_PER_BATCH: 16
24
+ BASE_LR: 0.02
25
+ STEPS: (60000, 80000)
26
+ MAX_ITER: 90000
27
+ INPUT:
28
+ MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800)
29
+ VERSION: 2
preprocess/humanparsing/mhp_extension/detectron2/projects/TridentNet/configs/tridentnet_fast_R_101_C4_3x.yaml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ _BASE_: "Base-TridentNet-Fast-C4.yaml"
2
+ MODEL:
3
+ WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl"
4
+ MASK_ON: False
5
+ RESNETS:
6
+ DEPTH: 101
7
+ SOLVER:
8
+ STEPS: (210000, 250000)
9
+ MAX_ITER: 270000
preprocess/humanparsing/mhp_extension/detectron2/projects/TridentNet/configs/tridentnet_fast_R_50_C4_1x.yaml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ _BASE_: "Base-TridentNet-Fast-C4.yaml"
2
+ MODEL:
3
+ WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl"
4
+ MASK_ON: False
5
+ RESNETS:
6
+ DEPTH: 50
preprocess/humanparsing/mhp_extension/detectron2/projects/TridentNet/configs/tridentnet_fast_R_50_C4_3x.yaml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ _BASE_: "Base-TridentNet-Fast-C4.yaml"
2
+ MODEL:
3
+ WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl"
4
+ MASK_ON: False
5
+ RESNETS:
6
+ DEPTH: 50
7
+ SOLVER:
8
+ STEPS: (210000, 250000)
9
+ MAX_ITER: 270000
preprocess/humanparsing/mhp_extension/detectron2/projects/TridentNet/train_net.py ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
3
+
4
+ """
5
+ TridentNet Training Script.
6
+
7
+ This script is a simplified version of the training script in detectron2/tools.
8
+ """
9
+
10
+ import os
11
+
12
+ from detectron2.checkpoint import DetectionCheckpointer
13
+ from detectron2.config import get_cfg
14
+ from detectron2.engine import DefaultTrainer, default_argument_parser, default_setup, launch
15
+ from detectron2.evaluation import COCOEvaluator
16
+
17
+ from tridentnet import add_tridentnet_config
18
+
19
+
20
+ class Trainer(DefaultTrainer):
21
+ @classmethod
22
+ def build_evaluator(cls, cfg, dataset_name, output_folder=None):
23
+ if output_folder is None:
24
+ output_folder = os.path.join(cfg.OUTPUT_DIR, "inference")
25
+ return COCOEvaluator(dataset_name, cfg, True, output_folder)
26
+
27
+
28
+ def setup(args):
29
+ """
30
+ Create configs and perform basic setups.
31
+ """
32
+ cfg = get_cfg()
33
+ add_tridentnet_config(cfg)
34
+ cfg.merge_from_file(args.config_file)
35
+ cfg.merge_from_list(args.opts)
36
+ cfg.freeze()
37
+ default_setup(cfg, args)
38
+ return cfg
39
+
40
+
41
+ def main(args):
42
+ cfg = setup(args)
43
+
44
+ if args.eval_only:
45
+ model = Trainer.build_model(cfg)
46
+ DetectionCheckpointer(model, save_dir=cfg.OUTPUT_DIR).resume_or_load(
47
+ cfg.MODEL.WEIGHTS, resume=args.resume
48
+ )
49
+ res = Trainer.test(cfg, model)
50
+ return res
51
+
52
+ trainer = Trainer(cfg)
53
+ trainer.resume_or_load(resume=args.resume)
54
+ return trainer.train()
55
+
56
+
57
+ if __name__ == "__main__":
58
+ args = default_argument_parser().parse_args()
59
+ print("Command Line Args:", args)
60
+ launch(
61
+ main,
62
+ args.num_gpus,
63
+ num_machines=args.num_machines,
64
+ machine_rank=args.machine_rank,
65
+ dist_url=args.dist_url,
66
+ args=(args,),
67
+ )
preprocess/humanparsing/mhp_extension/detectron2/projects/TridentNet/tridentnet/__init__.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+ from .config import add_tridentnet_config
3
+ from .trident_backbone import (
4
+ TridentBottleneckBlock,
5
+ build_trident_resnet_backbone,
6
+ make_trident_stage,
7
+ )
8
+ from .trident_rpn import TridentRPN
9
+ from .trident_rcnn import TridentRes5ROIHeads, TridentStandardROIHeads
preprocess/humanparsing/mhp_extension/detectron2/projects/TridentNet/tridentnet/config.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
3
+
4
+ from detectron2.config import CfgNode as CN
5
+
6
+
7
+ def add_tridentnet_config(cfg):
8
+ """
9
+ Add config for tridentnet.
10
+ """
11
+ _C = cfg
12
+
13
+ _C.MODEL.TRIDENT = CN()
14
+
15
+ # Number of branches for TridentNet.
16
+ _C.MODEL.TRIDENT.NUM_BRANCH = 3
17
+ # Specify the dilations for each branch.
18
+ _C.MODEL.TRIDENT.BRANCH_DILATIONS = [1, 2, 3]
19
+ # Specify the stage for applying trident blocks. Default stage is Res4 according to the
20
+ # TridentNet paper.
21
+ _C.MODEL.TRIDENT.TRIDENT_STAGE = "res4"
22
+ # Specify the test branch index TridentNet Fast inference:
23
+ # - use -1 to aggregate results of all branches during inference.
24
+ # - otherwise, only using specified branch for fast inference. Recommended setting is
25
+ # to use the middle branch.
26
+ _C.MODEL.TRIDENT.TEST_BRANCH_IDX = 1
preprocess/humanparsing/mhp_extension/detectron2/projects/TridentNet/tridentnet/trident_backbone.py ADDED
@@ -0,0 +1,223 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+ import fvcore.nn.weight_init as weight_init
3
+ import torch
4
+ import torch.nn.functional as F
5
+
6
+ from detectron2.layers import Conv2d, FrozenBatchNorm2d, get_norm
7
+ from detectron2.modeling import BACKBONE_REGISTRY, ResNet, ResNetBlockBase, make_stage
8
+ from detectron2.modeling.backbone.resnet import BasicStem, BottleneckBlock, DeformBottleneckBlock
9
+
10
+ from .trident_conv import TridentConv
11
+
12
+ __all__ = ["TridentBottleneckBlock", "make_trident_stage", "build_trident_resnet_backbone"]
13
+
14
+
15
+ class TridentBottleneckBlock(ResNetBlockBase):
16
+ def __init__(
17
+ self,
18
+ in_channels,
19
+ out_channels,
20
+ *,
21
+ bottleneck_channels,
22
+ stride=1,
23
+ num_groups=1,
24
+ norm="BN",
25
+ stride_in_1x1=False,
26
+ num_branch=3,
27
+ dilations=(1, 2, 3),
28
+ concat_output=False,
29
+ test_branch_idx=-1,
30
+ ):
31
+ """
32
+ Args:
33
+ num_branch (int): the number of branches in TridentNet.
34
+ dilations (tuple): the dilations of multiple branches in TridentNet.
35
+ concat_output (bool): if concatenate outputs of multiple branches in TridentNet.
36
+ Use 'True' for the last trident block.
37
+ """
38
+ super().__init__(in_channels, out_channels, stride)
39
+
40
+ assert num_branch == len(dilations)
41
+
42
+ self.num_branch = num_branch
43
+ self.concat_output = concat_output
44
+ self.test_branch_idx = test_branch_idx
45
+
46
+ if in_channels != out_channels:
47
+ self.shortcut = Conv2d(
48
+ in_channels,
49
+ out_channels,
50
+ kernel_size=1,
51
+ stride=stride,
52
+ bias=False,
53
+ norm=get_norm(norm, out_channels),
54
+ )
55
+ else:
56
+ self.shortcut = None
57
+
58
+ stride_1x1, stride_3x3 = (stride, 1) if stride_in_1x1 else (1, stride)
59
+
60
+ self.conv1 = Conv2d(
61
+ in_channels,
62
+ bottleneck_channels,
63
+ kernel_size=1,
64
+ stride=stride_1x1,
65
+ bias=False,
66
+ norm=get_norm(norm, bottleneck_channels),
67
+ )
68
+
69
+ self.conv2 = TridentConv(
70
+ bottleneck_channels,
71
+ bottleneck_channels,
72
+ kernel_size=3,
73
+ stride=stride_3x3,
74
+ paddings=dilations,
75
+ bias=False,
76
+ groups=num_groups,
77
+ dilations=dilations,
78
+ num_branch=num_branch,
79
+ test_branch_idx=test_branch_idx,
80
+ norm=get_norm(norm, bottleneck_channels),
81
+ )
82
+
83
+ self.conv3 = Conv2d(
84
+ bottleneck_channels,
85
+ out_channels,
86
+ kernel_size=1,
87
+ bias=False,
88
+ norm=get_norm(norm, out_channels),
89
+ )
90
+
91
+ for layer in [self.conv1, self.conv2, self.conv3, self.shortcut]:
92
+ if layer is not None: # shortcut can be None
93
+ weight_init.c2_msra_fill(layer)
94
+
95
+ def forward(self, x):
96
+ num_branch = self.num_branch if self.training or self.test_branch_idx == -1 else 1
97
+ if not isinstance(x, list):
98
+ x = [x] * num_branch
99
+ out = [self.conv1(b) for b in x]
100
+ out = [F.relu_(b) for b in out]
101
+
102
+ out = self.conv2(out)
103
+ out = [F.relu_(b) for b in out]
104
+
105
+ out = [self.conv3(b) for b in out]
106
+
107
+ if self.shortcut is not None:
108
+ shortcut = [self.shortcut(b) for b in x]
109
+ else:
110
+ shortcut = x
111
+
112
+ out = [out_b + shortcut_b for out_b, shortcut_b in zip(out, shortcut)]
113
+ out = [F.relu_(b) for b in out]
114
+ if self.concat_output:
115
+ out = torch.cat(out)
116
+ return out
117
+
118
+
119
+ def make_trident_stage(block_class, num_blocks, first_stride, **kwargs):
120
+ """
121
+ Create a resnet stage by creating many blocks for TridentNet.
122
+ """
123
+ blocks = []
124
+ for i in range(num_blocks - 1):
125
+ blocks.append(block_class(stride=first_stride if i == 0 else 1, **kwargs))
126
+ kwargs["in_channels"] = kwargs["out_channels"]
127
+ blocks.append(block_class(stride=1, concat_output=True, **kwargs))
128
+ return blocks
129
+
130
+
131
+ @BACKBONE_REGISTRY.register()
132
+ def build_trident_resnet_backbone(cfg, input_shape):
133
+ """
134
+ Create a ResNet instance from config for TridentNet.
135
+
136
+ Returns:
137
+ ResNet: a :class:`ResNet` instance.
138
+ """
139
+ # need registration of new blocks/stems?
140
+ norm = cfg.MODEL.RESNETS.NORM
141
+ stem = BasicStem(
142
+ in_channels=input_shape.channels,
143
+ out_channels=cfg.MODEL.RESNETS.STEM_OUT_CHANNELS,
144
+ norm=norm,
145
+ )
146
+ freeze_at = cfg.MODEL.BACKBONE.FREEZE_AT
147
+
148
+ if freeze_at >= 1:
149
+ for p in stem.parameters():
150
+ p.requires_grad = False
151
+ stem = FrozenBatchNorm2d.convert_frozen_batchnorm(stem)
152
+
153
+ # fmt: off
154
+ out_features = cfg.MODEL.RESNETS.OUT_FEATURES
155
+ depth = cfg.MODEL.RESNETS.DEPTH
156
+ num_groups = cfg.MODEL.RESNETS.NUM_GROUPS
157
+ width_per_group = cfg.MODEL.RESNETS.WIDTH_PER_GROUP
158
+ bottleneck_channels = num_groups * width_per_group
159
+ in_channels = cfg.MODEL.RESNETS.STEM_OUT_CHANNELS
160
+ out_channels = cfg.MODEL.RESNETS.RES2_OUT_CHANNELS
161
+ stride_in_1x1 = cfg.MODEL.RESNETS.STRIDE_IN_1X1
162
+ res5_dilation = cfg.MODEL.RESNETS.RES5_DILATION
163
+ deform_on_per_stage = cfg.MODEL.RESNETS.DEFORM_ON_PER_STAGE
164
+ deform_modulated = cfg.MODEL.RESNETS.DEFORM_MODULATED
165
+ deform_num_groups = cfg.MODEL.RESNETS.DEFORM_NUM_GROUPS
166
+ num_branch = cfg.MODEL.TRIDENT.NUM_BRANCH
167
+ branch_dilations = cfg.MODEL.TRIDENT.BRANCH_DILATIONS
168
+ trident_stage = cfg.MODEL.TRIDENT.TRIDENT_STAGE
169
+ test_branch_idx = cfg.MODEL.TRIDENT.TEST_BRANCH_IDX
170
+ # fmt: on
171
+ assert res5_dilation in {1, 2}, "res5_dilation cannot be {}.".format(res5_dilation)
172
+
173
+ num_blocks_per_stage = {50: [3, 4, 6, 3], 101: [3, 4, 23, 3], 152: [3, 8, 36, 3]}[depth]
174
+
175
+ stages = []
176
+
177
+ res_stage_idx = {"res2": 2, "res3": 3, "res4": 4, "res5": 5}
178
+ out_stage_idx = [res_stage_idx[f] for f in out_features]
179
+ trident_stage_idx = res_stage_idx[trident_stage]
180
+ max_stage_idx = max(out_stage_idx)
181
+ for idx, stage_idx in enumerate(range(2, max_stage_idx + 1)):
182
+ dilation = res5_dilation if stage_idx == 5 else 1
183
+ first_stride = 1 if idx == 0 or (stage_idx == 5 and dilation == 2) else 2
184
+ stage_kargs = {
185
+ "num_blocks": num_blocks_per_stage[idx],
186
+ "first_stride": first_stride,
187
+ "in_channels": in_channels,
188
+ "bottleneck_channels": bottleneck_channels,
189
+ "out_channels": out_channels,
190
+ "num_groups": num_groups,
191
+ "norm": norm,
192
+ "stride_in_1x1": stride_in_1x1,
193
+ "dilation": dilation,
194
+ }
195
+ if stage_idx == trident_stage_idx:
196
+ assert not deform_on_per_stage[
197
+ idx
198
+ ], "Not support deformable conv in Trident blocks yet."
199
+ stage_kargs["block_class"] = TridentBottleneckBlock
200
+ stage_kargs["num_branch"] = num_branch
201
+ stage_kargs["dilations"] = branch_dilations
202
+ stage_kargs["test_branch_idx"] = test_branch_idx
203
+ stage_kargs.pop("dilation")
204
+ elif deform_on_per_stage[idx]:
205
+ stage_kargs["block_class"] = DeformBottleneckBlock
206
+ stage_kargs["deform_modulated"] = deform_modulated
207
+ stage_kargs["deform_num_groups"] = deform_num_groups
208
+ else:
209
+ stage_kargs["block_class"] = BottleneckBlock
210
+ blocks = (
211
+ make_trident_stage(**stage_kargs)
212
+ if stage_idx == trident_stage_idx
213
+ else make_stage(**stage_kargs)
214
+ )
215
+ in_channels = out_channels
216
+ out_channels *= 2
217
+ bottleneck_channels *= 2
218
+
219
+ if freeze_at >= stage_idx:
220
+ for block in blocks:
221
+ block.freeze()
222
+ stages.append(blocks)
223
+ return ResNet(stem, stages, out_features=out_features)
preprocess/humanparsing/mhp_extension/detectron2/projects/TridentNet/tridentnet/trident_conv.py ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+ import torch
3
+ from torch import nn
4
+ from torch.nn import functional as F
5
+ from torch.nn.modules.utils import _pair
6
+
7
+ from detectron2.layers.wrappers import _NewEmptyTensorOp
8
+
9
+
10
+ class TridentConv(nn.Module):
11
+ def __init__(
12
+ self,
13
+ in_channels,
14
+ out_channels,
15
+ kernel_size,
16
+ stride=1,
17
+ paddings=0,
18
+ dilations=1,
19
+ groups=1,
20
+ num_branch=1,
21
+ test_branch_idx=-1,
22
+ bias=False,
23
+ norm=None,
24
+ activation=None,
25
+ ):
26
+ super(TridentConv, self).__init__()
27
+ self.in_channels = in_channels
28
+ self.out_channels = out_channels
29
+ self.kernel_size = _pair(kernel_size)
30
+ self.num_branch = num_branch
31
+ self.stride = _pair(stride)
32
+ self.groups = groups
33
+ self.with_bias = bias
34
+ if isinstance(paddings, int):
35
+ paddings = [paddings] * self.num_branch
36
+ if isinstance(dilations, int):
37
+ dilations = [dilations] * self.num_branch
38
+ self.paddings = [_pair(padding) for padding in paddings]
39
+ self.dilations = [_pair(dilation) for dilation in dilations]
40
+ self.test_branch_idx = test_branch_idx
41
+ self.norm = norm
42
+ self.activation = activation
43
+
44
+ assert len({self.num_branch, len(self.paddings), len(self.dilations)}) == 1
45
+
46
+ self.weight = nn.Parameter(
47
+ torch.Tensor(out_channels, in_channels // groups, *self.kernel_size)
48
+ )
49
+ if bias:
50
+ self.bias = nn.Parameter(torch.Tensor(out_channels))
51
+ else:
52
+ self.bias = None
53
+
54
+ nn.init.kaiming_uniform_(self.weight, nonlinearity="relu")
55
+ if self.bias is not None:
56
+ nn.init.constant_(self.bias, 0)
57
+
58
+ def forward(self, inputs):
59
+ num_branch = self.num_branch if self.training or self.test_branch_idx == -1 else 1
60
+ assert len(inputs) == num_branch
61
+
62
+ if inputs[0].numel() == 0:
63
+ output_shape = [
64
+ (i + 2 * p - (di * (k - 1) + 1)) // s + 1
65
+ for i, p, di, k, s in zip(
66
+ inputs[0].shape[-2:], self.padding, self.dilation, self.kernel_size, self.stride
67
+ )
68
+ ]
69
+ output_shape = [input[0].shape[0], self.weight.shape[0]] + output_shape
70
+ return [_NewEmptyTensorOp.apply(input, output_shape) for input in inputs]
71
+
72
+ if self.training or self.test_branch_idx == -1:
73
+ outputs = [
74
+ F.conv2d(input, self.weight, self.bias, self.stride, padding, dilation, self.groups)
75
+ for input, dilation, padding in zip(inputs, self.dilations, self.paddings)
76
+ ]
77
+ else:
78
+ outputs = [
79
+ F.conv2d(
80
+ inputs[0],
81
+ self.weight,
82
+ self.bias,
83
+ self.stride,
84
+ self.paddings[self.test_branch_idx],
85
+ self.dilations[self.test_branch_idx],
86
+ self.groups,
87
+ )
88
+ ]
89
+
90
+ if self.norm is not None:
91
+ outputs = [self.norm(x) for x in outputs]
92
+ if self.activation is not None:
93
+ outputs = [self.activation(x) for x in outputs]
94
+ return outputs
95
+
96
+ def extra_repr(self):
97
+ tmpstr = "in_channels=" + str(self.in_channels)
98
+ tmpstr += ", out_channels=" + str(self.out_channels)
99
+ tmpstr += ", kernel_size=" + str(self.kernel_size)
100
+ tmpstr += ", num_branch=" + str(self.num_branch)
101
+ tmpstr += ", test_branch_idx=" + str(self.test_branch_idx)
102
+ tmpstr += ", stride=" + str(self.stride)
103
+ tmpstr += ", paddings=" + str(self.paddings)
104
+ tmpstr += ", dilations=" + str(self.dilations)
105
+ tmpstr += ", groups=" + str(self.groups)
106
+ tmpstr += ", bias=" + str(self.with_bias)
107
+ return tmpstr
preprocess/humanparsing/mhp_extension/detectron2/projects/TridentNet/tridentnet/trident_rcnn.py ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+ from detectron2.layers import batched_nms
3
+ from detectron2.modeling import ROI_HEADS_REGISTRY, StandardROIHeads
4
+ from detectron2.modeling.roi_heads.roi_heads import Res5ROIHeads
5
+ from detectron2.structures import Instances
6
+
7
+
8
+ def merge_branch_instances(instances, num_branch, nms_thresh, topk_per_image):
9
+ """
10
+ Merge detection results from different branches of TridentNet.
11
+ Return detection results by applying non-maximum suppression (NMS) on bounding boxes
12
+ and keep the unsuppressed boxes and other instances (e.g mask) if any.
13
+
14
+ Args:
15
+ instances (list[Instances]): A list of N * num_branch instances that store detection
16
+ results. Contain N images and each image has num_branch instances.
17
+ num_branch (int): Number of branches used for merging detection results for each image.
18
+ nms_thresh (float): The threshold to use for box non-maximum suppression. Value in [0, 1].
19
+ topk_per_image (int): The number of top scoring detections to return. Set < 0 to return
20
+ all detections.
21
+
22
+ Returns:
23
+ results: (list[Instances]): A list of N instances, one for each image in the batch,
24
+ that stores the topk most confidence detections after merging results from multiple
25
+ branches.
26
+ """
27
+ if num_branch == 1:
28
+ return instances
29
+
30
+ batch_size = len(instances) // num_branch
31
+ results = []
32
+ for i in range(batch_size):
33
+ instance = Instances.cat([instances[i + batch_size * j] for j in range(num_branch)])
34
+
35
+ # Apply per-class NMS
36
+ keep = batched_nms(
37
+ instance.pred_boxes.tensor, instance.scores, instance.pred_classes, nms_thresh
38
+ )
39
+ keep = keep[:topk_per_image]
40
+ result = instance[keep]
41
+
42
+ results.append(result)
43
+
44
+ return results
45
+
46
+
47
+ @ROI_HEADS_REGISTRY.register()
48
+ class TridentRes5ROIHeads(Res5ROIHeads):
49
+ """
50
+ The TridentNet ROIHeads in a typical "C4" R-CNN model.
51
+ See :class:`Res5ROIHeads`.
52
+ """
53
+
54
+ def __init__(self, cfg, input_shape):
55
+ super().__init__(cfg, input_shape)
56
+
57
+ self.num_branch = cfg.MODEL.TRIDENT.NUM_BRANCH
58
+ self.trident_fast = cfg.MODEL.TRIDENT.TEST_BRANCH_IDX != -1
59
+
60
+ def forward(self, images, features, proposals, targets=None):
61
+ """
62
+ See :class:`Res5ROIHeads.forward`.
63
+ """
64
+ num_branch = self.num_branch if self.training or not self.trident_fast else 1
65
+ all_targets = targets * num_branch if targets is not None else None
66
+ pred_instances, losses = super().forward(images, features, proposals, all_targets)
67
+ del images, all_targets, targets
68
+
69
+ if self.training:
70
+ return pred_instances, losses
71
+ else:
72
+ pred_instances = merge_branch_instances(
73
+ pred_instances,
74
+ num_branch,
75
+ self.box_predictor.test_nms_thresh,
76
+ self.box_predictor.test_topk_per_image,
77
+ )
78
+
79
+ return pred_instances, {}
80
+
81
+
82
+ @ROI_HEADS_REGISTRY.register()
83
+ class TridentStandardROIHeads(StandardROIHeads):
84
+ """
85
+ The `StandardROIHeads` for TridentNet.
86
+ See :class:`StandardROIHeads`.
87
+ """
88
+
89
+ def __init__(self, cfg, input_shape):
90
+ super(TridentStandardROIHeads, self).__init__(cfg, input_shape)
91
+
92
+ self.num_branch = cfg.MODEL.TRIDENT.NUM_BRANCH
93
+ self.trident_fast = cfg.MODEL.TRIDENT.TEST_BRANCH_IDX != -1
94
+
95
+ def forward(self, images, features, proposals, targets=None):
96
+ """
97
+ See :class:`Res5ROIHeads.forward`.
98
+ """
99
+ # Use 1 branch if using trident_fast during inference.
100
+ num_branch = self.num_branch if self.training or not self.trident_fast else 1
101
+ # Duplicate targets for all branches in TridentNet.
102
+ all_targets = targets * num_branch if targets is not None else None
103
+ pred_instances, losses = super().forward(images, features, proposals, all_targets)
104
+ del images, all_targets, targets
105
+
106
+ if self.training:
107
+ return pred_instances, losses
108
+ else:
109
+ pred_instances = merge_branch_instances(
110
+ pred_instances,
111
+ num_branch,
112
+ self.box_predictor.test_nms_thresh,
113
+ self.box_predictor.test_topk_per_image,
114
+ )
115
+
116
+ return pred_instances, {}
preprocess/humanparsing/mhp_extension/detectron2/projects/TridentNet/tridentnet/trident_rpn.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+ import torch
3
+
4
+ from detectron2.modeling import PROPOSAL_GENERATOR_REGISTRY
5
+ from detectron2.modeling.proposal_generator.rpn import RPN
6
+ from detectron2.structures import ImageList
7
+
8
+
9
+ @PROPOSAL_GENERATOR_REGISTRY.register()
10
+ class TridentRPN(RPN):
11
+ """
12
+ Trident RPN subnetwork.
13
+ """
14
+
15
+ def __init__(self, cfg, input_shape):
16
+ super(TridentRPN, self).__init__(cfg, input_shape)
17
+
18
+ self.num_branch = cfg.MODEL.TRIDENT.NUM_BRANCH
19
+ self.trident_fast = cfg.MODEL.TRIDENT.TEST_BRANCH_IDX != -1
20
+
21
+ def forward(self, images, features, gt_instances=None):
22
+ """
23
+ See :class:`RPN.forward`.
24
+ """
25
+ num_branch = self.num_branch if self.training or not self.trident_fast else 1
26
+ # Duplicate images and gt_instances for all branches in TridentNet.
27
+ all_images = ImageList(
28
+ torch.cat([images.tensor] * num_branch), images.image_sizes * num_branch
29
+ )
30
+ all_gt_instances = gt_instances * num_branch if gt_instances is not None else None
31
+
32
+ return super(TridentRPN, self).forward(all_images, features, all_gt_instances)
preprocess/humanparsing/mhp_extension/detectron2/setup.cfg ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [isort]
2
+ line_length=100
3
+ multi_line_output=3
4
+ include_trailing_comma=True
5
+ known_standard_library=numpy,setuptools,mock
6
+ skip=./datasets,docs
7
+ skip_glob=*/__init__.py
8
+ known_myself=detectron2
9
+ known_third_party=fvcore,matplotlib,cv2,torch,torchvision,PIL,pycocotools,yacs,termcolor,cityscapesscripts,tabulate,tqdm,scipy,lvis,psutil,pkg_resources,caffe2,onnx
10
+ no_lines_before=STDLIB,THIRDPARTY
11
+ sections=FUTURE,STDLIB,THIRDPARTY,myself,FIRSTPARTY,LOCALFOLDER
12
+ default_section=FIRSTPARTY
13
+
14
+ [mypy]
15
+ python_version=3.6
16
+ ignore_missing_imports = True
17
+ warn_unused_configs = True
18
+ disallow_untyped_defs = True
19
+ check_untyped_defs = True
20
+ warn_unused_ignores = True
21
+ warn_redundant_casts = True
22
+ show_column_numbers = True
23
+ follow_imports = silent
24
+ allow_redefinition = True
25
+ ; Require all functions to be annotated
26
+ disallow_incomplete_defs = True
preprocess/humanparsing/mhp_extension/detectron2/setup.py ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
3
+
4
+ import glob
5
+ import os
6
+ import shutil
7
+ from os import path
8
+ from setuptools import find_packages, setup
9
+ from typing import List
10
+ import torch
11
+ from torch.utils.cpp_extension import CUDA_HOME, CppExtension, CUDAExtension
12
+
13
+ torch_ver = [int(x) for x in torch.__version__.split(".")[:2]]
14
+ assert torch_ver >= [1, 4], "Requires PyTorch >= 1.4"
15
+
16
+
17
+ def get_version():
18
+ init_py_path = path.join(path.abspath(path.dirname(__file__)), "detectron2", "__init__.py")
19
+ init_py = open(init_py_path, "r").readlines()
20
+ version_line = [l.strip() for l in init_py if l.startswith("__version__")][0]
21
+ version = version_line.split("=")[-1].strip().strip("'\"")
22
+
23
+ # The following is used to build release packages.
24
+ # Users should never use it.
25
+ suffix = os.getenv("D2_VERSION_SUFFIX", "")
26
+ version = version + suffix
27
+ if os.getenv("BUILD_NIGHTLY", "0") == "1":
28
+ from datetime import datetime
29
+
30
+ date_str = datetime.today().strftime("%y%m%d")
31
+ version = version + ".dev" + date_str
32
+
33
+ new_init_py = [l for l in init_py if not l.startswith("__version__")]
34
+ new_init_py.append('__version__ = "{}"\n'.format(version))
35
+ with open(init_py_path, "w") as f:
36
+ f.write("".join(new_init_py))
37
+ return version
38
+
39
+
40
+ def get_extensions():
41
+ this_dir = path.dirname(path.abspath(__file__))
42
+ extensions_dir = path.join(this_dir, "detectron2", "layers", "csrc")
43
+
44
+ main_source = path.join(extensions_dir, "vision.cpp")
45
+ sources = glob.glob(path.join(extensions_dir, "**", "*.cpp"))
46
+ source_cuda = glob.glob(path.join(extensions_dir, "**", "*.cu")) + glob.glob(
47
+ path.join(extensions_dir, "*.cu")
48
+ )
49
+
50
+ sources = [main_source] + sources
51
+ extension = CppExtension
52
+
53
+ extra_compile_args = {"cxx": []}
54
+ define_macros = []
55
+
56
+ if (
57
+ torch.cuda.is_available() and CUDA_HOME is not None and os.path.isdir(CUDA_HOME)
58
+ ) or os.getenv("FORCE_CUDA", "0") == "1":
59
+ extension = CUDAExtension
60
+ sources += source_cuda
61
+ define_macros += [("WITH_CUDA", None)]
62
+ extra_compile_args["nvcc"] = [
63
+ "-DCUDA_HAS_FP16=1",
64
+ "-D__CUDA_NO_HALF_OPERATORS__",
65
+ "-D__CUDA_NO_HALF_CONVERSIONS__",
66
+ "-D__CUDA_NO_HALF2_OPERATORS__",
67
+ ]
68
+
69
+ # It's better if pytorch can do this by default ..
70
+ CC = os.environ.get("CC", None)
71
+ if CC is not None:
72
+ extra_compile_args["nvcc"].append("-ccbin={}".format(CC))
73
+
74
+ include_dirs = [extensions_dir]
75
+
76
+ ext_modules = [
77
+ extension(
78
+ "detectron2._C",
79
+ sources,
80
+ include_dirs=include_dirs,
81
+ define_macros=define_macros,
82
+ extra_compile_args=extra_compile_args,
83
+ )
84
+ ]
85
+
86
+ return ext_modules
87
+
88
+
89
+ def get_model_zoo_configs() -> List[str]:
90
+ """
91
+ Return a list of configs to include in package for model zoo. Copy over these configs inside
92
+ detectron2/model_zoo.
93
+ """
94
+
95
+ # Use absolute paths while symlinking.
96
+ source_configs_dir = path.join(path.dirname(path.realpath(__file__)), "configs")
97
+ destination = path.join(
98
+ path.dirname(path.realpath(__file__)), "detectron2", "model_zoo", "configs"
99
+ )
100
+ # Symlink the config directory inside package to have a cleaner pip install.
101
+
102
+ # Remove stale symlink/directory from a previous build.
103
+ if path.exists(source_configs_dir):
104
+ if path.islink(destination):
105
+ os.unlink(destination)
106
+ elif path.isdir(destination):
107
+ shutil.rmtree(destination)
108
+
109
+ if not path.exists(destination):
110
+ try:
111
+ os.symlink(source_configs_dir, destination)
112
+ except OSError:
113
+ # Fall back to copying if symlink fails: ex. on Windows.
114
+ shutil.copytree(source_configs_dir, destination)
115
+
116
+ config_paths = glob.glob("configs/**/*.yaml", recursive=True)
117
+ return config_paths
118
+
119
+
120
+ setup(
121
+ name="detectron2",
122
+ version=get_version(),
123
+ author="FAIR",
124
+ url="https://github.com/facebookresearch/detectron2",
125
+ description="Detectron2 is FAIR's next-generation research "
126
+ "platform for object detection and segmentation.",
127
+ packages=find_packages(exclude=("configs", "tests*")),
128
+ package_data={"detectron2.model_zoo": get_model_zoo_configs()},
129
+ python_requires=">=3.6",
130
+ install_requires=[
131
+ "termcolor>=1.1",
132
+ "Pillow", # you can also use pillow-simd for better performance
133
+ "yacs>=0.1.6",
134
+ "tabulate",
135
+ "cloudpickle",
136
+ "matplotlib",
137
+ "mock",
138
+ "tqdm>4.29.0",
139
+ "tensorboard",
140
+ "fvcore>=0.1.1",
141
+ "future", # used by caffe2
142
+ "pydot", # used to save caffe2 SVGs
143
+ ],
144
+ extras_require={
145
+ "all": ["shapely", "psutil"],
146
+ "dev": [
147
+ "flake8==3.7.9",
148
+ "isort",
149
+ "black @ git+https://github.com/psf/black@673327449f86fce558adde153bb6cbe54bfebad2",
150
+ "flake8-bugbear",
151
+ "flake8-comprehensions",
152
+ ],
153
+ },
154
+ ext_modules=get_extensions(),
155
+ cmdclass={"build_ext": torch.utils.cpp_extension.BuildExtension},
156
+ )
preprocess/humanparsing/mhp_extension/detectron2/tests/README.md ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ ## Unit Tests
2
+
3
+ To run the unittests, do:
4
+ ```
5
+ cd detectron2
6
+ python -m unittest discover -v -s ./tests
7
+ ```
8
+
9
+ There are also end-to-end inference & training tests, in [dev/run_*_tests.sh](../dev).
preprocess/humanparsing/mhp_extension/detectron2/tests/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
preprocess/humanparsing/mhp_extension/detectron2/tests/data/__init__.py ADDED
File without changes
preprocess/humanparsing/mhp_extension/detectron2/tests/data/test_coco.py ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+ import json
3
+ import numpy as np
4
+ import os
5
+ import tempfile
6
+ import unittest
7
+ import pycocotools
8
+
9
+ from detectron2.data import DatasetCatalog, MetadataCatalog
10
+ from detectron2.data.datasets.coco import convert_to_coco_dict, load_coco_json
11
+ from detectron2.structures import BoxMode
12
+
13
+
14
+ def make_mask():
15
+ """
16
+ Makes a donut shaped binary mask.
17
+ """
18
+ H = 100
19
+ W = 100
20
+ mask = np.zeros([H, W], dtype=np.uint8)
21
+ for x in range(W):
22
+ for y in range(H):
23
+ d = np.linalg.norm(np.array([W, H]) / 2 - np.array([x, y]))
24
+ if d > 10 and d < 20:
25
+ mask[y, x] = 1
26
+ return mask
27
+
28
+
29
+ def make_dataset_dicts(mask):
30
+ """
31
+ Returns a list of dicts that represents a single COCO data point for
32
+ object detection. The single instance given by `mask` is represented by
33
+ RLE.
34
+ """
35
+ record = {}
36
+ record["file_name"] = "test"
37
+ record["image_id"] = 0
38
+ record["height"] = mask.shape[0]
39
+ record["width"] = mask.shape[1]
40
+
41
+ y, x = np.nonzero(mask)
42
+ segmentation = pycocotools.mask.encode(np.asarray(mask, order="F"))
43
+ min_x = np.min(x)
44
+ max_x = np.max(x)
45
+ min_y = np.min(y)
46
+ max_y = np.max(y)
47
+ obj = {
48
+ "bbox": [min_x, min_y, max_x, max_y],
49
+ "bbox_mode": BoxMode.XYXY_ABS,
50
+ "category_id": 0,
51
+ "iscrowd": 0,
52
+ "segmentation": segmentation,
53
+ }
54
+ record["annotations"] = [obj]
55
+ return [record]
56
+
57
+
58
+ class TestRLEToJson(unittest.TestCase):
59
+ def test(self):
60
+ # Make a dummy dataset.
61
+ mask = make_mask()
62
+ DatasetCatalog.register("test_dataset", lambda: make_dataset_dicts(mask))
63
+ MetadataCatalog.get("test_dataset").set(thing_classes=["test_label"])
64
+
65
+ # Dump to json.
66
+ json_dict = convert_to_coco_dict("test_dataset")
67
+ with tempfile.TemporaryDirectory() as tmpdir:
68
+ json_file_name = os.path.join(tmpdir, "test.json")
69
+ with open(json_file_name, "w") as f:
70
+ json.dump(json_dict, f)
71
+ # Load from json.
72
+ dicts = load_coco_json(json_file_name, "")
73
+
74
+ # Check the loaded mask matches the original.
75
+ anno = dicts[0]["annotations"][0]
76
+ loaded_mask = pycocotools.mask.decode(anno["segmentation"])
77
+ self.assertTrue(np.array_equal(loaded_mask, mask))
preprocess/humanparsing/mhp_extension/detectron2/tests/data/test_detection_utils.py ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
2
+
3
+ import copy
4
+ import numpy as np
5
+ import unittest
6
+ import pycocotools.mask as mask_util
7
+
8
+ from detectron2.data import detection_utils
9
+ from detectron2.data import transforms as T
10
+ from detectron2.structures import BitMasks, BoxMode
11
+
12
+
13
+ class TestTransformAnnotations(unittest.TestCase):
14
+ def test_transform_simple_annotation(self):
15
+ transforms = T.TransformList([T.HFlipTransform(400)])
16
+ anno = {
17
+ "bbox": np.asarray([10, 10, 200, 300]),
18
+ "bbox_mode": BoxMode.XYXY_ABS,
19
+ "category_id": 3,
20
+ "segmentation": [[10, 10, 100, 100, 100, 10], [150, 150, 200, 150, 200, 200]],
21
+ }
22
+
23
+ output = detection_utils.transform_instance_annotations(anno, transforms, (400, 400))
24
+ self.assertTrue(np.allclose(output["bbox"], [200, 10, 390, 300]))
25
+ self.assertEqual(len(output["segmentation"]), len(anno["segmentation"]))
26
+ self.assertTrue(np.allclose(output["segmentation"][0], [390, 10, 300, 100, 300, 10]))
27
+
28
+ detection_utils.annotations_to_instances([output, output], (400, 400))
29
+
30
+ def test_flip_keypoints(self):
31
+ transforms = T.TransformList([T.HFlipTransform(400)])
32
+ anno = {
33
+ "bbox": np.asarray([10, 10, 200, 300]),
34
+ "bbox_mode": BoxMode.XYXY_ABS,
35
+ "keypoints": np.random.rand(17, 3) * 50 + 15,
36
+ }
37
+
38
+ output = detection_utils.transform_instance_annotations(
39
+ copy.deepcopy(anno),
40
+ transforms,
41
+ (400, 400),
42
+ keypoint_hflip_indices=detection_utils.create_keypoint_hflip_indices(
43
+ ["keypoints_coco_2017_train"]
44
+ ),
45
+ )
46
+ # The first keypoint is nose
47
+ self.assertTrue(np.allclose(output["keypoints"][0, 0], 400 - anno["keypoints"][0, 0]))
48
+ # The last 16 keypoints are 8 left-right pairs
49
+ self.assertTrue(
50
+ np.allclose(
51
+ output["keypoints"][1:, 0].reshape(-1, 2)[:, ::-1],
52
+ 400 - anno["keypoints"][1:, 0].reshape(-1, 2),
53
+ )
54
+ )
55
+ self.assertTrue(
56
+ np.allclose(
57
+ output["keypoints"][1:, 1:].reshape(-1, 2, 2)[:, ::-1, :],
58
+ anno["keypoints"][1:, 1:].reshape(-1, 2, 2),
59
+ )
60
+ )
61
+
62
+ def test_transform_RLE(self):
63
+ transforms = T.TransformList([T.HFlipTransform(400)])
64
+ mask = np.zeros((300, 400), order="F").astype("uint8")
65
+ mask[:, :200] = 1
66
+
67
+ anno = {
68
+ "bbox": np.asarray([10, 10, 200, 300]),
69
+ "bbox_mode": BoxMode.XYXY_ABS,
70
+ "segmentation": mask_util.encode(mask[:, :, None])[0],
71
+ "category_id": 3,
72
+ }
73
+ output = detection_utils.transform_instance_annotations(
74
+ copy.deepcopy(anno), transforms, (300, 400)
75
+ )
76
+ mask = output["segmentation"]
77
+ self.assertTrue((mask[:, 200:] == 1).all())
78
+ self.assertTrue((mask[:, :200] == 0).all())
79
+
80
+ inst = detection_utils.annotations_to_instances(
81
+ [output, output], (400, 400), mask_format="bitmask"
82
+ )
83
+ self.assertTrue(isinstance(inst.gt_masks, BitMasks))
84
+
85
+ def test_transform_RLE_resize(self):
86
+ transforms = T.TransformList(
87
+ [T.HFlipTransform(400), T.ScaleTransform(300, 400, 400, 400, "bilinear")]
88
+ )
89
+ mask = np.zeros((300, 400), order="F").astype("uint8")
90
+ mask[:, :200] = 1
91
+
92
+ anno = {
93
+ "bbox": np.asarray([10, 10, 200, 300]),
94
+ "bbox_mode": BoxMode.XYXY_ABS,
95
+ "segmentation": mask_util.encode(mask[:, :, None])[0],
96
+ "category_id": 3,
97
+ }
98
+ output = detection_utils.transform_instance_annotations(
99
+ copy.deepcopy(anno), transforms, (400, 400)
100
+ )
101
+
102
+ inst = detection_utils.annotations_to_instances(
103
+ [output, output], (400, 400), mask_format="bitmask"
104
+ )
105
+ self.assertTrue(isinstance(inst.gt_masks, BitMasks))
106
+
107
+ def test_gen_crop(self):
108
+ instance = {"bbox": [10, 10, 100, 100], "bbox_mode": BoxMode.XYXY_ABS}
109
+ t = detection_utils.gen_crop_transform_with_instance((10, 10), (150, 150), instance)
110
+ # the box center must fall into the cropped region
111
+ self.assertTrue(t.x0 <= 55 <= t.x0 + t.w)
112
+
113
+ def test_gen_crop_outside_boxes(self):
114
+ instance = {"bbox": [10, 10, 100, 100], "bbox_mode": BoxMode.XYXY_ABS}
115
+ with self.assertRaises(AssertionError):
116
+ detection_utils.gen_crop_transform_with_instance((10, 10), (15, 15), instance)
preprocess/humanparsing/mhp_extension/detectron2/tests/data/test_rotation_transform.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+ import numpy as np
3
+ import unittest
4
+
5
+ from detectron2.data.transforms.transform import RotationTransform
6
+
7
+
8
+ class TestRotationTransform(unittest.TestCase):
9
+ def assertEqualsArrays(self, a1, a2):
10
+ self.assertTrue(np.allclose(a1, a2))
11
+
12
+ def randomData(self, h=5, w=5):
13
+ image = np.random.rand(h, w)
14
+ coords = np.array([[i, j] for j in range(h + 1) for i in range(w + 1)], dtype=float)
15
+ return image, coords, h, w
16
+
17
+ def test180(self):
18
+ image, coords, h, w = self.randomData(6, 6)
19
+ rot = RotationTransform(h, w, 180, expand=False, center=None)
20
+ self.assertEqualsArrays(rot.apply_image(image), image[::-1, ::-1])
21
+ rotated_coords = [[w - c[0], h - c[1]] for c in coords]
22
+ self.assertEqualsArrays(rot.apply_coords(coords), rotated_coords)
23
+
24
+ def test45_coords(self):
25
+ _, coords, h, w = self.randomData(4, 6)
26
+ rot = RotationTransform(h, w, 45, expand=False, center=None)
27
+ rotated_coords = [
28
+ [(x + y - (h + w) / 2) / np.sqrt(2) + w / 2, h / 2 + (y + (w - h) / 2 - x) / np.sqrt(2)]
29
+ for (x, y) in coords
30
+ ]
31
+ self.assertEqualsArrays(rot.apply_coords(coords), rotated_coords)
32
+
33
+ def test90(self):
34
+ image, coords, h, w = self.randomData()
35
+ rot = RotationTransform(h, w, 90, expand=False, center=None)
36
+ self.assertEqualsArrays(rot.apply_image(image), image.T[::-1])
37
+ rotated_coords = [[c[1], w - c[0]] for c in coords]
38
+ self.assertEqualsArrays(rot.apply_coords(coords), rotated_coords)
39
+
40
+ def test90_expand(self): # non-square image
41
+ image, coords, h, w = self.randomData(h=5, w=8)
42
+ rot = RotationTransform(h, w, 90, expand=True, center=None)
43
+ self.assertEqualsArrays(rot.apply_image(image), image.T[::-1])
44
+ rotated_coords = [[c[1], w - c[0]] for c in coords]
45
+ self.assertEqualsArrays(rot.apply_coords(coords), rotated_coords)
46
+
47
+ def test_center_expand(self):
48
+ # center has no effect if expand=True because it only affects shifting
49
+ image, coords, h, w = self.randomData(h=5, w=8)
50
+ angle = np.random.randint(360)
51
+ rot1 = RotationTransform(h, w, angle, expand=True, center=None)
52
+ rot2 = RotationTransform(h, w, angle, expand=True, center=(0, 0))
53
+ rot3 = RotationTransform(h, w, angle, expand=True, center=(h, w))
54
+ rot4 = RotationTransform(h, w, angle, expand=True, center=(2, 5))
55
+ for r1 in [rot1, rot2, rot3, rot4]:
56
+ for r2 in [rot1, rot2, rot3, rot4]:
57
+ self.assertEqualsArrays(r1.apply_image(image), r2.apply_image(image))
58
+ self.assertEqualsArrays(r1.apply_coords(coords), r2.apply_coords(coords))
59
+
60
+
61
+ if __name__ == "__main__":
62
+ unittest.main()
preprocess/humanparsing/mhp_extension/detectron2/tests/data/test_sampler.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
2
+ import unittest
3
+ from torch.utils.data.sampler import SequentialSampler
4
+
5
+ from detectron2.data.samplers import GroupedBatchSampler
6
+
7
+
8
+ class TestGroupedBatchSampler(unittest.TestCase):
9
+ def test_missing_group_id(self):
10
+ sampler = SequentialSampler(list(range(100)))
11
+ group_ids = [1] * 100
12
+ samples = GroupedBatchSampler(sampler, group_ids, 2)
13
+
14
+ for mini_batch in samples:
15
+ self.assertEqual(len(mini_batch), 2)
16
+
17
+ def test_groups(self):
18
+ sampler = SequentialSampler(list(range(100)))
19
+ group_ids = [1, 0] * 50
20
+ samples = GroupedBatchSampler(sampler, group_ids, 2)
21
+
22
+ for mini_batch in samples:
23
+ self.assertEqual((mini_batch[0] + mini_batch[1]) % 2, 0)
preprocess/humanparsing/mhp_extension/detectron2/tests/data/test_transforms.py ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
3
+
4
+ import logging
5
+ import numpy as np
6
+ import unittest
7
+ from unittest import mock
8
+
9
+ from detectron2.config import get_cfg
10
+ from detectron2.data import detection_utils
11
+ from detectron2.data import transforms as T
12
+ from detectron2.utils.logger import setup_logger
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class TestTransforms(unittest.TestCase):
18
+ def setUp(self):
19
+ setup_logger()
20
+
21
+ def test_apply_rotated_boxes(self):
22
+ np.random.seed(125)
23
+ cfg = get_cfg()
24
+ is_train = True
25
+ transform_gen = detection_utils.build_transform_gen(cfg, is_train)
26
+ image = np.random.rand(200, 300)
27
+ image, transforms = T.apply_transform_gens(transform_gen, image)
28
+ image_shape = image.shape[:2] # h, w
29
+ assert image_shape == (800, 1200)
30
+ annotation = {"bbox": [179, 97, 62, 40, -56]}
31
+
32
+ boxes = np.array([annotation["bbox"]], dtype=np.float64) # boxes.shape = (1, 5)
33
+ transformed_bbox = transforms.apply_rotated_box(boxes)[0]
34
+
35
+ expected_bbox = np.array([484, 388, 248, 160, 56], dtype=np.float64)
36
+ err_msg = "transformed_bbox = {}, expected {}".format(transformed_bbox, expected_bbox)
37
+ assert np.allclose(transformed_bbox, expected_bbox), err_msg
38
+
39
+ def test_apply_rotated_boxes_unequal_scaling_factor(self):
40
+ np.random.seed(125)
41
+ h, w = 400, 200
42
+ newh, neww = 800, 800
43
+ image = np.random.rand(h, w)
44
+ transform_gen = []
45
+ transform_gen.append(T.Resize(shape=(newh, neww)))
46
+ image, transforms = T.apply_transform_gens(transform_gen, image)
47
+ image_shape = image.shape[:2] # h, w
48
+ assert image_shape == (newh, neww)
49
+
50
+ boxes = np.array(
51
+ [
52
+ [150, 100, 40, 20, 0],
53
+ [150, 100, 40, 20, 30],
54
+ [150, 100, 40, 20, 90],
55
+ [150, 100, 40, 20, -90],
56
+ ],
57
+ dtype=np.float64,
58
+ )
59
+ transformed_boxes = transforms.apply_rotated_box(boxes)
60
+
61
+ expected_bboxes = np.array(
62
+ [
63
+ [600, 200, 160, 40, 0],
64
+ [600, 200, 144.22205102, 52.91502622, 49.10660535],
65
+ [600, 200, 80, 80, 90],
66
+ [600, 200, 80, 80, -90],
67
+ ],
68
+ dtype=np.float64,
69
+ )
70
+ err_msg = "transformed_boxes = {}, expected {}".format(transformed_boxes, expected_bboxes)
71
+ assert np.allclose(transformed_boxes, expected_bboxes), err_msg
72
+
73
+ def test_print_transform_gen(self):
74
+ t = T.RandomCrop("relative", (100, 100))
75
+ self.assertTrue(str(t) == "RandomCrop(crop_type='relative', crop_size=(100, 100))")
76
+
77
+ t = T.RandomFlip(prob=0.5)
78
+ self.assertTrue(str(t) == "RandomFlip(prob=0.5)")
79
+
80
+ t = T.RandomFlip()
81
+ self.assertTrue(str(t) == "RandomFlip()")
82
+
83
+ def test_random_apply_prob_out_of_range_check(self):
84
+ # GIVEN
85
+ test_probabilities = {0.0: True, 0.5: True, 1.0: True, -0.01: False, 1.01: False}
86
+
87
+ # WHEN
88
+ for given_probability, is_valid in test_probabilities.items():
89
+ # THEN
90
+ if not is_valid:
91
+ self.assertRaises(AssertionError, T.RandomApply, None, prob=given_probability)
92
+ else:
93
+ T.RandomApply(T.NoOpTransform(), prob=given_probability)
94
+
95
+ def test_random_apply_wrapping_transform_gen_probability_occured_evaluation(self):
96
+ # GIVEN
97
+ transform_mock = mock.MagicMock(name="MockTransform", spec=T.TransformGen)
98
+ image_mock = mock.MagicMock(name="MockImage")
99
+ random_apply = T.RandomApply(transform_mock, prob=0.001)
100
+
101
+ # WHEN
102
+ with mock.patch.object(random_apply, "_rand_range", return_value=0.0001):
103
+ transform = random_apply.get_transform(image_mock)
104
+
105
+ # THEN
106
+ transform_mock.get_transform.assert_called_once_with(image_mock)
107
+ self.assertIsNot(transform, transform_mock)
108
+
109
+ def test_random_apply_wrapping_std_transform_probability_occured_evaluation(self):
110
+ # GIVEN
111
+ transform_mock = mock.MagicMock(name="MockTransform", spec=T.Transform)
112
+ image_mock = mock.MagicMock(name="MockImage")
113
+ random_apply = T.RandomApply(transform_mock, prob=0.001)
114
+
115
+ # WHEN
116
+ with mock.patch.object(random_apply, "_rand_range", return_value=0.0001):
117
+ transform = random_apply.get_transform(image_mock)
118
+
119
+ # THEN
120
+ self.assertIs(transform, transform_mock)
121
+
122
+ def test_random_apply_probability_not_occured_evaluation(self):
123
+ # GIVEN
124
+ transform_mock = mock.MagicMock(name="MockTransform", spec=T.TransformGen)
125
+ image_mock = mock.MagicMock(name="MockImage")
126
+ random_apply = T.RandomApply(transform_mock, prob=0.001)
127
+
128
+ # WHEN
129
+ with mock.patch.object(random_apply, "_rand_range", return_value=0.9):
130
+ transform = random_apply.get_transform(image_mock)
131
+
132
+ # THEN
133
+ transform_mock.get_transform.assert_not_called()
134
+ self.assertIsInstance(transform, T.NoOpTransform)
preprocess/humanparsing/mhp_extension/detectron2/tests/layers/__init__.py ADDED
File without changes
preprocess/humanparsing/mhp_extension/detectron2/tests/layers/test_mask_ops.py ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
3
+
4
+ import contextlib
5
+ import io
6
+ import numpy as np
7
+ import unittest
8
+ from collections import defaultdict
9
+ import torch
10
+ import tqdm
11
+ from fvcore.common.benchmark import benchmark
12
+ from fvcore.common.file_io import PathManager
13
+ from pycocotools.coco import COCO
14
+ from tabulate import tabulate
15
+ from torch.nn import functional as F
16
+
17
+ from detectron2.data import MetadataCatalog
18
+ from detectron2.layers.mask_ops import (
19
+ pad_masks,
20
+ paste_mask_in_image_old,
21
+ paste_masks_in_image,
22
+ scale_boxes,
23
+ )
24
+ from detectron2.structures import BitMasks, Boxes, BoxMode, PolygonMasks
25
+ from detectron2.structures.masks import polygons_to_bitmask
26
+
27
+
28
+ def iou_between_full_image_bit_masks(a, b):
29
+ intersect = (a & b).sum()
30
+ union = (a | b).sum()
31
+ return intersect / union
32
+
33
+
34
+ def rasterize_polygons_with_grid_sample(full_image_bit_mask, box, mask_size, threshold=0.5):
35
+ x0, y0, x1, y1 = box[0], box[1], box[2], box[3]
36
+
37
+ img_h, img_w = full_image_bit_mask.shape
38
+
39
+ mask_y = np.arange(0.0, mask_size) + 0.5 # mask y sample coords in [0.5, mask_size - 0.5]
40
+ mask_x = np.arange(0.0, mask_size) + 0.5 # mask x sample coords in [0.5, mask_size - 0.5]
41
+ mask_y = mask_y / mask_size * (y1 - y0) + y0
42
+ mask_x = mask_x / mask_size * (x1 - x0) + x0
43
+
44
+ mask_x = (mask_x - 0.5) / (img_w - 1) * 2 + -1
45
+ mask_y = (mask_y - 0.5) / (img_h - 1) * 2 + -1
46
+ gy, gx = torch.meshgrid(torch.from_numpy(mask_y), torch.from_numpy(mask_x))
47
+ ind = torch.stack([gx, gy], dim=-1).to(dtype=torch.float32)
48
+
49
+ full_image_bit_mask = torch.from_numpy(full_image_bit_mask)
50
+ mask = F.grid_sample(
51
+ full_image_bit_mask[None, None, :, :].to(dtype=torch.float32),
52
+ ind[None, :, :, :],
53
+ align_corners=True,
54
+ )
55
+
56
+ return mask[0, 0] >= threshold
57
+
58
+
59
+ class TestMaskCropPaste(unittest.TestCase):
60
+ def setUp(self):
61
+ json_file = MetadataCatalog.get("coco_2017_val_100").json_file
62
+ if not PathManager.isfile(json_file):
63
+ raise unittest.SkipTest("{} not found".format(json_file))
64
+ with contextlib.redirect_stdout(io.StringIO()):
65
+ json_file = PathManager.get_local_path(json_file)
66
+ self.coco = COCO(json_file)
67
+
68
+ def test_crop_paste_consistency(self):
69
+ """
70
+ rasterize_polygons_within_box (used in training)
71
+ and
72
+ paste_masks_in_image (used in inference)
73
+ should be inverse operations to each other.
74
+
75
+ This function runs several implementation of the above two operations and prints
76
+ the reconstruction error.
77
+ """
78
+
79
+ anns = self.coco.loadAnns(self.coco.getAnnIds(iscrowd=False)) # avoid crowd annotations
80
+
81
+ selected_anns = anns[:100]
82
+
83
+ ious = []
84
+ for ann in tqdm.tqdm(selected_anns):
85
+ results = self.process_annotation(ann)
86
+ ious.append([k[2] for k in results])
87
+
88
+ ious = np.array(ious)
89
+ mean_ious = ious.mean(axis=0)
90
+ table = []
91
+ res_dic = defaultdict(dict)
92
+ for row, iou in zip(results, mean_ious):
93
+ table.append((row[0], row[1], iou))
94
+ res_dic[row[0]][row[1]] = iou
95
+ print(tabulate(table, headers=["rasterize", "paste", "iou"], tablefmt="simple"))
96
+ # assert that the reconstruction is good:
97
+ self.assertTrue(res_dic["polygon"]["aligned"] > 0.94)
98
+ self.assertTrue(res_dic["roialign"]["aligned"] > 0.95)
99
+
100
+ def process_annotation(self, ann, mask_side_len=28):
101
+ # Parse annotation data
102
+ img_info = self.coco.loadImgs(ids=[ann["image_id"]])[0]
103
+ height, width = img_info["height"], img_info["width"]
104
+ gt_polygons = [np.array(p, dtype=np.float64) for p in ann["segmentation"]]
105
+ gt_bbox = BoxMode.convert(ann["bbox"], BoxMode.XYWH_ABS, BoxMode.XYXY_ABS)
106
+ gt_bit_mask = polygons_to_bitmask(gt_polygons, height, width)
107
+
108
+ # Run rasterize ..
109
+ torch_gt_bbox = torch.tensor(gt_bbox).to(dtype=torch.float32).reshape(-1, 4)
110
+ box_bitmasks = {
111
+ "polygon": PolygonMasks([gt_polygons]).crop_and_resize(torch_gt_bbox, mask_side_len)[0],
112
+ "gridsample": rasterize_polygons_with_grid_sample(gt_bit_mask, gt_bbox, mask_side_len),
113
+ "roialign": BitMasks(torch.from_numpy(gt_bit_mask[None, :, :])).crop_and_resize(
114
+ torch_gt_bbox, mask_side_len
115
+ )[0],
116
+ }
117
+
118
+ # Run paste ..
119
+ results = defaultdict(dict)
120
+ for k, box_bitmask in box_bitmasks.items():
121
+ padded_bitmask, scale = pad_masks(box_bitmask[None, :, :], 1)
122
+ scaled_boxes = scale_boxes(torch_gt_bbox, scale)
123
+
124
+ r = results[k]
125
+ r["old"] = paste_mask_in_image_old(
126
+ padded_bitmask[0], scaled_boxes[0], height, width, threshold=0.5
127
+ )
128
+ r["aligned"] = paste_masks_in_image(
129
+ box_bitmask[None, :, :], Boxes(torch_gt_bbox), (height, width)
130
+ )[0]
131
+
132
+ table = []
133
+ for rasterize_method, r in results.items():
134
+ for paste_method, mask in r.items():
135
+ mask = np.asarray(mask)
136
+ iou = iou_between_full_image_bit_masks(gt_bit_mask.astype("uint8"), mask)
137
+ table.append((rasterize_method, paste_method, iou))
138
+ return table
139
+
140
+ def test_polygon_area(self):
141
+ # Draw polygon boxes
142
+ for d in [5.0, 10.0, 1000.0]:
143
+ polygon = PolygonMasks([[[0, 0, 0, d, d, d, d, 0]]])
144
+ area = polygon.area()[0]
145
+ target = d ** 2
146
+ self.assertEqual(area, target)
147
+
148
+ # Draw polygon triangles
149
+ for d in [5.0, 10.0, 1000.0]:
150
+ polygon = PolygonMasks([[[0, 0, 0, d, d, d]]])
151
+ area = polygon.area()[0]
152
+ target = d ** 2 / 2
153
+ self.assertEqual(area, target)
154
+
155
+
156
+ def benchmark_paste():
157
+ S = 800
158
+ H, W = image_shape = (S, S)
159
+ N = 64
160
+ torch.manual_seed(42)
161
+ masks = torch.rand(N, 28, 28)
162
+
163
+ center = torch.rand(N, 2) * 600 + 100
164
+ wh = torch.clamp(torch.randn(N, 2) * 40 + 200, min=50)
165
+ x0y0 = torch.clamp(center - wh * 0.5, min=0.0)
166
+ x1y1 = torch.clamp(center + wh * 0.5, max=S)
167
+ boxes = Boxes(torch.cat([x0y0, x1y1], axis=1))
168
+
169
+ def func(device, n=3):
170
+ m = masks.to(device=device)
171
+ b = boxes.to(device=device)
172
+
173
+ def bench():
174
+ for _ in range(n):
175
+ paste_masks_in_image(m, b, image_shape)
176
+ if device.type == "cuda":
177
+ torch.cuda.synchronize()
178
+
179
+ return bench
180
+
181
+ specs = [{"device": torch.device("cpu"), "n": 3}]
182
+ if torch.cuda.is_available():
183
+ specs.append({"device": torch.device("cuda"), "n": 3})
184
+
185
+ benchmark(func, "paste_masks", specs, num_iters=10, warmup_iters=2)
186
+
187
+
188
+ if __name__ == "__main__":
189
+ benchmark_paste()
190
+ unittest.main()
preprocess/humanparsing/mhp_extension/detectron2/tests/layers/test_nms_rotated.py ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+ from __future__ import absolute_import, division, print_function, unicode_literals
3
+ import numpy as np
4
+ import unittest
5
+ import torch
6
+ from torchvision import ops
7
+
8
+ from detectron2.layers import batched_nms, batched_nms_rotated, nms_rotated
9
+
10
+
11
+ def nms_edit_distance(keep1, keep2):
12
+ """
13
+ Compare the "keep" result of two nms call.
14
+ They are allowed to be different in terms of edit distance
15
+ due to floating point precision issues, e.g.,
16
+ if a box happen to have an IoU of 0.5 with another box,
17
+ one implentation may choose to keep it while another may discard it.
18
+ """
19
+ if torch.equal(keep1, keep2):
20
+ # they should be equal most of the time
21
+ return 0
22
+ keep1, keep2 = tuple(keep1.cpu()), tuple(keep2.cpu())
23
+ m, n = len(keep1), len(keep2)
24
+
25
+ # edit distance with DP
26
+ f = [np.arange(n + 1), np.arange(n + 1)]
27
+ for i in range(m):
28
+ cur_row = i % 2
29
+ other_row = (i + 1) % 2
30
+ f[other_row][0] = i + 1
31
+ for j in range(n):
32
+ f[other_row][j + 1] = (
33
+ f[cur_row][j]
34
+ if keep1[i] == keep2[j]
35
+ else min(min(f[cur_row][j], f[cur_row][j + 1]), f[other_row][j]) + 1
36
+ )
37
+ return f[m % 2][n]
38
+
39
+
40
+ class TestNMSRotated(unittest.TestCase):
41
+ def reference_horizontal_nms(self, boxes, scores, iou_threshold):
42
+ """
43
+ Args:
44
+ box_scores (N, 5): boxes in corner-form and probabilities.
45
+ (Note here 5 == 4 + 1, i.e., 4-dim horizontal box + 1-dim prob)
46
+ iou_threshold: intersection over union threshold.
47
+ Returns:
48
+ picked: a list of indexes of the kept boxes
49
+ """
50
+ picked = []
51
+ _, indexes = scores.sort(descending=True)
52
+ while len(indexes) > 0:
53
+ current = indexes[0]
54
+ picked.append(current.item())
55
+ if len(indexes) == 1:
56
+ break
57
+ current_box = boxes[current, :]
58
+ indexes = indexes[1:]
59
+ rest_boxes = boxes[indexes, :]
60
+ iou = ops.box_iou(rest_boxes, current_box.unsqueeze(0)).squeeze(1)
61
+ indexes = indexes[iou <= iou_threshold]
62
+
63
+ return torch.as_tensor(picked)
64
+
65
+ def _create_tensors(self, N):
66
+ boxes = torch.rand(N, 4) * 100
67
+ # Note: the implementation of this function in torchvision is:
68
+ # boxes[:, 2:] += torch.rand(N, 2) * 100
69
+ # but it does not guarantee non-negative widths/heights constraints:
70
+ # boxes[:, 2] >= boxes[:, 0] and boxes[:, 3] >= boxes[:, 1]:
71
+ boxes[:, 2:] += boxes[:, :2]
72
+ scores = torch.rand(N)
73
+ return boxes, scores
74
+
75
+ def test_batched_nms_rotated_0_degree_cpu(self):
76
+ N = 2000
77
+ num_classes = 50
78
+ boxes, scores = self._create_tensors(N)
79
+ idxs = torch.randint(0, num_classes, (N,))
80
+ rotated_boxes = torch.zeros(N, 5)
81
+ rotated_boxes[:, 0] = (boxes[:, 0] + boxes[:, 2]) / 2.0
82
+ rotated_boxes[:, 1] = (boxes[:, 1] + boxes[:, 3]) / 2.0
83
+ rotated_boxes[:, 2] = boxes[:, 2] - boxes[:, 0]
84
+ rotated_boxes[:, 3] = boxes[:, 3] - boxes[:, 1]
85
+ err_msg = "Rotated NMS with 0 degree is incompatible with horizontal NMS for IoU={}"
86
+ for iou in [0.2, 0.5, 0.8]:
87
+ backup = boxes.clone()
88
+ keep_ref = batched_nms(boxes, scores, idxs, iou)
89
+ assert torch.allclose(boxes, backup), "boxes modified by batched_nms"
90
+ backup = rotated_boxes.clone()
91
+ keep = batched_nms_rotated(rotated_boxes, scores, idxs, iou)
92
+ assert torch.allclose(
93
+ rotated_boxes, backup
94
+ ), "rotated_boxes modified by batched_nms_rotated"
95
+ self.assertLessEqual(nms_edit_distance(keep, keep_ref), 1, err_msg.format(iou))
96
+
97
+ @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available")
98
+ def test_batched_nms_rotated_0_degree_cuda(self):
99
+ N = 2000
100
+ num_classes = 50
101
+ boxes, scores = self._create_tensors(N)
102
+ idxs = torch.randint(0, num_classes, (N,))
103
+ rotated_boxes = torch.zeros(N, 5)
104
+ rotated_boxes[:, 0] = (boxes[:, 0] + boxes[:, 2]) / 2.0
105
+ rotated_boxes[:, 1] = (boxes[:, 1] + boxes[:, 3]) / 2.0
106
+ rotated_boxes[:, 2] = boxes[:, 2] - boxes[:, 0]
107
+ rotated_boxes[:, 3] = boxes[:, 3] - boxes[:, 1]
108
+ err_msg = "Rotated NMS with 0 degree is incompatible with horizontal NMS for IoU={}"
109
+ for iou in [0.2, 0.5, 0.8]:
110
+ backup = boxes.clone()
111
+ keep_ref = batched_nms(boxes.cuda(), scores.cuda(), idxs, iou)
112
+ self.assertTrue(torch.allclose(boxes, backup), "boxes modified by batched_nms")
113
+ backup = rotated_boxes.clone()
114
+ keep = batched_nms_rotated(rotated_boxes.cuda(), scores.cuda(), idxs, iou)
115
+ self.assertTrue(
116
+ torch.allclose(rotated_boxes, backup),
117
+ "rotated_boxes modified by batched_nms_rotated",
118
+ )
119
+ self.assertLessEqual(nms_edit_distance(keep, keep_ref), 1, err_msg.format(iou))
120
+
121
+ def test_nms_rotated_0_degree_cpu(self):
122
+ N = 1000
123
+ boxes, scores = self._create_tensors(N)
124
+ rotated_boxes = torch.zeros(N, 5)
125
+ rotated_boxes[:, 0] = (boxes[:, 0] + boxes[:, 2]) / 2.0
126
+ rotated_boxes[:, 1] = (boxes[:, 1] + boxes[:, 3]) / 2.0
127
+ rotated_boxes[:, 2] = boxes[:, 2] - boxes[:, 0]
128
+ rotated_boxes[:, 3] = boxes[:, 3] - boxes[:, 1]
129
+ err_msg = "Rotated NMS incompatible between CPU and reference implementation for IoU={}"
130
+ for iou in [0.5]:
131
+ keep_ref = self.reference_horizontal_nms(boxes, scores, iou)
132
+ keep = nms_rotated(rotated_boxes, scores, iou)
133
+ self.assertLessEqual(nms_edit_distance(keep, keep_ref), 1, err_msg.format(iou))
134
+
135
+ def test_nms_rotated_90_degrees_cpu(self):
136
+ N = 1000
137
+ boxes, scores = self._create_tensors(N)
138
+ rotated_boxes = torch.zeros(N, 5)
139
+ rotated_boxes[:, 0] = (boxes[:, 0] + boxes[:, 2]) / 2.0
140
+ rotated_boxes[:, 1] = (boxes[:, 1] + boxes[:, 3]) / 2.0
141
+ # Note for rotated_boxes[:, 2] and rotated_boxes[:, 3]:
142
+ # widths and heights are intentionally swapped here for 90 degrees case
143
+ # so that the reference horizontal nms could be used
144
+ rotated_boxes[:, 2] = boxes[:, 3] - boxes[:, 1]
145
+ rotated_boxes[:, 3] = boxes[:, 2] - boxes[:, 0]
146
+
147
+ rotated_boxes[:, 4] = torch.ones(N) * 90
148
+ err_msg = "Rotated NMS incompatible between CPU and reference implementation for IoU={}"
149
+ for iou in [0.2, 0.5, 0.8]:
150
+ keep_ref = self.reference_horizontal_nms(boxes, scores, iou)
151
+ keep = nms_rotated(rotated_boxes, scores, iou)
152
+ assert torch.equal(keep, keep_ref), err_msg.format(iou)
153
+
154
+ def test_nms_rotated_180_degrees_cpu(self):
155
+ N = 1000
156
+ boxes, scores = self._create_tensors(N)
157
+ rotated_boxes = torch.zeros(N, 5)
158
+ rotated_boxes[:, 0] = (boxes[:, 0] + boxes[:, 2]) / 2.0
159
+ rotated_boxes[:, 1] = (boxes[:, 1] + boxes[:, 3]) / 2.0
160
+ rotated_boxes[:, 2] = boxes[:, 2] - boxes[:, 0]
161
+ rotated_boxes[:, 3] = boxes[:, 3] - boxes[:, 1]
162
+ rotated_boxes[:, 4] = torch.ones(N) * 180
163
+ err_msg = "Rotated NMS incompatible between CPU and reference implementation for IoU={}"
164
+ for iou in [0.2, 0.5, 0.8]:
165
+ keep_ref = self.reference_horizontal_nms(boxes, scores, iou)
166
+ keep = nms_rotated(rotated_boxes, scores, iou)
167
+ assert torch.equal(keep, keep_ref), err_msg.format(iou)
168
+
169
+ @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available")
170
+ def test_nms_rotated_0_degree_cuda(self):
171
+ N = 1000
172
+ boxes, scores = self._create_tensors(N)
173
+ rotated_boxes = torch.zeros(N, 5)
174
+ rotated_boxes[:, 0] = (boxes[:, 0] + boxes[:, 2]) / 2.0
175
+ rotated_boxes[:, 1] = (boxes[:, 1] + boxes[:, 3]) / 2.0
176
+ rotated_boxes[:, 2] = boxes[:, 2] - boxes[:, 0]
177
+ rotated_boxes[:, 3] = boxes[:, 3] - boxes[:, 1]
178
+ err_msg = "Rotated NMS incompatible between CPU and CUDA for IoU={}"
179
+
180
+ for iou in [0.2, 0.5, 0.8]:
181
+ r_cpu = nms_rotated(rotated_boxes, scores, iou)
182
+ r_cuda = nms_rotated(rotated_boxes.cuda(), scores.cuda(), iou)
183
+
184
+ assert torch.equal(r_cpu, r_cuda.cpu()), err_msg.format(iou)
185
+
186
+
187
+ if __name__ == "__main__":
188
+ unittest.main()
preprocess/humanparsing/mhp_extension/detectron2/tests/layers/test_roi_align.py ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+ import numpy as np
3
+ import unittest
4
+ import cv2
5
+ import torch
6
+ from fvcore.common.benchmark import benchmark
7
+
8
+ from detectron2.layers.roi_align import ROIAlign
9
+
10
+
11
+ class ROIAlignTest(unittest.TestCase):
12
+ def test_forward_output(self):
13
+ input = np.arange(25).reshape(5, 5).astype("float32")
14
+ """
15
+ 0 1 2 3 4
16
+ 5 6 7 8 9
17
+ 10 11 12 13 14
18
+ 15 16 17 18 19
19
+ 20 21 22 23 24
20
+ """
21
+
22
+ output = self._simple_roialign(input, [1, 1, 3, 3], (4, 4), aligned=False)
23
+ output_correct = self._simple_roialign(input, [1, 1, 3, 3], (4, 4), aligned=True)
24
+
25
+ # without correction:
26
+ old_results = [
27
+ [7.5, 8, 8.5, 9],
28
+ [10, 10.5, 11, 11.5],
29
+ [12.5, 13, 13.5, 14],
30
+ [15, 15.5, 16, 16.5],
31
+ ]
32
+
33
+ # with 0.5 correction:
34
+ correct_results = [
35
+ [4.5, 5.0, 5.5, 6.0],
36
+ [7.0, 7.5, 8.0, 8.5],
37
+ [9.5, 10.0, 10.5, 11.0],
38
+ [12.0, 12.5, 13.0, 13.5],
39
+ ]
40
+ # This is an upsampled version of [[6, 7], [11, 12]]
41
+
42
+ self.assertTrue(np.allclose(output.flatten(), np.asarray(old_results).flatten()))
43
+ self.assertTrue(
44
+ np.allclose(output_correct.flatten(), np.asarray(correct_results).flatten())
45
+ )
46
+
47
+ # Also see similar issues in tensorflow at
48
+ # https://github.com/tensorflow/tensorflow/issues/26278
49
+
50
+ def test_resize(self):
51
+ H, W = 30, 30
52
+ input = np.random.rand(H, W).astype("float32") * 100
53
+ box = [10, 10, 20, 20]
54
+ output = self._simple_roialign(input, box, (5, 5), aligned=True)
55
+
56
+ input2x = cv2.resize(input, (W // 2, H // 2), interpolation=cv2.INTER_LINEAR)
57
+ box2x = [x / 2 for x in box]
58
+ output2x = self._simple_roialign(input2x, box2x, (5, 5), aligned=True)
59
+ diff = np.abs(output2x - output)
60
+ self.assertTrue(diff.max() < 1e-4)
61
+
62
+ def _simple_roialign(self, img, box, resolution, aligned=True):
63
+ """
64
+ RoiAlign with scale 1.0 and 0 sample ratio.
65
+ """
66
+ if isinstance(resolution, int):
67
+ resolution = (resolution, resolution)
68
+ op = ROIAlign(resolution, 1.0, 0, aligned=aligned)
69
+ input = torch.from_numpy(img[None, None, :, :].astype("float32"))
70
+
71
+ rois = [0] + list(box)
72
+ rois = torch.from_numpy(np.asarray(rois)[None, :].astype("float32"))
73
+ output = op.forward(input, rois)
74
+ if torch.cuda.is_available():
75
+ output_cuda = op.forward(input.cuda(), rois.cuda()).cpu()
76
+ self.assertTrue(torch.allclose(output, output_cuda))
77
+ return output[0, 0]
78
+
79
+ def _simple_roialign_with_grad(self, img, box, resolution, device):
80
+ if isinstance(resolution, int):
81
+ resolution = (resolution, resolution)
82
+
83
+ op = ROIAlign(resolution, 1.0, 0, aligned=True)
84
+ input = torch.from_numpy(img[None, None, :, :].astype("float32"))
85
+
86
+ rois = [0] + list(box)
87
+ rois = torch.from_numpy(np.asarray(rois)[None, :].astype("float32"))
88
+ input = input.to(device=device)
89
+ rois = rois.to(device=device)
90
+ input.requires_grad = True
91
+ output = op.forward(input, rois)
92
+ return input, output
93
+
94
+ def test_empty_box(self):
95
+ img = np.random.rand(5, 5)
96
+ box = [3, 4, 5, 4]
97
+ o = self._simple_roialign(img, box, 7)
98
+ self.assertTrue(o.shape == (7, 7))
99
+ self.assertTrue((o == 0).all())
100
+
101
+ for dev in ["cpu"] + ["cuda"] if torch.cuda.is_available() else []:
102
+ input, output = self._simple_roialign_with_grad(img, box, 7, torch.device(dev))
103
+ output.sum().backward()
104
+ self.assertTrue(torch.allclose(input.grad, torch.zeros_like(input)))
105
+
106
+ def test_empty_batch(self):
107
+ input = torch.zeros(0, 3, 10, 10, dtype=torch.float32)
108
+ rois = torch.zeros(0, 5, dtype=torch.float32)
109
+ op = ROIAlign((7, 7), 1.0, 0, aligned=True)
110
+ output = op.forward(input, rois)
111
+ self.assertTrue(output.shape == (0, 3, 7, 7))
112
+
113
+
114
+ def benchmark_roi_align():
115
+ from detectron2 import _C
116
+
117
+ def random_boxes(mean_box, stdev, N, maxsize):
118
+ ret = torch.rand(N, 4) * stdev + torch.tensor(mean_box, dtype=torch.float)
119
+ ret.clamp_(min=0, max=maxsize)
120
+ return ret
121
+
122
+ def func(N, C, H, W, nboxes_per_img):
123
+ input = torch.rand(N, C, H, W)
124
+ boxes = []
125
+ batch_idx = []
126
+ for k in range(N):
127
+ b = random_boxes([80, 80, 130, 130], 24, nboxes_per_img, H)
128
+ # try smaller boxes:
129
+ # b = random_boxes([100, 100, 110, 110], 4, nboxes_per_img, H)
130
+ boxes.append(b)
131
+ batch_idx.append(torch.zeros(nboxes_per_img, 1, dtype=torch.float32) + k)
132
+ boxes = torch.cat(boxes, axis=0)
133
+ batch_idx = torch.cat(batch_idx, axis=0)
134
+ boxes = torch.cat([batch_idx, boxes], axis=1)
135
+
136
+ input = input.cuda()
137
+ boxes = boxes.cuda()
138
+
139
+ def bench():
140
+ _C.roi_align_forward(input, boxes, 1.0, 7, 7, 0, True)
141
+ torch.cuda.synchronize()
142
+
143
+ return bench
144
+
145
+ args = [dict(N=2, C=512, H=256, W=256, nboxes_per_img=500)]
146
+ benchmark(func, "cuda_roialign", args, num_iters=20, warmup_iters=1)
147
+
148
+
149
+ if __name__ == "__main__":
150
+ if torch.cuda.is_available():
151
+ benchmark_roi_align()
152
+ unittest.main()
preprocess/humanparsing/mhp_extension/detectron2/tests/layers/test_roi_align_rotated.py ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+ import logging
3
+ import unittest
4
+ import cv2
5
+ import torch
6
+ from torch.autograd import Variable, gradcheck
7
+
8
+ from detectron2.layers.roi_align import ROIAlign
9
+ from detectron2.layers.roi_align_rotated import ROIAlignRotated
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ class ROIAlignRotatedTest(unittest.TestCase):
15
+ def _box_to_rotated_box(self, box, angle):
16
+ return [
17
+ (box[0] + box[2]) / 2.0,
18
+ (box[1] + box[3]) / 2.0,
19
+ box[2] - box[0],
20
+ box[3] - box[1],
21
+ angle,
22
+ ]
23
+
24
+ def _rot90(self, img, num):
25
+ num = num % 4 # note: -1 % 4 == 3
26
+ for _ in range(num):
27
+ img = img.transpose(0, 1).flip(0)
28
+ return img
29
+
30
+ def test_forward_output_0_90_180_270(self):
31
+ for i in range(4):
32
+ # i = 0, 1, 2, 3 corresponding to 0, 90, 180, 270 degrees
33
+ img = torch.arange(25, dtype=torch.float32).reshape(5, 5)
34
+ """
35
+ 0 1 2 3 4
36
+ 5 6 7 8 9
37
+ 10 11 12 13 14
38
+ 15 16 17 18 19
39
+ 20 21 22 23 24
40
+ """
41
+ box = [1, 1, 3, 3]
42
+ rotated_box = self._box_to_rotated_box(box=box, angle=90 * i)
43
+
44
+ result = self._simple_roi_align_rotated(img=img, box=rotated_box, resolution=(4, 4))
45
+
46
+ # Here's an explanation for 0 degree case:
47
+ # point 0 in the original input lies at [0.5, 0.5]
48
+ # (the center of bin [0, 1] x [0, 1])
49
+ # point 1 in the original input lies at [1.5, 0.5], etc.
50
+ # since the resolution is (4, 4) that divides [1, 3] x [1, 3]
51
+ # into 4 x 4 equal bins,
52
+ # the top-left bin is [1, 1.5] x [1, 1.5], and its center
53
+ # (1.25, 1.25) lies at the 3/4 position
54
+ # between point 0 and point 1, point 5 and point 6,
55
+ # point 0 and point 5, point 1 and point 6, so it can be calculated as
56
+ # 0.25*(0*0.25+1*0.75)+(5*0.25+6*0.75)*0.75 = 4.5
57
+ result_expected = torch.tensor(
58
+ [
59
+ [4.5, 5.0, 5.5, 6.0],
60
+ [7.0, 7.5, 8.0, 8.5],
61
+ [9.5, 10.0, 10.5, 11.0],
62
+ [12.0, 12.5, 13.0, 13.5],
63
+ ]
64
+ )
65
+ # This is also an upsampled version of [[6, 7], [11, 12]]
66
+
67
+ # When the box is rotated by 90 degrees CCW,
68
+ # the result would be rotated by 90 degrees CW, thus it's -i here
69
+ result_expected = self._rot90(result_expected, -i)
70
+
71
+ assert torch.allclose(result, result_expected)
72
+
73
+ def test_resize(self):
74
+ H, W = 30, 30
75
+ input = torch.rand(H, W) * 100
76
+ box = [10, 10, 20, 20]
77
+ rotated_box = self._box_to_rotated_box(box, angle=0)
78
+ output = self._simple_roi_align_rotated(img=input, box=rotated_box, resolution=(5, 5))
79
+
80
+ input2x = cv2.resize(input.numpy(), (W // 2, H // 2), interpolation=cv2.INTER_LINEAR)
81
+ input2x = torch.from_numpy(input2x)
82
+ box2x = [x / 2 for x in box]
83
+ rotated_box2x = self._box_to_rotated_box(box2x, angle=0)
84
+ output2x = self._simple_roi_align_rotated(img=input2x, box=rotated_box2x, resolution=(5, 5))
85
+ assert torch.allclose(output2x, output)
86
+
87
+ def _simple_roi_align_rotated(self, img, box, resolution):
88
+ """
89
+ RoiAlignRotated with scale 1.0 and 0 sample ratio.
90
+ """
91
+ op = ROIAlignRotated(output_size=resolution, spatial_scale=1.0, sampling_ratio=0)
92
+ input = img[None, None, :, :]
93
+
94
+ rois = [0] + list(box)
95
+ rois = torch.tensor(rois, dtype=torch.float32)[None, :]
96
+ result_cpu = op.forward(input, rois)
97
+ if torch.cuda.is_available():
98
+ result_cuda = op.forward(input.cuda(), rois.cuda())
99
+ assert torch.allclose(result_cpu, result_cuda.cpu())
100
+ return result_cpu[0, 0]
101
+
102
+ def test_empty_box(self):
103
+ img = torch.rand(5, 5)
104
+ out = self._simple_roi_align_rotated(img, [2, 3, 0, 0, 0], (7, 7))
105
+ self.assertTrue((out == 0).all())
106
+
107
+ def test_roi_align_rotated_gradcheck_cpu(self):
108
+ dtype = torch.float64
109
+ device = torch.device("cpu")
110
+ roi_align_rotated_op = ROIAlignRotated(
111
+ output_size=(5, 5), spatial_scale=0.5, sampling_ratio=1
112
+ ).to(dtype=dtype, device=device)
113
+ x = torch.rand(1, 1, 10, 10, dtype=dtype, device=device, requires_grad=True)
114
+ # roi format is (batch index, x_center, y_center, width, height, angle)
115
+ rois = torch.tensor(
116
+ [[0, 4.5, 4.5, 9, 9, 0], [0, 2, 7, 4, 4, 0], [0, 7, 7, 4, 4, 0]],
117
+ dtype=dtype,
118
+ device=device,
119
+ )
120
+
121
+ def func(input):
122
+ return roi_align_rotated_op(input, rois)
123
+
124
+ assert gradcheck(func, (x,)), "gradcheck failed for RoIAlignRotated CPU"
125
+ assert gradcheck(func, (x.transpose(2, 3),)), "gradcheck failed for RoIAlignRotated CPU"
126
+
127
+ @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available")
128
+ def test_roi_align_rotated_gradient_cuda(self):
129
+ """
130
+ Compute gradients for ROIAlignRotated with multiple bounding boxes on the GPU,
131
+ and compare the result with ROIAlign
132
+ """
133
+ # torch.manual_seed(123)
134
+ dtype = torch.float64
135
+ device = torch.device("cuda")
136
+ pool_h, pool_w = (5, 5)
137
+
138
+ roi_align = ROIAlign(output_size=(pool_h, pool_w), spatial_scale=1, sampling_ratio=2).to(
139
+ device=device
140
+ )
141
+
142
+ roi_align_rotated = ROIAlignRotated(
143
+ output_size=(pool_h, pool_w), spatial_scale=1, sampling_ratio=2
144
+ ).to(device=device)
145
+
146
+ x = torch.rand(1, 1, 10, 10, dtype=dtype, device=device, requires_grad=True)
147
+ # x_rotated = x.clone() won't work (will lead to grad_fun=CloneBackward)!
148
+ x_rotated = Variable(x.data.clone(), requires_grad=True)
149
+
150
+ # roi_rotated format is (batch index, x_center, y_center, width, height, angle)
151
+ rois_rotated = torch.tensor(
152
+ [[0, 4.5, 4.5, 9, 9, 0], [0, 2, 7, 4, 4, 0], [0, 7, 7, 4, 4, 0]],
153
+ dtype=dtype,
154
+ device=device,
155
+ )
156
+
157
+ y_rotated = roi_align_rotated(x_rotated, rois_rotated)
158
+ s_rotated = y_rotated.sum()
159
+ s_rotated.backward()
160
+
161
+ # roi format is (batch index, x1, y1, x2, y2)
162
+ rois = torch.tensor(
163
+ [[0, 0, 0, 9, 9], [0, 0, 5, 4, 9], [0, 5, 5, 9, 9]], dtype=dtype, device=device
164
+ )
165
+
166
+ y = roi_align(x, rois)
167
+ s = y.sum()
168
+ s.backward()
169
+
170
+ assert torch.allclose(
171
+ x.grad, x_rotated.grad
172
+ ), "gradients for ROIAlign and ROIAlignRotated mismatch on CUDA"
173
+
174
+
175
+ if __name__ == "__main__":
176
+ unittest.main()
preprocess/humanparsing/mhp_extension/detectron2/tests/modeling/__init__.py ADDED
File without changes
preprocess/humanparsing/mhp_extension/detectron2/tests/modeling/test_anchor_generator.py ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+ import logging
3
+ import unittest
4
+ import torch
5
+
6
+ from detectron2.config import get_cfg
7
+ from detectron2.layers import ShapeSpec
8
+ from detectron2.modeling.anchor_generator import DefaultAnchorGenerator, RotatedAnchorGenerator
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ class TestAnchorGenerator(unittest.TestCase):
14
+ def test_default_anchor_generator(self):
15
+ cfg = get_cfg()
16
+ cfg.MODEL.ANCHOR_GENERATOR.SIZES = [[32, 64]]
17
+ cfg.MODEL.ANCHOR_GENERATOR.ASPECT_RATIOS = [[0.25, 1, 4]]
18
+
19
+ anchor_generator = DefaultAnchorGenerator(cfg, [ShapeSpec(stride=4)])
20
+
21
+ # only the last two dimensions of features matter here
22
+ num_images = 2
23
+ features = {"stage3": torch.rand(num_images, 96, 1, 2)}
24
+ anchors = anchor_generator([features["stage3"]])
25
+ expected_anchor_tensor = torch.tensor(
26
+ [
27
+ [-32.0, -8.0, 32.0, 8.0],
28
+ [-16.0, -16.0, 16.0, 16.0],
29
+ [-8.0, -32.0, 8.0, 32.0],
30
+ [-64.0, -16.0, 64.0, 16.0],
31
+ [-32.0, -32.0, 32.0, 32.0],
32
+ [-16.0, -64.0, 16.0, 64.0],
33
+ [-28.0, -8.0, 36.0, 8.0], # -28.0 == -32.0 + STRIDE (4)
34
+ [-12.0, -16.0, 20.0, 16.0],
35
+ [-4.0, -32.0, 12.0, 32.0],
36
+ [-60.0, -16.0, 68.0, 16.0],
37
+ [-28.0, -32.0, 36.0, 32.0],
38
+ [-12.0, -64.0, 20.0, 64.0],
39
+ ]
40
+ )
41
+
42
+ assert torch.allclose(anchors[0].tensor, expected_anchor_tensor)
43
+
44
+ def test_default_anchor_generator_centered(self):
45
+ # test explicit args
46
+ anchor_generator = DefaultAnchorGenerator(
47
+ sizes=[32, 64], aspect_ratios=[0.25, 1, 4], strides=[4]
48
+ )
49
+
50
+ # only the last two dimensions of features matter here
51
+ num_images = 2
52
+ features = {"stage3": torch.rand(num_images, 96, 1, 2)}
53
+ expected_anchor_tensor = torch.tensor(
54
+ [
55
+ [-30.0, -6.0, 34.0, 10.0],
56
+ [-14.0, -14.0, 18.0, 18.0],
57
+ [-6.0, -30.0, 10.0, 34.0],
58
+ [-62.0, -14.0, 66.0, 18.0],
59
+ [-30.0, -30.0, 34.0, 34.0],
60
+ [-14.0, -62.0, 18.0, 66.0],
61
+ [-26.0, -6.0, 38.0, 10.0],
62
+ [-10.0, -14.0, 22.0, 18.0],
63
+ [-2.0, -30.0, 14.0, 34.0],
64
+ [-58.0, -14.0, 70.0, 18.0],
65
+ [-26.0, -30.0, 38.0, 34.0],
66
+ [-10.0, -62.0, 22.0, 66.0],
67
+ ]
68
+ )
69
+
70
+ anchors = anchor_generator([features["stage3"]])
71
+ assert torch.allclose(anchors[0].tensor, expected_anchor_tensor)
72
+
73
+ # doesn't work yet
74
+ # anchors = torch.jit.script(anchor_generator)([features["stage3"]])
75
+ # assert torch.allclose(anchors[0].tensor, expected_anchor_tensor)
76
+
77
+ def test_rrpn_anchor_generator(self):
78
+ cfg = get_cfg()
79
+ cfg.MODEL.ANCHOR_GENERATOR.SIZES = [[32, 64]]
80
+ cfg.MODEL.ANCHOR_GENERATOR.ASPECT_RATIOS = [[0.25, 1, 4]]
81
+ cfg.MODEL.ANCHOR_GENERATOR.ANGLES = [0, 45] # test single list[float]
82
+ anchor_generator = RotatedAnchorGenerator(cfg, [ShapeSpec(stride=4)])
83
+
84
+ # only the last two dimensions of features matter here
85
+ num_images = 2
86
+ features = {"stage3": torch.rand(num_images, 96, 1, 2)}
87
+ anchors = anchor_generator([features["stage3"]])
88
+ expected_anchor_tensor = torch.tensor(
89
+ [
90
+ [0.0, 0.0, 64.0, 16.0, 0.0],
91
+ [0.0, 0.0, 64.0, 16.0, 45.0],
92
+ [0.0, 0.0, 32.0, 32.0, 0.0],
93
+ [0.0, 0.0, 32.0, 32.0, 45.0],
94
+ [0.0, 0.0, 16.0, 64.0, 0.0],
95
+ [0.0, 0.0, 16.0, 64.0, 45.0],
96
+ [0.0, 0.0, 128.0, 32.0, 0.0],
97
+ [0.0, 0.0, 128.0, 32.0, 45.0],
98
+ [0.0, 0.0, 64.0, 64.0, 0.0],
99
+ [0.0, 0.0, 64.0, 64.0, 45.0],
100
+ [0.0, 0.0, 32.0, 128.0, 0.0],
101
+ [0.0, 0.0, 32.0, 128.0, 45.0],
102
+ [4.0, 0.0, 64.0, 16.0, 0.0], # 4.0 == 0.0 + STRIDE (4)
103
+ [4.0, 0.0, 64.0, 16.0, 45.0],
104
+ [4.0, 0.0, 32.0, 32.0, 0.0],
105
+ [4.0, 0.0, 32.0, 32.0, 45.0],
106
+ [4.0, 0.0, 16.0, 64.0, 0.0],
107
+ [4.0, 0.0, 16.0, 64.0, 45.0],
108
+ [4.0, 0.0, 128.0, 32.0, 0.0],
109
+ [4.0, 0.0, 128.0, 32.0, 45.0],
110
+ [4.0, 0.0, 64.0, 64.0, 0.0],
111
+ [4.0, 0.0, 64.0, 64.0, 45.0],
112
+ [4.0, 0.0, 32.0, 128.0, 0.0],
113
+ [4.0, 0.0, 32.0, 128.0, 45.0],
114
+ ]
115
+ )
116
+
117
+ assert torch.allclose(anchors[0].tensor, expected_anchor_tensor)
118
+
119
+
120
+ if __name__ == "__main__":
121
+ unittest.main()
preprocess/humanparsing/mhp_extension/detectron2/tests/modeling/test_box2box_transform.py ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+ import logging
3
+ import unittest
4
+ import torch
5
+
6
+ from detectron2.modeling.box_regression import Box2BoxTransform, Box2BoxTransformRotated
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ def random_boxes(mean_box, stdev, N):
12
+ return torch.rand(N, 4) * stdev + torch.tensor(mean_box, dtype=torch.float)
13
+
14
+
15
+ class TestBox2BoxTransform(unittest.TestCase):
16
+ def test_reconstruction(self):
17
+ weights = (5, 5, 10, 10)
18
+ b2b_tfm = Box2BoxTransform(weights=weights)
19
+ src_boxes = random_boxes([10, 10, 20, 20], 1, 10)
20
+ dst_boxes = random_boxes([10, 10, 20, 20], 1, 10)
21
+
22
+ devices = [torch.device("cpu")]
23
+ if torch.cuda.is_available():
24
+ devices.append(torch.device("cuda"))
25
+ for device in devices:
26
+ src_boxes = src_boxes.to(device=device)
27
+ dst_boxes = dst_boxes.to(device=device)
28
+ deltas = b2b_tfm.get_deltas(src_boxes, dst_boxes)
29
+ dst_boxes_reconstructed = b2b_tfm.apply_deltas(deltas, src_boxes)
30
+ assert torch.allclose(dst_boxes, dst_boxes_reconstructed)
31
+
32
+
33
+ def random_rotated_boxes(mean_box, std_length, std_angle, N):
34
+ return torch.cat(
35
+ [torch.rand(N, 4) * std_length, torch.rand(N, 1) * std_angle], dim=1
36
+ ) + torch.tensor(mean_box, dtype=torch.float)
37
+
38
+
39
+ class TestBox2BoxTransformRotated(unittest.TestCase):
40
+ def test_reconstruction(self):
41
+ weights = (5, 5, 10, 10, 1)
42
+ b2b_transform = Box2BoxTransformRotated(weights=weights)
43
+ src_boxes = random_rotated_boxes([10, 10, 20, 20, -30], 5, 60.0, 10)
44
+ dst_boxes = random_rotated_boxes([10, 10, 20, 20, -30], 5, 60.0, 10)
45
+
46
+ devices = [torch.device("cpu")]
47
+ if torch.cuda.is_available():
48
+ devices.append(torch.device("cuda"))
49
+ for device in devices:
50
+ src_boxes = src_boxes.to(device=device)
51
+ dst_boxes = dst_boxes.to(device=device)
52
+ deltas = b2b_transform.get_deltas(src_boxes, dst_boxes)
53
+ dst_boxes_reconstructed = b2b_transform.apply_deltas(deltas, src_boxes)
54
+ assert torch.allclose(dst_boxes[:, :4], dst_boxes_reconstructed[:, :4], atol=1e-5)
55
+ # angle difference has to be normalized
56
+ assert torch.allclose(
57
+ (dst_boxes[:, 4] - dst_boxes_reconstructed[:, 4] + 180.0) % 360.0 - 180.0,
58
+ torch.zeros_like(dst_boxes[:, 4]),
59
+ atol=1e-4,
60
+ )
61
+
62
+
63
+ if __name__ == "__main__":
64
+ unittest.main()
preprocess/humanparsing/mhp_extension/detectron2/tests/modeling/test_fast_rcnn.py ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+ import logging
3
+ import unittest
4
+ import torch
5
+
6
+ from detectron2.layers import ShapeSpec
7
+ from detectron2.modeling.box_regression import Box2BoxTransform, Box2BoxTransformRotated
8
+ from detectron2.modeling.roi_heads.fast_rcnn import FastRCNNOutputLayers
9
+ from detectron2.modeling.roi_heads.rotated_fast_rcnn import RotatedFastRCNNOutputLayers
10
+ from detectron2.structures import Boxes, Instances, RotatedBoxes
11
+ from detectron2.utils.events import EventStorage
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class FastRCNNTest(unittest.TestCase):
17
+ def test_fast_rcnn(self):
18
+ torch.manual_seed(132)
19
+
20
+ box_head_output_size = 8
21
+
22
+ box_predictor = FastRCNNOutputLayers(
23
+ ShapeSpec(channels=box_head_output_size),
24
+ box2box_transform=Box2BoxTransform(weights=(10, 10, 5, 5)),
25
+ num_classes=5,
26
+ )
27
+ feature_pooled = torch.rand(2, box_head_output_size)
28
+ predictions = box_predictor(feature_pooled)
29
+
30
+ proposal_boxes = torch.tensor([[0.8, 1.1, 3.2, 2.8], [2.3, 2.5, 7, 8]], dtype=torch.float32)
31
+ gt_boxes = torch.tensor([[1, 1, 3, 3], [2, 2, 6, 6]], dtype=torch.float32)
32
+ proposal = Instances((10, 10))
33
+ proposal.proposal_boxes = Boxes(proposal_boxes)
34
+ proposal.gt_boxes = Boxes(gt_boxes)
35
+ proposal.gt_classes = torch.tensor([1, 2])
36
+
37
+ with EventStorage(): # capture events in a new storage to discard them
38
+ losses = box_predictor.losses(predictions, [proposal])
39
+
40
+ expected_losses = {
41
+ "loss_cls": torch.tensor(1.7951188087),
42
+ "loss_box_reg": torch.tensor(4.0357131958),
43
+ }
44
+ for name in expected_losses.keys():
45
+ assert torch.allclose(losses[name], expected_losses[name])
46
+
47
+ def test_fast_rcnn_empty_batch(self, device="cpu"):
48
+ box_predictor = FastRCNNOutputLayers(
49
+ ShapeSpec(channels=10),
50
+ box2box_transform=Box2BoxTransform(weights=(10, 10, 5, 5)),
51
+ num_classes=8,
52
+ ).to(device=device)
53
+
54
+ logits = torch.randn(0, 100, requires_grad=True, device=device)
55
+ deltas = torch.randn(0, 4, requires_grad=True, device=device)
56
+ losses = box_predictor.losses([logits, deltas], [])
57
+ for value in losses.values():
58
+ self.assertTrue(torch.allclose(value, torch.zeros_like(value)))
59
+ sum(losses.values()).backward()
60
+ self.assertTrue(logits.grad is not None)
61
+ self.assertTrue(deltas.grad is not None)
62
+
63
+ predictions, _ = box_predictor.inference([logits, deltas], [])
64
+ self.assertEqual(len(predictions), 0)
65
+
66
+ @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available")
67
+ def test_fast_rcnn_empty_batch_cuda(self):
68
+ self.test_fast_rcnn_empty_batch(device=torch.device("cuda"))
69
+
70
+ def test_fast_rcnn_rotated(self):
71
+ torch.manual_seed(132)
72
+ box_head_output_size = 8
73
+
74
+ box_predictor = RotatedFastRCNNOutputLayers(
75
+ ShapeSpec(channels=box_head_output_size),
76
+ box2box_transform=Box2BoxTransformRotated(weights=(10, 10, 5, 5, 1)),
77
+ num_classes=5,
78
+ )
79
+ feature_pooled = torch.rand(2, box_head_output_size)
80
+ predictions = box_predictor(feature_pooled)
81
+ proposal_boxes = torch.tensor(
82
+ [[2, 1.95, 2.4, 1.7, 0], [4.65, 5.25, 4.7, 5.5, 0]], dtype=torch.float32
83
+ )
84
+ gt_boxes = torch.tensor([[2, 2, 2, 2, 0], [4, 4, 4, 4, 0]], dtype=torch.float32)
85
+ proposal = Instances((10, 10))
86
+ proposal.proposal_boxes = RotatedBoxes(proposal_boxes)
87
+ proposal.gt_boxes = RotatedBoxes(gt_boxes)
88
+ proposal.gt_classes = torch.tensor([1, 2])
89
+
90
+ with EventStorage(): # capture events in a new storage to discard them
91
+ losses = box_predictor.losses(predictions, [proposal])
92
+
93
+ # Note: the expected losses are slightly different even if
94
+ # the boxes are essentially the same as in the FastRCNNOutput test, because
95
+ # bbox_pred in FastRCNNOutputLayers have different Linear layers/initialization
96
+ # between the two cases.
97
+ expected_losses = {
98
+ "loss_cls": torch.tensor(1.7920907736),
99
+ "loss_box_reg": torch.tensor(4.0410838127),
100
+ }
101
+ for name in expected_losses.keys():
102
+ assert torch.allclose(losses[name], expected_losses[name])
103
+
104
+
105
+ if __name__ == "__main__":
106
+ unittest.main()
preprocess/humanparsing/mhp_extension/detectron2/tests/modeling/test_model_e2e.py ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
2
+
3
+
4
+ import unittest
5
+ import torch
6
+
7
+ import detectron2.model_zoo as model_zoo
8
+ from detectron2.config import get_cfg
9
+ from detectron2.modeling import build_model
10
+ from detectron2.structures import BitMasks, Boxes, ImageList, Instances
11
+ from detectron2.utils.events import EventStorage
12
+
13
+
14
+ def get_model_zoo(config_path):
15
+ """
16
+ Like model_zoo.get, but do not load any weights (even pretrained)
17
+ """
18
+ cfg_file = model_zoo.get_config_file(config_path)
19
+ cfg = get_cfg()
20
+ cfg.merge_from_file(cfg_file)
21
+ if not torch.cuda.is_available():
22
+ cfg.MODEL.DEVICE = "cpu"
23
+ return build_model(cfg)
24
+
25
+
26
+ def create_model_input(img, inst=None):
27
+ if inst is not None:
28
+ return {"image": img, "instances": inst}
29
+ else:
30
+ return {"image": img}
31
+
32
+
33
+ def get_empty_instance(h, w):
34
+ inst = Instances((h, w))
35
+ inst.gt_boxes = Boxes(torch.rand(0, 4))
36
+ inst.gt_classes = torch.tensor([]).to(dtype=torch.int64)
37
+ inst.gt_masks = BitMasks(torch.rand(0, h, w))
38
+ return inst
39
+
40
+
41
+ def get_regular_bitmask_instances(h, w):
42
+ inst = Instances((h, w))
43
+ inst.gt_boxes = Boxes(torch.rand(3, 4))
44
+ inst.gt_boxes.tensor[:, 2:] += inst.gt_boxes.tensor[:, :2]
45
+ inst.gt_classes = torch.tensor([3, 4, 5]).to(dtype=torch.int64)
46
+ inst.gt_masks = BitMasks((torch.rand(3, h, w) > 0.5))
47
+ return inst
48
+
49
+
50
+ class ModelE2ETest:
51
+ def setUp(self):
52
+ torch.manual_seed(43)
53
+ self.model = get_model_zoo(self.CONFIG_PATH)
54
+
55
+ def _test_eval(self, input_sizes):
56
+ inputs = [create_model_input(torch.rand(3, s[0], s[1])) for s in input_sizes]
57
+ self.model.eval()
58
+ self.model(inputs)
59
+
60
+ def _test_train(self, input_sizes, instances):
61
+ assert len(input_sizes) == len(instances)
62
+ inputs = [
63
+ create_model_input(torch.rand(3, s[0], s[1]), inst)
64
+ for s, inst in zip(input_sizes, instances)
65
+ ]
66
+ self.model.train()
67
+ with EventStorage():
68
+ losses = self.model(inputs)
69
+ sum(losses.values()).backward()
70
+ del losses
71
+
72
+ def _inf_tensor(self, *shape):
73
+ return 1.0 / torch.zeros(*shape, device=self.model.device)
74
+
75
+ def _nan_tensor(self, *shape):
76
+ return torch.zeros(*shape, device=self.model.device).fill_(float("nan"))
77
+
78
+ def test_empty_data(self):
79
+ instances = [get_empty_instance(200, 250), get_empty_instance(200, 249)]
80
+ self._test_eval([(200, 250), (200, 249)])
81
+ self._test_train([(200, 250), (200, 249)], instances)
82
+
83
+ @unittest.skipIf(not torch.cuda.is_available(), "CUDA unavailable")
84
+ def test_eval_tocpu(self):
85
+ model = get_model_zoo(self.CONFIG_PATH).cpu()
86
+ model.eval()
87
+ input_sizes = [(200, 250), (200, 249)]
88
+ inputs = [create_model_input(torch.rand(3, s[0], s[1])) for s in input_sizes]
89
+ model(inputs)
90
+
91
+
92
+ class MaskRCNNE2ETest(ModelE2ETest, unittest.TestCase):
93
+ CONFIG_PATH = "COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml"
94
+
95
+ def test_half_empty_data(self):
96
+ instances = [get_empty_instance(200, 250), get_regular_bitmask_instances(200, 249)]
97
+ self._test_train([(200, 250), (200, 249)], instances)
98
+
99
+ # This test is flaky because in some environment the output features are zero due to relu
100
+ # def test_rpn_inf_nan_data(self):
101
+ # self.model.eval()
102
+ # for tensor in [self._inf_tensor, self._nan_tensor]:
103
+ # images = ImageList(tensor(1, 3, 512, 512), [(510, 510)])
104
+ # features = {
105
+ # "p2": tensor(1, 256, 256, 256),
106
+ # "p3": tensor(1, 256, 128, 128),
107
+ # "p4": tensor(1, 256, 64, 64),
108
+ # "p5": tensor(1, 256, 32, 32),
109
+ # "p6": tensor(1, 256, 16, 16),
110
+ # }
111
+ # props, _ = self.model.proposal_generator(images, features)
112
+ # self.assertEqual(len(props[0]), 0)
113
+
114
+ def test_roiheads_inf_nan_data(self):
115
+ self.model.eval()
116
+ for tensor in [self._inf_tensor, self._nan_tensor]:
117
+ images = ImageList(tensor(1, 3, 512, 512), [(510, 510)])
118
+ features = {
119
+ "p2": tensor(1, 256, 256, 256),
120
+ "p3": tensor(1, 256, 128, 128),
121
+ "p4": tensor(1, 256, 64, 64),
122
+ "p5": tensor(1, 256, 32, 32),
123
+ "p6": tensor(1, 256, 16, 16),
124
+ }
125
+ props = [Instances((510, 510))]
126
+ props[0].proposal_boxes = Boxes([[10, 10, 20, 20]]).to(device=self.model.device)
127
+ props[0].objectness_logits = torch.tensor([1.0]).reshape(1, 1)
128
+ det, _ = self.model.roi_heads(images, features, props)
129
+ self.assertEqual(len(det[0]), 0)
130
+
131
+
132
+ class RetinaNetE2ETest(ModelE2ETest, unittest.TestCase):
133
+ CONFIG_PATH = "COCO-Detection/retinanet_R_50_FPN_1x.yaml"
134
+
135
+ def test_inf_nan_data(self):
136
+ self.model.eval()
137
+ self.model.score_threshold = -999999999
138
+ for tensor in [self._inf_tensor, self._nan_tensor]:
139
+ images = ImageList(tensor(1, 3, 512, 512), [(510, 510)])
140
+ features = [
141
+ tensor(1, 256, 128, 128),
142
+ tensor(1, 256, 64, 64),
143
+ tensor(1, 256, 32, 32),
144
+ tensor(1, 256, 16, 16),
145
+ tensor(1, 256, 8, 8),
146
+ ]
147
+ anchors = self.model.anchor_generator(features)
148
+ box_cls, box_delta = self.model.head(features)
149
+ box_cls = [tensor(*k.shape) for k in box_cls]
150
+ box_delta = [tensor(*k.shape) for k in box_delta]
151
+ det = self.model.inference(box_cls, box_delta, anchors, images.image_sizes)
152
+ # all predictions (if any) are infinite or nan
153
+ if len(det[0]):
154
+ self.assertTrue(torch.isfinite(det[0].pred_boxes.tensor).sum() == 0)
preprocess/humanparsing/mhp_extension/detectron2/tests/modeling/test_roi_heads.py ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+ import logging
3
+ import unittest
4
+ import torch
5
+
6
+ from detectron2.config import get_cfg
7
+ from detectron2.modeling.backbone import build_backbone
8
+ from detectron2.modeling.proposal_generator.build import build_proposal_generator
9
+ from detectron2.modeling.roi_heads import build_roi_heads
10
+ from detectron2.structures import Boxes, ImageList, Instances, RotatedBoxes
11
+ from detectron2.utils.events import EventStorage
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class ROIHeadsTest(unittest.TestCase):
17
+ def test_roi_heads(self):
18
+ torch.manual_seed(121)
19
+ cfg = get_cfg()
20
+ cfg.MODEL.ROI_HEADS.NAME = "StandardROIHeads"
21
+ cfg.MODEL.ROI_BOX_HEAD.NAME = "FastRCNNConvFCHead"
22
+ cfg.MODEL.ROI_BOX_HEAD.NUM_FC = 2
23
+ cfg.MODEL.ROI_BOX_HEAD.POOLER_TYPE = "ROIAlignV2"
24
+ cfg.MODEL.ROI_BOX_HEAD.BBOX_REG_WEIGHTS = (10, 10, 5, 5)
25
+ backbone = build_backbone(cfg)
26
+ num_images = 2
27
+ images_tensor = torch.rand(num_images, 20, 30)
28
+ image_sizes = [(10, 10), (20, 30)]
29
+ images = ImageList(images_tensor, image_sizes)
30
+ num_channels = 1024
31
+ features = {"res4": torch.rand(num_images, num_channels, 1, 2)}
32
+
33
+ image_shape = (15, 15)
34
+ gt_boxes0 = torch.tensor([[1, 1, 3, 3], [2, 2, 6, 6]], dtype=torch.float32)
35
+ gt_instance0 = Instances(image_shape)
36
+ gt_instance0.gt_boxes = Boxes(gt_boxes0)
37
+ gt_instance0.gt_classes = torch.tensor([2, 1])
38
+ gt_boxes1 = torch.tensor([[1, 5, 2, 8], [7, 3, 10, 5]], dtype=torch.float32)
39
+ gt_instance1 = Instances(image_shape)
40
+ gt_instance1.gt_boxes = Boxes(gt_boxes1)
41
+ gt_instance1.gt_classes = torch.tensor([1, 2])
42
+ gt_instances = [gt_instance0, gt_instance1]
43
+
44
+ proposal_generator = build_proposal_generator(cfg, backbone.output_shape())
45
+ roi_heads = build_roi_heads(cfg, backbone.output_shape())
46
+
47
+ with EventStorage(): # capture events in a new storage to discard them
48
+ proposals, proposal_losses = proposal_generator(images, features, gt_instances)
49
+ _, detector_losses = roi_heads(images, features, proposals, gt_instances)
50
+
51
+ expected_losses = {
52
+ "loss_cls": torch.tensor(4.4236516953),
53
+ "loss_box_reg": torch.tensor(0.0091214813),
54
+ }
55
+ for name in expected_losses.keys():
56
+ self.assertTrue(torch.allclose(detector_losses[name], expected_losses[name]))
57
+
58
+ def test_rroi_heads(self):
59
+ torch.manual_seed(121)
60
+ cfg = get_cfg()
61
+ cfg.MODEL.PROPOSAL_GENERATOR.NAME = "RRPN"
62
+ cfg.MODEL.ANCHOR_GENERATOR.NAME = "RotatedAnchorGenerator"
63
+ cfg.MODEL.ROI_HEADS.NAME = "RROIHeads"
64
+ cfg.MODEL.ROI_BOX_HEAD.NAME = "FastRCNNConvFCHead"
65
+ cfg.MODEL.ROI_BOX_HEAD.NUM_FC = 2
66
+ cfg.MODEL.RPN.BBOX_REG_WEIGHTS = (1, 1, 1, 1, 1)
67
+ cfg.MODEL.RPN.HEAD_NAME = "StandardRPNHead"
68
+ cfg.MODEL.ROI_BOX_HEAD.POOLER_TYPE = "ROIAlignRotated"
69
+ cfg.MODEL.ROI_BOX_HEAD.BBOX_REG_WEIGHTS = (10, 10, 5, 5, 1)
70
+ backbone = build_backbone(cfg)
71
+ num_images = 2
72
+ images_tensor = torch.rand(num_images, 20, 30)
73
+ image_sizes = [(10, 10), (20, 30)]
74
+ images = ImageList(images_tensor, image_sizes)
75
+ num_channels = 1024
76
+ features = {"res4": torch.rand(num_images, num_channels, 1, 2)}
77
+
78
+ image_shape = (15, 15)
79
+ gt_boxes0 = torch.tensor([[2, 2, 2, 2, 30], [4, 4, 4, 4, 0]], dtype=torch.float32)
80
+ gt_instance0 = Instances(image_shape)
81
+ gt_instance0.gt_boxes = RotatedBoxes(gt_boxes0)
82
+ gt_instance0.gt_classes = torch.tensor([2, 1])
83
+ gt_boxes1 = torch.tensor([[1.5, 5.5, 1, 3, 0], [8.5, 4, 3, 2, -50]], dtype=torch.float32)
84
+ gt_instance1 = Instances(image_shape)
85
+ gt_instance1.gt_boxes = RotatedBoxes(gt_boxes1)
86
+ gt_instance1.gt_classes = torch.tensor([1, 2])
87
+ gt_instances = [gt_instance0, gt_instance1]
88
+
89
+ proposal_generator = build_proposal_generator(cfg, backbone.output_shape())
90
+ roi_heads = build_roi_heads(cfg, backbone.output_shape())
91
+
92
+ with EventStorage(): # capture events in a new storage to discard them
93
+ proposals, proposal_losses = proposal_generator(images, features, gt_instances)
94
+ _, detector_losses = roi_heads(images, features, proposals, gt_instances)
95
+
96
+ expected_losses = {
97
+ "loss_cls": torch.tensor(4.381618499755859),
98
+ "loss_box_reg": torch.tensor(0.0011829272843897343),
99
+ }
100
+ for name in expected_losses.keys():
101
+ err_msg = "detector_losses[{}] = {}, expected losses = {}".format(
102
+ name, detector_losses[name], expected_losses[name]
103
+ )
104
+ self.assertTrue(torch.allclose(detector_losses[name], expected_losses[name]), err_msg)
105
+
106
+
107
+ if __name__ == "__main__":
108
+ unittest.main()
preprocess/humanparsing/mhp_extension/detectron2/tests/modeling/test_roi_pooler.py ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+ import logging
3
+ import unittest
4
+ import torch
5
+
6
+ from detectron2.modeling.poolers import ROIPooler
7
+ from detectron2.structures import Boxes, RotatedBoxes
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ class TestROIPooler(unittest.TestCase):
13
+ def _rand_boxes(self, num_boxes, x_max, y_max):
14
+ coords = torch.rand(num_boxes, 4)
15
+ coords[:, 0] *= x_max
16
+ coords[:, 1] *= y_max
17
+ coords[:, 2] *= x_max
18
+ coords[:, 3] *= y_max
19
+ boxes = torch.zeros(num_boxes, 4)
20
+ boxes[:, 0] = torch.min(coords[:, 0], coords[:, 2])
21
+ boxes[:, 1] = torch.min(coords[:, 1], coords[:, 3])
22
+ boxes[:, 2] = torch.max(coords[:, 0], coords[:, 2])
23
+ boxes[:, 3] = torch.max(coords[:, 1], coords[:, 3])
24
+ return boxes
25
+
26
+ def _test_roialignv2_roialignrotated_match(self, device):
27
+ pooler_resolution = 14
28
+ canonical_level = 4
29
+ canonical_scale_factor = 2 ** canonical_level
30
+ pooler_scales = (1.0 / canonical_scale_factor,)
31
+ sampling_ratio = 0
32
+
33
+ N, C, H, W = 2, 4, 10, 8
34
+ N_rois = 10
35
+ std = 11
36
+ mean = 0
37
+ feature = (torch.rand(N, C, H, W) - 0.5) * 2 * std + mean
38
+
39
+ features = [feature.to(device)]
40
+
41
+ rois = []
42
+ rois_rotated = []
43
+ for _ in range(N):
44
+ boxes = self._rand_boxes(
45
+ num_boxes=N_rois, x_max=W * canonical_scale_factor, y_max=H * canonical_scale_factor
46
+ )
47
+
48
+ rotated_boxes = torch.zeros(N_rois, 5)
49
+ rotated_boxes[:, 0] = (boxes[:, 0] + boxes[:, 2]) / 2.0
50
+ rotated_boxes[:, 1] = (boxes[:, 1] + boxes[:, 3]) / 2.0
51
+ rotated_boxes[:, 2] = boxes[:, 2] - boxes[:, 0]
52
+ rotated_boxes[:, 3] = boxes[:, 3] - boxes[:, 1]
53
+ rois.append(Boxes(boxes).to(device))
54
+ rois_rotated.append(RotatedBoxes(rotated_boxes).to(device))
55
+
56
+ roialignv2_pooler = ROIPooler(
57
+ output_size=pooler_resolution,
58
+ scales=pooler_scales,
59
+ sampling_ratio=sampling_ratio,
60
+ pooler_type="ROIAlignV2",
61
+ )
62
+
63
+ roialignv2_out = roialignv2_pooler(features, rois)
64
+
65
+ roialignrotated_pooler = ROIPooler(
66
+ output_size=pooler_resolution,
67
+ scales=pooler_scales,
68
+ sampling_ratio=sampling_ratio,
69
+ pooler_type="ROIAlignRotated",
70
+ )
71
+
72
+ roialignrotated_out = roialignrotated_pooler(features, rois_rotated)
73
+
74
+ self.assertTrue(torch.allclose(roialignv2_out, roialignrotated_out, atol=1e-4))
75
+
76
+ def test_roialignv2_roialignrotated_match_cpu(self):
77
+ self._test_roialignv2_roialignrotated_match(device="cpu")
78
+
79
+ @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available")
80
+ def test_roialignv2_roialignrotated_match_cuda(self):
81
+ self._test_roialignv2_roialignrotated_match(device="cuda")
82
+
83
+
84
+ if __name__ == "__main__":
85
+ unittest.main()
preprocess/humanparsing/mhp_extension/detectron2/tests/modeling/test_rpn.py ADDED
@@ -0,0 +1,234 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+ import logging
3
+ import unittest
4
+ import torch
5
+
6
+ from detectron2.config import get_cfg
7
+ from detectron2.modeling.backbone import build_backbone
8
+ from detectron2.modeling.proposal_generator.build import build_proposal_generator
9
+ from detectron2.modeling.proposal_generator.rpn_outputs import find_top_rpn_proposals
10
+ from detectron2.structures import Boxes, ImageList, Instances, RotatedBoxes
11
+ from detectron2.utils.events import EventStorage
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class RPNTest(unittest.TestCase):
17
+ def test_rpn(self):
18
+ torch.manual_seed(121)
19
+ cfg = get_cfg()
20
+ cfg.MODEL.PROPOSAL_GENERATOR.NAME = "RPN"
21
+ cfg.MODEL.ANCHOR_GENERATOR.NAME = "DefaultAnchorGenerator"
22
+ cfg.MODEL.RPN.BBOX_REG_WEIGHTS = (1, 1, 1, 1)
23
+ backbone = build_backbone(cfg)
24
+ proposal_generator = build_proposal_generator(cfg, backbone.output_shape())
25
+ num_images = 2
26
+ images_tensor = torch.rand(num_images, 20, 30)
27
+ image_sizes = [(10, 10), (20, 30)]
28
+ images = ImageList(images_tensor, image_sizes)
29
+ image_shape = (15, 15)
30
+ num_channels = 1024
31
+ features = {"res4": torch.rand(num_images, num_channels, 1, 2)}
32
+ gt_boxes = torch.tensor([[1, 1, 3, 3], [2, 2, 6, 6]], dtype=torch.float32)
33
+ gt_instances = Instances(image_shape)
34
+ gt_instances.gt_boxes = Boxes(gt_boxes)
35
+ with EventStorage(): # capture events in a new storage to discard them
36
+ proposals, proposal_losses = proposal_generator(
37
+ images, features, [gt_instances[0], gt_instances[1]]
38
+ )
39
+
40
+ expected_losses = {
41
+ "loss_rpn_cls": torch.tensor(0.0804563984),
42
+ "loss_rpn_loc": torch.tensor(0.0990132466),
43
+ }
44
+ for name in expected_losses.keys():
45
+ err_msg = "proposal_losses[{}] = {}, expected losses = {}".format(
46
+ name, proposal_losses[name], expected_losses[name]
47
+ )
48
+ self.assertTrue(torch.allclose(proposal_losses[name], expected_losses[name]), err_msg)
49
+
50
+ expected_proposal_boxes = [
51
+ Boxes(torch.tensor([[0, 0, 10, 10], [7.3365392685, 0, 10, 10]])),
52
+ Boxes(
53
+ torch.tensor(
54
+ [
55
+ [0, 0, 30, 20],
56
+ [0, 0, 16.7862777710, 13.1362524033],
57
+ [0, 0, 30, 13.3173446655],
58
+ [0, 0, 10.8602609634, 20],
59
+ [7.7165775299, 0, 27.3875980377, 20],
60
+ ]
61
+ )
62
+ ),
63
+ ]
64
+
65
+ expected_objectness_logits = [
66
+ torch.tensor([0.1225359365, -0.0133192837]),
67
+ torch.tensor([0.1415634006, 0.0989848152, 0.0565387346, -0.0072308783, -0.0428492837]),
68
+ ]
69
+
70
+ for proposal, expected_proposal_box, im_size, expected_objectness_logit in zip(
71
+ proposals, expected_proposal_boxes, image_sizes, expected_objectness_logits
72
+ ):
73
+ self.assertEqual(len(proposal), len(expected_proposal_box))
74
+ self.assertEqual(proposal.image_size, im_size)
75
+ self.assertTrue(
76
+ torch.allclose(proposal.proposal_boxes.tensor, expected_proposal_box.tensor)
77
+ )
78
+ self.assertTrue(torch.allclose(proposal.objectness_logits, expected_objectness_logit))
79
+
80
+ def test_rrpn(self):
81
+ torch.manual_seed(121)
82
+ cfg = get_cfg()
83
+ cfg.MODEL.PROPOSAL_GENERATOR.NAME = "RRPN"
84
+ cfg.MODEL.ANCHOR_GENERATOR.NAME = "RotatedAnchorGenerator"
85
+ cfg.MODEL.ANCHOR_GENERATOR.SIZES = [[32, 64]]
86
+ cfg.MODEL.ANCHOR_GENERATOR.ASPECT_RATIOS = [[0.25, 1]]
87
+ cfg.MODEL.ANCHOR_GENERATOR.ANGLES = [[0, 60]]
88
+ cfg.MODEL.RPN.BBOX_REG_WEIGHTS = (1, 1, 1, 1, 1)
89
+ cfg.MODEL.RPN.HEAD_NAME = "StandardRPNHead"
90
+ backbone = build_backbone(cfg)
91
+ proposal_generator = build_proposal_generator(cfg, backbone.output_shape())
92
+ num_images = 2
93
+ images_tensor = torch.rand(num_images, 20, 30)
94
+ image_sizes = [(10, 10), (20, 30)]
95
+ images = ImageList(images_tensor, image_sizes)
96
+ image_shape = (15, 15)
97
+ num_channels = 1024
98
+ features = {"res4": torch.rand(num_images, num_channels, 1, 2)}
99
+ gt_boxes = torch.tensor([[2, 2, 2, 2, 0], [4, 4, 4, 4, 0]], dtype=torch.float32)
100
+ gt_instances = Instances(image_shape)
101
+ gt_instances.gt_boxes = RotatedBoxes(gt_boxes)
102
+ with EventStorage(): # capture events in a new storage to discard them
103
+ proposals, proposal_losses = proposal_generator(
104
+ images, features, [gt_instances[0], gt_instances[1]]
105
+ )
106
+
107
+ expected_losses = {
108
+ "loss_rpn_cls": torch.tensor(0.043263837695121765),
109
+ "loss_rpn_loc": torch.tensor(0.14432406425476074),
110
+ }
111
+ for name in expected_losses.keys():
112
+ err_msg = "proposal_losses[{}] = {}, expected losses = {}".format(
113
+ name, proposal_losses[name], expected_losses[name]
114
+ )
115
+ self.assertTrue(torch.allclose(proposal_losses[name], expected_losses[name]), err_msg)
116
+
117
+ expected_proposal_boxes = [
118
+ RotatedBoxes(
119
+ torch.tensor(
120
+ [
121
+ [0.60189795, 1.24095452, 61.98131943, 18.03621292, -4.07244873],
122
+ [15.64940453, 1.69624567, 59.59749603, 16.34339333, 2.62692475],
123
+ [-3.02982378, -2.69752932, 67.90952301, 59.62455750, 59.97010040],
124
+ [16.71863365, 1.98309708, 35.61507797, 32.81484985, 62.92267227],
125
+ [0.49432933, -7.92979717, 67.77606201, 62.93098450, -1.85656738],
126
+ [8.00880814, 1.36017394, 121.81007385, 32.74150467, 50.44297409],
127
+ [16.44299889, -4.82221127, 63.39775848, 61.22503662, 54.12270737],
128
+ [5.00000000, 5.00000000, 10.00000000, 10.00000000, -0.76943970],
129
+ [17.64130402, -0.98095351, 61.40377808, 16.28918839, 55.53118134],
130
+ [0.13016054, 4.60568953, 35.80157471, 32.30180359, 62.52872086],
131
+ [-4.26460743, 0.39604485, 124.30079651, 31.84611320, -1.58203125],
132
+ [7.52815342, -0.91636634, 62.39784622, 15.45565224, 60.79549789],
133
+ ]
134
+ )
135
+ ),
136
+ RotatedBoxes(
137
+ torch.tensor(
138
+ [
139
+ [0.07734215, 0.81635046, 65.33510590, 17.34688377, -1.51821899],
140
+ [-3.41833067, -3.11320257, 64.17595673, 60.55617905, 58.27033234],
141
+ [20.67383385, -6.16561556, 63.60531998, 62.52315903, 54.85546494],
142
+ [15.00000000, 10.00000000, 30.00000000, 20.00000000, -0.18218994],
143
+ [9.22646523, -6.84775209, 62.09895706, 65.46472931, -2.74307251],
144
+ [15.00000000, 4.93451595, 30.00000000, 9.86903191, -0.60272217],
145
+ [8.88342094, 2.65560246, 120.95362854, 32.45022202, 55.75970078],
146
+ [16.39088631, 2.33887148, 34.78761292, 35.61492920, 60.81977463],
147
+ [9.78298569, 10.00000000, 19.56597137, 20.00000000, -0.86660767],
148
+ [1.28576660, 5.49873352, 34.93610382, 33.22600174, 60.51599884],
149
+ [17.58912468, -1.63270092, 62.96052551, 16.45713997, 52.91245270],
150
+ [5.64749718, -1.90428460, 62.37649155, 16.19474792, 61.09543991],
151
+ [0.82255805, 2.34931135, 118.83985901, 32.83671188, 56.50753784],
152
+ [-5.33874989, 1.64404404, 125.28501892, 33.35424042, -2.80731201],
153
+ ]
154
+ )
155
+ ),
156
+ ]
157
+
158
+ expected_objectness_logits = [
159
+ torch.tensor(
160
+ [
161
+ 0.10111768,
162
+ 0.09112845,
163
+ 0.08466332,
164
+ 0.07589971,
165
+ 0.06650183,
166
+ 0.06350251,
167
+ 0.04299347,
168
+ 0.01864817,
169
+ 0.00986163,
170
+ 0.00078543,
171
+ -0.04573630,
172
+ -0.04799230,
173
+ ]
174
+ ),
175
+ torch.tensor(
176
+ [
177
+ 0.11373727,
178
+ 0.09377633,
179
+ 0.05281663,
180
+ 0.05143715,
181
+ 0.04040275,
182
+ 0.03250912,
183
+ 0.01307789,
184
+ 0.01177734,
185
+ 0.00038105,
186
+ -0.00540255,
187
+ -0.01194804,
188
+ -0.01461012,
189
+ -0.03061717,
190
+ -0.03599222,
191
+ ]
192
+ ),
193
+ ]
194
+
195
+ torch.set_printoptions(precision=8, sci_mode=False)
196
+
197
+ for proposal, expected_proposal_box, im_size, expected_objectness_logit in zip(
198
+ proposals, expected_proposal_boxes, image_sizes, expected_objectness_logits
199
+ ):
200
+ self.assertEqual(len(proposal), len(expected_proposal_box))
201
+ self.assertEqual(proposal.image_size, im_size)
202
+ # It seems that there's some randomness in the result across different machines:
203
+ # This test can be run on a local machine for 100 times with exactly the same result,
204
+ # However, a different machine might produce slightly different results,
205
+ # thus the atol here.
206
+ err_msg = "computed proposal boxes = {}, expected {}".format(
207
+ proposal.proposal_boxes.tensor, expected_proposal_box.tensor
208
+ )
209
+ self.assertTrue(
210
+ torch.allclose(
211
+ proposal.proposal_boxes.tensor, expected_proposal_box.tensor, atol=1e-5
212
+ ),
213
+ err_msg,
214
+ )
215
+
216
+ err_msg = "computed objectness logits = {}, expected {}".format(
217
+ proposal.objectness_logits, expected_objectness_logit
218
+ )
219
+ self.assertTrue(
220
+ torch.allclose(proposal.objectness_logits, expected_objectness_logit, atol=1e-5),
221
+ err_msg,
222
+ )
223
+
224
+ def test_rpn_proposals_inf(self):
225
+ N, Hi, Wi, A = 3, 3, 3, 3
226
+ proposals = [torch.rand(N, Hi * Wi * A, 4)]
227
+ pred_logits = [torch.rand(N, Hi * Wi * A)]
228
+ pred_logits[0][1][3:5].fill_(float("inf"))
229
+ images = ImageList.from_tensors([torch.rand(3, 10, 10)] * 3)
230
+ find_top_rpn_proposals(proposals, pred_logits, images, 0.5, 1000, 1000, 0, False)
231
+
232
+
233
+ if __name__ == "__main__":
234
+ unittest.main()
preprocess/humanparsing/mhp_extension/detectron2/tests/structures/__init__.py ADDED
File without changes
preprocess/humanparsing/mhp_extension/detectron2/tests/structures/test_boxes.py ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+ import json
3
+ import math
4
+ import numpy as np
5
+ import unittest
6
+ import torch
7
+
8
+ from detectron2.structures import Boxes, BoxMode, pairwise_iou
9
+
10
+
11
+ class TestBoxMode(unittest.TestCase):
12
+ def _convert_xy_to_wh(self, x):
13
+ return BoxMode.convert(x, BoxMode.XYXY_ABS, BoxMode.XYWH_ABS)
14
+
15
+ def _convert_xywha_to_xyxy(self, x):
16
+ return BoxMode.convert(x, BoxMode.XYWHA_ABS, BoxMode.XYXY_ABS)
17
+
18
+ def _convert_xywh_to_xywha(self, x):
19
+ return BoxMode.convert(x, BoxMode.XYWH_ABS, BoxMode.XYWHA_ABS)
20
+
21
+ def test_box_convert_list(self):
22
+ for tp in [list, tuple]:
23
+ box = tp([5.0, 5.0, 10.0, 10.0])
24
+ output = self._convert_xy_to_wh(box)
25
+ self.assertIsInstance(output, tp)
26
+ self.assertIsInstance(output[0], float)
27
+ self.assertEqual(output, tp([5.0, 5.0, 5.0, 5.0]))
28
+
29
+ with self.assertRaises(Exception):
30
+ self._convert_xy_to_wh([box])
31
+
32
+ def test_box_convert_array(self):
33
+ box = np.asarray([[5, 5, 10, 10], [1, 1, 2, 3]])
34
+ output = self._convert_xy_to_wh(box)
35
+ self.assertEqual(output.dtype, box.dtype)
36
+ self.assertEqual(output.shape, box.shape)
37
+ self.assertTrue((output[0] == [5, 5, 5, 5]).all())
38
+ self.assertTrue((output[1] == [1, 1, 1, 2]).all())
39
+
40
+ def test_box_convert_cpu_tensor(self):
41
+ box = torch.tensor([[5, 5, 10, 10], [1, 1, 2, 3]])
42
+ output = self._convert_xy_to_wh(box)
43
+ self.assertEqual(output.dtype, box.dtype)
44
+ self.assertEqual(output.shape, box.shape)
45
+ output = output.numpy()
46
+ self.assertTrue((output[0] == [5, 5, 5, 5]).all())
47
+ self.assertTrue((output[1] == [1, 1, 1, 2]).all())
48
+
49
+ @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available")
50
+ def test_box_convert_cuda_tensor(self):
51
+ box = torch.tensor([[5, 5, 10, 10], [1, 1, 2, 3]]).cuda()
52
+ output = self._convert_xy_to_wh(box)
53
+ self.assertEqual(output.dtype, box.dtype)
54
+ self.assertEqual(output.shape, box.shape)
55
+ self.assertEqual(output.device, box.device)
56
+ output = output.cpu().numpy()
57
+ self.assertTrue((output[0] == [5, 5, 5, 5]).all())
58
+ self.assertTrue((output[1] == [1, 1, 1, 2]).all())
59
+
60
+ def test_box_convert_xywha_to_xyxy_list(self):
61
+ for tp in [list, tuple]:
62
+ box = tp([50, 50, 30, 20, 0])
63
+ output = self._convert_xywha_to_xyxy(box)
64
+ self.assertIsInstance(output, tp)
65
+ self.assertEqual(output, tp([35, 40, 65, 60]))
66
+
67
+ with self.assertRaises(Exception):
68
+ self._convert_xywha_to_xyxy([box])
69
+
70
+ def test_box_convert_xywha_to_xyxy_array(self):
71
+ for dtype in [np.float64, np.float32]:
72
+ box = np.asarray(
73
+ [
74
+ [50, 50, 30, 20, 0],
75
+ [50, 50, 30, 20, 90],
76
+ [1, 1, math.sqrt(2), math.sqrt(2), -45],
77
+ ],
78
+ dtype=dtype,
79
+ )
80
+ output = self._convert_xywha_to_xyxy(box)
81
+ self.assertEqual(output.dtype, box.dtype)
82
+ expected = np.asarray([[35, 40, 65, 60], [40, 35, 60, 65], [0, 0, 2, 2]], dtype=dtype)
83
+ self.assertTrue(np.allclose(output, expected, atol=1e-6), "output={}".format(output))
84
+
85
+ def test_box_convert_xywha_to_xyxy_tensor(self):
86
+ for dtype in [torch.float32, torch.float64]:
87
+ box = torch.tensor(
88
+ [
89
+ [50, 50, 30, 20, 0],
90
+ [50, 50, 30, 20, 90],
91
+ [1, 1, math.sqrt(2), math.sqrt(2), -45],
92
+ ],
93
+ dtype=dtype,
94
+ )
95
+ output = self._convert_xywha_to_xyxy(box)
96
+ self.assertEqual(output.dtype, box.dtype)
97
+ expected = torch.tensor([[35, 40, 65, 60], [40, 35, 60, 65], [0, 0, 2, 2]], dtype=dtype)
98
+
99
+ self.assertTrue(torch.allclose(output, expected, atol=1e-6), "output={}".format(output))
100
+
101
+ def test_box_convert_xywh_to_xywha_list(self):
102
+ for tp in [list, tuple]:
103
+ box = tp([50, 50, 30, 20])
104
+ output = self._convert_xywh_to_xywha(box)
105
+ self.assertIsInstance(output, tp)
106
+ self.assertEqual(output, tp([65, 60, 30, 20, 0]))
107
+
108
+ with self.assertRaises(Exception):
109
+ self._convert_xywh_to_xywha([box])
110
+
111
+ def test_box_convert_xywh_to_xywha_array(self):
112
+ for dtype in [np.float64, np.float32]:
113
+ box = np.asarray([[30, 40, 70, 60], [30, 40, 60, 70], [-1, -1, 2, 2]], dtype=dtype)
114
+ output = self._convert_xywh_to_xywha(box)
115
+ self.assertEqual(output.dtype, box.dtype)
116
+ expected = np.asarray(
117
+ [[65, 70, 70, 60, 0], [60, 75, 60, 70, 0], [0, 0, 2, 2, 0]], dtype=dtype
118
+ )
119
+ self.assertTrue(np.allclose(output, expected, atol=1e-6), "output={}".format(output))
120
+
121
+ def test_box_convert_xywh_to_xywha_tensor(self):
122
+ for dtype in [torch.float32, torch.float64]:
123
+ box = torch.tensor([[30, 40, 70, 60], [30, 40, 60, 70], [-1, -1, 2, 2]], dtype=dtype)
124
+ output = self._convert_xywh_to_xywha(box)
125
+ self.assertEqual(output.dtype, box.dtype)
126
+ expected = torch.tensor(
127
+ [[65, 70, 70, 60, 0], [60, 75, 60, 70, 0], [0, 0, 2, 2, 0]], dtype=dtype
128
+ )
129
+
130
+ self.assertTrue(torch.allclose(output, expected, atol=1e-6), "output={}".format(output))
131
+
132
+ def test_json_serializable(self):
133
+ payload = {"box_mode": BoxMode.XYWH_REL}
134
+ try:
135
+ json.dumps(payload)
136
+ except Exception:
137
+ self.fail("JSON serialization failed")
138
+
139
+ def test_json_deserializable(self):
140
+ payload = '{"box_mode": 2}'
141
+ obj = json.loads(payload)
142
+ try:
143
+ obj["box_mode"] = BoxMode(obj["box_mode"])
144
+ except Exception:
145
+ self.fail("JSON deserialization failed")
146
+
147
+
148
+ class TestBoxIOU(unittest.TestCase):
149
+ def test_pairwise_iou(self):
150
+ boxes1 = torch.tensor([[0.0, 0.0, 1.0, 1.0], [0.0, 0.0, 1.0, 1.0]])
151
+
152
+ boxes2 = torch.tensor(
153
+ [
154
+ [0.0, 0.0, 1.0, 1.0],
155
+ [0.0, 0.0, 0.5, 1.0],
156
+ [0.0, 0.0, 1.0, 0.5],
157
+ [0.0, 0.0, 0.5, 0.5],
158
+ [0.5, 0.5, 1.0, 1.0],
159
+ [0.5, 0.5, 1.5, 1.5],
160
+ ]
161
+ )
162
+
163
+ expected_ious = torch.tensor(
164
+ [
165
+ [1.0, 0.5, 0.5, 0.25, 0.25, 0.25 / (2 - 0.25)],
166
+ [1.0, 0.5, 0.5, 0.25, 0.25, 0.25 / (2 - 0.25)],
167
+ ]
168
+ )
169
+
170
+ ious = pairwise_iou(Boxes(boxes1), Boxes(boxes2))
171
+
172
+ self.assertTrue(torch.allclose(ious, expected_ious))
173
+
174
+
175
+ class TestBoxes(unittest.TestCase):
176
+ def test_empty_cat(self):
177
+ x = Boxes.cat([])
178
+ self.assertTrue(x.tensor.shape, (0, 4))
179
+
180
+
181
+ if __name__ == "__main__":
182
+ unittest.main()
preprocess/humanparsing/mhp_extension/detectron2/tests/structures/test_imagelist.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+
3
+ import unittest
4
+ from typing import Sequence
5
+ import torch
6
+
7
+ from detectron2.structures import ImageList
8
+
9
+
10
+ class TestImageList(unittest.TestCase):
11
+ def test_imagelist_padding_shape(self):
12
+ class TensorToImageList(torch.nn.Module):
13
+ def forward(self, tensors: Sequence[torch.Tensor]):
14
+ return ImageList.from_tensors(tensors, 4).tensor
15
+
16
+ func = torch.jit.trace(
17
+ TensorToImageList(), ([torch.ones((3, 10, 10), dtype=torch.float32)],)
18
+ )
19
+ ret = func([torch.ones((3, 15, 20), dtype=torch.float32)])
20
+ self.assertEqual(list(ret.shape), [1, 3, 16, 20], str(ret.shape))
21
+
22
+ func = torch.jit.trace(
23
+ TensorToImageList(),
24
+ (
25
+ [
26
+ torch.ones((3, 16, 10), dtype=torch.float32),
27
+ torch.ones((3, 13, 11), dtype=torch.float32),
28
+ ],
29
+ ),
30
+ )
31
+ ret = func(
32
+ [
33
+ torch.ones((3, 25, 20), dtype=torch.float32),
34
+ torch.ones((3, 10, 10), dtype=torch.float32),
35
+ ]
36
+ )
37
+ # does not support calling with different #images
38
+ self.assertEqual(list(ret.shape), [2, 3, 28, 20], str(ret.shape))
preprocess/humanparsing/mhp_extension/detectron2/tests/structures/test_instances.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+ import unittest
3
+ import torch
4
+
5
+ from detectron2.structures import Instances
6
+
7
+
8
+ class TestInstancesIndexing(unittest.TestCase):
9
+ def test_int_indexing(self):
10
+ attr1 = torch.tensor([[0.0, 0.0, 1.0], [0.0, 0.0, 0.5], [0.0, 0.0, 1.0], [0.0, 0.5, 0.5]])
11
+ attr2 = torch.tensor([0.1, 0.2, 0.3, 0.4])
12
+ instances = Instances((100, 100))
13
+ instances.attr1 = attr1
14
+ instances.attr2 = attr2
15
+ for i in range(-len(instances), len(instances)):
16
+ inst = instances[i]
17
+ self.assertEqual((inst.attr1 == attr1[i]).all(), True)
18
+ self.assertEqual((inst.attr2 == attr2[i]).all(), True)
19
+
20
+ self.assertRaises(IndexError, lambda: instances[len(instances)])
21
+ self.assertRaises(IndexError, lambda: instances[-len(instances) - 1])
22
+
23
+
24
+ if __name__ == "__main__":
25
+ unittest.main()
preprocess/humanparsing/mhp_extension/detectron2/tests/structures/test_rotated_boxes.py ADDED
@@ -0,0 +1,357 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+ from __future__ import absolute_import, division, print_function, unicode_literals
3
+ import logging
4
+ import math
5
+ import random
6
+ import unittest
7
+ import torch
8
+ from fvcore.common.benchmark import benchmark
9
+
10
+ from detectron2.layers.rotated_boxes import pairwise_iou_rotated
11
+ from detectron2.structures.boxes import Boxes
12
+ from detectron2.structures.rotated_boxes import RotatedBoxes, pairwise_iou
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class TestRotatedBoxesLayer(unittest.TestCase):
18
+ def test_iou_0_dim_cpu(self):
19
+ boxes1 = torch.rand(0, 5, dtype=torch.float32)
20
+ boxes2 = torch.rand(10, 5, dtype=torch.float32)
21
+ expected_ious = torch.zeros(0, 10, dtype=torch.float32)
22
+ ious = pairwise_iou_rotated(boxes1, boxes2)
23
+ self.assertTrue(torch.allclose(ious, expected_ious))
24
+
25
+ boxes1 = torch.rand(10, 5, dtype=torch.float32)
26
+ boxes2 = torch.rand(0, 5, dtype=torch.float32)
27
+ expected_ious = torch.zeros(10, 0, dtype=torch.float32)
28
+ ious = pairwise_iou_rotated(boxes1, boxes2)
29
+ self.assertTrue(torch.allclose(ious, expected_ious))
30
+
31
+ @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available")
32
+ def test_iou_0_dim_cuda(self):
33
+ boxes1 = torch.rand(0, 5, dtype=torch.float32)
34
+ boxes2 = torch.rand(10, 5, dtype=torch.float32)
35
+ expected_ious = torch.zeros(0, 10, dtype=torch.float32)
36
+ ious_cuda = pairwise_iou_rotated(boxes1.cuda(), boxes2.cuda())
37
+ self.assertTrue(torch.allclose(ious_cuda.cpu(), expected_ious))
38
+
39
+ boxes1 = torch.rand(10, 5, dtype=torch.float32)
40
+ boxes2 = torch.rand(0, 5, dtype=torch.float32)
41
+ expected_ious = torch.zeros(10, 0, dtype=torch.float32)
42
+ ious_cuda = pairwise_iou_rotated(boxes1.cuda(), boxes2.cuda())
43
+ self.assertTrue(torch.allclose(ious_cuda.cpu(), expected_ious))
44
+
45
+ def test_iou_half_overlap_cpu(self):
46
+ boxes1 = torch.tensor([[0.5, 0.5, 1.0, 1.0, 0.0]], dtype=torch.float32)
47
+ boxes2 = torch.tensor([[0.25, 0.5, 0.5, 1.0, 0.0]], dtype=torch.float32)
48
+ expected_ious = torch.tensor([[0.5]], dtype=torch.float32)
49
+ ious = pairwise_iou_rotated(boxes1, boxes2)
50
+ self.assertTrue(torch.allclose(ious, expected_ious))
51
+
52
+ @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available")
53
+ def test_iou_half_overlap_cuda(self):
54
+ boxes1 = torch.tensor([[0.5, 0.5, 1.0, 1.0, 0.0]], dtype=torch.float32)
55
+ boxes2 = torch.tensor([[0.25, 0.5, 0.5, 1.0, 0.0]], dtype=torch.float32)
56
+ expected_ious = torch.tensor([[0.5]], dtype=torch.float32)
57
+ ious_cuda = pairwise_iou_rotated(boxes1.cuda(), boxes2.cuda())
58
+ self.assertTrue(torch.allclose(ious_cuda.cpu(), expected_ious))
59
+
60
+ def test_iou_precision(self):
61
+ for device in ["cpu"] + ["cuda"] if torch.cuda.is_available() else []:
62
+ boxes1 = torch.tensor([[565, 565, 10, 10.0, 0]], dtype=torch.float32, device=device)
63
+ boxes2 = torch.tensor([[565, 565, 10, 8.3, 0]], dtype=torch.float32, device=device)
64
+ iou = 8.3 / 10.0
65
+ expected_ious = torch.tensor([[iou]], dtype=torch.float32)
66
+ ious = pairwise_iou_rotated(boxes1, boxes2)
67
+ self.assertTrue(torch.allclose(ious.cpu(), expected_ious))
68
+
69
+ @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available")
70
+ def test_iou_too_many_boxes_cuda(self):
71
+ s1, s2 = 5, 1289035
72
+ boxes1 = torch.zeros(s1, 5)
73
+ boxes2 = torch.zeros(s2, 5)
74
+ ious_cuda = pairwise_iou_rotated(boxes1.cuda(), boxes2.cuda())
75
+ self.assertTupleEqual(tuple(ious_cuda.shape), (s1, s2))
76
+
77
+ def test_iou_extreme(self):
78
+ # Cause floating point issues in cuda kernels (#1266)
79
+ for device in ["cpu"] + ["cuda"] if torch.cuda.is_available() else []:
80
+ boxes1 = torch.tensor([[160.0, 153.0, 230.0, 23.0, -37.0]], device=device)
81
+ boxes2 = torch.tensor(
82
+ [
83
+ [
84
+ -1.117407639806935e17,
85
+ 1.3858420478349148e18,
86
+ 1000.0000610351562,
87
+ 1000.0000610351562,
88
+ 1612.0,
89
+ ]
90
+ ],
91
+ device=device,
92
+ )
93
+ ious = pairwise_iou_rotated(boxes1, boxes2)
94
+ self.assertTrue(ious.min() >= 0, ious)
95
+
96
+
97
+ class TestRotatedBoxesStructure(unittest.TestCase):
98
+ def test_clip_area_0_degree(self):
99
+ for _ in range(50):
100
+ num_boxes = 100
101
+ boxes_5d = torch.zeros(num_boxes, 5)
102
+ boxes_5d[:, 0] = torch.FloatTensor(num_boxes).uniform_(-100, 500)
103
+ boxes_5d[:, 1] = torch.FloatTensor(num_boxes).uniform_(-100, 500)
104
+ boxes_5d[:, 2] = torch.FloatTensor(num_boxes).uniform_(0, 500)
105
+ boxes_5d[:, 3] = torch.FloatTensor(num_boxes).uniform_(0, 500)
106
+ # Convert from (x_ctr, y_ctr, w, h, 0) to (x1, y1, x2, y2)
107
+ boxes_4d = torch.zeros(num_boxes, 4)
108
+ boxes_4d[:, 0] = boxes_5d[:, 0] - boxes_5d[:, 2] / 2.0
109
+ boxes_4d[:, 1] = boxes_5d[:, 1] - boxes_5d[:, 3] / 2.0
110
+ boxes_4d[:, 2] = boxes_5d[:, 0] + boxes_5d[:, 2] / 2.0
111
+ boxes_4d[:, 3] = boxes_5d[:, 1] + boxes_5d[:, 3] / 2.0
112
+
113
+ image_size = (500, 600)
114
+ test_boxes_4d = Boxes(boxes_4d)
115
+ test_boxes_5d = RotatedBoxes(boxes_5d)
116
+ # Before clip
117
+ areas_4d = test_boxes_4d.area()
118
+ areas_5d = test_boxes_5d.area()
119
+ self.assertTrue(torch.allclose(areas_4d, areas_5d, atol=1e-1, rtol=1e-5))
120
+ # After clip
121
+ test_boxes_4d.clip(image_size)
122
+ test_boxes_5d.clip(image_size)
123
+ areas_4d = test_boxes_4d.area()
124
+ areas_5d = test_boxes_5d.area()
125
+ self.assertTrue(torch.allclose(areas_4d, areas_5d, atol=1e-1, rtol=1e-5))
126
+
127
+ def test_clip_area_arbitrary_angle(self):
128
+ num_boxes = 100
129
+ boxes_5d = torch.zeros(num_boxes, 5)
130
+ boxes_5d[:, 0] = torch.FloatTensor(num_boxes).uniform_(-100, 500)
131
+ boxes_5d[:, 1] = torch.FloatTensor(num_boxes).uniform_(-100, 500)
132
+ boxes_5d[:, 2] = torch.FloatTensor(num_boxes).uniform_(0, 500)
133
+ boxes_5d[:, 3] = torch.FloatTensor(num_boxes).uniform_(0, 500)
134
+ boxes_5d[:, 4] = torch.FloatTensor(num_boxes).uniform_(-1800, 1800)
135
+ clip_angle_threshold = random.uniform(0, 180)
136
+
137
+ image_size = (500, 600)
138
+ test_boxes_5d = RotatedBoxes(boxes_5d)
139
+ # Before clip
140
+ areas_before = test_boxes_5d.area()
141
+ # After clip
142
+ test_boxes_5d.clip(image_size, clip_angle_threshold)
143
+ areas_diff = test_boxes_5d.area() - areas_before
144
+
145
+ # the areas should only decrease after clipping
146
+ self.assertTrue(torch.all(areas_diff <= 0))
147
+ # whenever the box is clipped (thus the area shrinks),
148
+ # the angle for the box must be within the clip_angle_threshold
149
+ # Note that the clip function will normalize the angle range
150
+ # to be within (-180, 180]
151
+ self.assertTrue(
152
+ torch.all(torch.abs(boxes_5d[:, 4][torch.where(areas_diff < 0)]) < clip_angle_threshold)
153
+ )
154
+
155
+ def test_normalize_angles(self):
156
+ # torch.manual_seed(0)
157
+ for _ in range(50):
158
+ num_boxes = 100
159
+ boxes_5d = torch.zeros(num_boxes, 5)
160
+ boxes_5d[:, 0] = torch.FloatTensor(num_boxes).uniform_(-100, 500)
161
+ boxes_5d[:, 1] = torch.FloatTensor(num_boxes).uniform_(-100, 500)
162
+ boxes_5d[:, 2] = torch.FloatTensor(num_boxes).uniform_(0, 500)
163
+ boxes_5d[:, 3] = torch.FloatTensor(num_boxes).uniform_(0, 500)
164
+ boxes_5d[:, 4] = torch.FloatTensor(num_boxes).uniform_(-1800, 1800)
165
+ rotated_boxes = RotatedBoxes(boxes_5d)
166
+ normalized_boxes = rotated_boxes.clone()
167
+ normalized_boxes.normalize_angles()
168
+ self.assertTrue(torch.all(normalized_boxes.tensor[:, 4] >= -180))
169
+ self.assertTrue(torch.all(normalized_boxes.tensor[:, 4] < 180))
170
+ # x, y, w, h should not change
171
+ self.assertTrue(torch.allclose(boxes_5d[:, :4], normalized_boxes.tensor[:, :4]))
172
+ # the cos/sin values of the angles should stay the same
173
+
174
+ self.assertTrue(
175
+ torch.allclose(
176
+ torch.cos(boxes_5d[:, 4] * math.pi / 180),
177
+ torch.cos(normalized_boxes.tensor[:, 4] * math.pi / 180),
178
+ atol=1e-5,
179
+ )
180
+ )
181
+
182
+ self.assertTrue(
183
+ torch.allclose(
184
+ torch.sin(boxes_5d[:, 4] * math.pi / 180),
185
+ torch.sin(normalized_boxes.tensor[:, 4] * math.pi / 180),
186
+ atol=1e-5,
187
+ )
188
+ )
189
+
190
+ def test_pairwise_iou_0_degree(self):
191
+ for device in ["cpu"] + ["cuda"] if torch.cuda.is_available() else []:
192
+ boxes1 = torch.tensor(
193
+ [[0.5, 0.5, 1.0, 1.0, 0.0], [0.5, 0.5, 1.0, 1.0, 0.0]],
194
+ dtype=torch.float32,
195
+ device=device,
196
+ )
197
+ boxes2 = torch.tensor(
198
+ [
199
+ [0.5, 0.5, 1.0, 1.0, 0.0],
200
+ [0.25, 0.5, 0.5, 1.0, 0.0],
201
+ [0.5, 0.25, 1.0, 0.5, 0.0],
202
+ [0.25, 0.25, 0.5, 0.5, 0.0],
203
+ [0.75, 0.75, 0.5, 0.5, 0.0],
204
+ [1.0, 1.0, 1.0, 1.0, 0.0],
205
+ ],
206
+ dtype=torch.float32,
207
+ device=device,
208
+ )
209
+ expected_ious = torch.tensor(
210
+ [
211
+ [1.0, 0.5, 0.5, 0.25, 0.25, 0.25 / (2 - 0.25)],
212
+ [1.0, 0.5, 0.5, 0.25, 0.25, 0.25 / (2 - 0.25)],
213
+ ],
214
+ dtype=torch.float32,
215
+ device=device,
216
+ )
217
+ ious = pairwise_iou(RotatedBoxes(boxes1), RotatedBoxes(boxes2))
218
+ self.assertTrue(torch.allclose(ious, expected_ious))
219
+
220
+ def test_pairwise_iou_45_degrees(self):
221
+ for device in ["cpu"] + ["cuda"] if torch.cuda.is_available() else []:
222
+ boxes1 = torch.tensor(
223
+ [
224
+ [1, 1, math.sqrt(2), math.sqrt(2), 45],
225
+ [1, 1, 2 * math.sqrt(2), 2 * math.sqrt(2), -45],
226
+ ],
227
+ dtype=torch.float32,
228
+ device=device,
229
+ )
230
+ boxes2 = torch.tensor([[1, 1, 2, 2, 0]], dtype=torch.float32, device=device)
231
+ expected_ious = torch.tensor([[0.5], [0.5]], dtype=torch.float32, device=device)
232
+ ious = pairwise_iou(RotatedBoxes(boxes1), RotatedBoxes(boxes2))
233
+ self.assertTrue(torch.allclose(ious, expected_ious))
234
+
235
+ def test_pairwise_iou_orthogonal(self):
236
+ for device in ["cpu"] + ["cuda"] if torch.cuda.is_available() else []:
237
+ boxes1 = torch.tensor([[5, 5, 10, 6, 55]], dtype=torch.float32, device=device)
238
+ boxes2 = torch.tensor([[5, 5, 10, 6, -35]], dtype=torch.float32, device=device)
239
+ iou = (6.0 * 6.0) / (6.0 * 6.0 + 4.0 * 6.0 + 4.0 * 6.0)
240
+ expected_ious = torch.tensor([[iou]], dtype=torch.float32, device=device)
241
+ ious = pairwise_iou(RotatedBoxes(boxes1), RotatedBoxes(boxes2))
242
+ self.assertTrue(torch.allclose(ious, expected_ious))
243
+
244
+ def test_pairwise_iou_large_close_boxes(self):
245
+ for device in ["cpu"] + ["cuda"] if torch.cuda.is_available() else []:
246
+ boxes1 = torch.tensor(
247
+ [[299.500000, 417.370422, 600.000000, 364.259186, 27.1828]],
248
+ dtype=torch.float32,
249
+ device=device,
250
+ )
251
+ boxes2 = torch.tensor(
252
+ [[299.500000, 417.370422, 600.000000, 364.259155, 27.1828]],
253
+ dtype=torch.float32,
254
+ device=device,
255
+ )
256
+ iou = 364.259155 / 364.259186
257
+ expected_ious = torch.tensor([[iou]], dtype=torch.float32, device=device)
258
+ ious = pairwise_iou(RotatedBoxes(boxes1), RotatedBoxes(boxes2))
259
+ self.assertTrue(torch.allclose(ious, expected_ious))
260
+
261
+ def test_pairwise_iou_many_boxes(self):
262
+ for device in ["cpu"] + ["cuda"] if torch.cuda.is_available() else []:
263
+ num_boxes1 = 100
264
+ num_boxes2 = 200
265
+ boxes1 = torch.stack(
266
+ [
267
+ torch.tensor(
268
+ [5 + 20 * i, 5 + 20 * i, 10, 10, 0], dtype=torch.float32, device=device
269
+ )
270
+ for i in range(num_boxes1)
271
+ ]
272
+ )
273
+ boxes2 = torch.stack(
274
+ [
275
+ torch.tensor(
276
+ [5 + 20 * i, 5 + 20 * i, 10, 1 + 9 * i / num_boxes2, 0],
277
+ dtype=torch.float32,
278
+ device=device,
279
+ )
280
+ for i in range(num_boxes2)
281
+ ]
282
+ )
283
+ expected_ious = torch.zeros(num_boxes1, num_boxes2, dtype=torch.float32, device=device)
284
+ for i in range(min(num_boxes1, num_boxes2)):
285
+ expected_ious[i][i] = (1 + 9 * i / num_boxes2) / 10.0
286
+ ious = pairwise_iou(RotatedBoxes(boxes1), RotatedBoxes(boxes2))
287
+ self.assertTrue(torch.allclose(ious, expected_ious))
288
+
289
+ def test_pairwise_iou_issue1207_simplified(self):
290
+ for device in ["cpu"] + ["cuda"] if torch.cuda.is_available() else []:
291
+ # Simplified test case of D2-issue-1207
292
+ boxes1 = torch.tensor([[3, 3, 8, 2, -45.0]], device=device)
293
+ boxes2 = torch.tensor([[6, 0, 8, 2, -45.0]], device=device)
294
+ iou = 0.0
295
+ expected_ious = torch.tensor([[iou]], dtype=torch.float32, device=device)
296
+
297
+ ious = pairwise_iou(RotatedBoxes(boxes1), RotatedBoxes(boxes2))
298
+ self.assertTrue(torch.allclose(ious, expected_ious))
299
+
300
+ def test_pairwise_iou_issue1207(self):
301
+ for device in ["cpu"] + ["cuda"] if torch.cuda.is_available() else []:
302
+ # The original test case in D2-issue-1207
303
+ boxes1 = torch.tensor([[160.0, 153.0, 230.0, 23.0, -37.0]], device=device)
304
+ boxes2 = torch.tensor([[190.0, 127.0, 80.0, 21.0, -46.0]], device=device)
305
+
306
+ iou = 0.0
307
+ expected_ious = torch.tensor([[iou]], dtype=torch.float32, device=device)
308
+
309
+ ious = pairwise_iou(RotatedBoxes(boxes1), RotatedBoxes(boxes2))
310
+ self.assertTrue(torch.allclose(ious, expected_ious))
311
+
312
+ def test_empty_cat(self):
313
+ x = RotatedBoxes.cat([])
314
+ self.assertTrue(x.tensor.shape, (0, 5))
315
+
316
+
317
+ def benchmark_rotated_iou():
318
+ num_boxes1 = 200
319
+ num_boxes2 = 500
320
+ boxes1 = torch.stack(
321
+ [
322
+ torch.tensor([5 + 20 * i, 5 + 20 * i, 10, 10, 0], dtype=torch.float32)
323
+ for i in range(num_boxes1)
324
+ ]
325
+ )
326
+ boxes2 = torch.stack(
327
+ [
328
+ torch.tensor(
329
+ [5 + 20 * i, 5 + 20 * i, 10, 1 + 9 * i / num_boxes2, 0], dtype=torch.float32
330
+ )
331
+ for i in range(num_boxes2)
332
+ ]
333
+ )
334
+
335
+ def func(dev, n=1):
336
+ b1 = boxes1.to(device=dev)
337
+ b2 = boxes2.to(device=dev)
338
+
339
+ def bench():
340
+ for _ in range(n):
341
+ pairwise_iou_rotated(b1, b2)
342
+ if dev.type == "cuda":
343
+ torch.cuda.synchronize()
344
+
345
+ return bench
346
+
347
+ # only run it once per timed loop, since it's slow
348
+ args = [{"dev": torch.device("cpu"), "n": 1}]
349
+ if torch.cuda.is_available():
350
+ args.append({"dev": torch.device("cuda"), "n": 10})
351
+
352
+ benchmark(func, "rotated_iou", args, warmup_iters=3)
353
+
354
+
355
+ if __name__ == "__main__":
356
+ unittest.main()
357
+ benchmark_rotated_iou()
preprocess/humanparsing/mhp_extension/detectron2/tests/test_checkpoint.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+ import unittest
3
+ from collections import OrderedDict
4
+ import torch
5
+ from torch import nn
6
+
7
+ from detectron2.checkpoint.c2_model_loading import align_and_update_state_dicts
8
+ from detectron2.utils.logger import setup_logger
9
+
10
+
11
+ class TestCheckpointer(unittest.TestCase):
12
+ def setUp(self):
13
+ setup_logger()
14
+
15
+ def create_complex_model(self):
16
+ m = nn.Module()
17
+ m.block1 = nn.Module()
18
+ m.block1.layer1 = nn.Linear(2, 3)
19
+ m.layer2 = nn.Linear(3, 2)
20
+ m.res = nn.Module()
21
+ m.res.layer2 = nn.Linear(3, 2)
22
+
23
+ state_dict = OrderedDict()
24
+ state_dict["layer1.weight"] = torch.rand(3, 2)
25
+ state_dict["layer1.bias"] = torch.rand(3)
26
+ state_dict["layer2.weight"] = torch.rand(2, 3)
27
+ state_dict["layer2.bias"] = torch.rand(2)
28
+ state_dict["res.layer2.weight"] = torch.rand(2, 3)
29
+ state_dict["res.layer2.bias"] = torch.rand(2)
30
+ return m, state_dict
31
+
32
+ def test_complex_model_loaded(self):
33
+ for add_data_parallel in [False, True]:
34
+ model, state_dict = self.create_complex_model()
35
+ if add_data_parallel:
36
+ model = nn.DataParallel(model)
37
+ model_sd = model.state_dict()
38
+
39
+ align_and_update_state_dicts(model_sd, state_dict)
40
+ for loaded, stored in zip(model_sd.values(), state_dict.values()):
41
+ # different tensor references
42
+ self.assertFalse(id(loaded) == id(stored))
43
+ # same content
44
+ self.assertTrue(loaded.equal(stored))
45
+
46
+
47
+ if __name__ == "__main__":
48
+ unittest.main()
preprocess/humanparsing/mhp_extension/detectron2/tests/test_config.py ADDED
@@ -0,0 +1,240 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
3
+
4
+
5
+ import os
6
+ import tempfile
7
+ import unittest
8
+ import torch
9
+
10
+ from detectron2.config import configurable, downgrade_config, get_cfg, upgrade_config
11
+ from detectron2.layers import ShapeSpec
12
+
13
+ _V0_CFG = """
14
+ MODEL:
15
+ RPN_HEAD:
16
+ NAME: "TEST"
17
+ VERSION: 0
18
+ """
19
+
20
+ _V1_CFG = """
21
+ MODEL:
22
+ WEIGHT: "/path/to/weight"
23
+ """
24
+
25
+
26
+ class TestConfigVersioning(unittest.TestCase):
27
+ def test_upgrade_downgrade_consistency(self):
28
+ cfg = get_cfg()
29
+ # check that custom is preserved
30
+ cfg.USER_CUSTOM = 1
31
+
32
+ down = downgrade_config(cfg, to_version=0)
33
+ up = upgrade_config(down)
34
+ self.assertTrue(up == cfg)
35
+
36
+ def _merge_cfg_str(self, cfg, merge_str):
37
+ f = tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False)
38
+ try:
39
+ f.write(merge_str)
40
+ f.close()
41
+ cfg.merge_from_file(f.name)
42
+ finally:
43
+ os.remove(f.name)
44
+ return cfg
45
+
46
+ def test_auto_upgrade(self):
47
+ cfg = get_cfg()
48
+ latest_ver = cfg.VERSION
49
+ cfg.USER_CUSTOM = 1
50
+
51
+ self._merge_cfg_str(cfg, _V0_CFG)
52
+
53
+ self.assertEqual(cfg.MODEL.RPN.HEAD_NAME, "TEST")
54
+ self.assertEqual(cfg.VERSION, latest_ver)
55
+
56
+ def test_guess_v1(self):
57
+ cfg = get_cfg()
58
+ latest_ver = cfg.VERSION
59
+ self._merge_cfg_str(cfg, _V1_CFG)
60
+ self.assertEqual(cfg.VERSION, latest_ver)
61
+
62
+
63
+ class _TestClassA(torch.nn.Module):
64
+ @configurable
65
+ def __init__(self, arg1, arg2, arg3=3):
66
+ super().__init__()
67
+ self.arg1 = arg1
68
+ self.arg2 = arg2
69
+ self.arg3 = arg3
70
+ assert arg1 == 1
71
+ assert arg2 == 2
72
+ assert arg3 == 3
73
+
74
+ @classmethod
75
+ def from_config(cls, cfg):
76
+ args = {"arg1": cfg.ARG1, "arg2": cfg.ARG2}
77
+ return args
78
+
79
+
80
+ class _TestClassB(_TestClassA):
81
+ @configurable
82
+ def __init__(self, input_shape, arg1, arg2, arg3=3):
83
+ """
84
+ Doc of _TestClassB
85
+ """
86
+ assert input_shape == "shape"
87
+ super().__init__(arg1, arg2, arg3)
88
+
89
+ @classmethod
90
+ def from_config(cls, cfg, input_shape): # test extra positional arg in from_config
91
+ args = {"arg1": cfg.ARG1, "arg2": cfg.ARG2}
92
+ args["input_shape"] = input_shape
93
+ return args
94
+
95
+
96
+ class _LegacySubClass(_TestClassB):
97
+ # an old subclass written in cfg style
98
+ def __init__(self, cfg, input_shape, arg4=4):
99
+ super().__init__(cfg, input_shape)
100
+ assert self.arg1 == 1
101
+ assert self.arg2 == 2
102
+ assert self.arg3 == 3
103
+
104
+
105
+ class _NewSubClassNewInit(_TestClassB):
106
+ # test new subclass with a new __init__
107
+ @configurable
108
+ def __init__(self, input_shape, arg4=4, **kwargs):
109
+ super().__init__(input_shape, **kwargs)
110
+ assert self.arg1 == 1
111
+ assert self.arg2 == 2
112
+ assert self.arg3 == 3
113
+
114
+
115
+ class _LegacySubClassNotCfg(_TestClassB):
116
+ # an old subclass written in cfg style, but argument is not called "cfg"
117
+ def __init__(self, config, input_shape):
118
+ super().__init__(config, input_shape)
119
+ assert self.arg1 == 1
120
+ assert self.arg2 == 2
121
+ assert self.arg3 == 3
122
+
123
+
124
+ class _TestClassC(_TestClassB):
125
+ @classmethod
126
+ def from_config(cls, cfg, input_shape, **kwargs): # test extra kwarg overwrite
127
+ args = {"arg1": cfg.ARG1, "arg2": cfg.ARG2}
128
+ args["input_shape"] = input_shape
129
+ args.update(kwargs)
130
+ return args
131
+
132
+
133
+ class _TestClassD(_TestClassA):
134
+ @configurable
135
+ def __init__(self, input_shape: ShapeSpec, arg1: int, arg2, arg3=3):
136
+ assert input_shape == "shape"
137
+ super().__init__(arg1, arg2, arg3)
138
+
139
+ # _TestClassA.from_config does not have input_shape args.
140
+ # Test whether input_shape will be forwarded to __init__
141
+
142
+
143
+ class TestConfigurable(unittest.TestCase):
144
+ def testInitWithArgs(self):
145
+ _ = _TestClassA(arg1=1, arg2=2, arg3=3)
146
+ _ = _TestClassB("shape", arg1=1, arg2=2)
147
+ _ = _TestClassC("shape", arg1=1, arg2=2)
148
+ _ = _TestClassD("shape", arg1=1, arg2=2, arg3=3)
149
+
150
+ def testPatchedAttr(self):
151
+ self.assertTrue("Doc" in _TestClassB.__init__.__doc__)
152
+ self.assertEqual(_TestClassD.__init__.__annotations__["arg1"], int)
153
+
154
+ def testInitWithCfg(self):
155
+ cfg = get_cfg()
156
+ cfg.ARG1 = 1
157
+ cfg.ARG2 = 2
158
+ cfg.ARG3 = 3
159
+ _ = _TestClassA(cfg)
160
+ _ = _TestClassB(cfg, input_shape="shape")
161
+ _ = _TestClassC(cfg, input_shape="shape")
162
+ _ = _TestClassD(cfg, input_shape="shape")
163
+ _ = _LegacySubClass(cfg, input_shape="shape")
164
+ _ = _NewSubClassNewInit(cfg, input_shape="shape")
165
+ _ = _LegacySubClassNotCfg(cfg, input_shape="shape")
166
+ with self.assertRaises(TypeError):
167
+ # disallow forwarding positional args to __init__ since it's prone to errors
168
+ _ = _TestClassD(cfg, "shape")
169
+
170
+ # call with kwargs instead
171
+ _ = _TestClassA(cfg=cfg)
172
+ _ = _TestClassB(cfg=cfg, input_shape="shape")
173
+ _ = _TestClassC(cfg=cfg, input_shape="shape")
174
+ _ = _TestClassD(cfg=cfg, input_shape="shape")
175
+ _ = _LegacySubClass(cfg=cfg, input_shape="shape")
176
+ _ = _NewSubClassNewInit(cfg=cfg, input_shape="shape")
177
+ _ = _LegacySubClassNotCfg(config=cfg, input_shape="shape")
178
+
179
+ def testInitWithCfgOverwrite(self):
180
+ cfg = get_cfg()
181
+ cfg.ARG1 = 1
182
+ cfg.ARG2 = 999 # wrong config
183
+ with self.assertRaises(AssertionError):
184
+ _ = _TestClassA(cfg, arg3=3)
185
+
186
+ # overwrite arg2 with correct config later:
187
+ _ = _TestClassA(cfg, arg2=2, arg3=3)
188
+ _ = _TestClassB(cfg, input_shape="shape", arg2=2, arg3=3)
189
+ _ = _TestClassC(cfg, input_shape="shape", arg2=2, arg3=3)
190
+ _ = _TestClassD(cfg, input_shape="shape", arg2=2, arg3=3)
191
+
192
+ # call with kwargs cfg=cfg instead
193
+ _ = _TestClassA(cfg=cfg, arg2=2, arg3=3)
194
+ _ = _TestClassB(cfg=cfg, input_shape="shape", arg2=2, arg3=3)
195
+ _ = _TestClassC(cfg=cfg, input_shape="shape", arg2=2, arg3=3)
196
+ _ = _TestClassD(cfg=cfg, input_shape="shape", arg2=2, arg3=3)
197
+
198
+ def testInitWithCfgWrongArgs(self):
199
+ cfg = get_cfg()
200
+ cfg.ARG1 = 1
201
+ cfg.ARG2 = 2
202
+ with self.assertRaises(TypeError):
203
+ _ = _TestClassB(cfg, "shape", not_exist=1)
204
+ with self.assertRaises(TypeError):
205
+ _ = _TestClassC(cfg, "shape", not_exist=1)
206
+ with self.assertRaises(TypeError):
207
+ _ = _TestClassD(cfg, "shape", not_exist=1)
208
+
209
+ def testBadClass(self):
210
+ class _BadClass1:
211
+ @configurable
212
+ def __init__(self, a=1, b=2):
213
+ pass
214
+
215
+ class _BadClass2:
216
+ @configurable
217
+ def __init__(self, a=1, b=2):
218
+ pass
219
+
220
+ def from_config(self, cfg): # noqa
221
+ pass
222
+
223
+ class _BadClass3:
224
+ @configurable
225
+ def __init__(self, a=1, b=2):
226
+ pass
227
+
228
+ # bad name: must be cfg
229
+ @classmethod
230
+ def from_config(cls, config): # noqa
231
+ pass
232
+
233
+ with self.assertRaises(AttributeError):
234
+ _ = _BadClass1(a=1)
235
+
236
+ with self.assertRaises(TypeError):
237
+ _ = _BadClass2(a=1)
238
+
239
+ with self.assertRaises(TypeError):
240
+ _ = _BadClass3(get_cfg())
preprocess/humanparsing/mhp_extension/detectron2/tests/test_export_caffe2.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+ # -*- coding: utf-8 -*-
3
+
4
+ import copy
5
+ import numpy as np
6
+ import os
7
+ import tempfile
8
+ import unittest
9
+ import cv2
10
+ import torch
11
+ from fvcore.common.file_io import PathManager
12
+
13
+ from detectron2 import model_zoo
14
+ from detectron2.checkpoint import DetectionCheckpointer
15
+ from detectron2.config import get_cfg
16
+ from detectron2.data import DatasetCatalog
17
+ from detectron2.modeling import build_model
18
+ from detectron2.utils.logger import setup_logger
19
+
20
+
21
+ @unittest.skipIf(os.environ.get("CIRCLECI"), "Require COCO data and model zoo.")
22
+ class TestCaffe2Export(unittest.TestCase):
23
+ def setUp(self):
24
+ setup_logger()
25
+
26
+ def _test_model(self, config_path, device="cpu"):
27
+ # requires extra dependencies
28
+ from detectron2.export import Caffe2Model, add_export_config, export_caffe2_model
29
+
30
+ cfg = get_cfg()
31
+ cfg.merge_from_file(model_zoo.get_config_file(config_path))
32
+ cfg = add_export_config(cfg)
33
+ cfg.MODEL.DEVICE = device
34
+
35
+ model = build_model(cfg)
36
+ DetectionCheckpointer(model).load(model_zoo.get_checkpoint_url(config_path))
37
+
38
+ inputs = [{"image": self._get_test_image()}]
39
+ c2_model = export_caffe2_model(cfg, model, copy.deepcopy(inputs))
40
+
41
+ with tempfile.TemporaryDirectory(prefix="detectron2_unittest") as d:
42
+ c2_model.save_protobuf(d)
43
+ c2_model.save_graph(os.path.join(d, "test.svg"), inputs=copy.deepcopy(inputs))
44
+ c2_model = Caffe2Model.load_protobuf(d)
45
+ c2_model(inputs)[0]["instances"]
46
+
47
+ def _get_test_image(self):
48
+ try:
49
+ file_name = DatasetCatalog.get("coco_2017_train")[0]["file_name"]
50
+ assert PathManager.exists(file_name)
51
+ except Exception:
52
+ self.skipTest("COCO dataset not available.")
53
+
54
+ with PathManager.open(file_name, "rb") as f:
55
+ buf = f.read()
56
+ img = cv2.imdecode(np.frombuffer(buf, dtype=np.uint8), cv2.IMREAD_COLOR)
57
+ assert img is not None, file_name
58
+ return torch.from_numpy(img.transpose(2, 0, 1))
59
+
60
+ def testMaskRCNN(self):
61
+ self._test_model("COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml")
62
+
63
+ @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available")
64
+ def testMaskRCNNGPU(self):
65
+ self._test_model("COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml", device="cuda")
66
+
67
+ def testRetinaNet(self):
68
+ self._test_model("COCO-Detection/retinanet_R_50_FPN_3x.yaml")
69
+
70
+ def testPanopticFPN(self):
71
+ self._test_model("COCO-PanopticSegmentation/panoptic_fpn_R_50_3x.yaml")
preprocess/humanparsing/mhp_extension/detectron2/tests/test_model_analysis.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
2
+
3
+
4
+ import unittest
5
+ import torch
6
+
7
+ import detectron2.model_zoo as model_zoo
8
+ from detectron2.config import get_cfg
9
+ from detectron2.modeling import build_model
10
+ from detectron2.utils.analysis import flop_count_operators, parameter_count
11
+
12
+
13
+ def get_model_zoo(config_path):
14
+ """
15
+ Like model_zoo.get, but do not load any weights (even pretrained)
16
+ """
17
+ cfg_file = model_zoo.get_config_file(config_path)
18
+ cfg = get_cfg()
19
+ cfg.merge_from_file(cfg_file)
20
+ if not torch.cuda.is_available():
21
+ cfg.MODEL.DEVICE = "cpu"
22
+ return build_model(cfg)
23
+
24
+
25
+ class RetinaNetTest(unittest.TestCase):
26
+ def setUp(self):
27
+ self.model = get_model_zoo("COCO-Detection/retinanet_R_50_FPN_1x.yaml")
28
+
29
+ def test_flop(self):
30
+ # RetinaNet supports flop-counting with random inputs
31
+ inputs = [{"image": torch.rand(3, 800, 800)}]
32
+ res = flop_count_operators(self.model, inputs)
33
+ self.assertTrue(int(res["conv"]), 146) # 146B flops
34
+
35
+ def test_param_count(self):
36
+ res = parameter_count(self.model)
37
+ self.assertTrue(res[""], 37915572)
38
+ self.assertTrue(res["backbone"], 31452352)
39
+
40
+
41
+ class FasterRCNNTest(unittest.TestCase):
42
+ def setUp(self):
43
+ self.model = get_model_zoo("COCO-Detection/faster_rcnn_R_50_FPN_1x.yaml")
44
+
45
+ def test_flop(self):
46
+ # Faster R-CNN supports flop-counting with random inputs
47
+ inputs = [{"image": torch.rand(3, 800, 800)}]
48
+ res = flop_count_operators(self.model, inputs)
49
+
50
+ # This only checks flops for backbone & proposal generator
51
+ # Flops for box head is not conv, and depends on #proposals, which is
52
+ # almost 0 for random inputs.
53
+ self.assertTrue(int(res["conv"]), 117)
54
+
55
+ def test_param_count(self):
56
+ res = parameter_count(self.model)
57
+ self.assertTrue(res[""], 41699936)
58
+ self.assertTrue(res["backbone"], 26799296)
preprocess/humanparsing/mhp_extension/detectron2/tests/test_model_zoo.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2
+ import logging
3
+ import unittest
4
+
5
+ from detectron2 import model_zoo
6
+ from detectron2.modeling import FPN, GeneralizedRCNN
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ class TestModelZoo(unittest.TestCase):
12
+ def test_get_returns_model(self):
13
+ model = model_zoo.get("Misc/scratch_mask_rcnn_R_50_FPN_3x_gn.yaml", trained=False)
14
+ self.assertIsInstance(model, GeneralizedRCNN)
15
+ self.assertIsInstance(model.backbone, FPN)
16
+
17
+ def test_get_invalid_model(self):
18
+ self.assertRaises(RuntimeError, model_zoo.get, "Invalid/config.yaml")
19
+
20
+ def test_get_url(self):
21
+ url = model_zoo.get_checkpoint_url("Misc/scratch_mask_rcnn_R_50_FPN_3x_gn.yaml")
22
+ self.assertEqual(
23
+ url,
24
+ "https://dl.fbaipublicfiles.com/detectron2/Misc/scratch_mask_rcnn_R_50_FPN_3x_gn/138602908/model_final_01ca85.pkl", # noqa
25
+ )
26
+
27
+
28
+ if __name__ == "__main__":
29
+ unittest.main()