module documentation

Utilities for adapting LDF annotations to Albumentations.

The augmentation engine receives annotations in Luxonis Data Format (LDF), converts them to Albumentations-compatible arrays before spatial transforms, and converts them back afterward. This module keeps those boundary conversions centralized.

Layout conversions
Target LDF layout Albumentations layout
Masks (C, H, W) or (N, H, W) (H, W, C) or (H, W, N)
Bounding boxes [c, x, y, w, h] [xmin, ymin, xmax, ymax, c, i]
Keypoints (N, 3K) normalized rows (NK, 3) pixel-space rows

The appended bbox index i is used during postprocessing to keep bbox-associated labels, such as instance masks and keypoints, aligned with the bounding boxes that survive augmentation and filtering.

Function postprocess_bboxes Convert augmented bounding boxes back to LDF format.
Function postprocess_keypoints Convert augmented keypoints back to normalized LDF rows.
Function postprocess_mask Convert an augmented mask back to the LDF layout.
Function preprocess_bboxes Convert LDF bounding boxes to Albumentations format.
Function preprocess_keypoints Convert normalized LDF keypoints to absolute coordinates.
Function preprocess_mask Convert an LDF mask to the Albumentations mask layout.
Function yield_batches Yield fixed-size chunks of sample dictionaries.
Type Variable T Undocumented
def postprocess_bboxes(bboxes: np.ndarray, area_threshold: float = 0.0004) -> tuple[np.ndarray, np.ndarray]:

Convert augmented bounding boxes back to LDF format.

Bounding boxes smaller than area_threshold are discarded. The appended bbox indices are returned alongside the boxes so labels tied to boxes, such as instance masks, keypoints, arrays, and metadata, can be filtered in the same way.

Examples

>>> bboxes = np.array([[0.1, 0.2, 0.4, 0.6, 2, 5]], dtype=float)
>>> out_bboxes, ordering = postprocess_bboxes(bboxes)
>>> np.round(out_bboxes, 2).tolist()
[[2.0, 0.1, 0.2, 0.3, 0.4]]
>>> ordering.tolist()
[5]
>>> tiny = np.array([[0.0, 0.0, 0.01, 0.01, 1, 3]], dtype=float)
>>> postprocess_bboxes(tiny, area_threshold=0.1)[0].shape
(0, 5)
>>> postprocess_bboxes(np.empty((0, 6)), area_threshold=0.1)[0].shape
(0, 5)
Parameters
bboxes:np.ndarrayAugmented bounding boxes of shape (N, 6) in [xmin, ymin, xmax, ymax, c, i] format, where c is the class ID and i is the stable bbox index.
area_threshold:floatMinimum normalized box area wh required for a box to remain valid.
Returns
A tuple containing
  • Bounding boxes of shape (M, 5) in [c, x, y, w, h] format, where M ≤ N and c is the class ID.
  • Integer indices of shape (M) identifying which original bbox-associated labels survived filtering.
def postprocess_keypoints(keypoints: np.ndarray, bboxes_ordering: np.ndarray, image_height: int, image_width: int, n_keypoints: int) -> np.ndarray:

Convert augmented keypoints back to normalized LDF rows.

The input contains pixel-space keypoints after Albumentations has applied spatial transforms. This function restores the original annotation grouping, keeps only annotations associated with surviving bounding boxes, marks out-of-image keypoints as invisible, clips coordinates to the image extent, and normalizes coordinates back to [0, 1].

Examples

>>> keypoints = np.array([[5, 5, 2], [12, -1, 2]], dtype=float)
>>> out = postprocess_keypoints(keypoints, np.array([0]), 10, 10, 2)
>>> np.round(out, 2).tolist()
[[0.5, 0.5, 2.0, 1.0, 0.0, 0.0]]
>>> keypoints = np.array([[1, 1, 2], [9, 9, 1]], dtype=float)
>>> postprocess_keypoints(keypoints, np.array([1, 0]), 10, 10, 1).tolist()
[[0.9, 0.9, 1.0], [0.1, 0.1, 2.0]]
Parameters
keypoints:np.ndarrayAugmented keypoints of shape (NK, D), where the first 3K values per annotation are (x, y, v) triplets and any extra columns are discarded.
bboxes_ordering:np.ndarrayIndices of bbox-associated annotations that survived bbox postprocessing.
image_height:intHeight of the augmented image used to normalize y coordinates.
image_width:intWidth of the augmented image used to normalize x coordinates.
n_keypoints:intNumber of keypoints K in each annotation.
Returns
np.ndarrayKeypoints of shape (M, 3K), where M is the number of retained annotations. Each row is in normalized LDF format [x1, y1, v1, x2, y2, v2, …].
def postprocess_mask(mask: np.ndarray) -> np.ndarray:

