BerfScene / utils /formatting_utils.py
3v324v23's picture
init
2f85de4
raw
history blame
No virus
5.9 kB
# 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)