Duskfallcrew jonigata commited on
Commit
379c20c
·
0 Parent(s):

Duplicate from jonigata/PoseMaker2

Browse files

Co-authored-by: Naoyuki Hirayama <[email protected]>

.gitattributes ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tflite filter=lfs diff=lfs merge=lfs -text
29
+ *.tgz filter=lfs diff=lfs merge=lfs -text
30
+ *.wasm filter=lfs diff=lfs merge=lfs -text
31
+ *.xz filter=lfs diff=lfs merge=lfs -text
32
+ *.zip filter=lfs diff=lfs merge=lfs -text
33
+ *.zst filter=lfs diff=lfs merge=lfs -text
34
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
Dockerfile ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker
2
+ # you will also find guides on how best to write your Dockerfile
3
+
4
+ FROM python:3.9
5
+
6
+ WORKDIR /code
7
+
8
+ COPY ./requirements.txt /code/requirements.txt
9
+
10
+ RUN apt-get update && apt-get upgrade -y && apt-get install -y libgl1-mesa-dev
11
+ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
12
+ RUN mim install mmcv-full==1.7.0
13
+ RUN pip install mmdet mmpose
14
+
15
+ # Set up a new user named "user" with user ID 1000
16
+ RUN useradd -m -u 1000 user
17
+
18
+ # Switch to the "user" user
19
+ USER user
20
+
21
+ # Set home to the user's home directory
22
+ ENV HOME=/home/user \
23
+ PATH=/home/user/.local/bin:$PATH
24
+
25
+ # Set the working directory to the user's home directory
26
+ WORKDIR $HOME/app
27
+
28
+ # Copy the current directory contents into the container at $HOME/app setting the owner to the user
29
+ COPY --chown=user . $HOME/app
30
+
31
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
README.md ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Demo Docker Gradio
3
+ emoji: 📈
4
+ colorFrom: indigo
5
+ colorTo: indigo
6
+ sdk: docker
7
+ pinned: false
8
+ license: apache-2.0
9
+ duplicated_from: jonigata/PoseMaker2
10
+ ---
11
+
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
external/coco.py ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ dataset_info = dict(
2
+ dataset_name='coco',
3
+ paper_info=dict(
4
+ author='Lin, Tsung-Yi and Maire, Michael and '
5
+ 'Belongie, Serge and Hays, James and '
6
+ 'Perona, Pietro and Ramanan, Deva and '
7
+ r'Doll{\'a}r, Piotr and Zitnick, C Lawrence',
8
+ title='Microsoft coco: Common objects in context',
9
+ container='European conference on computer vision',
10
+ year='2014',
11
+ homepage='http://cocodataset.org/',
12
+ ),
13
+ keypoint_info={
14
+ 0:
15
+ dict(name='nose', id=0, color=[51, 153, 255], type='upper', swap=''),
16
+ 1:
17
+ dict(
18
+ name='left_eye',
19
+ id=1,
20
+ color=[51, 153, 255],
21
+ type='upper',
22
+ swap='right_eye'),
23
+ 2:
24
+ dict(
25
+ name='right_eye',
26
+ id=2,
27
+ color=[51, 153, 255],
28
+ type='upper',
29
+ swap='left_eye'),
30
+ 3:
31
+ dict(
32
+ name='left_ear',
33
+ id=3,
34
+ color=[51, 153, 255],
35
+ type='upper',
36
+ swap='right_ear'),
37
+ 4:
38
+ dict(
39
+ name='right_ear',
40
+ id=4,
41
+ color=[51, 153, 255],
42
+ type='upper',
43
+ swap='left_ear'),
44
+ 5:
45
+ dict(
46
+ name='left_shoulder',
47
+ id=5,
48
+ color=[0, 255, 0],
49
+ type='upper',
50
+ swap='right_shoulder'),
51
+ 6:
52
+ dict(
53
+ name='right_shoulder',
54
+ id=6,
55
+ color=[255, 128, 0],
56
+ type='upper',
57
+ swap='left_shoulder'),
58
+ 7:
59
+ dict(
60
+ name='left_elbow',
61
+ id=7,
62
+ color=[0, 255, 0],
63
+ type='upper',
64
+ swap='right_elbow'),
65
+ 8:
66
+ dict(
67
+ name='right_elbow',
68
+ id=8,
69
+ color=[255, 128, 0],
70
+ type='upper',
71
+ swap='left_elbow'),
72
+ 9:
73
+ dict(
74
+ name='left_wrist',
75
+ id=9,
76
+ color=[0, 255, 0],
77
+ type='upper',
78
+ swap='right_wrist'),
79
+ 10:
80
+ dict(
81
+ name='right_wrist',
82
+ id=10,
83
+ color=[255, 128, 0],
84
+ type='upper',
85
+ swap='left_wrist'),
86
+ 11:
87
+ dict(
88
+ name='left_hip',
89
+ id=11,
90
+ color=[0, 255, 0],
91
+ type='lower',
92
+ swap='right_hip'),
93
+ 12:
94
+ dict(
95
+ name='right_hip',
96
+ id=12,
97
+ color=[255, 128, 0],
98
+ type='lower',
99
+ swap='left_hip'),
100
+ 13:
101
+ dict(
102
+ name='left_knee',
103
+ id=13,
104
+ color=[0, 255, 0],
105
+ type='lower',
106
+ swap='right_knee'),
107
+ 14:
108
+ dict(
109
+ name='right_knee',
110
+ id=14,
111
+ color=[255, 128, 0],
112
+ type='lower',
113
+ swap='left_knee'),
114
+ 15:
115
+ dict(
116
+ name='left_ankle',
117
+ id=15,
118
+ color=[0, 255, 0],
119
+ type='lower',
120
+ swap='right_ankle'),
121
+ 16:
122
+ dict(
123
+ name='right_ankle',
124
+ id=16,
125
+ color=[255, 128, 0],
126
+ type='lower',
127
+ swap='left_ankle')
128
+ },
129
+ skeleton_info={
130
+ 0:
131
+ dict(link=('left_ankle', 'left_knee'), id=0, color=[0, 255, 0]),
132
+ 1:
133
+ dict(link=('left_knee', 'left_hip'), id=1, color=[0, 255, 0]),
134
+ 2:
135
+ dict(link=('right_ankle', 'right_knee'), id=2, color=[255, 128, 0]),
136
+ 3:
137
+ dict(link=('right_knee', 'right_hip'), id=3, color=[255, 128, 0]),
138
+ 4:
139
+ dict(link=('left_hip', 'right_hip'), id=4, color=[51, 153, 255]),
140
+ 5:
141
+ dict(link=('left_shoulder', 'left_hip'), id=5, color=[51, 153, 255]),
142
+ 6:
143
+ dict(link=('right_shoulder', 'right_hip'), id=6, color=[51, 153, 255]),
144
+ 7:
145
+ dict(
146
+ link=('left_shoulder', 'right_shoulder'),
147
+ id=7,
148
+ color=[51, 153, 255]),
149
+ 8:
150
+ dict(link=('left_shoulder', 'left_elbow'), id=8, color=[0, 255, 0]),
151
+ 9:
152
+ dict(
153
+ link=('right_shoulder', 'right_elbow'), id=9, color=[255, 128, 0]),
154
+ 10:
155
+ dict(link=('left_elbow', 'left_wrist'), id=10, color=[0, 255, 0]),
156
+ 11:
157
+ dict(link=('right_elbow', 'right_wrist'), id=11, color=[255, 128, 0]),
158
+ 12:
159
+ dict(link=('left_eye', 'right_eye'), id=12, color=[51, 153, 255]),
160
+ 13:
161
+ dict(link=('nose', 'left_eye'), id=13, color=[51, 153, 255]),
162
+ 14:
163
+ dict(link=('nose', 'right_eye'), id=14, color=[51, 153, 255]),
164
+ 15:
165
+ dict(link=('left_eye', 'left_ear'), id=15, color=[51, 153, 255]),
166
+ 16:
167
+ dict(link=('right_eye', 'right_ear'), id=16, color=[51, 153, 255]),
168
+ 17:
169
+ dict(link=('left_ear', 'left_shoulder'), id=17, color=[51, 153, 255]),
170
+ 18:
171
+ dict(
172
+ link=('right_ear', 'right_shoulder'), id=18, color=[51, 153, 255])
173
+ },
174
+ joint_weights=[
175
+ 1., 1., 1., 1., 1., 1., 1., 1.2, 1.2, 1.5, 1.5, 1., 1., 1.2, 1.2, 1.5,
176
+ 1.5
177
+ ],
178
+ sigmas=[
179
+ 0.026, 0.025, 0.025, 0.035, 0.035, 0.079, 0.079, 0.072, 0.072, 0.062,
180
+ 0.062, 0.107, 0.107, 0.087, 0.087, 0.089, 0.089
181
+ ])
external/default_runtime.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ checkpoint_config = dict(interval=10)
2
+
3
+ log_config = dict(
4
+ interval=50,
5
+ hooks=[
6
+ dict(type='TextLoggerHook'),
7
+ # dict(type='TensorboardLoggerHook')
8
+ # dict(type='PaviLoggerHook') # for internal services
9
+ ])
10
+
11
+ log_level = 'INFO'
12
+ load_from = None
13
+ resume_from = None
14
+ dist_params = dict(backend='nccl')
15
+ workflow = [('train', 1)]
16
+
17
+ # disable opencv multithreading to avoid system being overloaded
18
+ opencv_num_threads = 0
19
+ # set multi-process start method as `fork` to speed up the training
20
+ mp_start_method = 'fork'
external/faster_rcnn_r50_fpn_coco.py ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ checkpoint_config = dict(interval=1)
2
+ # yapf:disable
3
+ log_config = dict(
4
+ interval=50,
5
+ hooks=[
6
+ dict(type='TextLoggerHook'),
7
+ # dict(type='TensorboardLoggerHook')
8
+ ])
9
+ # yapf:enable
10
+ dist_params = dict(backend='nccl')
11
+ log_level = 'INFO'
12
+ load_from = None
13
+ resume_from = None
14
+ workflow = [('train', 1)]
15
+ # optimizer
16
+ optimizer = dict(type='SGD', lr=0.02, momentum=0.9, weight_decay=0.0001)
17
+ optimizer_config = dict(grad_clip=None)
18
+ # learning policy
19
+ lr_config = dict(
20
+ policy='step',
21
+ warmup='linear',
22
+ warmup_iters=500,
23
+ warmup_ratio=0.001,
24
+ step=[8, 11])
25
+ total_epochs = 12
26
+
27
+ model = dict(
28
+ type='FasterRCNN',
29
+ pretrained='torchvision://resnet50',
30
+ backbone=dict(
31
+ type='ResNet',
32
+ depth=50,
33
+ num_stages=4,
34
+ out_indices=(0, 1, 2, 3),
35
+ frozen_stages=1,
36
+ norm_cfg=dict(type='BN', requires_grad=True),
37
+ norm_eval=True,
38
+ style='pytorch'),
39
+ neck=dict(
40
+ type='FPN',
41
+ in_channels=[256, 512, 1024, 2048],
42
+ out_channels=256,
43
+ num_outs=5),
44
+ rpn_head=dict(
45
+ type='RPNHead',
46
+ in_channels=256,
47
+ feat_channels=256,
48
+ anchor_generator=dict(
49
+ type='AnchorGenerator',
50
+ scales=[8],
51
+ ratios=[0.5, 1.0, 2.0],
52
+ strides=[4, 8, 16, 32, 64]),
53
+ bbox_coder=dict(
54
+ type='DeltaXYWHBBoxCoder',
55
+ target_means=[.0, .0, .0, .0],
56
+ target_stds=[1.0, 1.0, 1.0, 1.0]),
57
+ loss_cls=dict(
58
+ type='CrossEntropyLoss', use_sigmoid=True, loss_weight=1.0),
59
+ loss_bbox=dict(type='L1Loss', loss_weight=1.0)),
60
+ roi_head=dict(
61
+ type='StandardRoIHead',
62
+ bbox_roi_extractor=dict(
63
+ type='SingleRoIExtractor',
64
+ roi_layer=dict(type='RoIAlign', output_size=7, sampling_ratio=0),
65
+ out_channels=256,
66
+ featmap_strides=[4, 8, 16, 32]),
67
+ bbox_head=dict(
68
+ type='Shared2FCBBoxHead',
69
+ in_channels=256,
70
+ fc_out_channels=1024,
71
+ roi_feat_size=7,
72
+ num_classes=80,
73
+ bbox_coder=dict(
74
+ type='DeltaXYWHBBoxCoder',
75
+ target_means=[0., 0., 0., 0.],
76
+ target_stds=[0.1, 0.1, 0.2, 0.2]),
77
+ reg_class_agnostic=False,
78
+ loss_cls=dict(
79
+ type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0),
80
+ loss_bbox=dict(type='L1Loss', loss_weight=1.0))),
81
+ # model training and testing settings
82
+ train_cfg=dict(
83
+ rpn=dict(
84
+ assigner=dict(
85
+ type='MaxIoUAssigner',
86
+ pos_iou_thr=0.7,
87
+ neg_iou_thr=0.3,
88
+ min_pos_iou=0.3,
89
+ match_low_quality=True,
90
+ ignore_iof_thr=-1),
91
+ sampler=dict(
92
+ type='RandomSampler',
93
+ num=256,
94
+ pos_fraction=0.5,
95
+ neg_pos_ub=-1,
96
+ add_gt_as_proposals=False),
97
+ allowed_border=-1,
98
+ pos_weight=-1,
99
+ debug=False),
100
+ rpn_proposal=dict(
101
+ nms_pre=2000,
102
+ max_per_img=1000,
103
+ nms=dict(type='nms', iou_threshold=0.7),
104
+ min_bbox_size=0),
105
+ rcnn=dict(
106
+ assigner=dict(
107
+ type='MaxIoUAssigner',
108
+ pos_iou_thr=0.5,
109
+ neg_iou_thr=0.5,
110
+ min_pos_iou=0.5,
111
+ match_low_quality=False,
112
+ ignore_iof_thr=-1),
113
+ sampler=dict(
114
+ type='RandomSampler',
115
+ num=512,
116
+ pos_fraction=0.25,
117
+ neg_pos_ub=-1,
118
+ add_gt_as_proposals=True),
119
+ pos_weight=-1,
120
+ debug=False)),
121
+ test_cfg=dict(
122
+ rpn=dict(
123
+ nms_pre=1000,
124
+ max_per_img=1000,
125
+ nms=dict(type='nms', iou_threshold=0.7),
126
+ min_bbox_size=0),
127
+ rcnn=dict(
128
+ score_thr=0.05,
129
+ nms=dict(type='nms', iou_threshold=0.5),
130
+ max_per_img=100)
131
+ # soft-nms is also supported for rcnn testing
132
+ # e.g., nms=dict(type='soft_nms', iou_threshold=0.5, min_score=0.05)
133
+ ))
134
+
135
+ dataset_type = 'CocoDataset'
136
+ data_root = 'data/coco'
137
+ img_norm_cfg = dict(
138
+ mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
139
+ train_pipeline = [
140
+ dict(type='LoadImageFromFile'),
141
+ dict(type='LoadAnnotations', with_bbox=True),
142
+ dict(type='Resize', img_scale=(1333, 800), keep_ratio=True),
143
+ dict(type='RandomFlip', flip_ratio=0.5),
144
+ dict(type='Normalize', **img_norm_cfg),
145
+ dict(type='Pad', size_divisor=32),
146
+ dict(type='DefaultFormatBundle'),
147
+ dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']),
148
+ ]
149
+ test_pipeline = [
150
+ dict(type='LoadImageFromFile'),
151
+ dict(
152
+ type='MultiScaleFlipAug',
153
+ img_scale=(1333, 800),
154
+ flip=False,
155
+ transforms=[
156
+ dict(type='Resize', keep_ratio=True),
157
+ dict(type='RandomFlip'),
158
+ dict(type='Normalize', **img_norm_cfg),
159
+ dict(type='Pad', size_divisor=32),
160
+ dict(type='DefaultFormatBundle'),
161
+ dict(type='Collect', keys=['img']),
162
+ ])
163
+ ]
164
+ data = dict(
165
+ samples_per_gpu=2,
166
+ workers_per_gpu=2,
167
+ train=dict(
168
+ type=dataset_type,
169
+ ann_file=f'{data_root}/annotations/instances_train2017.json',
170
+ img_prefix=f'{data_root}/train2017/',
171
+ pipeline=train_pipeline),
172
+ val=dict(
173
+ type=dataset_type,
174
+ ann_file=f'{data_root}/annotations/instances_val2017.json',
175
+ img_prefix=f'{data_root}/val2017/',
176
+ pipeline=test_pipeline),
177
+ test=dict(
178
+ type=dataset_type,
179
+ ann_file=f'{data_root}/annotations/instances_val2017.json',
180
+ img_prefix=f'{data_root}/val2017/',
181
+ pipeline=test_pipeline))
182
+ evaluation = dict(interval=1, metric='bbox')
external/hrnet_w48_coco_256x192.py ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ _base_ = [
2
+ 'default_runtime.py',
3
+ 'coco.py'
4
+ ]
5
+ evaluation = dict(interval=10, metric='mAP', save_best='AP')
6
+
7
+ optimizer = dict(
8
+ type='Adam',
9
+ lr=5e-4,
10
+ )
11
+ optimizer_config = dict(grad_clip=None)
12
+ # learning policy
13
+ lr_config = dict(
14
+ policy='step',
15
+ warmup='linear',
16
+ warmup_iters=500,
17
+ warmup_ratio=0.001,
18
+ step=[170, 200])
19
+ total_epochs = 210
20
+ channel_cfg = dict(
21
+ num_output_channels=17,
22
+ dataset_joints=17,
23
+ dataset_channel=[
24
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
25
+ ],
26
+ inference_channel=[
27
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
28
+ ])
29
+
30
+ # model settings
31
+ model = dict(
32
+ type='TopDown',
33
+ pretrained='https://download.openmmlab.com/mmpose/'
34
+ 'pretrain_models/hrnet_w48-8ef0771d.pth',
35
+ backbone=dict(
36
+ type='HRNet',
37
+ in_channels=3,
38
+ extra=dict(
39
+ stage1=dict(
40
+ num_modules=1,
41
+ num_branches=1,
42
+ block='BOTTLENECK',
43
+ num_blocks=(4, ),
44
+ num_channels=(64, )),
45
+ stage2=dict(
46
+ num_modules=1,
47
+ num_branches=2,
48
+ block='BASIC',
49
+ num_blocks=(4, 4),
50
+ num_channels=(48, 96)),
51
+ stage3=dict(
52
+ num_modules=4,
53
+ num_branches=3,
54
+ block='BASIC',
55
+ num_blocks=(4, 4, 4),
56
+ num_channels=(48, 96, 192)),
57
+ stage4=dict(
58
+ num_modules=3,
59
+ num_branches=4,
60
+ block='BASIC',
61
+ num_blocks=(4, 4, 4, 4),
62
+ num_channels=(48, 96, 192, 384))),
63
+ ),
64
+ keypoint_head=dict(
65
+ type='TopdownHeatmapSimpleHead',
66
+ in_channels=48,
67
+ out_channels=channel_cfg['num_output_channels'],
68
+ num_deconv_layers=0,
69
+ extra=dict(final_conv_kernel=1, ),
70
+ loss_keypoint=dict(type='JointsMSELoss', use_target_weight=True)),
71
+ train_cfg=dict(),
72
+ test_cfg=dict(
73
+ flip_test=True,
74
+ post_process='default',
75
+ shift_heatmap=True,
76
+ modulate_kernel=11))
77
+
78
+ data_cfg = dict(
79
+ image_size=[192, 256],
80
+ heatmap_size=[48, 64],
81
+ num_output_channels=channel_cfg['num_output_channels'],
82
+ num_joints=channel_cfg['dataset_joints'],
83
+ dataset_channel=channel_cfg['dataset_channel'],
84
+ inference_channel=channel_cfg['inference_channel'],
85
+ soft_nms=False,
86
+ nms_thr=1.0,
87
+ oks_thr=0.9,
88
+ vis_thr=0.2,
89
+ use_gt_bbox=False,
90
+ det_bbox_thr=0.0,
91
+ bbox_file='data/coco/person_detection_results/'
92
+ 'COCO_val2017_detections_AP_H_56_person.json',
93
+ )
94
+
95
+ train_pipeline = [
96
+ dict(type='LoadImageFromFile'),
97
+ dict(type='TopDownGetBboxCenterScale', padding=1.25),
98
+ dict(type='TopDownRandomShiftBboxCenter', shift_factor=0.16, prob=0.3),
99
+ dict(type='TopDownRandomFlip', flip_prob=0.5),
100
+ dict(
101
+ type='TopDownHalfBodyTransform',
102
+ num_joints_half_body=8,
103
+ prob_half_body=0.3),
104
+ dict(
105
+ type='TopDownGetRandomScaleRotation', rot_factor=40, scale_factor=0.5),
106
+ dict(type='TopDownAffine'),
107
+ dict(type='ToTensor'),
108
+ dict(
109
+ type='NormalizeTensor',
110
+ mean=[0.485, 0.456, 0.406],
111
+ std=[0.229, 0.224, 0.225]),
112
+ dict(type='TopDownGenerateTarget', sigma=2),
113
+ dict(
114
+ type='Collect',
115
+ keys=['img', 'target', 'target_weight'],
116
+ meta_keys=[
117
+ 'image_file', 'joints_3d', 'joints_3d_visible', 'center', 'scale',
118
+ 'rotation', 'bbox_score', 'flip_pairs'
119
+ ]),
120
+ ]
121
+
122
+ val_pipeline = [
123
+ dict(type='LoadImageFromFile'),
124
+ dict(type='TopDownGetBboxCenterScale', padding=1.25),
125
+ dict(type='TopDownAffine'),
126
+ dict(type='ToTensor'),
127
+ dict(
128
+ type='NormalizeTensor',
129
+ mean=[0.485, 0.456, 0.406],
130
+ std=[0.229, 0.224, 0.225]),
131
+ dict(
132
+ type='Collect',
133
+ keys=['img'],
134
+ meta_keys=[
135
+ 'image_file', 'center', 'scale', 'rotation', 'bbox_score',
136
+ 'flip_pairs'
137
+ ]),
138
+ ]
139
+
140
+ test_pipeline = val_pipeline
141
+
142
+ data_root = 'data/coco'
143
+ data = dict(
144
+ samples_per_gpu=32,
145
+ workers_per_gpu=2,
146
+ val_dataloader=dict(samples_per_gpu=32),
147
+ test_dataloader=dict(samples_per_gpu=32),
148
+ train=dict(
149
+ type='TopDownCocoDataset',
150
+ ann_file=f'{data_root}/annotations/person_keypoints_train2017.json',
151
+ img_prefix=f'{data_root}/train2017/',
152
+ data_cfg=data_cfg,
153
+ pipeline=train_pipeline,
154
+ dataset_info={{_base_.dataset_info}}),
155
+ val=dict(
156
+ type='TopDownCocoDataset',
157
+ ann_file=f'{data_root}/annotations/person_keypoints_val2017.json',
158
+ img_prefix=f'{data_root}/val2017/',
159
+ data_cfg=data_cfg,
160
+ pipeline=val_pipeline,
161
+ dataset_info={{_base_.dataset_info}}),
162
+ test=dict(
163
+ type='TopDownCocoDataset',
164
+ ann_file=f'{data_root}/annotations/person_keypoints_val2017.json',
165
+ img_prefix=f'{data_root}/val2017/',
166
+ data_cfg=data_cfg,
167
+ pipeline=test_pipeline,
168
+ dataset_info={{_base_.dataset_info}}),
169
+ )
faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:047c8118fc5ca88ba5ae1fab72f2cd6b070501fe3af2f3cba5cfa9a89b44b03e
3
+ size 167287506
fileservice.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, Request, Response
2
+
3
+ filenames = ["js/poseMaker.js"]
4
+ contents = '\n'.join([open(x).read() for x in filenames])
5
+
6
+ app = FastAPI()
7
+
8
+ @app.middleware("http")
9
+ async def insert_js(request: Request, call_next):
10
+ path = request.scope['path'] # get the request route
11
+ response = await call_next(request)
12
+
13
+ if path == "/":
14
+ response_body = ""
15
+ async for chunk in response.body_iterator:
16
+ response_body += chunk.decode()
17
+
18
+ some_javascript = f"""
19
+ <script type="text/javascript" defer>
20
+ {contents}
21
+ </script>
22
+ """
23
+
24
+ response_body = response_body.replace("</body>", some_javascript + "</body>")
25
+
26
+ del response.headers["content-length"]
27
+
28
+ return Response(
29
+ content=response_body,
30
+ status_code=response.status_code,
31
+ headers=dict(response.headers),
32
+ media_type=response.media_type
33
+ )
34
+
35
+ return response
hrnet_w48_coco_256x192-b9e0b3ab_20200708.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b9e0b3ab0439cb68e166c7543e59d2587cd8d7e9acf5ea62a8378eeb82fb50e5
3
+ size 255011654
js/poseMaker.js ADDED
@@ -0,0 +1,813 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ console.log("hello from poseEditor.js")
2
+ var canvas = null;
3
+ var ctx = null;
4
+ var canvasBg = null;
5
+
6
+ const wheelDisplayTime = 500;
7
+
8
+ const limbSeq = [
9
+ [1, 2], [2, 3], [3, 4], // 右腕
10
+ [1, 5], [5, 6], [6, 7], // 左腕
11
+ [1, 8], [8, 9], [9, 10], // 右胴→右脚
12
+ [1, 11], [11, 12], [12, 13], // 左胴→左脚
13
+ [1, 0], // 首
14
+ [0, 14], [14, 16], // 右目
15
+ [0, 15], [15, 17] // 左目
16
+ ];
17
+
18
+ function findParentNodeIndex(nodeIndex) {
19
+ // limbSeqの各要素の2番目の要素がjointIndexの場合、その要素の1番目の要素を返す
20
+ // 見つからないばあいは-1を返す
21
+ limbIndex = limbSeq.findIndex((limb) => limb[1] === nodeIndex);
22
+ return limbIndex === -1 ? -1 : limbSeq[limbIndex][0];
23
+ }
24
+
25
+ function cutOffLimb(pose, cutOffIndex) {
26
+ console.log(`cutOffLimb: ${cutOffIndex}`);
27
+ // 末端ノードの座標を削除する
28
+ var newPose = deepCopy(pose);
29
+ for (let i = 0; i < 18; i++) {
30
+ if (newPose[i] == null) {continue;}
31
+ // ルートまで検索し、その間にcuttOffIndexがあれば削除
32
+ var curr = i;
33
+ while (curr !== 1) {
34
+ console.log(`checking: ${i} -> ${curr}`);
35
+ let parent = findParentNodeIndex(curr);
36
+ if (parent === cutOffIndex) {
37
+ console.log(`cutOffLimb: ${i} -> ${cutOffIndex}`);
38
+ newPose[i] = null;
39
+ break;
40
+ }
41
+ curr = parent;
42
+ }
43
+ }
44
+ return newPose;
45
+ }
46
+
47
+ function repairPose(sourcePose) {
48
+ // TODO: ループには対応してないかも
49
+ var pose = sourcePose;
50
+ var newPose = new Array(18)
51
+ for (var k = 0; k < 3; k++) {
52
+ var processed = 0; // イテレーション用
53
+ for (let i = 0; i < 18; i++) {
54
+ if (pose[i] == null) {
55
+ let parent = findParentNodeIndex(i);
56
+ if (parent === -1) {continue;} // あり得ない
57
+ if (pose[parent] == null) {
58
+ console.log(`repair failed(A): ${i} -> parent loss`);
59
+ continue;
60
+ }
61
+
62
+ // サンプルデータから引っ張ってくる
63
+ var v = sampleCandidateSource[i].map((x, j) => x - sampleCandidateSource[parent][j]);
64
+ newPose[i] = pose[parent].map((x, j) => x + v[j]);
65
+ console.log(`repaired: ${i} -> ${newPose[newPose.length - 1]}`);
66
+ processed++;
67
+ } else {
68
+ newPose[i] = pose[i].map(x => x);
69
+ }
70
+ }
71
+ if (processed === 0) {break;}
72
+ pose = newPose;
73
+ }
74
+ return newPose;
75
+ }
76
+
77
+ function deepCopy(arr) {
78
+ return JSON.parse(JSON.stringify(arr));
79
+ }
80
+
81
+ function distSq(p0, p1) {
82
+ return (p0[0] - p1[0]) ** 2 + (p0[1] - p1[1]) ** 2;
83
+ }
84
+
85
+ // poseDataの形式:[[[x1, y1], [x2, y2], ...],[[x3, y3], [x4, y4], ...], ...]
86
+ // 各要素が人間
87
+ // 人間の各要素が関節
88
+
89
+ function poseDataToCandidateAndSubset(poseData) {
90
+ let candidate = [];
91
+ let subset = [];
92
+ for (let i = 0; i < poseData.length; i++) {
93
+ let person = poseData[i];
94
+ let subsetElement = [];
95
+ for (let j = 0; j < person.length; j++) {
96
+ candidate.push(person[j]);
97
+ subsetElement.push(candidate.length - 1);
98
+ }
99
+ subset.push(subsetElement);
100
+ }
101
+ return [candidate, subset];
102
+ }
103
+
104
+ // サンプルデータ
105
+ const sampleCandidateSource = [[235, 158],[234, 220],[193, 222],[138, 263],[89, 308],[276, 220],[325, 264],[375, 309],[207, 347],[203, 433],[199, 523],[261, 347],[262, 430],[261, 522],[227, 148],[245, 148],[208, 158],[258, 154]].map((p) => [p[0], p[1] - 70]);
106
+ const sampleSubsetElementSource = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17];
107
+
108
+ // const sampleCandidateSource = [[618.00, 0.00], [618.00, 44.00], [304.00, 81.00], [482.00, 96.00], [66.00, 270.00], [171.00, 280.00], [618.00, 82.00], [307.00, 112.00], [460.00, 143.00], [0.00, 301.00], [65.00, 301.00], [172.00, 303.00], [584.00, 86.00], [275.00, 119.00], [420.00, 139.00], [0.00, 301.00], [41.00, 301.00], [144.00, 303.00], [544.00, 131.00], [348.00, 139.00], [262.00, 160.00], [0.00, 337.00], [52.00, 339.00], [130.00, 348.00], [570.00, 175.00], [283.00, 177.00], [78.00, 338.00], [172.00, 380.00], [651.00, 78.00], [338.00, 111.00], [505.00, 144.00], [92.00, 301.00], [198.00, 305.00], [661.00, 132.00], [349.00, 156.00], [541.00, 179.00], [106.00, 336.00], [203.00, 348.00], [305.00, 159.00], [665.00, 160.00], [563.00, 192.00], [80.00, 343.00], [181.00, 385.00], [614.00, 205.00], [291.00, 220.00], [432.00, 320.00], [152.00, 372.00], [43.00, 380.00], [0.00, 386.00], [623.00, 281.00], [306.00, 290.00], [92.00, 357.00], [509.00, 434.00], [304.00, 357.00], [622.00, 368.00], [47.00, 394.00], [0.00, 395.00], [142.00, 405.00], [535.00, 565.00], [655.00, 200.00], [337.00, 217.00], [467.00, 322.00], [191.00, 372.00], [83.00, 375.00], [344.00, 282.00], [655.00, 282.00], [103.00, 343.00], [237.00, 368.00], [22.00, 377.00], [0.00, 379.00], [460.00, 459.00], [305.00, 352.00], [638.00, 355.00], [0.00, 401.00], [110.00, 412.00], [411.00, 570.00], [608.00, 0.00], [608.00, 40.00], [297.00, 75.00], [469.00, 84.00], [0.00, 261.00], [58.00, 263.00], [165.00, 275.00], [625.00, 0.00], [625.00, 39.00], [309.00, 74.00], [486.00, 83.00], [71.00, 264.00], [180.00, 276.00], [599.00, 0.00], [599.00, 44.00], [284.00, 80.00], [440.00, 93.00], [48.00, 271.00], [0.00, 272.00], [157.00, 277.00], [634.00, 0.00], [633.00, 41.00], [319.00, 77.00], [79.00, 269.00], [190.00, 277.00]];
109
+ // const sampleSubsetElementSource = [1.00,6.00,12.00,18.00,24.00,28.00,33.00,39.00,43.00,49.00,54.00,59.00,65.00,72.00,77.00,84.00,90.00,97.00,32.98,18.00],[5.00,11.00,17.00,23.00,27.00,32.00,37.00,42.00,46.00,-1.00,-1.00,62.00,67.00,-1.00,82.00,88.00,95.00,100.00,25.45,15.00],[4.00,10.00,16.00,22.00,26.00,31.00,36.00,41.00,47.00,51.00,57.00,63.00,66.00,74.00,81.00,87.00,93.00,99.00,26.97,18.00],[3.00,8.00,14.00,19.00,25.00,30.00,35.00,40.00,45.00,52.00,58.00,61.00,70.00,75.00,79.00,86.00,92.00,-1.00,30.45,17.00],[2.00,7.00,13.00,20.00,-1.00,29.00,34.00,38.00,44.00,50.00,53.00,60.00,64.00,71.00,78.00,85.00,91.00,98.00,27.89,17.00],[0.00,-1.00,-1.00,-1.00,-1.00,-1.00,-1.00,-1.00,-1.00,-1.00,-1.00,-1.00,-1.00,-1.00,76.00,83.00,-1.00,96.00,3.33,4.00];
110
+
111
+ function makePoseFromCandidateAndSubsetElement(candidate, subsetElement) {
112
+ var pose = [];
113
+ for (let j = 0 ; j < 18; j++) {
114
+ let i = subsetElement[j];
115
+ pose.push(i < 0 || candidate[i] == null ? null : candidate[i].map((x)=>x));
116
+ }
117
+ return pose;
118
+ }
119
+
120
+ function makePoseDataFromCandidateAndSubset(candidate, subset) {
121
+ return subset.map(subsetElement => makePoseFromCandidateAndSubsetElement(candidate, subsetElement));
122
+ }
123
+
124
+ function addPerson() {
125
+ var dx = Math.random() * 100;
126
+ var dy = Math.random() * 100;
127
+
128
+ poseData.push(
129
+ makePoseFromCandidateAndSubsetElement(
130
+ sampleCandidateSource.map(point => [point[0] + dx, point[1] + dy]),
131
+ sampleSubsetElementSource));
132
+
133
+ addHistory();
134
+ Redraw();
135
+ }
136
+
137
+ function removePerson(personIndex) {
138
+ poseData.splice(personIndex, 1);
139
+ addHistory();
140
+ Redraw();
141
+ }
142
+
143
+ function repairPerson(personIndex) {
144
+ poseData[personIndex] = repairPose(poseData[personIndex]);
145
+ addHistory();
146
+ Redraw();
147
+ }
148
+
149
+ function cutOffPersonLimb(personIndex, limbIndex) {
150
+ poseData[personIndex] = cutOffLimb(poseData[personIndex], limbIndex);
151
+ console.log(poseData[personIndex]);
152
+ console.log(poseData);
153
+ addHistory();
154
+ Redraw();
155
+ }
156
+
157
+ // ドラッグ中の各キーが押されているかどうかのフラグ
158
+ var keyDownFlags = {};
159
+ // マウスカーソル
160
+ var mouseCursor = [-1, -1];
161
+
162
+ function cross(lhs, rhs) {return lhs[0] * rhs[1] - lhs[1] * rhs[0];}
163
+ function dot(lhs, rhs) {return lhs[0] * rhs[0] + lhs[1] * rhs[1];}
164
+ function directedAngleTo(lhs, rhs) {return Math.atan2(cross(lhs, rhs), dot(lhs, rhs));}
165
+
166
+ function isMouseOnCanvas() {
167
+ // mouseCursorがcanvasの範囲内にあるかどうかを判定
168
+ var rect = canvas.getBoundingClientRect();
169
+ var f = 0 <= mouseCursor[0] && mouseCursor[0] <= rect.width && 0 <= mouseCursor[1] && mouseCursor[1] <= rect.height;
170
+ return f;
171
+ }
172
+
173
+ function clearCanvas() {
174
+ var w = canvas.width;
175
+ var h = canvas.height;
176
+ ctx.fillStyle = 'black';
177
+ ctx.fillRect(0, 0, w, h);
178
+ }
179
+
180
+ function drawBackground() {
181
+ if (canvasBg != null) {
182
+ ctx.drawImage(canvasBg, 0, 0);
183
+ }
184
+ }
185
+
186
+ function resizeCanvas(width, height) {
187
+ canvas.width = width ? width : canvas.width;
188
+ canvas.height = height ? height : canvas.height;
189
+ Redraw();
190
+ }
191
+
192
+ function calculateCenter(shape) {
193
+ var center = shape.reduce(function(acc, point) {
194
+ if (point === null) {
195
+ acc[0] += point[0];
196
+ acc[1] += point[1];
197
+ }
198
+ return acc;
199
+ }, [0, 0]);
200
+ center[0] /= shape.length;
201
+ center[1] /= shape.length;
202
+ return center;
203
+ }
204
+
205
+ // v2d -> v3d
206
+ function rotateX(vector, angle) {
207
+ var x = vector[0];
208
+ var y = vector[1];
209
+ var z = 0;
210
+
211
+ // X軸に対して回転する
212
+ var x1 = x;
213
+ var y1 = y * Math.cos(angle) - z * Math.sin(angle);
214
+ var z1 = y * Math.sin(angle) + z * Math.cos(angle);
215
+
216
+ return [x1, y1, z1];
217
+ }
218
+
219
+ // v2d -> v3d
220
+ function rotateY(vector, angle) {
221
+ var x = vector[0];
222
+ var y = vector[1];
223
+ var z = 0;
224
+
225
+ // Y軸に対して回転する
226
+ var x1 = x * Math.cos(angle) + z * Math.sin(angle);
227
+ var y1 = y;
228
+ var z1 = -x * Math.sin(angle) + z * Math.cos(angle);
229
+
230
+ return [x1, y1, z1];
231
+ }
232
+
233
+ // v3d -> v2d
234
+ function perspectiveProjection(vector, cameraDistance) {
235
+ var x = vector[0];
236
+ var y = vector[1];
237
+ var z = vector[2];
238
+
239
+ if (z === 0) {
240
+ return [x, y];
241
+ }
242
+
243
+ var scale = cameraDistance / (cameraDistance - z);
244
+ var x1 = x * scale;
245
+ var y1 = y * scale;
246
+
247
+ return [x1, y1];
248
+ }
249
+
250
+ // v2d -> v3d
251
+ function rotateAndProject(f, p, c, angle) {
252
+ var v = [p[0] - c[0], p[1] - c[1]];
253
+ var v1 = f(v, angle);
254
+ var v2 = perspectiveProjection(v1, 500);
255
+ return [v2[0] + c[0], v2[1] + c[1]];
256
+ }
257
+
258
+ function drawBodyPose() {
259
+ let stickWidth = 4;
260
+ let imageSize = Math.min(canvas.width, canvas.height);
261
+ stickWidth *= imageSize / 512;
262
+
263
+ const colors = [[255, 0, 0], [255, 85, 0], [255, 170, 0], [255, 255, 0], [170, 255, 0], [85, 255, 0], [0, 255, 0],
264
+ [0, 255, 85], [0, 255, 170], [0, 255, 255], [0, 170, 255], [0, 85, 255], [0, 0, 255], [85, 0, 255],
265
+ [170, 0, 255], [255, 0, 255], [255, 0, 170], [255, 0, 85]];
266
+
267
+ ctx.globalAlpha = 0.6;
268
+
269
+ // edge
270
+ for (let i = 0; i < poseData.length; i++) {
271
+ const pose = poseData[i];
272
+
273
+ for (let j = 0; j < 17; j++) {
274
+ const p = pose[limbSeq[j][0]];
275
+ const q = pose[limbSeq[j][1]];
276
+ if (p == null || q == null) continue;
277
+ const [X0, Y0] = p;
278
+ const [X1, Y1] = q;
279
+ let angle = Math.atan2(Y1 - Y0, X1 - X0);
280
+ let magnitude = ((X0 - X1) ** 2 + (Y0 - Y1) ** 2) ** 0.5
281
+ let polygon = new Path2D();
282
+ polygon.ellipse((X0+X1)/2, (Y0+Y1)/2, magnitude / 2, stickWidth, angle, 0, 2 * Math.PI);
283
+ ctx.fillStyle = `rgb(${colors[j].join(',')})`;
284
+ ctx.fill(polygon);
285
+ }
286
+ }
287
+
288
+ ctx.globalAlpha = 1.0;
289
+
290
+ // node
291
+ for (let i = 0; i < poseData.length; i++) {
292
+ const pose = poseData[i];
293
+
294
+ ctx.font = '12px serif';
295
+ for (let j = 0; j < 18; j++) {
296
+ const p = pose[j];
297
+ if (p == null) continue;
298
+ const [x, y] = p;
299
+ ctx.beginPath();
300
+ ctx.arc(x, y, stickWidth, 0, 2 * Math.PI);
301
+ ctx.fillStyle = `rgb(${colors[j].join(',')})`;
302
+ ctx.fill();
303
+ // ctx.fillStyle = 'rgb(255,255,255)'
304
+ // ctx.fillText(j, x-3, y+4);
305
+ }
306
+ }
307
+ }
308
+
309
+ let lastWheeling = 0;
310
+
311
+ function drawUI() {
312
+ if (keyDownFlags['Space'] || keyDownFlags['BracketLeft'] || keyDownFlags['BracketRight'] ||
313
+ new Date().getTime() - lastWheeling < wheelDisplayTime) {
314
+ ctx.beginPath();
315
+ ctx.lineWidth=4;
316
+ ctx.arc(mouseCursor[0], mouseCursor[1], dragRange, 0, 2 * Math.PI);
317
+ ctx.strokeStyle = 'rgb(255,255,255)';
318
+ ctx.stroke();
319
+ }
320
+
321
+ if (isDragging && (dragMode == "rotate" || dragMode == "rotate2")) {
322
+ ctx.beginPath();
323
+ ctx.lineWidth=1;
324
+ ctx.strokeStyle = 'rgb(255,255,255)';
325
+ ctx.moveTo(dragStart[0], dragStart[1]);
326
+ ctx.lineTo(dragStart[0]+rotateBaseVector[0], dragStart[1]+rotateBaseVector[1]);
327
+ ctx.stroke();
328
+ }
329
+
330
+ let operationTextFlags = {
331
+ "Space": "Range Move",
332
+ "AltLeft": "Body Move",
333
+ "AltRight": "Body Move",
334
+ "ControlLeft": "Scale",
335
+ "ControlRight": "Scale",
336
+ "ShiftLeft": "Rotate",
337
+ "ShiftRight": "Rotate",
338
+ "KeyQ": "CutOff",
339
+ "KeyD": "Delete",
340
+ "KeyX": "X-Axis",
341
+ "KeyC": "Y-Axis",
342
+ "KeyR": "Repair",
343
+ }
344
+
345
+ // operationTextFlagsに含まれるものがkeyDownFlagsに含まれるばあい、そのキーの文字列を取得
346
+ let activeOperations = Object.keys(operationTextFlags).filter(key => keyDownFlags[key]);
347
+ if (activeOperations.length > 0) {
348
+ // 左上に表示
349
+ ctx.font = '20px serif';
350
+ ctx.fillStyle = 'rgb(255,255,255)';
351
+ ctx.fillText(operationTextFlags[activeOperations[0]], 10, 30);
352
+ }
353
+ }
354
+
355
+ function Redraw() {
356
+ clearCanvas();
357
+ drawBackground();
358
+ drawBodyPose();
359
+ drawUI();
360
+ }
361
+
362
+ function getNearestNode(p) {
363
+ let minDistSq = Infinity;
364
+ let personIndex = -1;
365
+ let nodeIndex = -1;
366
+ for (let i = 0; i < poseData.length; i++) {
367
+ const pose = poseData[i];
368
+ for (let j = 0; j < pose.length; j++) {
369
+ const q = pose[j];
370
+ if (q == null) continue;
371
+ const d = distSq(p, q);
372
+ if (d < minDistSq) {
373
+ minDistSq = d;
374
+ personIndex = i;
375
+ nodeIndex = j;
376
+ }
377
+ }
378
+ }
379
+ return [personIndex, nodeIndex, Math.sqrt(minDistSq)];
380
+ }
381
+
382
+ let dragRange = 64;
383
+ let dragRangeDelta = 16;
384
+
385
+ // ドラッグ中に座標を保持するための変数
386
+ let isDragging = false;
387
+ let dragStart = [0, 0];
388
+ let dragPersonIndex = -1;
389
+ let dragMarks = [];
390
+ let dragMode = "";
391
+ let rotateBaseVector = null;
392
+ let history = [];
393
+ let historyIndex = 0;
394
+
395
+ function clearHistory() {
396
+ history = [];
397
+ historyIndex = 0;
398
+ }
399
+
400
+ function addHistory() {
401
+ history = history.slice(0, historyIndex);
402
+ history.push(JSON.parse(JSON.stringify(poseData)));
403
+ historyIndex = history.length;
404
+ }
405
+
406
+ function undo() {
407
+ if (1 < historyIndex) {
408
+ historyIndex--;
409
+ poseData = deepCopy(history[historyIndex-1]);
410
+ Redraw();
411
+ }
412
+ }
413
+
414
+ function redo() {
415
+ if (historyIndex < history.length) {
416
+ historyIndex++;
417
+ poseData = deepCopy(history[historyIndex-1]);
418
+ Redraw();
419
+ }
420
+ }
421
+
422
+ function fetchLatestPoseData() {
423
+ return history[historyIndex-1];
424
+ }
425
+
426
+ function getCanvasPosition(event) {
427
+ const rect = canvas.getBoundingClientRect();
428
+ const x = event.clientX - rect.left;
429
+ const y = event.clientY - rect.top;
430
+ return [x, y];
431
+ }
432
+
433
+ function forEachMarkedNodes(fn) {
434
+ for (let i = 0; i < dragMarks.length; i++) {
435
+ for (let j = 0; j < dragMarks[i].length; j++) {
436
+ if (dragMarks[i][j]) {
437
+ fn(i, j, poseData[i][j]);
438
+ }
439
+ }
440
+ }
441
+ }
442
+
443
+ // Canvas要素上でマウスが押された場合に呼び出される関数
444
+ function handleMouseDown(event) {
445
+ const p = getCanvasPosition(event);
446
+ const [personIndex, nodeIndex, minDist] = getNearestNode(p);
447
+
448
+ if (keyDownFlags["KeyD"]) {removePerson(personIndex);return;}
449
+ if (keyDownFlags["KeyR"]) {repairPerson(personIndex);return;}
450
+
451
+ if (keyDownFlags["KeyQ"] && minDist < 16) {
452
+ console.log("pressed KeyQ");
453
+ cutOffPersonLimb(personIndex, nodeIndex);
454
+ return;
455
+ }
456
+
457
+ // ドラッグ処理の開始
458
+ dragStart = p;
459
+ dragMarks = poseData.map(pose => pose.map(node => false));
460
+
461
+ if (event.altKey || event.ctrlKey || event.shiftKey ||
462
+ keyDownFlags["KeyX"] || keyDownFlags["KeyC"]) {
463
+ // dragMarksを設定
464
+ dragMarks[personIndex] =
465
+ poseData[personIndex].map((node) => node != null);
466
+ isDragging = true;
467
+ if (event.altKey) {
468
+ dragMode = "move";
469
+ } else if (event.ctrlKey) {
470
+ dragMode = "scale";
471
+ } else if (event.shiftKey) {
472
+ dragMode = "rotate";
473
+ rotateBaseVector = [0, 0];
474
+ } else if (keyDownFlags["KeyX"]) {
475
+ dragMode = "rotateX";
476
+ } else if (keyDownFlags["KeyC"]) {
477
+ dragMode = "rotateY";
478
+ }
479
+ } else if (keyDownFlags["Space"]) {
480
+ dragMarks[personIndex] =
481
+ poseData[personIndex].map(
482
+ (node) => node != null && distSq(p, node) < dragRange ** 2);
483
+ isDragging = dragMarks[personIndex].some((mark) => mark);
484
+ dragMode = "move";
485
+ } else if (minDist < 16) {
486
+ dragMarks[personIndex][nodeIndex] = true;
487
+ isDragging = true;
488
+ dragMode = "move";
489
+ }
490
+ }
491
+
492
+ // Canvas要素上でマウスが動いた場合に呼び出される関数
493
+ function handleMouseMove(event) {
494
+ mouseCursor = getCanvasPosition(event);
495
+ if (isDragging) {
496
+ const p = getCanvasPosition(event);
497
+ const dragOffset = [p[0] - dragStart[0], p[1] - dragStart[1]];
498
+ const latestPoseData = fetchLatestPoseData();
499
+
500
+ if (dragMode == "scale") {
501
+ // 拡大縮小
502
+ let xScale = 1 + dragOffset[0] / canvas.width;
503
+ let yScale = 1 + dragOffset[0] / canvas.height;
504
+ forEachMarkedNodes((i, j, node) => {
505
+ const lp = latestPoseData[i][j];
506
+ node[0] = (lp[0] - dragStart[0]) * xScale + dragStart[0];
507
+ node[1] = (lp[1] - dragStart[1]) * yScale + dragStart[1];
508
+ });
509
+ } else if (dragMode == "rotate") {
510
+ rotateBaseVector = dragOffset;
511
+ if (!event.shiftKey) {
512
+ dragMode = "rotate2";
513
+ }
514
+ } else if (dragMode == "rotate2") {
515
+ // 回転
516
+ let angle = directedAngleTo(rotateBaseVector, dragOffset);
517
+ forEachMarkedNodes((i, j, node) => {
518
+ const lp = latestPoseData[i][j];
519
+ let x = lp[0] - dragStart[0];
520
+ let y = lp[1] - dragStart[1];
521
+ let sin = Math.sin(angle);
522
+ let cos = Math.cos(angle);
523
+ node[0] = x * cos - y * sin + dragStart[0];
524
+ node[1] = x * sin + y * cos + dragStart[1];
525
+ });
526
+ } else if (dragMode == "rotateX") {
527
+ const center = dragStart;
528
+ const angle = dragOffset[1] / -40;
529
+ forEachMarkedNodes((i, j, node) => {
530
+ const lp = latestPoseData[i][j];
531
+ const np = rotateAndProject(rotateX, lp, center, angle);
532
+ node[0] = np[0];
533
+ node[1] = np[1];
534
+ });
535
+ } else if (dragMode == "rotateY") {
536
+ const center = dragStart;
537
+ const angle = dragOffset[0] / 40;
538
+ forEachMarkedNodes((i, j, node) => {
539
+ const lp = latestPoseData[i][j];
540
+ const np = rotateAndProject(rotateY, lp, center, angle);
541
+ node[0] = np[0];
542
+ node[1] = np[1];
543
+ });
544
+ } else if (dragMode == "move") {
545
+ // 移動
546
+ forEachMarkedNodes((i, j, node) => {
547
+ const lp = latestPoseData[i][j];
548
+ node[0] = lp[0] + dragOffset[0];
549
+ node[1] = lp[1] + dragOffset[1];
550
+ });
551
+ }
552
+ }
553
+
554
+ Redraw();
555
+ }
556
+
557
+ function handleMouseUp(event) {
558
+ isDragging = false;
559
+ addHistory();
560
+ Redraw();
561
+ }
562
+
563
+ function handleMouseLeave(event) {
564
+ mouseCursor = [-1,-1];
565
+ handleMouseUp(event);
566
+ keyDownFlags = {};
567
+ }
568
+
569
+ function ModifyDragRange(delta) { dragRange = Math.max(dragRangeDelta, Math.min(512, dragRange + delta)); }
570
+
571
+ document.addEventListener('wheel', function(event) {
572
+ if (!isMouseOnCanvas()) {return;}
573
+ if (!event.altKey && !keyDownFlags['Space']) {return;}
574
+
575
+ event.preventDefault();
576
+ const deltaY = event.deltaY;
577
+ if (deltaY < 0) {ModifyDragRange(-dragRangeDelta);}
578
+ if (0 < deltaY) {ModifyDragRange(dragRangeDelta);}
579
+ lastWheeling = new Date().getTime();
580
+ Redraw();
581
+ window.setTimeout(function() { Redraw(); }, wheelDisplayTime+10);
582
+ }, {passive: false});
583
+
584
+ document.addEventListener("keydown", (event) => {
585
+ if (!isMouseOnCanvas()) {return;}
586
+
587
+ if (event.code == "BracketLeft") { ModifyDragRange(-dragRangeDelta); }
588
+ if (event.code == "BracketRight") { ModifyDragRange(dragRangeDelta); }
589
+ keyDownFlags[event.code] = true;
590
+ Redraw();
591
+ event.preventDefault();
592
+ });
593
+ document.addEventListener("keyup", (event) => {
594
+ if (!isMouseOnCanvas()) {return;}
595
+
596
+ keyDownFlags[event.code] = false;
597
+ if (event.ctrlKey && event.code == "KeyE") {
598
+ addPerson();
599
+ } else if (event.ctrlKey && event.code == "KeyZ") {
600
+ if (event.shiftKey) {
601
+ redo();
602
+ } else {
603
+ undo();
604
+ }
605
+ }
606
+ Redraw();
607
+ event.preventDefault();
608
+ });
609
+
610
+ function initializeEditor() {
611
+ console.log("initializeEditor");
612
+
613
+ canvas = document.getElementById('canvas');
614
+ ctx = canvas.getContext('2d');
615
+
616
+ canvas.addEventListener('mousedown', handleMouseDown);
617
+ canvas.addEventListener('mousemove', handleMouseMove);
618
+ canvas.addEventListener('mouseup', handleMouseUp);
619
+ canvas.addEventListener('mouseleave', handleMouseLeave);
620
+ poseData = [];
621
+ clearHistory();
622
+ }
623
+
624
+ function importPose(jsonData) {
625
+ if (jsonData != null) {
626
+ newPoseData = makePoseDataFromCandidateAndSubset(jsonData.candidate, jsonData.subset);
627
+ } else {
628
+ newPoseData = makePoseDataFromCandidateAndSubset(sampleCandidateSource, [sampleSubsetElementSource]);
629
+ }
630
+ poseData = poseData.concat(newPoseData);
631
+ addHistory();
632
+ Redraw();
633
+ }
634
+
635
+ /*
636
+ function savePose() {
637
+ const canvasUrl = canvas.toDataURL();
638
+
639
+ const createEl = document.createElement('a');
640
+ createEl.href = canvasUrl;
641
+
642
+ // This is the name of our downloaded file
643
+ createEl.download = "pose.png";
644
+
645
+ createEl.click();
646
+ createEl.remove();
647
+
648
+ var [candidate, subset] = poseDataToCandidateAndSubset(poseData);
649
+ return {candidate: candidate, subset: subset};
650
+ }
651
+ */
652
+
653
+ // crc32
654
+ // CRC32を初期化
655
+ function initCrc32Table() {
656
+ const crcTable = new Uint32Array(256);
657
+ for (let i = 0; i < 256; i++) {
658
+ let c = i;
659
+ for (let j = 0; j < 8; j++) {
660
+ c = (c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1);
661
+ }
662
+ crcTable[i] = c;
663
+ }
664
+ return crcTable;
665
+ }
666
+
667
+ // データのCRC32を計算
668
+ function getCrc32(data, crc=0) {
669
+ const crcTable = initCrc32Table();
670
+ crc = (crc ^ 0xFFFFFFFF) >>> 0;
671
+ for (let i = 0; i < data.length; i++) {
672
+ crc = crcTable[(crc ^ data[i]) & 0xFF] ^ (crc >>> 8);
673
+ }
674
+ return (crc ^ 0xFFFFFFFF) >>> 0;
675
+ }
676
+
677
+ function stringToUint8Array(str) {
678
+ var arr = new Uint8Array(str.length);
679
+ for (var i = 0; i < str.length; i++) {
680
+ arr[i] = str.charCodeAt(i);
681
+ }
682
+ return arr;
683
+ }
684
+
685
+ function base64ToUint8Array(base64Str) {
686
+ return stringToUint8Array(atob(base64Str));
687
+ }
688
+
689
+ function visitPng(png, type) {
690
+ var dataLength;
691
+ var chunkType;
692
+ var nextChunkPos;
693
+ var Signature = String.fromCharCode(137, 80, 78, 71, 13, 10, 26, 10);
694
+ var rpos = 0;
695
+
696
+ // シグネチャの確認
697
+ if (String.fromCharCode.apply(null, png.subarray(rpos, rpos += 8)) !== Signature) {
698
+ throw new Error('invalid signature');
699
+ }
700
+
701
+ // チャンクの探索
702
+ while (rpos < png.length) {
703
+ dataLength = (
704
+ (png[rpos++] << 24) |
705
+ (png[rpos++] << 16) |
706
+ (png[rpos++] << 8) |
707
+ (png[rpos++] )
708
+ ) >>> 0;
709
+
710
+ nextChunkPos = rpos + dataLength + 8;
711
+
712
+ chunkType = String.fromCharCode.apply(null, png.subarray(rpos, rpos += 4));
713
+
714
+ if (chunkType === type) {
715
+ return [rpos - 8, dataLength, nextChunkPos];
716
+ }
717
+
718
+ rpos = nextChunkPos;
719
+ }
720
+ }
721
+
722
+ function createChunk(type, data) {
723
+ var dataLength = data.length;
724
+ var chunk = new Uint8Array(4 + 4 + dataLength + 4);
725
+ var type = stringToUint8Array(type);
726
+ var pos = 0;
727
+
728
+ // length
729
+ chunk[pos++] = (dataLength >> 24) & 0xff;
730
+ chunk[pos++] = (dataLength >> 16) & 0xff;
731
+ chunk[pos++] = (dataLength >> 8) & 0xff;
732
+ chunk[pos++] = (dataLength ) & 0xff;
733
+
734
+ // type
735
+ chunk[pos++] = type[0];
736
+ chunk[pos++] = type[1];
737
+ chunk[pos++] = type[2];
738
+ chunk[pos++] = type[3];
739
+
740
+ // data
741
+ for (let i = 0; i < dataLength; ++i) {
742
+ chunk[pos++] = data[i];
743
+ }
744
+
745
+ //crc
746
+ initCrc32Table();
747
+ let crc = getCrc32(type);
748
+ crc = getCrc32(data, crc);
749
+ chunk[pos++] = (crc >> 24) & 0xff;
750
+ chunk[pos++] = (crc >> 16) & 0xff;
751
+ chunk[pos++] = (crc >> 8) & 0xff;
752
+ chunk[pos++] = (crc ) & 0xff;
753
+
754
+ return chunk;
755
+ }
756
+
757
+ function insertChunk(destBuffer, sourceBuffer, rpos, chunk) {
758
+ var pos = 0;
759
+
760
+ // IDAT チャンクの前までコピー
761
+ destBuffer.set(sourceBuffer.subarray(0, rpos), pos);
762
+ pos += rpos;
763
+
764
+ // hoGe チャンクをコピー
765
+ destBuffer.set(chunk, pos);
766
+ pos += chunk.length;
767
+
768
+ // IDAT チャンク以降をコピー
769
+ destBuffer.set(sourceBuffer.subarray(rpos), pos);
770
+ }
771
+
772
+ function mergeCanvasWithPose(keyword, content) {
773
+ const canvasUrl = canvas.toDataURL();
774
+
775
+ var insertion = stringToUint8Array(`${keyword}\0${content}`);
776
+ var chunk = createChunk("tEXt", insertion);
777
+ var sourceBuffer = base64ToUint8Array(canvasUrl.split(',')[1]);
778
+ var destBuffer = new Uint8Array(sourceBuffer.length + insertion.length + 12);
779
+
780
+ var [rpos, dataLength, nextChunkPos] = visitPng(sourceBuffer, "IHDR");
781
+ insertChunk(destBuffer, sourceBuffer, nextChunkPos, chunk);
782
+
783
+ var blob = new Blob([destBuffer], {type: "image/png"});
784
+ var url = URL.createObjectURL(blob);
785
+ return url;
786
+ }
787
+
788
+ function savePose() {
789
+ var [candidate, subset] = poseDataToCandidateAndSubset(poseData);
790
+ let jsonData = {candidate: candidate, subset: subset};
791
+
792
+ var url = mergeCanvasWithPose("openpose", JSON.stringify(jsonData));
793
+
794
+ const createEl = document.createElement('a');
795
+ createEl.href = url;
796
+
797
+ // This is the name of our downloaded file
798
+ createEl.download = "pose.png";
799
+
800
+ createEl.click();
801
+ createEl.remove();
802
+
803
+ return jsonData;
804
+ }
805
+
806
+ function importBackground(image) {
807
+ let m = new Image();
808
+ m.src = image;
809
+ m.onload = function() {
810
+ canvasBg = m;
811
+ Redraw();
812
+ }
813
+ }
main.py ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import json as js
3
+ import util
4
+ from fileservice import app
5
+ from pose import infer, draw
6
+
7
+
8
+ def image_changed(image):
9
+ if image == None:
10
+ return "estimation", {}
11
+
12
+ if 'openpose' in image.info:
13
+ print("pose found")
14
+ jsonText = image.info['openpose']
15
+ jsonObj = js.loads(jsonText)
16
+ subset = jsonObj['subset']
17
+ return f"""{image.width}px x {image.height}px, {len(subset)} indivisual(s)""", jsonText
18
+ else:
19
+ print("pose not found")
20
+ pose_result, returned_outputs = infer(util.pil2cv(image))
21
+
22
+ candidate = []
23
+ subset = []
24
+ for d in pose_result:
25
+ n = len(candidate)
26
+ if d['bbox'][4] < 0.9:
27
+ continue
28
+ keypoints = d['keypoints'][:, :2].tolist()
29
+ midpoint = [(keypoints[5][0] + keypoints[6][0]) / 2, (keypoints[5][1] + keypoints[6][1]) / 2]
30
+ keypoints.append(midpoint)
31
+ candidate.extend(util.convert_keypoints(keypoints))
32
+ m = len(candidate)
33
+ subset.append([j for j in range(n, m)])
34
+
35
+ jsonText = "{ \"candidate\": " + util.candidate_to_json_string(candidate) + ", \"subset\": " + util.subset_to_json_string(subset) + " }"
36
+ return f"""{image.width}px x {image.height}px, {len(subset)} indivisual(s)""", jsonText
37
+
38
+ html_text = f"""
39
+ <canvas id="canvas" width="512" height="512"></canvas><img id="canvas-background" style="display:none;"/>
40
+ """
41
+
42
+ with gr.Blocks(css="""button { min-width: 80px; }""") as demo:
43
+ with gr.Row():
44
+ with gr.Column(scale=1):
45
+ width = gr.Slider(label="Width", minimum=512, maximum=1024, step=64, value=512, interactive=True)
46
+ height = gr.Slider(label="Height", minimum=512, maximum=1024, step=64, value=512, interactive=True)
47
+ with gr.Accordion(label="Pose estimation", open=False):
48
+ source = gr.Image(type="pil")
49
+ estimationResult = gr.Markdown("""estimation""")
50
+ with gr.Row():
51
+ with gr.Column(min_width=80):
52
+ applySizeBtn = gr.Button(value="Apply size")
53
+ with gr.Column(min_width=80):
54
+ replaceBtn = gr.Button(value="Replace")
55
+ with gr.Column(min_width=80):
56
+ importBtn = gr.Button(value="Import")
57
+ with gr.Column(min_width=80):
58
+ bgBtn = gr.Button(value="Background")
59
+ with gr.Accordion(label="Json", open=False):
60
+ with gr.Row():
61
+ with gr.Column(min_width=80):
62
+ replaceWithJsonBtn = gr.Button(value="Replace")
63
+ with gr.Column(min_width=80):
64
+ importJsonBtn = gr.Button(value="Import")
65
+ gr.Markdown("""
66
+ | inout | how to |
67
+ | -----------------| ----------------------------------------------------------------------------------------- |
68
+ | Import | Paste json to "Json source" and click "Read", edit the width/height, then click "Replace" or "Import". |
69
+ | Export | click "Save" and "Copy to clipboard" of "Json" section. |
70
+ """)
71
+ json = gr.JSON(label="Json")
72
+ jsonSource = gr.Textbox(label="Json source", lines=10)
73
+ with gr.Accordion(label="Notes", open=False):
74
+ gr.Markdown("""
75
+ #### How to bring pose to ControlNet
76
+ 1. Press **Save** button
77
+ 2. **Drag** the file placed at the bottom left corder of browser
78
+ 3. **Drop** the file into ControlNet
79
+
80
+ #### Reuse pose image
81
+ Pose image generated by this tool has pose data in the image itself. You can reuse pose information by loading it as the image source instead of a regular image.
82
+
83
+ #### Points to note for pseudo-3D rotation
84
+ When performing pseudo-3D rotation on the X and Y axes, the projection is converted to 2D and Z-axis information is lost when the mouse button is released. This means that if you finish dragging while the shape is collapsed, you may not be able to restore it to its original state. In such a case, please use the "undo" function.
85
+
86
+ #### Pose estimation
87
+ In this project, MMPose is used for pose estimation.
88
+ """)
89
+ with gr.Column(scale=2):
90
+ html = gr.HTML(html_text)
91
+ with gr.Row():
92
+ with gr.Column(scale=1, min_width=60):
93
+ saveBtn = gr.Button(value="Save")
94
+ with gr.Column(scale=7):
95
+ gr.Markdown("""
96
+ - "ctrl + drag" to **scale**
97
+ - "alt + drag" to **move**
98
+ - "shift + drag" to **rotate** (move right first, release shift, then up or down)
99
+ - "space + drag" to **range-move**
100
+ - "[", "]" or "Alt + wheel" or "Space + wheel" to shrink or expand **range**
101
+ - "ctrl + Z", "shift + ctrl + Z" to **undo**, **redo**
102
+ - "ctrl + E" **add** new person
103
+ - "D + click" to **delete** person
104
+ - "Q + click" to **cut off** limb
105
+ - "X + drag" to **x-axis** pseudo-3D rotation
106
+ - "C + drag" to **y-axis** pseudo-3D rotation
107
+ - "R + click" to **repair**
108
+
109
+ When using Q, X, C, R, pressing and dont release until the operation is complete.
110
+
111
+ [Contact us for feature requests or bug reports (anonymous)](https://t.co/UC3jJOJJtS)
112
+ """)
113
+
114
+ width.change(fn=None, inputs=[width], _js="(w) => { resizeCanvas(w,null); }")
115
+ height.change(fn=None, inputs=[height], _js="(h) => { resizeCanvas(null,h); }")
116
+
117
+ source.change(
118
+ fn = image_changed,
119
+ inputs = [source],
120
+ outputs = [estimationResult, json])
121
+ applySizeBtn.click(
122
+ fn = lambda x: (x.width, x.height),
123
+ inputs = [source],
124
+ outputs = [width, height])
125
+ replaceBtn.click(
126
+ fn = None,
127
+ inputs = [json],
128
+ outputs = [],
129
+ _js="(json) => { initializeEditor(); importPose(json); return []; }")
130
+ importBtn.click(
131
+ fn = None,
132
+ inputs = [json],
133
+ outputs = [],
134
+ _js="(json) => { importPose(json); return []; }")
135
+ bgBtn.click(
136
+ fn = None,
137
+ inputs = [source],
138
+ outputs = [],
139
+ _js="(image) => { importBackground(image); return []; }"
140
+ )
141
+
142
+ saveBtn.click(
143
+ fn = None,
144
+ inputs = [], outputs = [json],
145
+ _js="() => { return [savePose()]; }")
146
+ jsonSource.change(
147
+ fn = lambda x: x,
148
+ inputs = [jsonSource], outputs = [json])
149
+ replaceWithJsonBtn.click(
150
+ fn = None,
151
+ inputs = [json],
152
+ outputs = [],
153
+ _js="(json) => { initializeEditor(); importPose(json); return []; }")
154
+ importJsonBtn.click(
155
+ fn = None,
156
+ inputs = [json],
157
+ outputs = [],
158
+ _js="(json) => { importPose(json); return []; }")
159
+ demo.load(fn=None, inputs=[], outputs=[], _js="() => { initializeEditor(); importPose(); return []; }")
160
+
161
+ print("mount")
162
+ gr.mount_gradio_app(app, demo, path="/")
pose.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from mmpose.apis import (inference_top_down_pose_model, init_pose_model,
2
+ process_mmdet_results, vis_pose_result)
3
+ from mmpose.datasets import DatasetInfo
4
+ from mmdet.apis import inference_detector, init_detector
5
+
6
+ det_model = init_detector(
7
+ "./external/faster_rcnn_r50_fpn_coco.py",
8
+ "./faster_rcnn_r50_fpn_1x_coco_20200130-047c8118.pth",
9
+ device="cpu")
10
+ pose_model = init_pose_model(
11
+ "./external/hrnet_w48_coco_256x192.py",
12
+ "./hrnet_w48_coco_256x192-b9e0b3ab_20200708.pth",
13
+ device="cpu")
14
+
15
+ dataset = pose_model.cfg.data['test']['type']
16
+ dataset_info = pose_model.cfg.data['test'].get('dataset_info', None)
17
+
18
+ dataset_info = DatasetInfo(dataset_info)
19
+
20
+ def infer(image):
21
+ mmdet_results = inference_detector(det_model, image)
22
+ person_results = process_mmdet_results(mmdet_results, 1)
23
+
24
+ pose_results, returned_outputs = inference_top_down_pose_model(
25
+ pose_model,
26
+ image,
27
+ person_results,
28
+ bbox_thr=0.3,
29
+ format='xyxy',
30
+ dataset=dataset,
31
+ dataset_info=dataset_info,
32
+ return_heatmap=False,
33
+ outputs=None)
34
+
35
+ return pose_results, returned_outputs
36
+
37
+ def draw(image, results):
38
+ return vis_pose_result(
39
+ pose_model,
40
+ image,
41
+ results,
42
+ dataset=dataset,
43
+ dataset_info=dataset_info,
44
+ kpt_score_thr=0.3,
45
+ radius=4,
46
+ thickness=3,
47
+ show=False,
48
+ out_file=None)
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ fastapi==0.92.0
2
+ gradio==3.18.0
3
+ numpy==1.23.5
4
+ opencv_python
5
+ scipy
6
+ torch
7
+ torchvision
8
+ openmim
util.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import cv2
3
+
4
+ def pil2cv(image):
5
+ ''' PIL型 -> OpenCV型 '''
6
+ new_image = np.array(image, dtype=np.uint8)
7
+ if new_image.ndim == 2: # モノクロ
8
+ pass
9
+ elif new_image.shape[2] == 3: # カラー
10
+ new_image = cv2.cvtColor(new_image, cv2.COLOR_RGB2BGR)
11
+ elif new_image.shape[2] == 4: # 透過
12
+ new_image = cv2.cvtColor(new_image, cv2.COLOR_RGBA2BGRA)
13
+ return new_image
14
+
15
+ def candidate_to_json_string(arr):
16
+ a = [f'[{x:.2f}, {y:.2f}]' for x, y, *_ in arr]
17
+ return '[' + ', '.join(a) + ']'
18
+
19
+ # make subset to json
20
+ def subset_to_json_string(arr):
21
+ arr_str = ','.join(['[' + ','.join([f'{num:.2f}' for num in row]) + ']' for row in arr])
22
+ return '[' + arr_str + ']'
23
+
24
+ keypoint_index_mapping = [
25
+ 0,
26
+ 17,
27
+ 6,
28
+ 8,
29
+ 10,
30
+ 5,
31
+ 7,
32
+ 9,
33
+ 12,
34
+ 14,
35
+ 16,
36
+ 11,
37
+ 13,
38
+ 15,
39
+ 2,
40
+ 1,
41
+ 4,
42
+ 3,
43
+ ]
44
+
45
+ def convert_keypoints(keypoints):
46
+ return [keypoints[i] for i in keypoint_index_mapping]