Skip to content

Commit 079d9f6

Browse files
committed
numpy and tensorflow docstrings
1 parent 1996488 commit 079d9f6

20 files changed

Lines changed: 1014 additions & 36 deletions

src/python/pose_format/numpy/pose_body.py

Lines changed: 165 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,38 @@
1212
# np.seterr(all='raise')
1313

1414
class NumPyPoseBody(PoseBody):
15-
tensor_reader = 'unpack_numpy'
15+
"""Represents pose information leveraging NumPy operations and structures.
16+
17+
* Inherits from: `PoseBody`
18+
* Implements pose info using NumPy operations and structures.
19+
* Provides method for operations: matrix, multiplication, interpolation and data type conversions
20+
21+
The `NumPyPoseBody` is an implementation of `PoseBody` base class.
22+
This subclass uses NumPy masked arrays to handle pose data.
23+
Makes it suitable for applications where you need NumPy-based operations.
24+
The masked arrays allow for efficient handling of missing or invalid pose values
25+
26+
The class also comes with methods to transform, modify, and operate on pose data, including matrix multiplication, interpolation,
27+
and conversions to other data types like PyTorch tensors or TensorFlow tensors
28+
29+
Parameters
30+
----------
31+
fps : float
32+
Frames per second, to represent the temporal aspect of pose data.
33+
data : Union[ma.MaskedArray, np.ndarray]
34+
Pose data either as a masked array or a regular numpy array.
35+
confidence : np.ndarray
36+
confidence array of the pose keypoints.
37+
38+
"""
39+
40+
tensor_reader = 'unpack_numpy'
41+
"""Specifies the method name for unpacking a numpy array (Value: 'unpack_numpy')."""
1642

