GIT-test/config_panel.py
2026-02-21 23:42:50 +08:00

681 lines
27 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
"""
参数控制面板配置文件
参数说明:
1. 曝光时间 (exposure)
- 范围100-50000微秒
- 作用:控制相机传感器的曝光时间
- 效果:
- 增大:画面变亮,激光点更明显,但可能过曝
- 减小:画面变暗,减少过曝,激光点更清晰
- 建议值5000微秒激光检测默认值光线暗时可适当增加
2. 增益 (gain)
- 范围0-24dB
- 作用:放大传感器信号,增强画面亮度
- 效果:
- 增大:画面变亮,噪声也会增加
- 减小:画面变暗,噪声减少
- 建议值0dB激光检测默认值仅在光线极暗时使用
3. 帧率 (fps)
- 范围1-120 FPS
- 作用:控制相机采集图像的频率
- 效果:
- 增大画面流畅度提高CPU占用增加
- 减小画面流畅度降低CPU占用减少
- 建议值30 FPS平衡流畅度和性能
4. ROI设置 (roi_x, roi_y, roi_width, roi_height)
- 作用:设置感兴趣区域,仅处理指定区域的图像
- 效果:
- 减小ROI提高处理速度减少CPU占用
- 增大ROI处理范围更广CPU占用增加
- 建议值默认全画幅0, 0, 640, 480激光点固定时可缩小ROI
5. HSV调节
- H通道 (H_low, H_high):控制颜色范围
- 范围0-255
- 作用:识别绿色激光的颜色范围
- 建议值35-85绿色激光默认范围
- S通道 (S_low, S_high):控制饱和度范围
- 范围0-255
- 作用:区分激光点和背景
- 建议值80-255过滤低饱和度背景
- V通道 (V_low, V_high):控制亮度范围
- 范围0-255
- 作用:区分激光点和暗背景
- 建议值80-255过滤暗背景
"""
import tkinter as tk
from tkinter import ttk
import threading
import queue
class ConfigPanel:
def __init__(self, update_callback):
"""
初始化参数控制面板
Args:
update_callback: 参数更新回调函数
"""
self.update_callback = update_callback
self.root = None
self.queue = queue.Queue()
self.thread = None
# 默认参数
self.defaults = {
'exposure': 5000, # 曝光时间(微秒)
'gain': 0, # 增益dB
'fps': 60, # 帧率FPS
'roi_x': 0, # ROI起始X坐标
'roi_y': 0, # ROI起始Y坐标
'roi_width': 640, # ROI宽度
'roi_height': 480, # ROI高度
'h_low': 35, # H通道最小值
'h_high': 85, # H通道最大值
's_low': 80, # S通道最小值
's_high': 255, # S通道最大值
'v_low': 80, # V通道最小值
'v_high': 255 # V通道最大值
}
# 当前参数
self.current = self.defaults.copy()
def start(self):
"""启动参数控制面板线程"""
self.thread = threading.Thread(target=self._run)
self.thread.daemon = True
self.thread.start()
def _run(self):
"""运行Tkinter主循环"""
self.root = tk.Tk()
self.root.title("参数控制面板")
self.root.geometry("400x600")
# 创建主框架
main_frame = ttk.Frame(self.root, padding="10")
main_frame.pack(fill=tk.BOTH, expand=True)
# 创建标签页
notebook = ttk.Notebook(main_frame)
notebook.pack(fill=tk.BOTH, expand=True)
# 曝光增益页面
exp_gain_frame = ttk.Frame(notebook, padding="10")
notebook.add(exp_gain_frame, text="曝光增益")
# 帧率页面
fps_frame = ttk.Frame(notebook, padding="10")
notebook.add(fps_frame, text="帧率")
# ROI页面
roi_frame = ttk.Frame(notebook, padding="10")
notebook.add(roi_frame, text="ROI设置")
# HSV页面
hsv_frame = ttk.Frame(notebook, padding="10")
notebook.add(hsv_frame, text="HSV调节")
# 形态学参数页面
morph_frame = ttk.Frame(notebook, padding="10")
notebook.add(morph_frame, text="形态学参数")
# 几何参数页面
geometry_frame = ttk.Frame(notebook, padding="10")
notebook.add(geometry_frame, text="几何参数")
# 检测模式页面
mode_frame = ttk.Frame(notebook, padding="10")
notebook.add(mode_frame, text="检测模式")
# 曝光滑块
self._create_exposure_controls(exp_gain_frame)
# 增益滑块
self._create_gain_controls(exp_gain_frame)
# 帧率滑块
self._create_fps_controls(fps_frame)
# ROI设置
self._create_roi_controls(roi_frame)
# HSV调节
self._create_hsv_controls(hsv_frame)
# 形态学参数设置
self._create_morph_controls(morph_frame)
# 几何参数设置
self._create_geometry_controls(geometry_frame)
# 检测模式设置
self._create_mode_controls(mode_frame)
# 启动队列处理
self.root.after(100, self._process_queue)
# 运行主循环
self.root.mainloop()
def _create_exposure_controls(self, parent):
"""创建曝光控制滑块"""
# 曝光时间说明
ttk.Label(parent, text="曝光时间 (100-50000μs):").pack(pady=5)
ttk.Label(parent, text="作用: 控制相机传感器曝光时间", font=('Arial', 8), foreground='gray').pack(pady=2)
ttk.Label(parent, text="增大: 画面变亮,激光点更明显,但可能过曝", font=('Arial', 8), foreground='gray').pack(pady=1)
ttk.Label(parent, text="减小: 画面变暗,减少过曝,激光点更清晰", font=('Arial', 8), foreground='gray').pack(pady=1)
ttk.Label(parent, text="建议值: 5000μs激光检测默认值", font=('Arial', 8), foreground='green').pack(pady=2)
def on_exposure_change(value):
self.current['exposure'] = int(float(value))
self.update_callback('exposure', int(float(value)))
exposure_scale = ttk.Scale(
parent,
from_=100,
to=50000,
orient=tk.HORIZONTAL,
length=300,
command=on_exposure_change
)
exposure_scale.set(self.defaults['exposure'])
exposure_scale.pack(pady=5)
self.exposure_var = tk.StringVar(value=str(self.defaults['exposure']))
ttk.Entry(parent, textvariable=self.exposure_var, width=10).pack(pady=5)
def update_exposure_from_entry():
try:
value = int(self.exposure_var.get())
if 100 <= value <= 50000:
exposure_scale.set(value)
on_exposure_change(value)
except ValueError:
pass
ttk.Button(parent, text="应用", command=update_exposure_from_entry).pack(pady=5)
def _create_gain_controls(self, parent):
"""创建增益控制滑块"""
# 增益说明
ttk.Label(parent, text="增益 (0-24dB):").pack(pady=5)
ttk.Label(parent, text="作用: 放大传感器信号,增强画面亮度", font=('Arial', 8), foreground='gray').pack(pady=2)
ttk.Label(parent, text="增大: 画面变亮,噪声也会增加", font=('Arial', 8), foreground='gray').pack(pady=1)
ttk.Label(parent, text="减小: 画面变暗,噪声减少", font=('Arial', 8), foreground='gray').pack(pady=1)
ttk.Label(parent, text="建议值: 0dB激光检测默认值仅在光线极暗时使用", font=('Arial', 8), foreground='green').pack(pady=2)
def on_gain_change(value):
gain_value = float(value) / 10.0
self.current['gain'] = gain_value
self.update_callback('gain', gain_value)
gain_scale = ttk.Scale(
parent,
from_=0,
to=240,
orient=tk.HORIZONTAL,
length=300,
command=on_gain_change
)
gain_scale.set(self.defaults['gain'] * 10)
gain_scale.pack(pady=5)
self.gain_var = tk.StringVar(value=str(self.defaults['gain']))
ttk.Entry(parent, textvariable=self.gain_var, width=10).pack(pady=5)
def update_gain_from_entry():
try:
value = float(self.gain_var.get())
if 0 <= value <= 24:
gain_scale.set(value * 10)
on_gain_change(value * 10)
except ValueError:
pass
ttk.Button(parent, text="应用", command=update_gain_from_entry).pack(pady=5)
def _create_fps_controls(self, parent):
"""创建帧率控制滑块"""
# 帧率说明
ttk.Label(parent, text="帧率 (1-120 FPS):").pack(pady=5)
ttk.Label(parent, text="作用: 控制相机采集图像的频率", font=('Arial', 8), foreground='gray').pack(pady=2)
ttk.Label(parent, text="增大: 画面流畅度提高CPU占用增加", font=('Arial', 8), foreground='gray').pack(pady=1)
ttk.Label(parent, text="减小: 画面流畅度降低CPU占用减少", font=('Arial', 8), foreground='gray').pack(pady=1)
ttk.Label(parent, text="建议值: 30 FPS平衡流畅度和性能", font=('Arial', 8), foreground='green').pack(pady=2)
def on_fps_change(value):
self.current['fps'] = int(float(value))
self.update_callback('fps', int(float(value)))
fps_scale = ttk.Scale(
parent,
from_=1,
to=120,
orient=tk.HORIZONTAL,
length=300,
command=on_fps_change
)
fps_scale.set(self.defaults['fps'])
fps_scale.pack(pady=5)
self.fps_var = tk.StringVar(value=str(self.defaults['fps']))
ttk.Entry(parent, textvariable=self.fps_var, width=10).pack(pady=5)
def update_fps_from_entry():
try:
value = int(self.fps_var.get())
if 1 <= value <= 120:
fps_scale.set(value)
on_fps_change(value)
except ValueError:
pass
ttk.Button(parent, text="应用", command=update_fps_from_entry).pack(pady=5)
def _create_roi_controls(self, parent):
"""创建ROI控制输入框"""
# ROI设置说明
ttk.Label(parent, text="ROI设置:").pack(pady=5)
ttk.Label(parent, text="作用: 设置感兴趣区域,仅处理指定区域的图像", font=('Arial', 8), foreground='gray').pack(pady=2)
ttk.Label(parent, text="减小ROI: 提高处理速度减少CPU占用", font=('Arial', 8), foreground='gray').pack(pady=1)
ttk.Label(parent, text="增大ROI: 处理范围更广CPU占用增加", font=('Arial', 8), foreground='gray').pack(pady=1)
ttk.Label(parent, text="建议值: 默认全画幅0, 0, 640, 480", font=('Arial', 8), foreground='green').pack(pady=2)
# ROI输入框
roi_frame = ttk.Frame(parent)
roi_frame.pack(fill=tk.X, pady=5)
# X坐标
ttk.Label(roi_frame, text="X:").grid(row=0, column=0, padx=5)
self.roi_x_var = tk.StringVar(value=str(self.defaults['roi_x']))
ttk.Entry(roi_frame, textvariable=self.roi_x_var, width=10).grid(row=0, column=1, padx=5)
# Y坐标
ttk.Label(roi_frame, text="Y:").grid(row=0, column=2, padx=5)
self.roi_y_var = tk.StringVar(value=str(self.defaults['roi_y']))
ttk.Entry(roi_frame, textvariable=self.roi_y_var, width=10).grid(row=0, column=3, padx=5)
# Width
ttk.Label(roi_frame, text="Width:").grid(row=1, column=0, padx=5)
self.roi_width_var = tk.StringVar(value=str(self.defaults['roi_width']))
ttk.Entry(roi_frame, textvariable=self.roi_width_var, width=10).grid(row=1, column=1, padx=5)
# Height
ttk.Label(roi_frame, text="Height:").grid(row=1, column=2, padx=5)
self.roi_height_var = tk.StringVar(value=str(self.defaults['roi_height']))
ttk.Entry(roi_frame, textvariable=self.roi_height_var, width=10).grid(row=1, column=3, padx=5)
def apply_roi():
try:
x = int(self.roi_x_var.get())
y = int(self.roi_y_var.get())
width = int(self.roi_width_var.get())
height = int(self.roi_height_var.get())
if x >= 0 and y >= 0 and width > 0 and height > 0:
self.current['roi_x'] = x
self.current['roi_y'] = y
self.current['roi_width'] = width
self.current['roi_height'] = height
self.update_callback('roi', (x, y, width, height))
except ValueError:
pass
ttk.Button(parent, text="应用ROI", command=apply_roi).pack(pady=10)
def _create_hsv_controls(self, parent):
"""创建HSV控制滑块"""
# HSV调节说明
ttk.Label(parent, text="HSV范围调节:").pack(pady=5)
ttk.Label(parent, text="作用: 微调绿色激光的识别范围", font=('Arial', 8), foreground='gray').pack(pady=2)
# H通道说明
ttk.Label(parent, text="H通道: 控制颜色范围绿色激光默认35-85", font=('Arial', 8), foreground='gray').pack(pady=2)
# S通道说明
ttk.Label(parent, text="S通道: 控制饱和度范围(过滤低饱和度背景)", font=('Arial', 8), foreground='gray').pack(pady=2)
# V通道说明
ttk.Label(parent, text="V通道: 控制亮度范围(过滤暗背景)", font=('Arial', 8), foreground='gray').pack(pady=2)
ttk.Label(parent, text="建议值: H(35-85), S(80-255), V(80-255)", font=('Arial', 8), foreground='green').pack(pady=2)
# H通道
h_frame = ttk.Frame(parent)
h_frame.pack(fill=tk.X, pady=5)
ttk.Label(h_frame, text="H_low:").grid(row=0, column=0, padx=5)
self.h_low_var = tk.StringVar(value=str(self.defaults['h_low']))
ttk.Entry(h_frame, textvariable=self.h_low_var, width=5).grid(row=0, column=1, padx=5)
ttk.Label(h_frame, text="H_high:").grid(row=0, column=2, padx=5)
self.h_high_var = tk.StringVar(value=str(self.defaults['h_high']))
ttk.Entry(h_frame, textvariable=self.h_high_var, width=5).grid(row=0, column=3, padx=5)
# S通道
s_frame = ttk.Frame(parent)
s_frame.pack(fill=tk.X, pady=5)
ttk.Label(s_frame, text="S_low:").grid(row=0, column=0, padx=5)
self.s_low_var = tk.StringVar(value=str(self.defaults['s_low']))
ttk.Entry(s_frame, textvariable=self.s_low_var, width=5).grid(row=0, column=1, padx=5)
ttk.Label(s_frame, text="S_high:").grid(row=0, column=2, padx=5)
self.s_high_var = tk.StringVar(value=str(self.defaults['s_high']))
ttk.Entry(s_frame, textvariable=self.s_high_var, width=5).grid(row=0, column=3, padx=5)
# V通道
v_frame = ttk.Frame(parent)
v_frame.pack(fill=tk.X, pady=5)
ttk.Label(v_frame, text="V_low:").grid(row=0, column=0, padx=5)
self.v_low_var = tk.StringVar(value=str(self.defaults['v_low']))
ttk.Entry(v_frame, textvariable=self.v_low_var, width=5).grid(row=0, column=1, padx=5)
ttk.Label(v_frame, text="V_high:").grid(row=0, column=2, padx=5)
self.v_high_var = tk.StringVar(value=str(self.defaults['v_high']))
ttk.Entry(v_frame, textvariable=self.v_high_var, width=5).grid(row=0, column=3, padx=5)
def apply_hsv():
try:
h_low = int(self.h_low_var.get())
h_high = int(self.h_high_var.get())
s_low = int(self.s_low_var.get())
s_high = int(self.s_high_var.get())
v_low = int(self.v_low_var.get())
v_high = int(self.v_high_var.get())
# 验证范围
if all(0 <= val <= 255 for val in [h_low, h_high, s_low, s_high, v_low, v_high]):
self.current['h_low'] = h_low
self.current['h_high'] = h_high
self.current['s_low'] = s_low
self.current['s_high'] = s_high
self.current['v_low'] = v_low
self.current['v_high'] = v_high
self.update_callback('hsv', (h_low, h_high, s_low, s_high, v_low, v_high))
except ValueError:
pass
ttk.Button(parent, text="应用HSV", command=apply_hsv).pack(pady=10)
def _process_queue(self):
"""处理队列中的消息"""
try:
while not self.queue.empty():
msg = self.queue.get_nowait()
# 处理消息
if msg['type'] == 'update_value':
param = msg['param']
value = msg['value']
if param in self.current:
self.current[param] = value
except queue.Empty:
pass
finally:
self.root.after(100, self._process_queue)
def stop(self):
"""停止参数控制面板"""
if self.root:
try:
# 使用after方法在Tkinter主线程中执行销毁操作
self.root.after(0, lambda: self._safe_destroy())
# 给Tkinter一些时间来处理销毁操作
import time
time.sleep(0.1)
except Exception as e:
print(f"停止控制面板时出错: {e}")
def _safe_destroy(self):
"""安全销毁Tkinter窗口"""
if self.root:
try:
self.root.quit()
self.root.destroy()
except Exception:
pass
def _create_morph_controls(self, parent):
"""创建形态学参数控制面板"""
# 形态学参数说明
ttk.Label(parent, text="形态学参数设置:").pack(pady=5)
ttk.Label(parent, text="作用: 控制LED点阵的聚合效果", font=('Arial', 8), foreground='gray').pack(pady=2)
# 闭运算核大小
ttk.Label(parent, text="闭运算核大小 (5-50):").pack(pady=5)
def on_kernel_size_change(value):
kernel_size = int(float(value))
open_kernel = 5 # 固定开运算核大小
self.update_callback('morph', (kernel_size, open_kernel))
kernel_scale = ttk.Scale(
parent,
from_=5,
to=50,
orient=tk.HORIZONTAL,
length=300,
command=on_kernel_size_change
)
kernel_scale.set(20) # 默认值
kernel_scale.pack(pady=5)
# 开运算核大小
ttk.Label(parent, text="开运算核大小 (3-15):").pack(pady=5)
def on_open_kernel_change(value):
open_kernel = int(float(value))
kernel_size = 20 # 固定闭运算核大小
self.update_callback('morph', (kernel_size, open_kernel))
open_kernel_scale = ttk.Scale(
parent,
from_=3,
to=15,
orient=tk.HORIZONTAL,
length=300,
command=on_open_kernel_change
)
open_kernel_scale.set(5) # 默认值
open_kernel_scale.pack(pady=5)
def _create_geometry_controls(self, parent):
"""创建几何参数控制面板"""
# 几何参数说明
ttk.Label(parent, text="几何参数设置:").pack(pady=5)
ttk.Label(parent, text="作用: 控制灯盘的几何特征过滤", font=('Arial', 8), foreground='gray').pack(pady=2)
# 最小灯盘面积
ttk.Label(parent, text="最小灯盘面积 (100-5000):").pack(pady=5)
def on_min_area_change(value):
min_area = int(float(value))
min_leds = 10 # 默认值
circularity = 0.4 # 默认值
self.update_callback('geometry', (min_area, min_leds, circularity))
min_area_scale = ttk.Scale(
parent,
from_=100,
to=5000,
orient=tk.HORIZONTAL,
length=300,
command=on_min_area_change
)
min_area_scale.set(500) # 默认值
min_area_scale.pack(pady=5)
# 最小LED数量
ttk.Label(parent, text="最小LED数量 (5-50):").pack(pady=5)
def on_min_leds_change(value):
min_leds = int(float(value))
min_area = 500 # 默认值
circularity = 0.4 # 默认值
self.update_callback('geometry', (min_area, min_leds, circularity))
min_leds_scale = ttk.Scale(
parent,
from_=5,
to=50,
orient=tk.HORIZONTAL,
length=300,
command=on_min_leds_change
)
min_leds_scale.set(10) # 默认值
min_leds_scale.pack(pady=5)
# 圆度阈值
ttk.Label(parent, text="圆度阈值 (0.1-1.0):").pack(pady=5)
def on_circularity_change(value):
circularity = float(value)
min_area = 500 # 默认值
min_leds = 10 # 默认值
self.update_callback('geometry', (min_area, min_leds, circularity))
circularity_scale = ttk.Scale(
parent,
from_=0.1,
to=1.0,
orient=tk.HORIZONTAL,
length=300,
command=on_circularity_change
)
circularity_scale.set(0.4) # 默认值
circularity_scale.pack(pady=5)
def _create_mode_controls(self, parent):
"""创建双模式控制界面"""
# 双模式说明
ttk.Label(parent, text="双模式激光识别系统设置:").pack(pady=5)
ttk.Label(parent, text="作用: 配置单点光斑和点阵灯盘双模式检测", font=('Arial', 8), foreground='gray').pack(pady=2)
# 算法使能区
ttk.Label(parent, text="算法使能:").pack(pady=5)
enable_a_var = tk.BooleanVar(value=True)
enable_b_var = tk.BooleanVar(value=True)
def on_enable_change():
enable_a = enable_a_var.get()
enable_b = enable_b_var.get()
# 确保至少启用一项
if not enable_a and not enable_b:
enable_b_var.set(True)
enable_b = True
self.update_callback('algorithm_enable', (enable_a, enable_b))
# 启用单点光斑检测
ttk.Checkbutton(
parent,
text="启用单点光斑检测 (模式A)",
variable=enable_a_var,
command=on_enable_change
).pack(pady=2, anchor=tk.W)
# 启用点阵灯盘检测
ttk.Checkbutton(
parent,
text="启用点阵灯盘检测 (模式B)",
variable=enable_b_var,
command=on_enable_change
).pack(pady=2, anchor=tk.W)
# 工作模式选择
ttk.Label(parent, text="工作模式:").pack(pady=5)
work_mode_var = tk.StringVar(value='auto')
def on_work_mode_change():
mode = work_mode_var.get()
self.update_callback('work_mode', mode)
# 全自动仲裁
ttk.Radiobutton(
parent,
text="全自动仲裁 (系统自动选择最优结果)",
variable=work_mode_var,
value='auto',
command=on_work_mode_change
).pack(pady=2, anchor=tk.W)
# 强制单点模式
ttk.Radiobutton(
parent,
text="强制单点模式 (仅运行模式A)",
variable=work_mode_var,
value='forced_single',
command=on_work_mode_change
).pack(pady=2, anchor=tk.W)
# 强制灯盘模式
ttk.Radiobutton(
parent,
text="强制灯盘模式 (仅运行模式B)",
variable=work_mode_var,
value='forced_lamp',
command=on_work_mode_change
).pack(pady=2, anchor=tk.W)
# 双模显示
ttk.Radiobutton(
parent,
text="双模显示 (同时显示两种模式结果)",
variable=work_mode_var,
value='dual_display',
command=on_work_mode_change
).pack(pady=2, anchor=tk.W)
# 性能优化设置
ttk.Label(parent, text="性能优化:").pack(pady=5)
use_alternating_var = tk.BooleanVar(value=False)
def on_performance_change():
use_alternating = use_alternating_var.get()
self.update_callback('performance_optimization', use_alternating)
ttk.Checkbutton(
parent,
text="使用交替帧策略 (减少CPU占用)",
variable=use_alternating_var,
command=on_performance_change
).pack(pady=2, anchor=tk.W)
# 手动干预按钮
ttk.Label(parent, text="手动干预:").pack(pady=5)
button_frame = ttk.Frame(parent)
button_frame.pack(fill=tk.X, pady=5)
ttk.Button(
button_frame,
text="锁定当前模式",
command=lambda: self.update_callback('lock_mode', True)
).pack(side=tk.LEFT, padx=5)
ttk.Button(
button_frame,
text="重置仲裁",
command=lambda: self.update_callback('reset_arbiter', True)
).pack(side=tk.LEFT, padx=5)
# 状态显示区域
ttk.Label(parent, text="系统状态:").pack(pady=5)
self.status_var = tk.StringVar(value="就绪")
ttk.Label(parent, textvariable=self.status_var, font=('Arial', 10), foreground='green').pack(pady=2)
self.mode_a_status_var = tk.StringVar(value="模式A: 未检测")
ttk.Label(parent, textvariable=self.mode_a_status_var, font=('Arial', 9)).pack(pady=1, anchor=tk.W)
self.mode_b_status_var = tk.StringVar(value="模式B: 未检测")
ttk.Label(parent, textvariable=self.mode_b_status_var, font=('Arial', 9)).pack(pady=1, anchor=tk.W)
self.decision_var = tk.StringVar(value="决策: 等待输入")
ttk.Label(parent, textvariable=self.decision_var, font=('Arial', 9), foreground='blue').pack(pady=1, anchor=tk.W)