File size: 5,902 Bytes
2f85de4 |
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 168 169 170 171 172 173 174 175 176 177 178 179 |
# python3.7
"""Contains utility functions used for formatting."""
import cv2
import numpy as np
__all__ = [
'format_time', 'format_range', 'format_image_size', 'format_image',
'raw_label_to_one_hot', 'one_hot_to_raw_label'
]
def format_time(seconds):
"""Formats seconds to readable time string.
Args:
seconds: Number of seconds to format.
Returns:
The formatted time string.
Raises:
ValueError: If the input `seconds` is less than 0.
"""
if seconds < 0:
raise ValueError(f'Input `seconds` should be greater than or equal to '
f'0, but `{seconds}` is received!')
# Returns seconds as float if less than 1 minute.
if seconds < 10:
return f'{seconds:7.3f} s'
if seconds < 60:
return f'{seconds:7.2f} s'
seconds = int(seconds + 0.5)
days, seconds = divmod(seconds, 86400)
hours, seconds = divmod(seconds, 3600)
minutes, seconds = divmod(seconds, 60)
if days:
return f'{days:2d} d {hours:02d} h'
if hours:
return f'{hours:2d} h {minutes:02d} m'
return f'{minutes:2d} m {seconds:02d} s'
def format_range(obj, min_val=None, max_val=None):
"""Formats the given object to a valid range.
If `min_val` or `max_val` is provided, both the starting value and the end
value will be clamped to range `[min_val, max_val]`.
NOTE: (a, b) is regarded as a valid range if and only if `a <= b`.
Args:
obj: The input object to format.
min_val: The minimum value to cut off the input range. If not provided,
the default minimum value is negative infinity. (default: None)
max_val: The maximum value to cut off the input range. If not provided,
the default maximum value is infinity. (default: None)
Returns:
A two-elements tuple, indicating the start and the end of the range.
Raises:
ValueError: If the input object is an invalid range.
"""
if not isinstance(obj, (tuple, list)):
raise ValueError(f'Input object must be a tuple or a list, '
f'but `{type(obj)}` received!')
if len(obj) != 2:
raise ValueError(f'Input object is expected to contain two elements, '
f'but `{len(obj)}` received!')
if obj[0] > obj[1]:
raise ValueError(f'The second element is expected to be equal to or '
f'greater than the first one, '
f'but `({obj[0]}, {obj[1]})` received!')
obj = list(obj)
if min_val is not None:
obj[0] = max(obj[0], min_val)
obj[1] = max(obj[1], min_val)
if max_val is not None:
obj[0] = min(obj[0], max_val)
obj[1] = min(obj[1], max_val)
return tuple(obj)
def format_image_size(size):
"""Formats the given image size to a two-element tuple.
A valid image size can be an integer, indicating both the height and the
width, OR can be a two-element list or tuple. Both height and width are
assumed to be positive integer.
Args:
size: The input size to format.
Returns:
A two-elements tuple, indicating the height and the width, respectively.
Raises:
ValueError: If the input size is invalid.
"""
if not isinstance(size, (int, tuple, list)):
raise ValueError(f'Input size must be an integer, a tuple, or a list, '
f'but `{type(size)}` received!')
if isinstance(size, int):
size = (size, size)
else:
if len(size) == 1:
size = (size[0], size[0])
if not len(size) == 2:
raise ValueError(f'Input size is expected to have two numbers at '
f'most, but `{len(size)}` numbers received!')
if not isinstance(size[0], int) or size[0] < 0:
raise ValueError(f'The height is expected to be a non-negative '
f'integer, but `{size[0]}` received!')
if not isinstance(size[1], int) or size[1] < 0:
raise ValueError(f'The width is expected to be a non-negative '
f'integer, but `{size[1]}` received!')
return tuple(size)
def format_image(image):
"""Formats an image read from `cv2`.
NOTE: This function will always return a 3-dimensional image (i.e., with
shape [H, W, C]) in pixel range [0, 255]. For color images, the channel
order of the input is expected to be with `BGR` or `BGRA`, which is the
raw image decoded by `cv2`; while the channel order of the output is set to
`RGB` or `RGBA` by default.
Args:
image: `np.ndarray`, an image read by `cv2.imread()` or
`cv2.imdecode()`.
Returns:
An image with shape [H, W, C] (where `C = 1` for grayscale image).
"""
if image.ndim == 2: # add additional axis if given a grayscale image
image = image[:, :, np.newaxis]
assert isinstance(image, np.ndarray)
assert image.dtype == np.uint8
assert image.ndim == 3 and image.shape[2] in [1, 3, 4]
if image.shape[2] == 3: # BGR image
return cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
if image.shape[2] == 4: # BGRA image
return cv2.cvtColor(image, cv2.COLOR_BGRA2RGBA)
return image
def raw_label_to_one_hot(raw_label, num_classes):
"""Converts a single label into one-hot vector.
Args:
raw_label: The raw label.
num_classes: Total number of classes.
Returns:
one-hot vector of the given raw label.
"""
one_hot = np.zeros(num_classes, dtype=np.float32)
one_hot[raw_label] = 1.0
return one_hot
def one_hot_to_raw_label(one_hot):
"""Converts a one-hot vector to a single value label.
Args:
one_hot: `np.ndarray`, a one-hot encoded vector.
Returns:
A single integer to represent the category.
"""
return np.argmax(one_hot)
|