2026-04-16 16:51:05 +08:00
|
|
|
|
# -*- coding: UTF-8 -*-
|
|
|
|
|
|
"""
|
|
|
|
|
|
数据范围转换器
|
|
|
|
|
|
将 Full Range (0-255) 转换为 Limited Range (16-235)
|
|
|
|
|
|
|
|
|
|
|
|
使用方法:
|
2026-04-20 11:48:38 +08:00
|
|
|
|
from app.data_range_converter import DataRangeConverter
|
2026-04-16 16:51:05 +08:00
|
|
|
|
|
|
|
|
|
|
converter = DataRangeConverter()
|
|
|
|
|
|
converted_params = converter.convert(pattern_params, "Limited")
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DataRangeConverter:
|
|
|
|
|
|
"""数据范围转换器"""
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, verbose=True):
|
|
|
|
|
|
"""
|
|
|
|
|
|
初始化转换器
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
verbose: 是否打印详细日志
|
|
|
|
|
|
"""
|
|
|
|
|
|
self.verbose = verbose
|
|
|
|
|
|
|
|
|
|
|
|
def convert_value(self, value):
|
|
|
|
|
|
"""
|
|
|
|
|
|
将单个 Full Range 值转换为 Limited Range
|
|
|
|
|
|
|
|
|
|
|
|
转换公式:
|
|
|
|
|
|
limited = 16 + (value / 255) × (235 - 16)
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
value: Full Range 值 (0-255)
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
int: Limited Range 值 (16-235)
|
|
|
|
|
|
"""
|
|
|
|
|
|
# 边界值直接映射
|
|
|
|
|
|
if value == 0:
|
|
|
|
|
|
return 16
|
|
|
|
|
|
elif value == 255:
|
|
|
|
|
|
return 235
|
|
|
|
|
|
else:
|
|
|
|
|
|
# 线性映射
|
|
|
|
|
|
limited = 16 + round((value / 255.0) * (235 - 16))
|
|
|
|
|
|
# 限制范围
|
|
|
|
|
|
return max(16, min(235, limited))
|
|
|
|
|
|
|
|
|
|
|
|
def convert_rgb(self, r, g, b):
|
|
|
|
|
|
"""
|
|
|
|
|
|
转换单个 RGB 值
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
r, g, b: Full Range RGB 值 (0-255)
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
tuple: Limited Range RGB 值 (16-235)
|
|
|
|
|
|
"""
|
|
|
|
|
|
return (self.convert_value(r), self.convert_value(g), self.convert_value(b))
|
|
|
|
|
|
|
|
|
|
|
|
def convert(self, pattern_params, data_range="Full"):
|
|
|
|
|
|
"""
|
|
|
|
|
|
转换图案参数列表
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
pattern_params: 图案参数列表 [[r,g,b], [r,g,b], ...]
|
|
|
|
|
|
data_range: "Full" 或 "Limited"
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
list: 转换后的图案参数列表
|
|
|
|
|
|
"""
|
|
|
|
|
|
# Full Range 不需要转换
|
|
|
|
|
|
if data_range == "Full":
|
|
|
|
|
|
if self.verbose:
|
|
|
|
|
|
print("✓ 使用 Full Range (0-255),无需转换")
|
|
|
|
|
|
return pattern_params
|
|
|
|
|
|
|
|
|
|
|
|
# Limited Range 需要转换
|
|
|
|
|
|
if data_range == "Limited":
|
|
|
|
|
|
if self.verbose:
|
|
|
|
|
|
self._print_header()
|
|
|
|
|
|
|
|
|
|
|
|
converted = []
|
|
|
|
|
|
for i, rgb in enumerate(pattern_params):
|
|
|
|
|
|
# 获取原始值
|
|
|
|
|
|
r_orig, g_orig, b_orig = rgb[0], rgb[1], rgb[2]
|
|
|
|
|
|
|
|
|
|
|
|
# 转换
|
|
|
|
|
|
r_new, g_new, b_new = self.convert_rgb(r_orig, g_orig, b_orig)
|
|
|
|
|
|
|
|
|
|
|
|
# 保存
|
|
|
|
|
|
converted.append([r_new, g_new, b_new])
|
|
|
|
|
|
|
|
|
|
|
|
# 打印日志(关键值)
|
|
|
|
|
|
if self.verbose:
|
|
|
|
|
|
self._print_conversion(
|
|
|
|
|
|
i, r_orig, g_orig, b_orig, r_new, g_new, b_new
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if self.verbose:
|
|
|
|
|
|
self._print_footer(len(pattern_params))
|
|
|
|
|
|
|
|
|
|
|
|
return converted
|
|
|
|
|
|
|
|
|
|
|
|
# 未知范围,返回原始值
|
|
|
|
|
|
else:
|
|
|
|
|
|
if self.verbose:
|
|
|
|
|
|
print(f"⚠ 未知的数据范围: {data_range},使用原始值")
|
|
|
|
|
|
return pattern_params
|
|
|
|
|
|
|
|
|
|
|
|
def _print_header(self):
|
|
|
|
|
|
"""打印转换头部信息"""
|
|
|
|
|
|
print("=" * 80)
|
|
|
|
|
|
print("【数据范围转换】Limited Range (16-235)")
|
|
|
|
|
|
print(" 转换公式: 16 + (value / 255) × (235 - 16)")
|
|
|
|
|
|
print("=" * 80)
|
|
|
|
|
|
|
|
|
|
|
|
def _print_conversion(self, index, r_orig, g_orig, b_orig, r_new, g_new, b_new):
|
|
|
|
|
|
"""
|
|
|
|
|
|
打印转换日志
|
|
|
|
|
|
|
|
|
|
|
|
策略:只打印关键值,避免刷屏
|
|
|
|
|
|
- 第一个和最后一个图案
|
|
|
|
|
|
- 0, 128, 255 等关键值
|
|
|
|
|
|
"""
|
|
|
|
|
|
# 判断是否需要打印
|
|
|
|
|
|
should_print = False
|
|
|
|
|
|
|
|
|
|
|
|
# 第一个和最后一个
|
|
|
|
|
|
if index == 0:
|
|
|
|
|
|
should_print = True
|
|
|
|
|
|
label = "黑色"
|
|
|
|
|
|
elif index == len([]) - 1: # 需要在外部判断
|
|
|
|
|
|
should_print = True
|
|
|
|
|
|
label = "白色"
|
|
|
|
|
|
# 关键 RGB 值
|
|
|
|
|
|
elif (
|
|
|
|
|
|
r_orig in [0, 128, 255]
|
|
|
|
|
|
or g_orig in [0, 128, 255]
|
|
|
|
|
|
or b_orig in [0, 128, 255]
|
|
|
|
|
|
):
|
|
|
|
|
|
should_print = True
|
|
|
|
|
|
if r_orig == 128:
|
|
|
|
|
|
label = "50% 灰"
|
|
|
|
|
|
else:
|
|
|
|
|
|
label = ""
|
|
|
|
|
|
|
|
|
|
|
|
if should_print:
|
|
|
|
|
|
diff = abs(r_new - r_orig)
|
|
|
|
|
|
print(
|
|
|
|
|
|
f" 图案 {index+1:2d} {label:8s}: "
|
|
|
|
|
|
f"RGB({r_orig:3d},{g_orig:3d},{b_orig:3d}) → "
|
|
|
|
|
|
f"RGB({r_new:3d},{g_new:3d},{b_new:3d}) "
|
|
|
|
|
|
f"(差值: {diff:+3d})"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def _print_footer(self, total_count):
|
|
|
|
|
|
"""打印转换尾部信息"""
|
|
|
|
|
|
print(f"✓ 转换完成,共 {total_count} 个图案")
|
|
|
|
|
|
print("=" * 80)
|
|
|
|
|
|
|
|
|
|
|
|
def get_info(self):
|
|
|
|
|
|
"""获取转换器信息"""
|
|
|
|
|
|
return {
|
|
|
|
|
|
"name": "Data Range Converter",
|
|
|
|
|
|
"version": "1.0.0",
|
|
|
|
|
|
"full_range": "0-255",
|
|
|
|
|
|
"limited_range": "16-235",
|
|
|
|
|
|
"formula": "16 + (value / 255) × 219",
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ========== 便捷函数 ==========
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def convert_pattern_params(pattern_params, data_range="Full", verbose=True):
|
|
|
|
|
|
"""
|
|
|
|
|
|
便捷函数:转换图案参数
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
pattern_params: 图案参数列表 [[r,g,b], [r,g,b], ...]
|
|
|
|
|
|
data_range: "Full" 或 "Limited"
|
|
|
|
|
|
verbose: 是否打印日志
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
list: 转换后的图案参数列表
|
|
|
|
|
|
|
|
|
|
|
|
示例:
|
2026-04-20 11:48:38 +08:00
|
|
|
|
>>> from app.data_range_converter import convert_pattern_params
|
2026-04-16 16:51:05 +08:00
|
|
|
|
>>> params = [[0,0,0], [255,255,255]]
|
|
|
|
|
|
>>> converted = convert_pattern_params(params, "Limited")
|
|
|
|
|
|
[[16,16,16], [235,235,235]]
|
|
|
|
|
|
"""
|
|
|
|
|
|
converter = DataRangeConverter(verbose=verbose)
|
|
|
|
|
|
return converter.convert(pattern_params, data_range)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def convert_single_rgb(r, g, b, data_range="Full"):
|
|
|
|
|
|
"""
|
|
|
|
|
|
便捷函数:转换单个 RGB 值
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
r, g, b: RGB 值 (0-255)
|
|
|
|
|
|
data_range: "Full" 或 "Limited"
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
tuple: 转换后的 RGB 值
|
|
|
|
|
|
|
|
|
|
|
|
示例:
|
2026-04-20 11:48:38 +08:00
|
|
|
|
>>> from app.data_range_converter import convert_single_rgb
|
2026-04-16 16:51:05 +08:00
|
|
|
|
>>> r, g, b = convert_single_rgb(0, 0, 0, "Limited")
|
|
|
|
|
|
(16, 16, 16)
|
|
|
|
|
|
"""
|
|
|
|
|
|
if data_range == "Full":
|
|
|
|
|
|
return (r, g, b)
|
|
|
|
|
|
|
|
|
|
|
|
converter = DataRangeConverter(verbose=False)
|
|
|
|
|
|
return converter.convert_rgb(r, g, b)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ========== 测试代码 ==========
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
"""测试转换器"""
|
|
|
|
|
|
|
|
|
|
|
|
print("=" * 80)
|
|
|
|
|
|
print("数据范围转换器 - 测试")
|
|
|
|
|
|
print("=" * 80)
|
|
|
|
|
|
|
|
|
|
|
|
# 测试 1: 基本转换
|
|
|
|
|
|
print("\n[测试 1] 基本转换...")
|
|
|
|
|
|
converter = DataRangeConverter(verbose=False)
|
|
|
|
|
|
|
|
|
|
|
|
test_values = [0, 16, 64, 128, 192, 235, 255]
|
|
|
|
|
|
print(" Full Range → Limited Range:")
|
|
|
|
|
|
for v in test_values:
|
|
|
|
|
|
limited = converter.convert_value(v)
|
|
|
|
|
|
diff = limited - v
|
|
|
|
|
|
print(f" {v:3d} → {limited:3d} (差值: {diff:+3d})")
|
|
|
|
|
|
|
|
|
|
|
|
# 测试 2: RGB 转换
|
|
|
|
|
|
print("\n[测试 2] RGB 转换...")
|
|
|
|
|
|
test_rgb = [
|
|
|
|
|
|
(0, 0, 0),
|
|
|
|
|
|
(128, 128, 128),
|
|
|
|
|
|
(255, 255, 255),
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
for r, g, b in test_rgb:
|
|
|
|
|
|
r_new, g_new, b_new = converter.convert_rgb(r, g, b)
|
|
|
|
|
|
print(f" RGB({r},{g},{b}) → RGB({r_new},{g_new},{b_new})")
|
|
|
|
|
|
|
|
|
|
|
|
# 测试 3: 完整转换流程
|
|
|
|
|
|
print("\n[测试 3] 完整转换流程...")
|
|
|
|
|
|
pattern_params = [
|
|
|
|
|
|
[255, 255, 255], # 100% 白
|
|
|
|
|
|
[230, 230, 230], # 90%
|
|
|
|
|
|
[204, 204, 204], # 80%
|
|
|
|
|
|
[128, 128, 128], # 50%
|
|
|
|
|
|
[0, 0, 0], # 0% 黑
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
converted = converter.convert(pattern_params, "Limited")
|
|
|
|
|
|
|
|
|
|
|
|
print("\n 对比:")
|
|
|
|
|
|
for i, (orig, conv) in enumerate(zip(pattern_params, converted)):
|
|
|
|
|
|
print(f" [{i+1}] {orig} → {conv}")
|
|
|
|
|
|
|
|
|
|
|
|
# 测试 4: 便捷函数
|
|
|
|
|
|
print("\n[测试 4] 便捷函数...")
|
|
|
|
|
|
result = convert_pattern_params(
|
|
|
|
|
|
[[0, 0, 0], [255, 255, 255]], "Limited", verbose=False
|
|
|
|
|
|
)
|
|
|
|
|
|
print(f" 结果: {result}")
|
|
|
|
|
|
|
|
|
|
|
|
r, g, b = convert_single_rgb(128, 128, 128, "Limited")
|
|
|
|
|
|
print(f" RGB(128,128,128) → RGB({r},{g},{b})")
|
|
|
|
|
|
|
|
|
|
|
|
# 测试 5: 获取信息
|
|
|
|
|
|
print("\n[测试 5] 转换器信息...")
|
|
|
|
|
|
info = converter.get_info()
|
|
|
|
|
|
for key, value in info.items():
|
|
|
|
|
|
print(f" {key}: {value}")
|
|
|
|
|
|
|
|
|
|
|
|
print("\n" + "=" * 80)
|
|
|
|
|
|
print("测试完成")
|
|
|
|
|
|
print("=" * 80)
|