# -*- 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)