1.1.0版本

This commit is contained in:
xinzhu.yin
2026-04-16 16:51:05 +08:00
commit c157e774e5
333 changed files with 70759 additions and 0 deletions

4
UniTAP/utils/__init__.py Normal file
View File

@@ -0,0 +1,4 @@
from .function_wrapper import function_scheduler
from .uicl_api import video_frame_to_rgb8, video_frame_to_ci, video_frame_save_to_file, calculate_crc, ImageFileFormat,\
calculate_dsc_crc
from .dscl_api import encode_video_frame, decode_video_frame, calculate_dsc_slice_size

56
UniTAP/utils/dscl_api.py Normal file
View File

@@ -0,0 +1,56 @@
from UniTAP.libs.lib_dscl.dscl_utils import encode_vf, decode_vf, dscl_image_calculate_crc, dscl_data_processing, calculate_slice_size
from UniTAP.common import VideoFrame, VideoFrameDSC, CompressionInfo
def encode_video_frame(src_video_frame: VideoFrame, info: CompressionInfo) -> VideoFrameDSC:
"""
Encode custom video frame `VideoFrame` with transferred DSC parameters to video frame with DSC data.
Args:
src_video_frame (`VideoFrame`)
info (`CompressionInfo`)
"""
return encode_vf(src_video_frame, info)
def decode_video_frame(src_video_frame: VideoFrameDSC) -> VideoFrame:
"""
Decode custom video frame `VideoFrameDSC` with DSC data.
Args:
src_video_frame (`VideoFrame`)
"""
return decode_vf(src_video_frame)
def dsc_video_frame_from_data(data: bytearray) -> VideoFrameDSC:
"""
Read PPS from DSC video frame data `VideoFrameDSC`.
Args:
data (`bytearray`)
"""
return dscl_data_processing(data)
def calculate_dsc_crc(video_frame: VideoFrameDSC) -> tuple:
"""
Calculate DSC CRC by DSC Video Frame.
Args:
video_frame ('VideoFrameDSC')
"""
if not isinstance(video_frame, VideoFrameDSC):
raise TypeError("Calculate DSC crc accept only VideoFrameDSC object!")
return dscl_image_calculate_crc(video_frame)
def calculate_dsc_slice_size(value: int, slice_number: int):
"""
Calculate DSC slice size by value and slice number.
Args:
value ('int')
slice_number ('int')
"""
return int(calculate_slice_size(value, slice_number))

View File

@@ -0,0 +1,43 @@
import sched
import time
def function_scheduler(exec_func, *args, interval: float = 1, timeout: float = 1) -> bool:
"""
Execute function or lambda with timeout and interval.
Args:
exec_func (function) - Function for execution.
*args - Arguments for executing function.
interval (float) - Function call interval.
timeout (float) - Timeout until the end of calls function.
Returns:
function_successful (bool) - function result
"""
__sched = sched.scheduler()
def __function_wrapper():
nonlocal function_successful
nonlocal time_start
if time.time() - time_start > timeout:
function_successful = False
for job in __sched.queue:
__sched.cancel(job)
return
if function_successful or exec_func(*args):
function_successful = True
for job in __sched.queue:
__sched.cancel(job)
function_successful = False
call_count = round(timeout / interval)
for call in range(0, call_count):
__sched.enter(call * interval, 1, __function_wrapper)
time_start = time.time()
__sched.run()
return function_successful

302
UniTAP/utils/tsi_logging.py Normal file
View File