Convert an augmented mask back to the LDF layout.

Examples

>>> postprocess_mask(np.array([[1, 0], [0, 1]])).shape
(1, 2, 2)
>>> mask = np.array([[[1, 5], [2, 6]], [[3, 7], [4, 8]]])
>>> postprocess_mask(mask).tolist()
[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
Parameters
mask:np.ndarrayAugmented mask. Two-dimensional inputs are interpreted as a single mask of shape (H, W). Three-dimensional inputs are expected to have shape (H, W, C) or (H, W, N).
Returns
np.ndarrayMask array in LDF channels-first layout. Two-dimensional inputs become (1, H, W), and three-dimensional inputs become (C, H, W) or (N, H, W).
def preprocess_bboxes(bboxes: np.ndarray, bbox_counter: int) -> np.ndarray:

Convert LDF bounding boxes to Albumentations format.

LDF stores bounding boxes as normalized [c, x, y, w, h] rows, where c is the class ID and x and y are the top-left corner. Albumentations expects normalized [xmin, ymin, xmax, ymax, c] rows. This function also appends a stable per-box index used after augmentation to keep bbox-associated labels aligned with boxes that survived filtering.

Examples

>>> bboxes = np.array([[2, 0.1, 0.2, 0.3, 0.4]])
>>> out = preprocess_bboxes(bboxes, bbox_counter=5)
>>> np.round(out[:, :4], 4).tolist()
[[0.1, 0.2, 0.4, 0.6]]
>>> out[:, 4:].astype(int).tolist()
[[2, 5]]
Parameters
bboxes:np.ndarrayBounding boxes of shape (N, 5) in [c, x, y, w, h] format.
bbox_counter:intOffset used to create the appended bbox indices. The first output row receives this value, the next receives bbox_counter + 1, and so on.
Returns
np.ndarrayBounding boxes of shape (N, 6) in [xmin, ymin, xmax, ymax, c, i] format, where i is the stable bbox index.
def preprocess_keypoints(keypoints: np.ndarray, height: int, width: int) -> np.ndarray:

Convert normalized LDF keypoints to absolute coordinates.

LDF stores keypoints as flattened rows of normalized (x, y, v) triplets. Albumentations expects one keypoint per row and uses pixel-space coordinates for spatial transforms.

Examples

>>> keypoints = np.array([[0.5, 0.25, 2, 1.0, 0.0, 1]])
>>> preprocess_keypoints(keypoints, height=8, width=4).tolist()
[[2.0, 2.0, 2.0], [4.0, 0.0, 1.0]]
>>> preprocess_keypoints(np.empty((0, 3)), 8, 4).shape
(0, 3)
Parameters
keypoints:np.ndarrayKeypoints of shape (N, 3K), where N is the number of annotations and K is the number of keypoints per annotation. Each row has format [x1, y1, v1, x2, y2, v2, …].
height:intImage height used to scale normalized y coordinates.
width:intImage width used to scale normalized x coordinates.
Returns
np.ndarrayKeypoints of shape (NK, 3) in pixel-space [x, y, v] format.
def preprocess_mask(seg: np.ndarray) -> np.ndarray:

Convert an LDF mask to the Albumentations mask layout.

Luxonis Data Format stores semantic masks as (C, H, W) and instance masks as (N, H, W). Albumentations expects the spatial dimensions first, so this function moves the channel or instance dimension to the end.

Examples

>>> seg = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
>>> preprocess_mask(seg).shape
(2, 2, 2)
>>> preprocess_mask(seg).tolist()
[[[1, 5], [2, 6]], [[3, 7], [4, 8]]]
Parameters
seg:np.ndarrayMask array of shape (C, H, W) or (N, H, W).
Returns
np.ndarrayMask array of shape (H, W, C) or (H, W, N).
def yield_batches(data_batch: list[dict[str, T]], batch_size: int) -> Iterator[dict[str, list[T]]]:

Yield fixed-size chunks of sample dictionaries.

Examples

>>> samples = [
...     {"image": "a", "label": 1},
...     {"image": "b", "label": 2},
...     {"image": "c", "label": 3},
... ]
>>> list(yield_batches(samples, 2))
[{'image': ['a', 'b'], 'label': [1, 2]}, {'image': ['c'], 'label': [3]}]
>>> next(yield_batches(samples, 10))["label"]
[1, 2, 3]
Parameters
data_batch:list[dict[str, T]]Dictionaries containing data for each sample. All dictionaries are expected to expose the same keys.
batch_size:intMaximum number of samples in each yielded batch.
Returns
Iterator[dict[str, list[T]]]Iterator over dictionaries grouped by key. For an input chunk of B samples, each yielded value maps every key to a list of B values.
T =

Undocumented

Value
TypeVar('T')