File size: 4,096 Bytes
f71ac1d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
"""This module contains array utility functions."""

from __future__ import annotations

from typing import overload

import numpy as np
import torch

from vis4d.common.typing import (
    ArrayLike,
    NDArrayBool,
    NDArrayF32,
    NDArrayF64,
    NDArrayI32,
    NDArrayI64,
    NDArrayNumber,
    NDArrayUI8,
    NDArrayUI16,
    NDArrayUI32,
)


# Bool dtypes
@overload
def array_to_numpy(
    data: ArrayLike, n_dims: int | None, dtype: type[np.bool_]
) -> NDArrayBool: ...


# Float dtypes
@overload
def array_to_numpy(
    data: ArrayLike | None, n_dims: int | None, dtype: type[np.float32]
) -> NDArrayF32: ...


@overload
def array_to_numpy(
    data: ArrayLike | None, n_dims: int | None, dtype: type[np.float64]
) -> NDArrayF64: ...


# Int dtypes
@overload
def array_to_numpy(
    data: ArrayLike | None, n_dims: int | None, dtype: type[np.int32]
) -> NDArrayI32: ...


@overload
def array_to_numpy(
    data: ArrayLike | None, n_dims: int | None, dtype: type[np.int64]
) -> NDArrayI64: ...


# UInt dtypes
@overload
def array_to_numpy(
    data: ArrayLike | None, n_dims: int | None, dtype: type[np.uint8]
) -> NDArrayUI8: ...


@overload
def array_to_numpy(
    data: ArrayLike | None, n_dims: int | None, dtype: type[np.uint16]
) -> NDArrayUI16: ...


@overload
def array_to_numpy(
    data: ArrayLike | None, n_dims: int | None, dtype: type[np.uint32]
) -> NDArrayUI32: ...


# Union of all dtypes
@overload
def array_to_numpy(
    data: ArrayLike | None, n_dims: int | None
) -> NDArrayNumber: ...


@overload
def array_to_numpy(data: None) -> None: ...


def array_to_numpy(
    data: ArrayLike | None,
    n_dims: int | None = None,
    dtype: (
        type[np.bool_]
        | type[np.float32]
        | type[np.float64]
        | type[np.int32]
        | type[np.int64]
        | type[np.uint8]
        | type[np.uint16]
        | type[np.uint32]
    ) = np.float32,
) -> NDArrayNumber | None:
    """Converts a given array like object to a numpy array.

    Helper function to convert an array like object to a numpy array.
    This functions converts torch.Tensors or Sequences to numpy arrays.

    If the argument is None, None will be returned.

    Examples:
        >>> convert_to_array([1,2,3])
        >>> # -> array([1,2,3])
        >>> convert_to_array(None)
        >>> # -> None
        >>> convert_to_array(torch.tensor([1,2,3]).cuda())
        >>> # -> array([1,2,3])
        >>> convert_to_array([1,2,3], n_dims = 2).shape
        >>> # -> [1, 3]

    Args:
        data (ArrayLike | None): ArrayLike object that should be converted
            to numpy.

        n_dims (int | None, optional): Target number of dimension of the array.
            If the provided array does not have this shape, it will be
            squeezed or exanded (from the left). If it still does not match,
            an error is raised.

        dtype (SUPPORTED_DTYPES, optional): Target dtype of the array. Defaults
            to np.float32.

    Raises:
        ValueError: If the provied array like objects can not be converted
            with the target dimensions.

    Returns:
        NDArrayNumber | None: The converted numpy array or None if None was
            provided.
    """
    if data is None:
        return data

    if isinstance(data, np.ndarray):
        array = data
    elif isinstance(data, torch.Tensor):
        array = np.asarray(data.detach().cpu().numpy())
    else:
        array = np.asarray(data)

    if n_dims is not None:
        # Squeeze if needed
        for _ in range(len(array.shape) - n_dims):
            if array.shape[0] == 1:
                array = array.squeeze(0)
            elif array.shape[-1] == 1:
                array = array.squeeze(-1)

        # expand if needed
        for _ in range(n_dims - len(array.shape)):
            array = np.expand_dims(array, 0)

        if len(array.shape) != n_dims:
            raise ValueError(
                f"Failed to convert target array of shape {array.shape} to"
                f"have {n_dims} dimensions."
            )

    return array.astype(dtype)  # type: ignore