Source code for cv_depot.core.enum
from typing import Any # noqa F401
from enum import Enum
import re
from lunchbox.enforce import Enforce
import numpy as np
# ------------------------------------------------------------------------------
'''
The enum module contains Enum classes for manging aspects of imagery such as bit
depths and video codecs.
'''
[docs]
class EnumBase(Enum):
def __repr__(self):
# type: () -> str
'''
str: String representation of enum.
'''
return f'{self.__class__.__name__}.{self.name.upper()}'
[docs]
@classmethod
def from_string(cls, string):
# type: (str) -> EnumBase
'''
Constructs an enum instance from a given string.
Args:
string (int): Enum string.
Raises:
EnforceError: If value given is not a string.
EnforceError: If no EnumBase type can be found for given string.
Returns:
EnumBase: Enum instance.
'''
msg = 'Value given is not a string. {a} != {b}.'
Enforce(string, 'instance of', str, message=msg)
lut = {x.name: x for x in cls.__members__.values()}
string = string.upper().replace('-', '_')
msg = '{a} is not a ' + cls.__name__ + ' option. '
msg += f'Options: {sorted(lut.keys())}.'
Enforce(string, 'in', lut.keys(), message=msg)
return lut[string]
# BITDEPTH----------------------------------------------------------------------
[docs]
class BitDepth(EnumBase):
'''
Legal bit depths.
Includes:
* FLOAT16
* FLOAT32
* UINT8
* INT8
'''
FLOAT16 = (np.float16, 16, True, float)
FLOAT32 = (np.float32, 32, True, float)
UINT8 = (np.uint8, 8, False, int)
INT8 = (np.int8, 8, True, int)
[docs]
def __init__(self, dtype, bits, signed, type_):
# type: (Any, int, bool, type) -> None
'''
Args:
dtype (numpy.type): Numpy datatype.
bits (int): Number of bits per channel.
signed (bool): Whether channel scalars are signed.
type_ (type): Python type of scalar. Options include: [int, float].
Returns:
BitDepth: BitDepth instance.
'''
self.dtype = dtype # type: ignore
self.bits = bits
self.signed = signed
self.type_ = type_
def __repr__(self):
# type: () -> str
return f'BitDepth.{self.name.upper()}'
[docs]
@staticmethod
def from_dtype(dtype):
# type: (Any) -> BitDepth
'''
Construct a BitDepth instance from a given numpy datatype.
Args:
dtype (numpy.type): Numpy datatype. Options include:
[float16, float32, uint8, int8].
Raises:
TypeError: If invlaid dtype is given.
Returns:
BitDepth: BitDepth instance of given type.
'''
if dtype == np.float16:
return BitDepth.FLOAT16
elif dtype == np.float32:
return BitDepth.FLOAT32
elif dtype == np.uint8:
return BitDepth.UINT8
elif dtype == np.int8:
return BitDepth.INT8
# needed because of numpy malarkey with __name__
if hasattr(dtype, '__name__'):
dtype = dtype.__name__
msg = f'{dtype} is not a supported bit depth.'
raise TypeError(msg)
# IMAGE-------------------------------------------------------------------------
[docs]
class ImageFormat(Enum):
'''
Legal image formats.
Includes:
* EXR
* PNG
* JPEG
* TIFF
'''
EXR = (
'exr', [BitDepth.FLOAT16, BitDepth.FLOAT32], list('rgba') + ['...'],
1023, True
)
PNG = ('png', [BitDepth.UINT8], list('rgba'), 4, False)
JPEG = ('jpeg', [BitDepth.UINT8], list('rgb'), 3, False)
TIFF = (
'tiff', [BitDepth.INT8, BitDepth.UINT8, BitDepth.FLOAT32],
list('rgba') + ['...'], 500, False
)
[docs]
def __init__(self, extension, bit_depths, channels, max_channels,
custom_metadata):
# type: (str, list[BitDepth], list[str], int, bool) -> None
'''
Args:
extension (str): Name of file extension.
bit_depths (list[BitDepth]): Supported bit depths.
channels (list[str]): Supported channels.
max_channels (int): Maximum number of channels supported.
custom_metadata (bool): Custom metadata support.
Returns:
ImageFormat: ImageFormat instance.
'''
self.extension = extension
self.bit_depths = bit_depths
self.channels = channels
self.max_channels = max_channels
self.custom_metadata = custom_metadata
def __repr__(self):
# type: () -> str
return f'''
<ImageFormat.{self.name.upper()}>
extension: {self.extension}
bit_depths: {[x.name for x in self.bit_depths]}
channels: {self.channels}
max_channels: {self.max_channels}
custom_metadata: {self.custom_metadata}'''[1:]
[docs]
@staticmethod
def from_extension(extension):
'''
Construct an ImageFormat instance for a given file extension.
Args:
extension (str): File extension.
Raises:
TypeError: If extension is illegal.
Returns:
ImageFormat: ImageFormat instance of given extension.
'''
exr_re = r'^\.?exr$'
png_re = r'^\.?png$'
jpeg_re = r'^\.?jpe?g$'
tiff_re = r'^\.?tiff?$'
if re.search(exr_re, extension, re.I):
return ImageFormat.EXR
elif re.search(png_re, extension, re.I):
return ImageFormat.PNG
elif re.search(jpeg_re, extension, re.I):
return ImageFormat.JPEG
elif re.search(tiff_re, extension, re.I):
return ImageFormat.TIFF
msg = f'ImageFormat not found for given extension: {extension}'
raise TypeError(msg)
# VIDEO-------------------------------------------------------------------------
[docs]
class VideoFormat(Enum):
'''
Legal video formats.
Includes:
* MP4
* MPEG
* MOV
* M4V
'''
MP4 = ('mp4', [BitDepth.UINT8], list('rgb'), 3, False)
MPEG = ('mpeg', [BitDepth.UINT8], list('rgb'), 3, False)
MOV = ('mov', [BitDepth.UINT8], list('rgb'), 3, False)
M4V = ('m4v', [BitDepth.UINT8], list('rgb'), 3, False)
[docs]
def __init__(self, extension, bit_depths, channels, max_channels,
custom_metadata):
# type: (str, list[BitDepth], list[str], int, bool) -> None
'''
Args:
extension (str): Name of file extension.
bit_depths (list[BitDepth]): Supported bit depths.
channels (list[str]): Supported channels.
max_channels (int): Maximum number of channels supported.
custom_metadata (bool): Custom metadata support.
Returns:
VideoFormat: VideoFormat instance.
'''
self.extension = extension
self.bit_depths = bit_depths
self.channels = channels
self.max_channels = max_channels
self.custom_metadata = custom_metadata
def __repr__(self):
# type: () -> str
return f'''
<VideoFormat.{self.name.upper()}>
extension: {self.extension}
bit_depths: {[x.name for x in self.bit_depths]}
channels: {self.channels}
max_channels: {self.max_channels}
custom_metadata: {self.custom_metadata}'''[1:]
[docs]
@staticmethod
def from_extension(extension):
'''
Construct an VideoFormat instance for a given file extension.
Args:
extension (str): File extension.
Raises:
TypeError: If extension is invalid.
Returns:
VideoFormat: VideoFormat instance of given extension.
'''
mp4_re = r'^\.?mp4$'
mpeg_re = r'^\.?mpe?g$'
mov_re = r'^\.?mov$'
m4v_re = r'^\.?m4v$'
if re.search(mp4_re, extension, re.I):
return VideoFormat.MP4
if re.search(mpeg_re, extension, re.I):
return VideoFormat.MPEG
if re.search(mov_re, extension, re.I):
return VideoFormat.MOV
if re.search(m4v_re, extension, re.I):
return VideoFormat.M4V
msg = f'VideoFormat not found for given extension: {extension}'
raise TypeError(msg)
# ------------------------------------------------------------------------------
[docs]
class VideoCodec(Enum):
'''
Legal video codecs.
Includes:
* H264
* H265
'''
H264 = ('h264', 'h264')
H265 = ('h265', 'hevc')
[docs]
def __init__(self, string, ffmpeg_code):
# type: (str, str) -> None
'''
Args:
string (str): String representation of codec.
ffmpeg_code (str): FFMPEG code.
'''
self.string = string
self.ffmpeg_code = ffmpeg_code
def __repr__(self):
# type: () -> str
return f'''
<VideoCodec.{self.name.upper()}>
string: {self.string}
ffmpeg_code: {self.ffmpeg_code}'''[1:]
# DIRECTION---------------------------------------------------------------------
[docs]
class Direction(EnumBase):
'''
Legal directions.
Includes:
* TOP
* BOTTOM
* LEFT
* RIGHT
'''
TOP = ('top')
BOTTOM = ('bottom')
LEFT = ('left')
RIGHT = ('right')
[docs]
class Anchor(EnumBase):
'''
Legal anchors.
Includes:
* TOP_LEFT
* TOP_CENTER
* TOP_RIGHT
* CENTER_LEFT
* CENTER_CENTER
* CENTER_RIGHT
* BOTTOM_LEFT
* BOTTOM_CENTER
* BOTTOM_RIGHT
'''
TOP_LEFT = ('top', 'left')
TOP_CENTER = ('top', 'center')
TOP_RIGHT = ('top', 'right')
CENTER_LEFT = ('center', 'left')
CENTER_CENTER = ('center', 'center')
CENTER_RIGHT = ('center', 'right')
BOTTOM_LEFT = ('bottom', 'left')
BOTTOM_CENTER = ('bottom', 'center')
BOTTOM_RIGHT = ('bottom', 'right')