python-wml 3.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of python-wml might be problematic. Click here for more details.
- python_wml-3.0.0.dist-info/LICENSE +23 -0
- python_wml-3.0.0.dist-info/METADATA +51 -0
- python_wml-3.0.0.dist-info/RECORD +164 -0
- python_wml-3.0.0.dist-info/WHEEL +5 -0
- python_wml-3.0.0.dist-info/top_level.txt +1 -0
- wml/__init__.py +0 -0
- wml/basic_data_def/__init__.py +2 -0
- wml/basic_data_def/detection_data_def.py +279 -0
- wml/basic_data_def/io_data_def.py +2 -0
- wml/basic_img_utils.py +816 -0
- wml/img_patch.py +92 -0
- wml/img_utils.py +571 -0
- wml/iotoolkit/__init__.py +17 -0
- wml/iotoolkit/aic_keypoint.py +115 -0
- wml/iotoolkit/baidu_mask_toolkit.py +244 -0
- wml/iotoolkit/base_dataset.py +210 -0
- wml/iotoolkit/bboxes_statistics.py +515 -0
- wml/iotoolkit/build.py +0 -0
- wml/iotoolkit/cityscapes_toolkit.py +183 -0
- wml/iotoolkit/classification_data_statistics.py +25 -0
- wml/iotoolkit/coco_data_fwd.py +225 -0
- wml/iotoolkit/coco_keypoints.py +118 -0
- wml/iotoolkit/coco_keypoints_fmt2.py +103 -0
- wml/iotoolkit/coco_toolkit.py +397 -0
- wml/iotoolkit/coco_wholebody.py +269 -0
- wml/iotoolkit/common.py +108 -0
- wml/iotoolkit/crowd_pose.py +146 -0
- wml/iotoolkit/fast_labelme.py +110 -0
- wml/iotoolkit/image_folder.py +95 -0
- wml/iotoolkit/imgs_cache.py +58 -0
- wml/iotoolkit/imgs_reader_mt.py +73 -0
- wml/iotoolkit/labelme_base.py +102 -0
- wml/iotoolkit/labelme_json_to_img.py +49 -0
- wml/iotoolkit/labelme_toolkit.py +117 -0
- wml/iotoolkit/labelme_toolkit_fwd.py +733 -0
- wml/iotoolkit/labelmemckeypoints_dataset.py +169 -0
- wml/iotoolkit/lspet.py +48 -0
- wml/iotoolkit/mapillary_vistas_toolkit.py +269 -0
- wml/iotoolkit/mat_data.py +90 -0
- wml/iotoolkit/mckeypoints_statistics.py +28 -0
- wml/iotoolkit/mot_datasets.py +62 -0
- wml/iotoolkit/mpii.py +108 -0
- wml/iotoolkit/npmckeypoints_dataset.py +164 -0
- wml/iotoolkit/o365_to_coco.py +136 -0
- wml/iotoolkit/object365_toolkit.py +156 -0
- wml/iotoolkit/object365v2_toolkit.py +71 -0
- wml/iotoolkit/pascal_voc_data.py +51 -0
- wml/iotoolkit/pascal_voc_toolkit.py +194 -0
- wml/iotoolkit/pascal_voc_toolkit_fwd.py +473 -0
- wml/iotoolkit/penn_action.py +57 -0
- wml/iotoolkit/rawframe_dataset.py +129 -0
- wml/iotoolkit/rewrite_pascal_voc.py +28 -0
- wml/iotoolkit/semantic_data.py +49 -0
- wml/iotoolkit/split_file_by_type.py +29 -0
- wml/iotoolkit/sports_mot_datasets.py +78 -0
- wml/iotoolkit/vis_objectdetection_dataset.py +70 -0
- wml/iotoolkit/vis_torch_data.py +39 -0
- wml/iotoolkit/yolo_toolkit.py +38 -0
- wml/object_detection2/__init__.py +4 -0
- wml/object_detection2/basic_visualization.py +37 -0
- wml/object_detection2/bboxes.py +812 -0
- wml/object_detection2/data_process_toolkit.py +146 -0
- wml/object_detection2/keypoints.py +292 -0
- wml/object_detection2/mask.py +120 -0
- wml/object_detection2/metrics/__init__.py +3 -0
- wml/object_detection2/metrics/build.py +15 -0
- wml/object_detection2/metrics/classifier_toolkit.py +440 -0
- wml/object_detection2/metrics/common.py +71 -0
- wml/object_detection2/metrics/mckps_toolkit.py +338 -0
- wml/object_detection2/metrics/toolkit.py +1953 -0
- wml/object_detection2/npod_toolkit.py +361 -0
- wml/object_detection2/odtools.py +243 -0
- wml/object_detection2/standard_names.py +75 -0
- wml/object_detection2/visualization.py +956 -0
- wml/object_detection2/wmath.py +34 -0
- wml/semantic/__init__.py +0 -0
- wml/semantic/basic_toolkit.py +65 -0
- wml/semantic/mask_utils.py +156 -0
- wml/semantic/semantic_test.py +21 -0
- wml/semantic/structures.py +1 -0
- wml/semantic/toolkit.py +105 -0
- wml/semantic/visualization_utils.py +658 -0
- wml/threadtoolkit.py +50 -0
- wml/walgorithm.py +228 -0
- wml/wcollections.py +212 -0
- wml/wfilesystem.py +487 -0
- wml/wml_utils.py +657 -0
- wml/wstructures/__init__.py +4 -0
- wml/wstructures/common.py +9 -0
- wml/wstructures/keypoints_train_toolkit.py +149 -0
- wml/wstructures/kps_structures.py +579 -0
- wml/wstructures/mask_structures.py +1161 -0
- wml/wtorch/__init__.py +8 -0
- wml/wtorch/bboxes.py +104 -0
- wml/wtorch/classes_suppression.py +24 -0
- wml/wtorch/conv_module.py +181 -0
- wml/wtorch/conv_ws.py +144 -0
- wml/wtorch/data/__init__.py +16 -0
- wml/wtorch/data/_utils/__init__.py +45 -0
- wml/wtorch/data/_utils/collate.py +183 -0
- wml/wtorch/data/_utils/fetch.py +47 -0
- wml/wtorch/data/_utils/pin_memory.py +121 -0
- wml/wtorch/data/_utils/signal_handling.py +72 -0
- wml/wtorch/data/_utils/worker.py +227 -0
- wml/wtorch/data/base_data_loader_iter.py +93 -0
- wml/wtorch/data/dataloader.py +501 -0
- wml/wtorch/data/datapipes/__init__.py +1 -0
- wml/wtorch/data/datapipes/iter/__init__.py +12 -0
- wml/wtorch/data/datapipes/iter/batch.py +126 -0
- wml/wtorch/data/datapipes/iter/callable.py +92 -0
- wml/wtorch/data/datapipes/iter/listdirfiles.py +37 -0
- wml/wtorch/data/datapipes/iter/loadfilesfromdisk.py +30 -0
- wml/wtorch/data/datapipes/iter/readfilesfromtar.py +60 -0
- wml/wtorch/data/datapipes/iter/readfilesfromzip.py +63 -0
- wml/wtorch/data/datapipes/iter/sampler.py +94 -0
- wml/wtorch/data/datapipes/utils/__init__.py +0 -0
- wml/wtorch/data/datapipes/utils/common.py +65 -0
- wml/wtorch/data/dataset.py +354 -0
- wml/wtorch/data/datasets/__init__.py +4 -0
- wml/wtorch/data/datasets/common.py +53 -0
- wml/wtorch/data/datasets/listdirfilesdataset.py +36 -0
- wml/wtorch/data/datasets/loadfilesfromdiskdataset.py +30 -0
- wml/wtorch/data/distributed.py +135 -0
- wml/wtorch/data/multi_processing_data_loader_iter.py +866 -0
- wml/wtorch/data/sampler.py +267 -0
- wml/wtorch/data/single_process_data_loader_iter.py +24 -0
- wml/wtorch/data/test_data_loader.py +26 -0
- wml/wtorch/dataset_toolkit.py +67 -0
- wml/wtorch/depthwise_separable_conv_module.py +98 -0
- wml/wtorch/dist.py +591 -0
- wml/wtorch/dropblock/__init__.py +6 -0
- wml/wtorch/dropblock/dropblock.py +228 -0
- wml/wtorch/dropblock/dropout.py +40 -0
- wml/wtorch/dropblock/scheduler.py +48 -0
- wml/wtorch/ema.py +61 -0
- wml/wtorch/fc_module.py +73 -0
- wml/wtorch/functional.py +34 -0
- wml/wtorch/iter_dataset.py +26 -0
- wml/wtorch/loss.py +69 -0
- wml/wtorch/nets/__init__.py +0 -0
- wml/wtorch/nets/ckpt_toolkit.py +219 -0
- wml/wtorch/nets/fpn.py +276 -0
- wml/wtorch/nets/hrnet/__init__.py +0 -0
- wml/wtorch/nets/hrnet/config.py +2 -0
- wml/wtorch/nets/hrnet/hrnet.py +494 -0
- wml/wtorch/nets/misc.py +249 -0
- wml/wtorch/nets/resnet/__init__.py +0 -0
- wml/wtorch/nets/resnet/layers/__init__.py +17 -0
- wml/wtorch/nets/resnet/layers/aspp.py +144 -0
- wml/wtorch/nets/resnet/layers/batch_norm.py +231 -0
- wml/wtorch/nets/resnet/layers/blocks.py +111 -0
- wml/wtorch/nets/resnet/layers/wrappers.py +110 -0
- wml/wtorch/nets/resnet/r50_config.py +38 -0
- wml/wtorch/nets/resnet/resnet.py +691 -0
- wml/wtorch/nets/shape_spec.py +20 -0
- wml/wtorch/nets/simple_fpn.py +101 -0
- wml/wtorch/nms.py +109 -0
- wml/wtorch/nn.py +896 -0
- wml/wtorch/ocr_block.py +193 -0
- wml/wtorch/summary.py +331 -0
- wml/wtorch/train_toolkit.py +603 -0
- wml/wtorch/transformer_blocks.py +266 -0
- wml/wtorch/utils.py +719 -0
- wml/wtorch/wlr_scheduler.py +100 -0
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import copy
|
|
3
|
+
import cv2
|
|
4
|
+
from wml.semantic.mask_utils import get_bboxes_by_contours,npresize_mask
|
|
5
|
+
import wml.object_detection2.bboxes as odb
|
|
6
|
+
import wml.basic_img_utils as bwmli
|
|
7
|
+
import wml.object_detection2.bboxes as odb
|
|
8
|
+
from .common import WBaseMaskLike
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class WMCKeypointsItem(WBaseMaskLike):
|
|
12
|
+
'''
|
|
13
|
+
每个WMCKeypointsItem共享同一个标签(label)
|
|
14
|
+
'''
|
|
15
|
+
def __init__(self,points,*,width=None,height=None):
|
|
16
|
+
'''
|
|
17
|
+
points: [points_nr,2]
|
|
18
|
+
'''
|
|
19
|
+
self.points = points.copy()
|
|
20
|
+
self.width = width
|
|
21
|
+
self.height = height
|
|
22
|
+
|
|
23
|
+
def copy(self):
|
|
24
|
+
return WMCKeypointsItem(self.points,width=self.width,height=self.height)
|
|
25
|
+
|
|
26
|
+
def bitmap(self,width=None,height=None):
|
|
27
|
+
'''
|
|
28
|
+
return: [H,W]
|
|
29
|
+
'''
|
|
30
|
+
raise RuntimeError(f"Not implement bitmap")
|
|
31
|
+
|
|
32
|
+
def resize(self,size,width=None,height=None):
|
|
33
|
+
'''
|
|
34
|
+
size:[w,h]
|
|
35
|
+
'''
|
|
36
|
+
if len(self.points)==0:
|
|
37
|
+
return self
|
|
38
|
+
if width is None:
|
|
39
|
+
width = self.width
|
|
40
|
+
if height is None:
|
|
41
|
+
height = self.height
|
|
42
|
+
|
|
43
|
+
w_scale = size[0]/width
|
|
44
|
+
h_scale = size[1]/height
|
|
45
|
+
scale = np.array([[w_scale,h_scale]],dtype=np.float32)
|
|
46
|
+
ori_type = self.points.dtype
|
|
47
|
+
points = self.points.astype(np.float32)*scale
|
|
48
|
+
points = points.astype(ori_type)
|
|
49
|
+
width = size[0]
|
|
50
|
+
height = size[1]
|
|
51
|
+
|
|
52
|
+
return WMCKeypointsItem(points=points,width=width,height=height)
|
|
53
|
+
|
|
54
|
+
def flip(self,direction,width=None,height=None):
|
|
55
|
+
if len(self.points)==0:
|
|
56
|
+
return self
|
|
57
|
+
if width is None:
|
|
58
|
+
width = self.width
|
|
59
|
+
if height is None:
|
|
60
|
+
height = self.height
|
|
61
|
+
|
|
62
|
+
if direction == WBaseMaskLike.HORIZONTAL:
|
|
63
|
+
self.points[:,0] = width-self.points[:,0]
|
|
64
|
+
elif direction == WBaseMaskLike.VERTICAL:
|
|
65
|
+
self.points[:,1] = height-self.points[:,1]
|
|
66
|
+
elif direction == WBaseMaskLike.DIAGONAL:
|
|
67
|
+
self.points[:,0] = width-self.points[:,0]
|
|
68
|
+
self.points[:,1] = height-self.points[:,1]
|
|
69
|
+
else:
|
|
70
|
+
info = f"unknow flip direction {direction}"
|
|
71
|
+
print(f"ERROR: {info}")
|
|
72
|
+
raise RuntimeError(info)
|
|
73
|
+
|
|
74
|
+
return self
|
|
75
|
+
|
|
76
|
+
def crop(self, bbox):
|
|
77
|
+
'''
|
|
78
|
+
bbox: [x0,y0,x1,y1], 包含边界
|
|
79
|
+
offset: [xoffset,yoffset]
|
|
80
|
+
如果offset is not None, 先offset再用bbox crop
|
|
81
|
+
'''
|
|
82
|
+
if not isinstance(bbox,np.ndarray):
|
|
83
|
+
bbox = np.array(bbox)
|
|
84
|
+
assert bbox.ndim == 1
|
|
85
|
+
|
|
86
|
+
# clip the boundary
|
|
87
|
+
bbox = bbox.copy()
|
|
88
|
+
bbox[0::2] = np.clip(bbox[0::2], 0, self.width-1)
|
|
89
|
+
bbox[1::2] = np.clip(bbox[1::2], 0, self.height-1)
|
|
90
|
+
x1, y1, x2, y2 = bbox
|
|
91
|
+
w = np.maximum(x2 - x1+1, 1)
|
|
92
|
+
h = np.maximum(y2 - y1+1, 1)
|
|
93
|
+
|
|
94
|
+
if len(self.points)>0:
|
|
95
|
+
keep = odb.is_points_in_bbox(self.points,bbox)
|
|
96
|
+
points = self.points[keep]
|
|
97
|
+
offset = np.reshape(np.array([x1,y1]),[1,2])
|
|
98
|
+
points = points-offset
|
|
99
|
+
else:
|
|
100
|
+
points = np.zeros([0,2],dtype=np.int32)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
cropped_kps = WMCKeypointsItem(points, width=w,height=h)
|
|
104
|
+
return cropped_kps
|
|
105
|
+
|
|
106
|
+
def filter_out_of_range(self):
|
|
107
|
+
bbox = [0,0,self.width-1,self.height-1]
|
|
108
|
+
n_kps = self.crop(bbox)
|
|
109
|
+
self.points = n_kps.points
|
|
110
|
+
return self
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def rotate(self, out_shape, angle, center=None, scale=1.0, fill_val=0):
|
|
114
|
+
#out_shape: [h,w]
|
|
115
|
+
"""See :func:`BaseInstancepoints.rotate`."""
|
|
116
|
+
if len(self.points) == 0:
|
|
117
|
+
rotated_points = WMCKeypointsItem([], width=out_shape[1],height=out_shape[0])
|
|
118
|
+
else:
|
|
119
|
+
rotate_matrix = cv2.getRotationMatrix2D(center, -angle, scale)
|
|
120
|
+
coords = self.points.copy()
|
|
121
|
+
# pad 1 to convert from format [x, y] to homogeneous
|
|
122
|
+
# coordinates format [x, y, 1]
|
|
123
|
+
coords = np.concatenate(
|
|
124
|
+
(coords, np.ones((coords.shape[0], 1), coords.dtype)),
|
|
125
|
+
axis=1) # [n, 3]
|
|
126
|
+
rotated_coords = np.matmul(
|
|
127
|
+
rotate_matrix[None, :, :],
|
|
128
|
+
coords[:, :, None])[..., 0] # [n, 2, 1] -> [n, 2]
|
|
129
|
+
rotated_points = WMCKeypointsItem(rotated_coords, width=out_shape[1],height=out_shape[0])
|
|
130
|
+
rotated_points = rotated_points.crop(np.array([0,0,out_shape[1]-1,out_shape[0]-1]))
|
|
131
|
+
return rotated_points
|
|
132
|
+
|
|
133
|
+
def warp_affine(self,M,out_shape,fill_val=0):
|
|
134
|
+
#out_shape: [h,w]
|
|
135
|
+
"""See :func:`BaseInstancepoints.rotate`."""
|
|
136
|
+
if len(self.points) == 0:
|
|
137
|
+
affined_points = WMCKeypointsItem([], width=out_shape[1],height=out_shape[0])
|
|
138
|
+
else:
|
|
139
|
+
coords = self.points.copy()
|
|
140
|
+
# pad 1 to convert from format [x, y] to homogeneous
|
|
141
|
+
# coordinates format [x, y, 1]
|
|
142
|
+
coords = np.concatenate(
|
|
143
|
+
(coords, np.ones((coords.shape[0], 1), coords.dtype)),
|
|
144
|
+
axis=1) # [n, 3]
|
|
145
|
+
affined_coords = np.matmul(
|
|
146
|
+
M[None, :, :],
|
|
147
|
+
coords[:, :, None])[..., 0] # [n, 2, 1] -> [n, 2]
|
|
148
|
+
affined_points = WMCKeypointsItem(affined_coords, width=out_shape[1],height=out_shape[0])
|
|
149
|
+
affined_points = affined_points.crop(np.array([0,0,out_shape[1]-1,out_shape[0]-1]))
|
|
150
|
+
return affined_points
|
|
151
|
+
|
|
152
|
+
def shear(self,
|
|
153
|
+
out_shape,
|
|
154
|
+
magnitude,
|
|
155
|
+
direction='horizontal',
|
|
156
|
+
border_value=0,
|
|
157
|
+
interpolation='bilinear'):
|
|
158
|
+
#out_shape: [h,w]
|
|
159
|
+
if len(self.points) == 0:
|
|
160
|
+
sheared_points = WMCKeypointsItem([], width=out_shape[1],height=out_shape[0])
|
|
161
|
+
else:
|
|
162
|
+
if direction == 'horizontal':
|
|
163
|
+
shear_matrix = np.stack([[1, magnitude],
|
|
164
|
+
[0, 1]]).astype(np.float32)
|
|
165
|
+
elif direction == 'vertical':
|
|
166
|
+
shear_matrix = np.stack([[1, 0], [magnitude,
|
|
167
|
+
1]]).astype(np.float32)
|
|
168
|
+
p = self.points.copy()
|
|
169
|
+
new_coords = np.matmul(shear_matrix, p.T) # [2, n]
|
|
170
|
+
new_coords[0, :] = np.clip(new_coords[0, :], 0,
|
|
171
|
+
out_shape[1])
|
|
172
|
+
new_coords[1, :] = np.clip(new_coords[1, :], 0,
|
|
173
|
+
out_shape[0])
|
|
174
|
+
sheared_points = new_coords.transpose((1, 0))
|
|
175
|
+
sheared_points = WMCKeypointsItem(sheared_points, width=out_shape[1],height=out_shape[0])
|
|
176
|
+
sheared_points.filter_out_of_range()
|
|
177
|
+
return sheared_points
|
|
178
|
+
|
|
179
|
+
def translate(self,
|
|
180
|
+
out_shape,
|
|
181
|
+
offset,
|
|
182
|
+
direction='horizontal',
|
|
183
|
+
fill_val=None,
|
|
184
|
+
interpolation=None):
|
|
185
|
+
"""Translate the Polygonpoints.
|
|
186
|
+
|
|
187
|
+
Example:
|
|
188
|
+
>>> self = Polygonpoints.random(dtype=np.int)
|
|
189
|
+
>>> out_shape = (self.height, self.width)
|
|
190
|
+
>>> new = self.translate(out_shape, 4., direction='horizontal')
|
|
191
|
+
>>> assert np.all(new.points[0][0][1::2] == self.points[0][0][1::2])
|
|
192
|
+
>>> assert np.all(new.points[0][0][0::2] == self.points[0][0][0::2] + 4) # noqa: E501
|
|
193
|
+
"""
|
|
194
|
+
if len(self.points) == 0:
|
|
195
|
+
res_points = WMCKeypointsItem([], width=out_shape[1],height=out_shape[0])
|
|
196
|
+
else:
|
|
197
|
+
p = self.points.copy()
|
|
198
|
+
if direction == WBaseMaskLike.HORIZONTAL:
|
|
199
|
+
p[:,0] = np.clip(p[:,0] + offset, 0, out_shape[1])
|
|
200
|
+
elif direction == WBaseMaskLike.VERTICAL:
|
|
201
|
+
p[:,1] = np.clip(p[:,1] + offset, 0, out_shape[0])
|
|
202
|
+
else:
|
|
203
|
+
info = f"error direction {direction}"
|
|
204
|
+
print(f"ERROR: {type(self).__name__} {info}")
|
|
205
|
+
raise RuntimeError(info)
|
|
206
|
+
res_points = WMCKeypointsItem(p, width=out_shape[1],height=out_shape[0])
|
|
207
|
+
res_points.filter_out_of_range()
|
|
208
|
+
|
|
209
|
+
return res_points
|
|
210
|
+
|
|
211
|
+
def offset(self,
|
|
212
|
+
offset):
|
|
213
|
+
'''
|
|
214
|
+
offset: [xoffset,yoffset]
|
|
215
|
+
'''
|
|
216
|
+
w = self.width+offset[0] if self.width is not None else None
|
|
217
|
+
h = self.height+offset[1] if self.height is not None else None
|
|
218
|
+
offset = np.reshape(np.array(offset),[1,2])
|
|
219
|
+
if len(self.points) == 0:
|
|
220
|
+
res_masks = WMCKeypointsItem([], width=w,height=h)
|
|
221
|
+
else:
|
|
222
|
+
p = self.points.copy()+offset
|
|
223
|
+
res_masks = WMCKeypointsItem(p, width=w,height=h)
|
|
224
|
+
res_masks.filter_out_of_range()
|
|
225
|
+
return res_masks
|
|
226
|
+
|
|
227
|
+
def valid(self):
|
|
228
|
+
if len(self.points) == 0:
|
|
229
|
+
return False
|
|
230
|
+
bbox = np.array([0,0,self.width,self.height])
|
|
231
|
+
try:
|
|
232
|
+
keep = odb.is_points_in_bbox(self.points,bbox)
|
|
233
|
+
except Exception as e:
|
|
234
|
+
print(e)
|
|
235
|
+
pass
|
|
236
|
+
self.points = self.points[keep]
|
|
237
|
+
return len(self.points)>0
|
|
238
|
+
|
|
239
|
+
def split2single_point(self):
|
|
240
|
+
'''
|
|
241
|
+
把多个点组成的Item拆分成由单个点组成的Item
|
|
242
|
+
'''
|
|
243
|
+
if len(self.points) <= 1:
|
|
244
|
+
return [copy.deepcopy(self)]
|
|
245
|
+
else:
|
|
246
|
+
return [WMCKeypointsItem(np.expand_dims(p,axis=0),width=self.width,height=self.height) for p in self.points]
|
|
247
|
+
|
|
248
|
+
@property
|
|
249
|
+
def shape(self):
|
|
250
|
+
return [len(self.points),self.height,self.width]
|
|
251
|
+
|
|
252
|
+
def numel(self):
|
|
253
|
+
return len(self.points)
|
|
254
|
+
|
|
255
|
+
def _update_shape(self,*,width=None,height=None):
|
|
256
|
+
if width is not None:
|
|
257
|
+
self.width = width
|
|
258
|
+
if height is not None:
|
|
259
|
+
self.height = height
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
class WMCKeypoints(WBaseMaskLike):
|
|
265
|
+
'''
|
|
266
|
+
每个WMCKeypoints包含多个WMCKeypointsItem, 每个Item与一个label相对应
|
|
267
|
+
WMCKeypoints与多个标签相对应
|
|
268
|
+
'''
|
|
269
|
+
def __init__(self,points,*,width=None,height=None) -> None:
|
|
270
|
+
assert width is not None, f"ERROR: width is None"
|
|
271
|
+
assert height is not None, f"ERROR: height is None"
|
|
272
|
+
super().__init__()
|
|
273
|
+
n_points = []
|
|
274
|
+
for p in points:
|
|
275
|
+
if not isinstance(p,WMCKeypointsItem):
|
|
276
|
+
n_points.append(WMCKeypointsItem(p,width=width,height=height))
|
|
277
|
+
else:
|
|
278
|
+
n_points.append(WMCKeypointsItem(p.points,width=width,height=height))
|
|
279
|
+
self.points = copy.deepcopy(n_points)
|
|
280
|
+
self.width = width
|
|
281
|
+
self.height = height
|
|
282
|
+
if self.width<5 or self.height<5:
|
|
283
|
+
print(f"WARNING: {self.__class__.__name__}: unnormal mask size, width={self.width}, height={self.height}")
|
|
284
|
+
|
|
285
|
+
@classmethod
|
|
286
|
+
def zeros(cls,*,width=None,height=None,shape=None):
|
|
287
|
+
'''
|
|
288
|
+
shape: [points_nr,H,W]
|
|
289
|
+
'''
|
|
290
|
+
if shape is not None:
|
|
291
|
+
width = shape[-1]
|
|
292
|
+
height = shape[-2]
|
|
293
|
+
return cls([],width=width,height=height)
|
|
294
|
+
|
|
295
|
+
@classmethod
|
|
296
|
+
def from_ndarray(cls,points,*,width=None,height=None):
|
|
297
|
+
raise RuntimeError(f"Unimplement from ndarray")
|
|
298
|
+
|
|
299
|
+
def copy(self):
|
|
300
|
+
return WMCKeypoints(self.points,width=self.width,height=self.height)
|
|
301
|
+
|
|
302
|
+
def __getitem__(self,idxs):
|
|
303
|
+
if isinstance(idxs,(list,tuple)) and (len(idxs)==2 or len(idxs)==3) and isinstance(idxs[0],slice):
|
|
304
|
+
sx = idxs[-1]
|
|
305
|
+
sy = idxs[-2]
|
|
306
|
+
if self.is_none_slice(sy) and self.is_flip_slice(sx):
|
|
307
|
+
return self.flip(WBaseMaskLike.HORIZONTAL)
|
|
308
|
+
elif self.is_none_slice(sx) and self.is_flip_slice(sy):
|
|
309
|
+
return self.flip(WBaseMaskLike.VERTICAL)
|
|
310
|
+
elif self.is_flip_slice(sx) and self.is_flip_slice(sy):
|
|
311
|
+
return self.flip(WBaseMaskLike.DIAGONAL)
|
|
312
|
+
bbox = self.slice2bbox(sx=sx,sy=sy)
|
|
313
|
+
return self.crop(bbox)
|
|
314
|
+
elif isinstance(idxs,(list,np.ndarray,tuple)):
|
|
315
|
+
idxs = np.array(idxs)
|
|
316
|
+
if idxs.dtype == np.bool:
|
|
317
|
+
idxs = np.where(idxs)[0]
|
|
318
|
+
try:
|
|
319
|
+
points = [self.points[idx] for idx in idxs]
|
|
320
|
+
except Exception as e:
|
|
321
|
+
print(e)
|
|
322
|
+
pass
|
|
323
|
+
return WMCKeypoints(points,width=self.width,height=self.height)
|
|
324
|
+
else:
|
|
325
|
+
return self.points[idxs]
|
|
326
|
+
|
|
327
|
+
def __setitem__(self,idxs,value):
|
|
328
|
+
if isinstance(idxs,(list,tuple)) and (len(idxs)==2 or len(idxs)==3) and isinstance(idxs[0],slice):
|
|
329
|
+
sx = idxs[-1]
|
|
330
|
+
sy = idxs[-2]
|
|
331
|
+
bbox = self.slice2bbox(sx=sx,sy=sy)
|
|
332
|
+
self.copy_from(value,bbox)
|
|
333
|
+
elif isinstance(idxs,(list,np.ndarray,tuple)):
|
|
334
|
+
idxs = np.array(idxs)
|
|
335
|
+
if idxs.dtype == np.bool:
|
|
336
|
+
idxs = np.where(idxs)[0]
|
|
337
|
+
if len(value) != len(idxs):
|
|
338
|
+
info = f"idxs size not equal value's size {len(idxs)} vs {len(value)}"
|
|
339
|
+
print(f"ERROR: {type(self).__name__}: {info}")
|
|
340
|
+
raise RuntimeError(info)
|
|
341
|
+
for i in idxs:
|
|
342
|
+
self.points[i] = value[i]
|
|
343
|
+
else:
|
|
344
|
+
info = f"unknow idxs type {type(idxs)}"
|
|
345
|
+
print(f"ERROR: {type(self).__name__}: {info}")
|
|
346
|
+
raise RuntimeError(info)
|
|
347
|
+
|
|
348
|
+
def __len__(self):
|
|
349
|
+
return len(self.points)
|
|
350
|
+
|
|
351
|
+
def __repr__(self):
|
|
352
|
+
s = self.__class__.__name__ + '('
|
|
353
|
+
s += f'num_keypoints={len(self.points)}, '
|
|
354
|
+
s += f'height={self.height}, '
|
|
355
|
+
s += f'width={self.width})'
|
|
356
|
+
return s
|
|
357
|
+
|
|
358
|
+
def bitmap(self,exclusion=None):
|
|
359
|
+
raise RuntimeError(f"Not implement bitmap")
|
|
360
|
+
|
|
361
|
+
def resize(self,size):
|
|
362
|
+
'''
|
|
363
|
+
size:[w,h]
|
|
364
|
+
'''
|
|
365
|
+
points = [m.resize(size,width=self.width,height=self.height) for m in self.points]
|
|
366
|
+
width = size[0]
|
|
367
|
+
height = size[1]
|
|
368
|
+
return WMCKeypoints(points=points,width=width,height=height)
|
|
369
|
+
|
|
370
|
+
def flip(self,direction=WBaseMaskLike.HORIZONTAL):
|
|
371
|
+
[m.flip(direction,width=self.width,height=self.height) for m in self.points]
|
|
372
|
+
return self
|
|
373
|
+
|
|
374
|
+
def crop(self,bbox):
|
|
375
|
+
'''
|
|
376
|
+
bbox: [x0,y0,x1,y1]
|
|
377
|
+
'''
|
|
378
|
+
bbox = np.array(bbox)
|
|
379
|
+
bbox = bbox.copy()
|
|
380
|
+
bbox[0::2] = np.clip(bbox[0::2], 0, self.width)
|
|
381
|
+
bbox[1::2] = np.clip(bbox[1::2], 0, self.height)
|
|
382
|
+
x1, y1, x2, y2 = bbox
|
|
383
|
+
w = np.maximum(x2 - x1+1, 1)
|
|
384
|
+
h = np.maximum(y2 - y1+1, 1)
|
|
385
|
+
points = [m.crop(bbox) for m in self.points]
|
|
386
|
+
return WMCKeypoints(points,width=w,height=h)
|
|
387
|
+
|
|
388
|
+
def slice2bbox(self,*,sx:slice,sy:slice):
|
|
389
|
+
x0 = sx.start if sx.start is not None else 0
|
|
390
|
+
x1 = sx.stop-1 if sx.stop is not None else self.width-1
|
|
391
|
+
y0 = sy.start if sy.start is not None else 0
|
|
392
|
+
y1 = sy.stop-1 if sy.stop is not None else self.height-1
|
|
393
|
+
return np.array([x0,y0,x1,y1],dtype=np.int)
|
|
394
|
+
|
|
395
|
+
@staticmethod
|
|
396
|
+
def is_flip_slice(s:slice):
|
|
397
|
+
return s.start is None and s.stop is None and s.step==-1
|
|
398
|
+
|
|
399
|
+
@staticmethod
|
|
400
|
+
def is_none_slice(s:slice):
|
|
401
|
+
return s.start is None and s.stop is None and s.step is None
|
|
402
|
+
|
|
403
|
+
@staticmethod
|
|
404
|
+
def get_bbox_size(bbox):
|
|
405
|
+
x1, y1, x2, y2 = bbox
|
|
406
|
+
w = np.maximum(x2 - x1, 1)
|
|
407
|
+
h = np.maximum(y2 - y1, 1)
|
|
408
|
+
return w,h
|
|
409
|
+
|
|
410
|
+
def copy_from(self,points,dst_bbox=None,src_bbox=None,update_size=False):
|
|
411
|
+
if src_bbox is not None:
|
|
412
|
+
points = points.crop(src_bbox)
|
|
413
|
+
if dst_bbox is not None:
|
|
414
|
+
w0,h0 = self.get_bbox_size(src_bbox)
|
|
415
|
+
w1,h1 = self.get_bbox_size(dst_bbox)
|
|
416
|
+
if w0!=w1 or h0!=h1:
|
|
417
|
+
info = f"dst_bbox and src_bbox expected to be equal."
|
|
418
|
+
print(f"ERROR: {info}")
|
|
419
|
+
raise RuntimeError(info)
|
|
420
|
+
|
|
421
|
+
if dst_bbox is not None:
|
|
422
|
+
if update_size:
|
|
423
|
+
w,h = self.get_bbox_size(dst_bbox)
|
|
424
|
+
self.width = w
|
|
425
|
+
self.height = h
|
|
426
|
+
self.points = [m.offset(dst_bbox[:2]) for m in points]
|
|
427
|
+
else:
|
|
428
|
+
self.points = points
|
|
429
|
+
if update_size:
|
|
430
|
+
self.width = points.width
|
|
431
|
+
self.height = points.height
|
|
432
|
+
[p._update_shape(width=self.width,height=self.height) for p in self.points]
|
|
433
|
+
return self
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
@classmethod
|
|
437
|
+
def from_bitmap_masks(cls,bitmap_masks):
|
|
438
|
+
raise RuntimeError(f"Not implement from bitmap masks")
|
|
439
|
+
|
|
440
|
+
@staticmethod
|
|
441
|
+
def concatenate(points):
|
|
442
|
+
ws = [m.width for m in points]
|
|
443
|
+
hs = [m.height for m in points]
|
|
444
|
+
ws = list(filter(lambda x:x is not None,ws))
|
|
445
|
+
hs = list(filter(lambda x:x is not None,hs))
|
|
446
|
+
if len(ws)>0:
|
|
447
|
+
nw = np.max(ws)
|
|
448
|
+
else:
|
|
449
|
+
nw = None
|
|
450
|
+
|
|
451
|
+
if len(hs)>0:
|
|
452
|
+
nh = np.max(hs)
|
|
453
|
+
else:
|
|
454
|
+
nh = None
|
|
455
|
+
new_points = []
|
|
456
|
+
for m in points:
|
|
457
|
+
new_points.extend(m.points)
|
|
458
|
+
return WMCKeypoints(new_points,width=nw,height=nh)
|
|
459
|
+
|
|
460
|
+
def rotate(self, out_shape, angle, center=None, scale=1.0, fill_val=0):
|
|
461
|
+
#out_shape: [h,w]
|
|
462
|
+
points = [m.rotate(out_shape, angle, center, scale, fill_val) for m in self.points]
|
|
463
|
+
width = out_shape[1]
|
|
464
|
+
height = out_shape[0]
|
|
465
|
+
return WMCKeypoints(points,width=width,height=height)
|
|
466
|
+
|
|
467
|
+
def warp_affine(self,M,out_shape,fill_val=0):
|
|
468
|
+
points = [m.warp_affine(M,out_shape,fill_val) for m in self.points]
|
|
469
|
+
width = out_shape[1]
|
|
470
|
+
height = out_shape[0]
|
|
471
|
+
return WMCKeypoints(points,width=width,height=height)
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
def shear(self,
|
|
475
|
+
out_shape,
|
|
476
|
+
magnitude,
|
|
477
|
+
direction='horizontal',
|
|
478
|
+
border_value=0,
|
|
479
|
+
interpolation='bilinear'):
|
|
480
|
+
#out_shape: [h,w]
|
|
481
|
+
points = [m.shear(out_shape, magnitude, direction, border_value, interpolation) for m in self.points]
|
|
482
|
+
width = out_shape[1]
|
|
483
|
+
height = out_shape[0]
|
|
484
|
+
return WMCKeypoints(points,width=width,height=height)
|
|
485
|
+
|
|
486
|
+
def translate(self,
|
|
487
|
+
out_shape,
|
|
488
|
+
offset,
|
|
489
|
+
direction=WBaseMaskLike.HORIZONTAL,
|
|
490
|
+
fill_val=None,
|
|
491
|
+
interpolation=None):
|
|
492
|
+
'''
|
|
493
|
+
out_shape: [H,W]
|
|
494
|
+
'''
|
|
495
|
+
points = [m.translate(out_shape, offset, direction, fill_val,interpolation) for m in self.points]
|
|
496
|
+
width = out_shape[1]
|
|
497
|
+
height = out_shape[0]
|
|
498
|
+
return WMCKeypoints(points,width=width,height=height)
|
|
499
|
+
|
|
500
|
+
def pad(self, out_shape, pad_val=0):
|
|
501
|
+
'''
|
|
502
|
+
out_shape: [H,W]
|
|
503
|
+
'''
|
|
504
|
+
"""padding has no effect on polygons`"""
|
|
505
|
+
return WMCKeypoints(self.points, height=out_shape[0],width=out_shape[1])
|
|
506
|
+
|
|
507
|
+
@property
|
|
508
|
+
def shape(self):
|
|
509
|
+
return (len(self.points),self.height,self.width)
|
|
510
|
+
|
|
511
|
+
def resize_mask_in_bboxes(self,bboxes,size=None,r=None):
|
|
512
|
+
'''
|
|
513
|
+
mask: [N,H,W]
|
|
514
|
+
bboxes: [N,4](x0,y0,x1,y1)
|
|
515
|
+
size: (new_w,new_h)
|
|
516
|
+
'''
|
|
517
|
+
return self.resize(size),None
|
|
518
|
+
|
|
519
|
+
def to_ndarray(self):
|
|
520
|
+
"""See :func:`BaseInstancepoints.to_ndarray`."""
|
|
521
|
+
return self.bitmap()
|
|
522
|
+
|
|
523
|
+
def get_bboxes(self):
|
|
524
|
+
gt_bboxes = [m.get_bbox() for m in self.points]
|
|
525
|
+
if len(gt_bboxes) == 0:
|
|
526
|
+
return np.zeros([0,4],dtype=np.float32)
|
|
527
|
+
gt_bboxes = np.stack(gt_bboxes,axis=0)
|
|
528
|
+
|
|
529
|
+
return gt_bboxes
|
|
530
|
+
|
|
531
|
+
def valid(self):
|
|
532
|
+
mask = [m.valid() for m in self.points]
|
|
533
|
+
return mask
|
|
534
|
+
|
|
535
|
+
@staticmethod
|
|
536
|
+
def split2single_point(kps,labels):
|
|
537
|
+
'''
|
|
538
|
+
让每个WMCKeypointsItem仅包含一个点
|
|
539
|
+
'''
|
|
540
|
+
res_kps = []
|
|
541
|
+
res_labels = []
|
|
542
|
+
for kp,l in zip(kps,labels):
|
|
543
|
+
n_kp = kp.split2single_point()
|
|
544
|
+
l = [l]*len(n_kp)
|
|
545
|
+
res_kps.extend(n_kp)
|
|
546
|
+
res_labels.extend(l)
|
|
547
|
+
|
|
548
|
+
if len(res_labels)>0 and isinstance(res_labels[0],(int,float)):
|
|
549
|
+
res_labels = np.array(res_labels,dtype=np.int32)
|
|
550
|
+
|
|
551
|
+
return WMCKeypoints(res_kps,width=kps.width,height=kps.height),res_labels
|
|
552
|
+
|
|
553
|
+
@staticmethod
|
|
554
|
+
def split2single_nppoint(kps,labels):
|
|
555
|
+
points,labels = WMCKeypoints.split2single_point(kps,labels)
|
|
556
|
+
res_points = []
|
|
557
|
+
for p in points:
|
|
558
|
+
res_points.append(p.points)
|
|
559
|
+
if len(res_points)>0:
|
|
560
|
+
res = np.concatenate(res_points,axis=0)
|
|
561
|
+
else:
|
|
562
|
+
res = np.zeros([0,2],dtype=np.float32)
|
|
563
|
+
labels = np.zeros([0],dtype=np.int32)
|
|
564
|
+
return res,labels
|
|
565
|
+
|
|
566
|
+
def check_consistency(self):
|
|
567
|
+
for kp in self.points:
|
|
568
|
+
if kp.width != self.width or kp.height != self.height:
|
|
569
|
+
info = f"Unmatch size WMCKeypoints shape {self.shape} vs WMCKeypointsItem shape {kp.shape}"
|
|
570
|
+
print(info)
|
|
571
|
+
raise RuntimeError(info)
|
|
572
|
+
|
|
573
|
+
def update_shape(self,*,width=None,height=None):
|
|
574
|
+
if width is not None:
|
|
575
|
+
self.width = width
|
|
576
|
+
if height is not None:
|
|
577
|
+
self.height = height
|
|
578
|
+
for mask in self.points:
|
|
579
|
+
mask._update_shape(width=width,height=height)
|