1743
def __init__(self, fps: float, data: Union[ma.MaskedArray, np.ndarray], confidence: np.ndarray):
44+
"""
45+
Initializes the NumPyPoseBody instance
46+
"""
1847
if isinstance(data, np.ndarray): # If array is not masked
1948
mask = confidence == 0 # 0 means no-mask, 1 means with-mask
2049
stacked_mask = np.stack([mask] * data.shape[-1], axis=3)
@@ -24,6 +53,21 @@ def __init__(self, fps: float, data: Union[ma.MaskedArray, np.ndarray], confiden
2453

2554
@classmethod
2655
def read_v0_0(cls, header: PoseHeader, reader: BufferReader, **unused_kwargs):
56+
"""
57+
Reads pose data from a given buffer reader using a specified data format version (see: ``docs/specs``).
58+
59+
Parameters
60+
----------
61+
header : PoseHeader
62+
Pose header information
63+
reader : BufferReader
64+
binary buffer reader
65+
66+
Returns
67+
-------
68+
NumPyPoseBody
69+
Instance of NumPyPoseBody with read pose data.
70+
"""
2771
fps, _frames = reader.unpack(ConstStructs.double_ushort)
2872

2973
_dims = max([len(c.format) for c in header.components]) - 1
@@ -64,6 +108,16 @@ def read_v0_0(cls, header: PoseHeader, reader: BufferReader, **unused_kwargs):
64108
return cls(fps, ma.stack(frames_d), ma.stack(frames_c))
65109

66110
def write(self, version: float, buffer: BinaryIO):
111+
"""
112+
Writes pose data to a binary buffer using specified data format version.
113+
114+
Parameters
115+
----------
116+
version : float
117+
Version of the data format.
118+
buffer : BinaryIO
119+
The binary buffer to write to.
120+
"""
67121
_frames, _people, _points, _dims = self.data.shape
68122
_frames = _frames if _frames < 65535 else 0 # TODO change from short to int
69123
buffer.write(ConstStructs.triple_ushort.pack(self.fps, _frames, _people))
@@ -73,9 +127,17 @@ def write(self, version: float, buffer: BinaryIO):
73127

74128
@property
75129
def mask(self):
130+
""" Returns mask associated with data. """
76131
return self.data.mask
77132

78133
def torch(self):
134+
"""converts current instance into a TorchPoseBody instance.
135+
136+
Returns
137+
-------
138+
TorchPoseBody
139+
The pose body data represented in PyTorch tensors.
140+
"""
79141
try:
80142
import torch
81143
except ImportError:
@@ -91,6 +153,14 @@ def torch(self):
91153
return TorchPoseBody(self.fps, torch_data, torch_confidence)
92154

93155
def tensorflow(self):
156+
"""
157+
converts current instance into a TensorflowPoseBody instance
158+
159+
Returns
160+
-------
161+
TensorflowPoseBody
162+
pose body data represented in TensorFlow tensors
163+
"""
94164
import tensorflow
95165
from ..tensorflow.pose_body import TensorflowPoseBody
96166

@@ -99,24 +169,79 @@ def tensorflow(self):
99169
return TensorflowPoseBody(self.fps, tf_data, tf_confidence)
100170

101171
def zero_filled(self):
172+
"""
173+
fills missing values with zeros.
174+
175+
Returns
176+
-------
177+
NumPyPoseBody
178+
changed pose body data.
179+
"""
102180
self.data = ma.array(self.data.filled(0), mask=self.data.mask)
103181
return self
104182

105183
def matmul(self, matrix: np.ndarray):
184+
"""
185+
Performs matrix multiplication on pose data.
186+
187+
Parameters
188+
----------
189+
matrix : np.ndarray
190+
matrix to multiply the pose data with
191+
192+
Returns
193+
-------
194+
NumPyPoseBody
195+
transformed pose body data
196+
"""
106197
data = ma.dot(self.data, matrix)
107198
return NumPyPoseBody(self.fps, data, self.confidence)
108199

109200
def flip(self, axis=0):
201+
"""
202+
flips pose data across a specified axis
203+
204+
Parameters
205+
----------
206+
axis : int, optional
207+
axis along which the pose data should be flipped.
208+
209+
Returns
210+
-------
211+
NumPyPoseBody
212+
flipped pose body data
213+
"""
110214
vec = np.ones(self.data.shape[-1])
111215
vec[axis] = -1
112216

113217
data = self.data * vec
114218
return NumPyPoseBody(self.fps, data, self.confidence)
115219

116220
def points_perspective(self):
221+
"""
222+
Transforms pose data to get a perspective based on points.
223+
224+
Returns
225+
-------
226+
ma.MaskedArray
227+
Transformed pose data
228+
"""
117229
return ma.transpose(self.data, axes=POINTS_DIMS)
118230

119231
def get_points(self, indexes: List[int]):
232+
"""
233+
Get points (keypoints) based on given indexes.
234+
235+
Parameters
236+
----------
237+
indexes : List[int]
238+
List of indices representing the keypoints to get.
239+
240+
Returns
241+
-------
242+
NumPyPoseBody
243+
Pose body data containing only a specified points.
244+
"""
120245
data = ma.transpose(self.data, axes=POINTS_DIMS)
121246
new_data = ma.transpose(data[indexes], axes=POINTS_DIMS)
122247

@@ -127,6 +252,19 @@ def get_points(self, indexes: List[int]):
127252
return NumPyPoseBody(self.fps, new_data, new_confidence)
128253

129254
def bbox(self, header: PoseHeader):
255+
"""
256+
Computes the bounding boxes for each component based on the pose data.
257+
258+
Parameters
259+
----------
260+
header : PoseHeader
261+
Pose header information.
262+
263+
Returns
264+
-------
265+
NumPyPoseBody
266+
Pose body data representing bounding boxes.
267+
"""
130268
data = ma.transpose(self.data, axes=POINTS_DIMS)
131269

132270
# Split data by components, `ma` doesn't support ".split"
@@ -151,6 +289,21 @@ def bbox(self, header: PoseHeader):
151289
return NumPyPoseBody(self.fps, new_data, confidence)
152290

153291
def interpolate(self, new_fps: int = None, kind='cubic'):
292+
"""
293+
Interpolates the pose data to match a new frame rate.
294+
295+
Parameters
296+
----------
297+
new_fps : int, optional
298+
The desired frame rate for interpolation.
299+
kind : str, optional
300+
The type of interpolation. Options include: "linear", "quadratic", and "cubic".
301+
302+
Returns
303+
-------
304+
NumPyPoseBody
305+
Interpolated pose body data.
306+
"""
154307
try:
155308
from scipy.interpolate import interp1d
156309
except ImportError:
@@ -222,6 +375,17 @@ def interpolate(self, new_fps: int = None, kind='cubic'):
222375
return NumPyPoseBody(fps=new_fps, data=dimensions, confidence=confidence)
223376

224377
def flatten(self):
378+
"""Flattens data and confidence arrays.
379+
380+
method reshapes data and confidence arrays to a two-dimensional array.
381+
The flattened array is filtered to remove rows where confidence is zero.
382+
383+
Returns
384+
--------
385+
numpy.ndarray
386+
flattened and filtered version of the data array.
387+
388+
"""
225389
shape = self.data.shape
226390
data = self.data.data.reshape(-1, shape[-1]) # Not masked data
227391
confidence = self.confidence.flatten()

src/python/pose_format/numpy/representation/distance.py

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,28 @@
22

33

44
class DistanceRepresentation:
5+
"""A class to compute the Euclidean distance between two sets of points.
6+
"""
57
def distance(self, p1s: ma.MaskedArray, p2s: ma.MaskedArray) -> ma.MaskedArray:
8+
"""
9+
Compute the Euclidean distance between two sets of points.
10+
11+
Parameters
12+
----------
13+
p1s : ma.MaskedArray
14+
First set of points.
15+
p2s : ma.MaskedArray
16+
Second set of points.
17+
18+
Returns
19+
-------
20+
ma.MaskedArray
21+
Euclidean distances between the two sets of points. The returned array has one fewer dimension than the input arrays, as the distance calculation collapses the last dimension.
22+
23+
Note
24+
-----
25+
this method assumes that input arrays `p1s` and `p2s` have same shape.
26+
"""
627
diff = p1s - p2s
728
square = ma.power(diff, 2)
829
sum_squares = square.sum(axis=-1)
@@ -11,9 +32,18 @@ def distance(self, p1s: ma.MaskedArray, p2s: ma.MaskedArray) -> ma.MaskedArray:
1132

1233
def __call__(self, p1s: ma.MaskedArray, p2s: ma.MaskedArray) -> ma.MaskedArray:
1334
"""
14-
Euclidean distance between two points
15-
:param p1s: ma.MaskedArray (Points, Batch, Len, Dims)
16-
:param p2s: ma.MaskedArray (Points, Batch, Len, Dims)
17-
:return: ma.MaskedArray (Points, Batch, Len)
35+
For `distance` method to compute Euclidean distance between two points.
36+
37+
Parameters
38+
----------
39+
p1s : ma.MaskedArray, shape (Points, Batch, Len, Dims)
40+
First set of points.
41+
p2s : ma.MaskedArray, shape (Points, Batch, Len, Dims)
42+
Second set of points.
43+
44+
Returns
45+
-------
46+
ma.MaskedArray, shape (Points, Batch, Len)
47+
Euclidean distances between the two sets of points.
1848
"""
1949
return self.distance(p1s, p2s)

src/python/pose_format/numpy/representation/distance_test.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,35 @@
1010

1111

1212
class TestDistanceRepresentation(TestCase):
13+
"""
14+
Unit tests for DistanceRepresentation class to computes Euclidean distance between sets of 3D points.
15+
"""
1316
def test_call_value_should_be_distance(self):
17+
"""
18+
Test if computed distance between two points is correct.
19+
20+
Note
21+
-----
22+
This test initializes two sets of 3D points:
23+
p1 = (1, 2, 3) and p2 = (4, 5, 6). It then checks if the
24+
computed Euclidean distance between p1 and p2 is sqrt(27).
25+
"""
26+
1427
p1s = ma.array([[[[1, 2, 3]]]], dtype=np.float32)
1528
p2s = ma.array([[[[4, 5, 6]]]], dtype=np.float32)
1629
distances = representation(p1s, p2s)
1730
self.assertAlmostEqual(float(distances[0][0][0]), math.sqrt(27), places=6)
1831

1932
def test_call_masked_value_should_be_zero(self):
33+
"""
34+
Test if the distance for masked values is zero.
35+
36+
Note
37+
-----
38+
This test checks scenario where one set of points
39+
is masked.The computed distance in such in such a case be 0.
40+
"""
41+
2042
mask = [[[[True, True, True]]]]
2143
p1s = ma.array([[[[1, 2, 3]]]], mask=mask, dtype=np.float32)
2244
p2s = ma.array([[[[4, 5, 6]]]], dtype=np.float32)

0 commit comments

Comments
 (0)