|
11 | 11 |
|
12 | 12 |
|
13 | 13 | class Pose:
|
14 |
| - """File IO for '.pose' file format, including the header and body""" |
15 |
| - |
16 |
| - def __init__(self, header: PoseHeader, body: PoseBody): |
17 |
| - """ |
18 |
| - :param header: PoseHeader |
19 |
| - :param body: PoseBody |
20 |
| - """ |
21 |
| - self.header = header |
22 |
| - self.body = body |
23 |
| - |
24 |
| - @staticmethod |
25 |
| - def read(buffer: bytes, pose_body: PoseBody = NumPyPoseBody): |
26 |
| - reader = BufferReader(buffer) |
27 |
| - header = PoseHeader.read(reader) |
28 |
| - body = pose_body.read(header, reader) |
29 |
| - |
30 |
| - return Pose(header, body) |
31 |
| - |
32 |
| - def write(self, buffer: BinaryIO): |
33 |
| - self.header.write(buffer) |
34 |
| - self.body.write(self.header.version, buffer) |
35 |
| - |
36 |
| - def focus(self): |
37 |
| - """ |
38 |
| - Gets the pose to start at (0,0) and have dimensions as big as needed |
39 |
| - """ |
40 |
| - mins = ma.min(self.body.data, axis=(0, 1, 2)) |
41 |
| - maxs = ma.max(self.body.data, axis=(0, 1, 2)) |
42 |
| - |
43 |
| - if np.count_nonzero(mins) > 0: # Only translate if there is a number to translate by |
44 |
| - self.body.data = ma.subtract(self.body.data, mins) |
45 |
| - |
46 |
| - dimensions = (maxs - mins).tolist() |
47 |
| - self.header.dimensions = PoseHeaderDimensions(*dimensions) |
48 |
| - |
49 |
| - def normalize(self, info: PoseNormalizationInfo, scale_factor: float = 1): |
50 |
| - """ |
51 |
| - Normalize the point to a fixed distance between two points |
52 |
| - """ |
53 |
| - mask = self.body.data.mask |
54 |
| - transposed = self.body.zero_filled().points_perspective() |
55 |
| - |
56 |
| - p1s = transposed[info.p1] |
57 |
| - p2s = transposed[info.p2] |
58 |
| - |
59 |
| - if transposed.shape[1] == 0: |
60 |
| - p1s = p1s[0] |
61 |
| - p2s = p2s[0] |
62 |
| - else: |
63 |
| - p1s = ma.concatenate(p1s) |
64 |
| - p2s = ma.concatenate(p2s) |
65 |
| - |
66 |
| - # Move all points so center is (0,0) |
67 |
| - center = np.mean((p2s + p1s) / 2, axis=0) |
68 |
| - self.body.data -= center |
69 |
| - |
70 |
| - mean_distance = np.mean(distance_batch(p1s, p2s)) |
71 |
| - |
72 |
| - scale = scale_factor / mean_distance # scale all points to dist/scale |
73 |
| - |
74 |
| - if round(scale, 5) != 1: |
75 |
| - self.body.data = ma.multiply(self.body.data, scale) |
76 |
| - |
77 |
| - self.body.data = ma.array(self.body.data, mask=mask) |
78 |
| - |
79 |
| - return self |
80 |
| - |
81 |
| - def frame_dropout(self, dropout_std=0.1): |
82 |
| - body, selected_indexes = self.body.frame_dropout(dropout_std=dropout_std) |
83 |
| - return Pose(header=self.header, body=body), selected_indexes |
84 |
| - |
85 |
| - def get_components(self, components: List[str]): |
86 |
| - indexes = [] |
87 |
| - new_components = [] |
88 |
| - |
89 |
| - idx = 0 |
90 |
| - for component in self.header.components: |
91 |
| - if component.name in components: |
92 |
| - new_components.append(component) |
93 |
| - indexes += list(range(idx, len(component.points) + idx)) |
94 |
| - idx += len(component.points) |
95 |
| - |
96 |
| - new_header = PoseHeader(self.header.version, self.header.dimensions, new_components) |
97 |
| - new_body = self.body.get_points(indexes) |
98 |
| - |
99 |
| - return Pose(header=new_header, body=new_body) |
100 |
| - |
101 |
| - def bbox(self): |
102 |
| - body = self.body.bbox(self.header) |
103 |
| - header = self.header.bbox() |
104 |
| - return Pose(header=header, body=body) |
105 |
| - |
106 |
| - pass_through_methods = { |
107 |
| - "augment2d", # Augment 2D points |
108 |
| - "interpolate", # Interpolate missing pose points |
109 |
| - "torch", # Convert body to torch |
110 |
| - "tensorflow", # Convert body to tensorflow |
111 |
| - "slice_step", # Step through the data |
112 |
| - } |
113 |
| - |
114 |
| - def __getattr__(self, attr): |
115 |
| - if attr not in Pose.pass_through_methods: |
116 |
| - raise AttributeError("Attribute '%s' doesn't exist on class Pose" % attr) |
117 |
| - |
118 |
| - def func(*args, **kwargs): |
119 |
| - prop = getattr(self.body, attr) |
120 |
| - body_res = prop(*args, **kwargs) |
| 14 | + """File IO for '.pose' file format, including the header and body""" |
121 | 15 |
|
122 |
| - if isinstance(body_res, PoseBody): |
123 |
| - header = self.header |
124 |
| - if hasattr(header, attr): |
125 |
| - header_res = getattr(header, attr)(*args, **kwargs) |
126 |
| - if isinstance(header_res, PoseHeader): |
127 |
| - header = header_res |
| 16 | + def __init__(self, header: PoseHeader, body: PoseBody): |
| 17 | + """ |
| 18 | + :param header: PoseHeader |
| 19 | + :param body: PoseBody |
| 20 | + """ |
| 21 | + self.header = header |
| 22 | + self.body = body |
128 | 23 |
|
129 |
| - return Pose(header, body_res) |
| 24 | + @staticmethod |
| 25 | + def read(buffer: bytes, pose_body: PoseBody = NumPyPoseBody): |
| 26 | + reader = BufferReader(buffer) |
| 27 | + header = PoseHeader.read(reader) |
| 28 | + body = pose_body.read(header, reader) |
130 | 29 |
|
131 |
| - return body_res |
| 30 | + return Pose(header, body) |
132 | 31 |
|
133 |
| - return func |
| 32 | + def write(self, buffer: BinaryIO): |
| 33 | + self.header.write(buffer) |
| 34 | + self.body.write(self.header.version, buffer) |
| 35 | + |
| 36 | + def focus(self): |
| 37 | + """ |
| 38 | + Gets the pose to start at (0,0) and have dimensions as big as needed |
| 39 | + """ |
| 40 | + mins = ma.min(self.body.data, axis=(0, 1, 2)) |
| 41 | + maxs = ma.max(self.body.data, axis=(0, 1, 2)) |
| 42 | + |
| 43 | + if np.count_nonzero(mins) > 0: # Only translate if there is a number to translate by |
| 44 | + self.body.data = ma.subtract(self.body.data, mins) |
| 45 | + |
| 46 | + dimensions = (maxs - mins).tolist() |
| 47 | + self.header.dimensions = PoseHeaderDimensions(*dimensions) |
| 48 | + |
| 49 | + def normalize(self, info: PoseNormalizationInfo, scale_factor: float = 1): |
| 50 | + """ |
| 51 | + Normalize the point to a fixed distance between two points |
| 52 | + """ |
| 53 | + mask = self.body.data.mask |
| 54 | + transposed = self.body.zero_filled().points_perspective() |
| 55 | + |
| 56 | + p1s = transposed[info.p1] |
| 57 | + p2s = transposed[info.p2] |
| 58 | + |
| 59 | + if transposed.shape[1] == 0: |
| 60 | + p1s = p1s[0] |
| 61 | + p2s = p2s[0] |
| 62 | + else: |
| 63 | + p1s = ma.concatenate(p1s) |
| 64 | + p2s = ma.concatenate(p2s) |
| 65 | + |
| 66 | + # Move all points so center is (0,0) |
| 67 | + center = np.mean((p2s + p1s) / 2, axis=0) |
| 68 | + self.body.data -= center |
| 69 | + |
| 70 | + mean_distance = np.mean(distance_batch(p1s, p2s)) |
| 71 | + |
| 72 | + scale = scale_factor / mean_distance # scale all points to dist/scale |
| 73 | + |
| 74 | + if round(scale, 5) != 1: |
| 75 | + self.body.data = ma.multiply(self.body.data, scale) |
| 76 | + |
| 77 | + self.body.data = ma.array(self.body.data, mask=mask) |
| 78 | + |
| 79 | + return self |
| 80 | + |
| 81 | + def normalize_distribution(self, mu=None, std=None): |
| 82 | + mu = mu if mu is not None else ma.mean(self.body.data, axis=(0, 1)) |
| 83 | + std = std if std is not None else ma.std(self.body.data, axis=(0, 1)) |
| 84 | + |
| 85 | + self.body.data = (self.body.data - mu) / std |
| 86 | + |
| 87 | + return mu, std |
| 88 | + |
| 89 | + def unnormalize_distribution(self, mu, std): |
| 90 | + self.body.data = (self.body.data * std) + mu |
| 91 | + |
| 92 | + def frame_dropout(self, dropout_std=0.1): |
| 93 | + body, selected_indexes = self.body.frame_dropout(dropout_std=dropout_std) |
| 94 | + return Pose(header=self.header, body=body), selected_indexes |
| 95 | + |
| 96 | + def get_components(self, components: List[str]): |
| 97 | + indexes = [] |
| 98 | + new_components = [] |
| 99 | + |
| 100 | + idx = 0 |
| 101 | + for component in self.header.components: |
| 102 | + if component.name in components: |
| 103 | + new_components.append(component) |
| 104 | + indexes += list(range(idx, len(component.points) + idx)) |
| 105 | + idx += len(component.points) |
| 106 | + |
| 107 | + new_header = PoseHeader(self.header.version, self.header.dimensions, new_components) |
| 108 | + new_body = self.body.get_points(indexes) |
| 109 | + |
| 110 | + return Pose(header=new_header, body=new_body) |
| 111 | + |
| 112 | + def bbox(self): |
| 113 | + body = self.body.bbox(self.header) |
| 114 | + header = self.header.bbox() |
| 115 | + return Pose(header=header, body=body) |
| 116 | + |
| 117 | + pass_through_methods = { |
| 118 | + "augment2d", # Augment 2D points |
| 119 | + "flip", # Flip pose on axis |
| 120 | + "interpolate", # Interpolate missing pose points |
| 121 | + "torch", # Convert body to torch |
| 122 | + "tensorflow", # Convert body to tensorflow |
| 123 | + "slice_step", # Step through the data |
| 124 | + } |
| 125 | + |
| 126 | + def __getattr__(self, attr): |
| 127 | + if attr not in Pose.pass_through_methods: |
| 128 | + raise AttributeError("Attribute '%s' doesn't exist on class Pose" % attr) |
| 129 | + |
| 130 | + def func(*args, **kwargs): |
| 131 | + prop = getattr(self.body, attr) |
| 132 | + body_res = prop(*args, **kwargs) |
| 133 | + |
| 134 | + if isinstance(body_res, PoseBody): |
| 135 | + header = self.header |
| 136 | + if hasattr(header, attr): |
| 137 | + header_res = getattr(header, attr)(*args, **kwargs) |
| 138 | + if isinstance(header_res, PoseHeader): |
| 139 | + header = header_res |
| 140 | + |
| 141 | + return Pose(header, body_res) |
| 142 | + |
| 143 | + return body_res |
| 144 | + |
| 145 | + return func |
0 commit comments