@@ -0,0 +1,302 @@
import logging as _logging
import os as _os
import sys as _sys
import _thread
import time as _time
import traceback as _traceback
import warnings
from logging import DEBUG
from logging import ERROR
from logging import CRITICAL
from logging import INFO
from logging import WARN
import threading
# Don't use this directly. Use get_logger() instead.
_logger = None
_logger_lock = threading.Lock()
_file_handler = None
def set_enabled(value: bool):
get_logger().disabled = not value
def is_enabled():
return not get_logger().disabled
def error_log(error_msg, level=ERROR):
"""Empty helper method."""
del error_msg, level
def _get_formatter():
return _logging.Formatter(
fmt='%(asctime)s.%(msecs)03d %(name)-12s %(levelname)-8s %(thread)-6d %(message)s',
datefmt='%H:%M:%S')
def _get_caller(offset=3):
"""Returns a code and frame object for the lowest non-logging stack frame."""
# Use sys._getframe(). This avoids creating a traceback object.
f = _sys._getframe(offset)
our_file = f.f_code.co_filename
f = f.f_back
while f:
code = f.f_code
if code.co_filename != our_file:
return code, f
f = f.f_back
return None, None
def _logger_find_caller(stack_info=False, stacklevel=1):
code, frame = _get_caller(4)
sinfo = None
if stack_info:
sinfo = '\n'.join(_traceback.format_stack())
if code:
return (code.co_filename, frame.f_lineno, code.co_name, sinfo)
else:
return '(unknown file)', 0, '(unknown function)', sinfo
def get_logger():
global _logger
# Use double-checked locking to avoid taking lock unnecessarily.
if _logger:
return _logger
_logger_lock.acquire()
try:
if _logger:
return _logger
logger = _logging.getLogger('unitap')
# Override findCaller on the logger to skip internal helper functions
logger.findCaller = _logger_find_caller
if not _logging.getLogger().handlers:
# Determine whether we are in an interactive environment
_interactive = False
try:
# This is only defined in interactive shells.
if _sys.ps1:
_interactive = True
except AttributeError:
# Even now, we may be in an interactive shell with `python -i`.
_interactive = _sys.flags.interactive
# If we are in an interactive environment (like Jupyter), set loglevel
# to INFO and pipe the output to stdout.
if _interactive:
logger.setLevel(INFO)
_logging_target = _sys.stdout
else:
_logging_target = _sys.stderr
# Add the output handler.
_handler = _logging.StreamHandler(_logging_target)
_handler.setFormatter(_get_formatter())
logger.addHandler(_handler)
logger.propagate = False
_logger = logger
return _logger
finally:
_logger_lock.release()
def log(level, msg, *args, **kwargs):
get_logger().log(level, msg, *args, **kwargs)
def debug(msg, *args, **kwargs):
get_logger().debug(msg, *args, **kwargs)
def error(msg, *args, **kwargs):
get_logger().error(msg, *args, **kwargs)
def fatal(msg, *args, **kwargs):
get_logger().fatal(msg, *args, **kwargs)
def info(msg, *args, **kwargs):
get_logger().info(msg, *args, **kwargs)
def warn(msg, *args, **kwargs):
get_logger().warning(msg, *args, **kwargs)
def warning(msg, *args, **kwargs):
get_logger().warning(msg, *args, **kwargs)
_level_names = {
CRITICAL: 'CRITICAL',
ERROR: 'ERROR',
WARN: 'WARN',
INFO: 'INFO',
DEBUG: 'DEBUG',
}
# Mask to convert integer thread ids to unsigned quantities for logging
# purposes
_THREAD_ID_MASK = 2 * _sys.maxsize + 1
_log_prefix = None # later set to google2_log_prefix
# Counter to keep track of number of log entries per token.
_log_counter_per_token = {}
def TaskLevelStatusMessage(msg):
error(msg)
def flush():
raise NotImplementedError()
# Code below is taken from pyglib/logging
def vlog(level, msg, *args, **kwargs):
get_logger().log(level, msg, *args, **kwargs)
def _GetNextLogCountPerToken(token):
global _log_counter_per_token
_log_counter_per_token[token] = 1 + _log_counter_per_token.get(token, -1)
return _log_counter_per_token[token]
def log_every_n(level, msg, n, *args):
"""Log 'msg % args' at level 'level' once per 'n' times.
Logs the 1st call, (N+1)st call, (2N+1)st call, etc.
Not threadsafe.
Args:
level: The level at which to log.
msg: The message to be logged.
n: The number of times this should be called before it is logged.
*args: The args to be substituted into the msg.
"""
count = _GetNextLogCountPerToken(_GetFileAndLine())
log_if(level, msg, not (count % n), *args)
def log_first_n(level, msg, n, *args):
"""Log 'msg % args' at level 'level' only first 'n' times.
Not threadsafe.
Args:
level: The level at which to log.
msg: The message to be logged.
n: The number of times this should be called before it is logged.
*args: The args to be substituted into the msg.
"""
count = _GetNextLogCountPerToken(_GetFileAndLine())
log_if(level, msg, count < n, *args)
def log_if(level, msg, condition, *args):
"""Log 'msg % args' at level 'level' only if condition is fulfilled."""
if condition:
vlog(level, msg, *args)
def _GetFileAndLine():
"""Returns (filename, linenumber) for the stack frame."""
code, f = _get_caller()
if not code:
return '<unknown>', 0
return code.co_filename, f.f_lineno
def google2_log_prefix(level, timestamp=None, file_and_line=None):
"""Assemble a logline prefix using the google2 format."""
global _level_names
# Record current time
now = timestamp or _time.time()
now_tuple = _time.localtime(now)
now_microsecond = int(1e6 * (now % 1.0))
(filename, line) = file_and_line or _GetFileAndLine()
basename = _os.path.basename(filename)
# Severity string
severity = 'I'
if level in _level_names:
severity = _level_names[level][0]
s = '%c%02d%02d %02d:%02d:%02d.%06d %5d %s:%d] ' % (
severity,
now_tuple[1], # month
now_tuple[2], # day
now_tuple[3], # hour
now_tuple[4], # min
now_tuple[5], # sec
now_microsecond,
_get_thread_id(),
basename,
line)
return s
def get_verbosity():
"""Return how much logging output will be produced."""
return get_logger().getEffectiveLevel()
def set_verbosity(v):
"""Sets the threshold for what messages will be logged."""
get_logger().setLevel(v)
def set_file_output(filename, file_mode="w"):
"""Set file handler"""
warnings.warn("This function is deprecated. Use `enable_file_logging` and `disable_file_logging` instead.",
DeprecationWarning)
enable_file_logging(filename, file_mode)
def enable_file_logging(filename, file_mode="w"):
global _file_handler
if _file_handler is None:
_file_handler = _logging.FileHandler(filename, mode=file_mode)
_file_handler.setFormatter(_get_formatter())
get_logger().addHandler(_file_handler)
else:
_file_handler.stream.close()
filename = _os.fspath(filename)
_file_handler.baseFilename = _os.path.abspath(filename)
_file_handler.stream = open(_file_handler.baseFilename, file_mode)
def disable_file_logging():
global _file_handler
if _file_handler is not None:
_file_handler.stream.close()
get_logger().removeHandler(_file_handler)
_file_handler = None
else:
warnings.warn("Cannot disable file logging because file logger is not defined")
def _get_thread_id():
"""Get id of current thread, suitable for logging as an unsigned quantity."""
thread_id = _thread.get_ident()
return thread_id & _THREAD_ID_MASK
_log_prefix = google2_log_prefix

