添加瞬时峰值测试
This commit is contained in:
@@ -34,6 +34,9 @@ DEFAULT_WINDOW_PERCENTAGES = [1, 2, 5, 10, 18, 25, 50, 75, 100]
|
||||
DEFAULT_CHESSBOARD_GRID = 5
|
||||
INSTANT_PEAK_WINDOW_PERCENTAGE = 10
|
||||
INSTANT_PEAK_CAPTURE_DELAY = 0.5
|
||||
INSTANT_PEAK_DROP_RATIO = 0.97
|
||||
INSTANT_PEAK_MIN_DROP_NITS = 2.0
|
||||
INSTANT_PEAK_SAMPLE_INTERVAL = 0.3
|
||||
|
||||
_TEMP_DIR = None
|
||||
_IMAGE_CACHE = {} # {(width, height, percentage): file_path}
|
||||
@@ -63,8 +66,8 @@ def _get_temp_dir():
|
||||
return _TEMP_DIR
|
||||
|
||||
|
||||
def _make_window_image_array(width, height, percentage):
|
||||
"""生成黑底+居中白窗的 numpy 图像,保持屏幕比例。"""
|
||||
def _make_window_image_array(width, height, percentage, window_level=255):
|
||||
"""生成黑底+居中窗口图像,保持屏幕比例。"""
|
||||
image = np.zeros((height, width, 3), dtype=np.uint8)
|
||||
if percentage >= 100:
|
||||
ww, wh = width, height
|
||||
@@ -74,18 +77,19 @@ def _make_window_image_array(width, height, percentage):
|
||||
wh = int(height * scale)
|
||||
x1 = (width - ww) // 2
|
||||
y1 = (height - wh) // 2
|
||||
image[y1:y1 + wh, x1:x1 + ww] = 255
|
||||
image[y1:y1 + wh, x1:x1 + ww] = int(window_level)
|
||||
return image
|
||||
|
||||
|
||||
def _ensure_window_image(width, height, percentage):
|
||||
def _ensure_window_image(width, height, percentage, window_level=255):
|
||||
"""生成或复用缓存的窗口 PNG 文件,返回路径。"""
|
||||
key = (width, height, percentage)
|
||||
level = max(0, min(255, int(window_level)))
|
||||
key = (width, height, percentage, level)
|
||||
cached = _IMAGE_CACHE.get(key)
|
||||
if cached and os.path.exists(cached):
|
||||
return cached
|
||||
arr = _make_window_image_array(width, height, percentage)
|
||||
fname = f"window_{width}x{height}_{percentage:03d}percent.png"
|
||||
arr = _make_window_image_array(width, height, percentage, level)
|
||||
fname = f"window_{width}x{height}_{percentage:03d}percent_{level:03d}lv.png"
|
||||
path = os.path.join(_get_temp_dir(), fname)
|
||||
Image.fromarray(arr, mode="RGB").save(path, format="PNG")
|
||||
_IMAGE_CACHE[key] = path
|
||||
@@ -536,6 +540,179 @@ def send_ld_instant_peak(self: "PQAutomationApp"):
|
||||
threading.Thread(target=send, daemon=True).start()
|
||||
|
||||
|
||||
def start_ld_instant_peak_tracking(self: "PQAutomationApp"):
|
||||
"""独立瞬时峰值测试:持续采样直到亮度回落或达到最长测量时长。"""
|
||||
if not self.signal_service.is_connected:
|
||||
messagebox.showwarning("警告", "请先连接 UCD323 设备")
|
||||
return
|
||||
if not self.ca:
|
||||
messagebox.showwarning("警告", "请先连接 CA410 色度计")
|
||||
return
|
||||
if getattr(self, "ld_peak_tracking", False):
|
||||
messagebox.showinfo("提示", "瞬时峰值测试正在进行中")
|
||||
return
|
||||
|
||||
try:
|
||||
window_percentage = int(float(self.ld_peak_window_size_var.get()))
|
||||
if window_percentage < 1 or window_percentage > 100:
|
||||
raise ValueError("窗口百分比超出范围")
|
||||
|
||||
window_luminance_percent = float(self.ld_peak_window_luminance_var.get())
|
||||
if window_luminance_percent < 1 or window_luminance_percent > 100:
|
||||
raise ValueError("窗口亮度超出范围")
|
||||
|
||||
max_duration = float(self.ld_peak_duration_var.get())
|
||||
if max_duration <= 0:
|
||||
raise ValueError("测量时长必须大于 0")
|
||||
|
||||
sample_interval = float(
|
||||
self.ld_peak_sample_interval_var.get()
|
||||
if hasattr(self, "ld_peak_sample_interval_var")
|
||||
else INSTANT_PEAK_SAMPLE_INTERVAL
|
||||
)
|
||||
if sample_interval <= 0:
|
||||
raise ValueError("采样间隔必须大于 0")
|
||||
except Exception as e:
|
||||
messagebox.showwarning("参数错误", f"请检查瞬时峰值参数: {e}")
|
||||
return
|
||||
|
||||
record_curve = bool(self.ld_peak_record_curve_var.get())
|
||||
window_level = int(round(window_luminance_percent / 100.0 * 255.0))
|
||||
pattern_label = f"黑场后切 {window_percentage}%窗口({window_luminance_percent:.0f}%亮度)"
|
||||
|
||||
self.ld_peak_tracking = True
|
||||
self.log_gui.log(
|
||||
f"⚡ 开始独立瞬时峰值测试: {pattern_label},最长 {max_duration:.1f}s",
|
||||
level="info",
|
||||
)
|
||||
_set_current_ld_pattern(self, "瞬时峰值亮度", pattern_label, window_percentage)
|
||||
|
||||
if hasattr(self, "ld_peak_start_btn"):
|
||||
self.ld_peak_start_btn.configure(state="disabled")
|
||||
if hasattr(self, "ld_peak_stop_btn"):
|
||||
self.ld_peak_stop_btn.configure(state="normal")
|
||||
|
||||
def run():
|
||||
peak_lv = None
|
||||
peak_time = None
|
||||
drop_time = None
|
||||
curve_count = 0
|
||||
|
||||
try:
|
||||
if not _apply_ld_ucd_params(self):
|
||||
return
|
||||
|
||||
width, height = self.signal_service.current_resolution()
|
||||
black_image = _ensure_solid_image(width, height, (0, 0, 0), "black")
|
||||
peak_image = _ensure_window_image(
|
||||
width,
|
||||
height,
|
||||
window_percentage,
|
||||
window_level,
|
||||
)
|
||||
|
||||
self.signal_service.send_image(black_image)
|
||||
time.sleep(INSTANT_PEAK_CAPTURE_DELAY)
|
||||
self.signal_service.send_image(peak_image)
|
||||
|
||||
started = time.time()
|
||||
while self.ld_peak_tracking:
|
||||
elapsed = time.time() - started
|
||||
if elapsed > max_duration:
|
||||
break
|
||||
|
||||
x, y, lv, _X, _Y, _Z = self.read_ca_xyLv()
|
||||
if lv is None:
|
||||
time.sleep(sample_interval)
|
||||
continue
|
||||
|
||||
if peak_lv is None or lv > peak_lv:
|
||||
peak_lv = float(lv)
|
||||
peak_time = elapsed
|
||||
|
||||
if record_curve:
|
||||
curve_count += 1
|
||||
self._dispatch_ui(
|
||||
self.ld_tree.insert,
|
||||
"",
|
||||
tk.END,
|
||||
values=(
|
||||
"瞬时峰值曲线",
|
||||
f"{window_percentage}%窗口@{window_luminance_percent:.0f}% t={elapsed:.2f}s",
|
||||
f"{float(lv):.4f}",
|
||||
f"{x:.4f}",
|
||||
f"{y:.4f}",
|
||||
datetime.datetime.now().strftime("%H:%M:%S"),
|
||||
),
|
||||
)
|
||||
|
||||
if peak_lv is not None:
|
||||
drop_threshold = max(
|
||||
peak_lv * INSTANT_PEAK_DROP_RATIO,
|
||||
peak_lv - INSTANT_PEAK_MIN_DROP_NITS,
|
||||
)
|
||||
if lv < drop_threshold and elapsed > (peak_time or 0):
|
||||
drop_time = elapsed
|
||||
break
|
||||
|
||||
self._dispatch_ui(
|
||||
self.ld_result_label.config,
|
||||
text=(
|
||||
f"亮度: {lv:.2f} cd/m² | 峰值: {(peak_lv or lv):.2f} cd/m²"
|
||||
f" | t: {elapsed:.2f}s"
|
||||
),
|
||||
)
|
||||
time.sleep(sample_interval)
|
||||
|
||||
if peak_lv is None:
|
||||
self._dispatch_ui(self.log_gui.log, "瞬时峰值测试未采到有效亮度", "warning")
|
||||
return
|
||||
|
||||
end_time = drop_time if drop_time is not None else (time.time() - started)
|
||||
sustain_time = max(0.0, end_time - (peak_time or 0.0))
|
||||
result_label = (
|
||||
f"峰值={peak_lv:.2f} cd/m², 持续={sustain_time:.2f}s"
|
||||
if drop_time is not None
|
||||
else f"峰值={peak_lv:.2f} cd/m², 持续>{sustain_time:.2f}s(未检测到回落)"
|
||||
)
|
||||
|
||||
self._dispatch_ui(
|
||||
self.ld_tree.insert,
|
||||
"",
|
||||
tk.END,
|
||||
values=(
|
||||
"瞬时峰值亮度",
|
||||
pattern_label,
|
||||
result_label,
|
||||
"--",
|
||||
"--",
|
||||
datetime.datetime.now().strftime("%H:%M:%S"),
|
||||
),
|
||||
)
|
||||
self._dispatch_ui(
|
||||
self.log_gui.log,
|
||||
f"瞬时峰值测试完成: {result_label},曲线点 {curve_count} 个",
|
||||
"success",
|
||||
)
|
||||
except Exception as e:
|
||||
self._dispatch_ui(self.log_gui.log, f"瞬时峰值测试异常: {e}", "error")
|
||||
finally:
|
||||
self.ld_peak_tracking = False
|
||||
if hasattr(self, "ld_peak_start_btn"):
|
||||
self._dispatch_ui(self.ld_peak_start_btn.configure, state="normal")
|
||||
if hasattr(self, "ld_peak_stop_btn"):
|
||||
self._dispatch_ui(self.ld_peak_stop_btn.configure, state="disabled")
|
||||
|
||||
threading.Thread(target=run, daemon=True).start()
|
||||
|
||||
|
||||
def stop_ld_instant_peak_tracking(self: "PQAutomationApp"):
|
||||
"""停止独立瞬时峰值连续采样。"""
|
||||
if getattr(self, "ld_peak_tracking", False):
|
||||
self.ld_peak_tracking = False
|
||||
self.log_gui.log("已请求停止瞬时峰值测试", level="info")
|
||||
|
||||
|
||||
def measure_ld_luminance(self: "PQAutomationApp"):
|
||||
"""测量当前显示的亮度并追加一行到 Treeview。"""
|
||||
if not self.ca:
|
||||
@@ -585,6 +762,7 @@ def clear_ld_records(self: "PQAutomationApp"):
|
||||
self.current_ld_percentage = None
|
||||
self.current_ld_test_item = None
|
||||
self.current_ld_pattern_label = None
|
||||
self.ld_peak_tracking = False
|
||||
self.log_gui.log("测试记录已清空", level="info")
|
||||
|
||||
|
||||
@@ -630,6 +808,8 @@ class LocalDimmingMixin:
|
||||
send_ld_checkerboard = send_ld_checkerboard
|
||||
send_ld_black_pattern = send_ld_black_pattern
|
||||
send_ld_instant_peak = send_ld_instant_peak
|
||||
start_ld_instant_peak_tracking = start_ld_instant_peak_tracking
|
||||
stop_ld_instant_peak_tracking = stop_ld_instant_peak_tracking
|
||||
measure_ld_luminance = measure_ld_luminance
|
||||
clear_ld_records = clear_ld_records
|
||||
save_local_dimming_results = save_local_dimming_results
|
||||
|
||||
Reference in New Issue
Block a user