95 lines
2.6 KiB
Python
95 lines
2.6 KiB
Python
|
|
import os
|
||
|
|
|
||
|
|
from enum import IntEnum
|
||
|
|
from .color_info import ColorInfo
|
||
|
|
from .data_info import DataInfo
|
||
|
|
from .timestamp import Timestamp
|
||
|
|
from PIL import Image
|
||
|
|
|
||
|
|
|
||
|
|
class ImageFileFormat(IntEnum):
|
||
|
|
"""
|
||
|
|
Describe all supported image file formats for saving `VideoFrame`:
|
||
|
|
- BIN.
|
||
|
|
- PPM.
|
||
|
|
- BMP.
|
||
|
|
- DSC.
|
||
|
|
"""
|
||
|
|
IFF_BIN = 0
|
||
|
|
IFF_PPM = 1
|
||
|
|
IFF_BMP = 2
|
||
|
|
IFF_DSC = 3
|
||
|
|
|
||
|
|
|
||
|
|
class VideoFrame:
|
||
|
|
|
||
|
|
"""
|
||
|
|
Class `VideoFrame` contains base information about video frame:
|
||
|
|
- Height (int).
|
||
|
|
- Width (int).
|
||
|
|
- Data (bytearray).
|
||
|
|
- Color info (object of `ColorInfo`).
|
||
|
|
- Data info (object of `DataInfo`).
|
||
|
|
- Timestamp (object of `Timestamp`).
|
||
|
|
"""
|
||
|
|
|
||
|
|
def __init__(self):
|
||
|
|
self.width = 0
|
||
|
|
self.height = 0
|
||
|
|
self.data = bytearray()
|
||
|
|
self.color_info = ColorInfo()
|
||
|
|
self.data_info = DataInfo()
|
||
|
|
self.timestamp = Timestamp(0)
|
||
|
|
self._compressed = False
|
||
|
|
|
||
|
|
def __str__(self):
|
||
|
|
return f"Resolution: {self.width}x{self.height}\n" \
|
||
|
|
f"Data length: {len(self.data)}\n" \
|
||
|
|
f"Color Info:\n{self.color_info}\n" \
|
||
|
|
f"Data Info:\n{self.data_info}\n" \
|
||
|
|
f"Timestamp:\n{self.timestamp}\n"
|
||
|
|
|
||
|
|
def is_compressed(self) -> bool:
|
||
|
|
return self._compressed
|
||
|
|
|
||
|
|
|
||
|
|
def get_vf_from_image(path: str, width: int, height: int) -> VideoFrame:
|
||
|
|
"""
|
||
|
|
|
||
|
|
Function allows getting prepared object of `VideoFrame` from external (custom) image.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
path (str) - full path to image.
|
||
|
|
width (int) - width of image.
|
||
|
|
height (int) - height of image.
|
||
|
|
|
||
|
|
"""
|
||
|
|
if not os.path.exists(path):
|
||
|
|
raise FileNotFoundError(f"Image: {path} is missing!")
|
||
|
|
|
||
|
|
image = Image.open(path)
|
||
|
|
|
||
|
|
if width < image.size[0] and height < image.size[1]:
|
||
|
|
image = image.crop((0, 0, width, height))
|
||
|
|
|
||
|
|
size = image.size
|
||
|
|
|
||
|
|
if size == [0, 0]:
|
||
|
|
raise ValueError("Invalid image size.")
|
||
|
|
|
||
|
|
convert_image = image.convert('RGB').resize(size=(width, height), resample=Image.Resampling.LANCZOS)
|
||
|
|
|
||
|
|
video_frame = VideoFrame()
|
||
|
|
video_frame.data = bytearray(convert_image.tobytes())
|
||
|
|
video_frame.width = width
|
||
|
|
video_frame.height = height
|
||
|
|
video_frame.color_info.bpc = 8
|
||
|
|
video_frame.color_info.color_format = ColorInfo.ColorFormat.CF_RGB
|
||
|
|
video_frame.color_info.colorimetry = ColorInfo.Colorimetry.CM_sRGB
|
||
|
|
# video_frame.color_info.dynamic_range = ColorInfo.DynamicRange.DR_VESA
|
||
|
|
video_frame.data_info.packing = DataInfo.Packing.P_PACKED
|
||
|
|
video_frame.data_info.component_order = DataInfo.ComponentOrder.CO_RGB
|
||
|
|
video_frame.data_info.alignment = DataInfo.Alignment.A_LSB
|
||
|
|
|
||
|
|
return video_frame
|