85
UniTAP/utils/uicl_api.py Normal file
View File

@@ -0,0 +1,85 @@
import os.path
import subprocess
from typing import Union
from UniTAP.libs.lib_uicl.uicl_utils import *
from .dscl_api import *
def image_file_format_to_uicl_format(file_format):
if file_format == ImageFileFormat.IFF_PPM:
return UICL_ImageFileFormat.FILE_FORMAT_PPM
elif file_format == ImageFileFormat.IFF_BIN:
return UICL_ImageFileFormat.FILE_FORMAT_BIN
elif file_format == ImageFileFormat.IFF_BMP:
return UICL_ImageFileFormat.FILE_FORMAT_BMP
else:
raise TypeError(f"Incorrect file format: {file_format}")
def video_frame_to_rgb8(src_video_frame: VideoFrame, data_info: DataInfo) -> VideoFrame:
rgb8_color_info = ColorInfo()
rgb8_color_info.color_format = ColorInfo.ColorFormat.CF_RGB
rgb8_color_info.bpc = 8
rgb8_color_info.dynamic_range = ColorInfo.DynamicRange.DR_VESA
return video_frame_to_ci(src_video_frame, rgb8_color_info, data_info)
def video_frame_to_ci(src_video_frame: VideoFrame, color_info: ColorInfo, data_info: DataInfo) -> VideoFrame:
src_image = image_from_vf(src_video_frame)
dst_image_params = image_params_from_size_and_ci(src_video_frame.width, src_video_frame.height,
color_info, data_info)
if dst_image_params.Alignment != UICL_Alignment.Alignment_LSB:
src_image = image_convert_to_parameters(src_image, dst_image_params)
dst_image_params.Alignment = UICL_Alignment.Alignment_LSB
return image_to_vf(image_convert_to_parameters(src_image, dst_image_params))
def video_frame_uicl_image(src_video_frame: VideoFrame, color_info: ColorInfo, data_info: DataInfo) -> UICL_Image:
src_image = image_from_vf(src_video_frame)
dst_image_params = image_params_from_size_and_ci(src_video_frame.width, src_video_frame.height,
color_info, data_info)
if dst_image_params.Alignment != UICL_Alignment.Alignment_LSB:
src_image = image_convert_to_parameters(src_image, dst_image_params)
dst_image_params.Alignment = UICL_Alignment.Alignment_LSB
return image_convert_to_parameters(src_image, dst_image_params)
def video_frame_save_to_file(video_frame: Union[VideoFrame, VideoFrameDSC], path: str, file_type: ImageFileFormat) -> bool:
if file_type == ImageFileFormat.IFF_DSC:
with open(path, "wb") as dsc_file:
if b'DSCF' not in video_frame.data:
dsc_file.write(b'DSCF')
dsc_file.write(video_frame.data)
return os.path.exists(path)
else:
if b'DSCF' not in video_frame.data:
if video_frame.data_info.component_order == DataInfo.ComponentOrder.CO_UCDRX:
data_info = DataInfo()
color_info = ColorInfo()
data_info.component_order = DataInfo.ComponentOrder.CO_RGB
data_info.alignment = DataInfo.Alignment.A_LSB
data_info.packing = DataInfo.Packing.P_PACKED
color_info.color_format = ColorInfo.ColorFormat.CF_RGB
color_info.bpc = 8
color_info.dynamic_range = ColorInfo.DynamicRange.DR_VESA
frame = video_frame_uicl_image(video_frame, color_info, data_info)
else:
frame = image_from_vf(video_frame)
return image_save_to_file(frame, path, image_file_format_to_uicl_format(file_type))
else:
vf = decode_video_frame(video_frame)
frame = image_from_vf(vf)
return image_save_to_file(frame, path, image_file_format_to_uicl_format(file_type))
def calculate_crc(video_frame: VideoFrame) -> tuple:
if not isinstance(video_frame, VideoFrame):
raise TypeError("Calculate crc accept only VideoFrame object!")
return uicl_image_calculate_crc(video_frame)