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

1007 lines
32 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. 设备枚举和选择
2. 相机打开和关闭
3. 图像采集和显示
4. 曝光、增益等参数控制
5. 异常处理和重连机制
6. 相机参数控制界面
前置条件:
1. 已安装海康 MVS SDK
2. 已配置 Python 环境
3. 相机已正确连接
"""
import sys
import ctypes
import threading
import time
import os
import tkinter as tk
from tkinter import ttk
# 添加SDK路径
sys.path.append('Python/MvImport')
from MvCameraControl_class import *
from MvErrorDefine_const import *
from CameraParams_header import *
import cv2
import numpy as np
# 枚举值定义
MV_EXPOSURE_AUTO_MODE_OFF = 0
MV_GAIN_AUTO_MODE_OFF = 0
MV_BALANCE_WHITE_AUTO_OFF = 0
class Camera:
"""
海康相机管理类
功能:
- 设备枚举和选择
- 相机打开和关闭
- 图像采集和显示
- 曝光、增益等参数控制
- 异常处理和重连机制
"""
def __init__(self):
"""
初始化相机管理器
"""
# 初始化SDK
MvCamera.MV_CC_Initialize()
self.cam = None # 相机实例
self.device_list = MV_CC_DEVICE_INFO_LIST() # 设备列表
self.is_open = False # 相机是否打开
self.is_grabbing = False # 是否正在取流
self.b_exit = False # 退出标志
self.work_thread = None # 工作线程
self.current_exp = 5000.0 # 当前曝光时间(微秒)
self.current_gain = 0.0 # 当前增益dB
self.current_fps = 30.0 # 当前帧率
self.last_frame = None # 最后一帧图像
def to_hex_str(self, num):
"""
将返回的错误码转换为十六进制显示
Args:
num: 错误码
Returns:
str: 十六进制字符串
"""
cha_dic = {10: 'a', 11: 'b', 12: 'c', 13: 'd', 14: 'e', 15: 'f'}
hex_str = ""
if num < 0:
num = num + 2 ** 32
while num >= 16:
digit = num % 16
hex_str = cha_dic.get(digit, str(digit)) + hex_str
num //= 16
hex_str = cha_dic.get(num, str(num)) + hex_str
return hex_str
def decoding_char(self, ctypes_char_array):
"""
安全地从 ctypes 字符数组中解码出字符串
Args:
ctypes_char_array: ctypes字符数组
Returns:
str: 解码后的字符串
"""
byte_str = memoryview(ctypes_char_array).tobytes()
# 在第一个空字符处截断
null_index = byte_str.find(b'\x00')
if null_index != -1:
byte_str = byte_str[:null_index]
# 多编码尝试解码
for encoding in ['gbk', 'utf-8', 'latin-1']:
try:
return byte_str.decode(encoding)
except UnicodeDecodeError:
continue
# 如果所有编码都失败,使用替换策略
return byte_str.decode('latin-1', errors='replace')
def enum_devices(self):
"""
枚举相机设备
Returns:
int: 设备数量
"""
self.device_list = MV_CC_DEVICE_INFO_LIST()
n_layer_type = (MV_GIGE_DEVICE | MV_USB_DEVICE)
ret = MvCamera.MV_CC_EnumDevices(n_layer_type, self.device_list)
if ret != 0:
print(f"枚举设备失败! ret = :{self.to_hex_str(ret)}")
return ret
if self.device_list.nDeviceNum == 0:
print("未找到设备")
return ret
print(f"找到 {self.device_list.nDeviceNum} 台设备!")
dev_list = []
target_model_found = False
for i in range(0, self.device_list.nDeviceNum):
mvcc_dev_info = cast(self.device_list.pDeviceInfo[i], POINTER(MV_CC_DEVICE_INFO)).contents
if mvcc_dev_info.nTLayerType == MV_GIGE_DEVICE:
print(f"\ngige device: [{i}]")
user_defined_name = self.decoding_char(mvcc_dev_info.SpecialInfo.stGigEInfo.chUserDefinedName)
model_name = self.decoding_char(mvcc_dev_info.SpecialInfo.stGigEInfo.chModelName)
print(f"device user define name: {user_defined_name}")
print(f"device model name: {model_name}")
# 检查是否为目标型号
if "MV-CS016-10UC" in model_name:
print("✓ 找到目标相机型号: MV-CS016-10UC")
target_model_found = True
nip1 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24)
nip2 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16)
nip3 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8)
nip4 = (mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff)
print(f"current ip: {nip1}.{nip2}.{nip3}.{nip4} ")
dev_list.append(f"[{i}]GigE: {user_defined_name} {model_name}({nip1}.{nip2}.{nip3}.{nip4})")
elif mvcc_dev_info.nTLayerType == MV_USB_DEVICE:
print(f"\nu3v device: [{i}]")
user_defined_name = self.decoding_char(mvcc_dev_info.SpecialInfo.stUsb3VInfo.chUserDefinedName)
model_name = self.decoding_char(mvcc_dev_info.SpecialInfo.stUsb3VInfo.chModelName)
print(f"device user define name: {user_defined_name}")
print(f"device model name: {model_name}")
# 检查是否为目标型号
if "MV-CS016-10UC" in model_name:
print("✓ 找到目标相机型号: MV-CS016-10UC")
target_model_found = True
str_serial_number = ""
for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chSerialNumber:
if per == 0:
break
str_serial_number = str_serial_number + chr(per)
print(f"user serial number: {str_serial_number}")
dev_list.append(f"[{i}]USB: {user_defined_name} {model_name}({str_serial_number})")
# 打印设备列表
print("\n设备列表:")
for i, dev in enumerate(dev_list):
print(f"{i}: {dev}")
# 提示用户是否找到目标型号
if not target_model_found:
print("\n⚠️ 警告: 未找到目标相机型号 MV-CS016-10UC")
print("⚠️ 系统仍将尝试使用找到的设备,但可能无法正常工作")
return self.device_list.nDeviceNum
def open_device(self, n_index):
"""
打开相机设备
Args:
n_index: 设备索引
Returns:
int: 错误码
"""
if self.is_open:
print("相机已经打开!")
return MV_E_CALLORDER
if n_index < 0 or n_index >= self.device_list.nDeviceNum:
print("请选择有效的相机索引!")
return MV_E_CALLORDER
# 选择设备并创建句柄
st_device_list = cast(self.device_list.pDeviceInfo[n_index], POINTER(MV_CC_DEVICE_INFO)).contents
self.cam = MvCamera()
# 句柄占用保护
ret = self.cam.MV_CC_CreateHandle(st_device_list)
if ret != 0:
print(f"创建设备句柄失败! ret = {self.to_hex_str(ret)}")
# 尝试清理可能的占用
try:
self.cam.MV_CC_DestroyHandle()
except:
pass
return ret
# 打开设备
ret = self.cam.MV_CC_OpenDevice()
if ret != 0:
print(f"打开设备失败! ret = {self.to_hex_str(ret)}")
try:
self.cam.MV_CC_DestroyHandle()
except:
pass
return ret
print("设备打开成功!")
self.is_open = True
# 设置触发模式为off
ret = self.cam.MV_CC_SetEnumValue("TriggerMode", MV_TRIGGER_MODE_OFF)
if ret != 0:
print(f"设置触发模式失败! ret = {self.to_hex_str(ret)}")
# 设置像素格式为 BGR8
ret = self.cam.MV_CC_SetEnumValue("PixelFormat", PixelType_Gvsp_BGR8_Packed)
if ret != 0:
print(f"设置像素格式失败! ret = {self.to_hex_str(ret)}")
# 设置连续采集模式
ret = self.cam.MV_CC_SetEnumValue("AcquisitionMode", 2) # Continuous
if ret != 0:
print(f"设置连续采集模式失败! ret = {self.to_hex_str(ret)}")
# 防过曝设置
self.setup_anti_overexposure()
return MV_OK
def setup_anti_overexposure(self):
"""
设置防过曝参数(点阵灯盘专用)
"""
# 关闭自动曝光,设置初始曝光时间(微秒)
ret = self.cam.MV_CC_SetEnumValue("ExposureAuto", MV_EXPOSURE_AUTO_MODE_OFF)
if ret != 0:
print(f"设置曝光自动模式失败! ret = {self.to_hex_str(ret)}")
# 点阵灯盘初始曝光时间3000-5000微秒
initial_exp = 4000.0
ret = self.cam.MV_CC_SetFloatValue("ExposureTime", initial_exp)
if ret != 0:
print(f"设置曝光时间失败! ret = {self.to_hex_str(ret)}")
else:
self.current_exp = initial_exp
print(f"初始曝光时间设置为: {initial_exp}μs")
# 关闭自动增益
ret = self.cam.MV_CC_SetEnumValue("GainAuto", MV_GAIN_AUTO_MODE_OFF)
if ret != 0:
print(f"设置增益自动模式失败! ret = {self.to_hex_str(ret)}")
ret = self.cam.MV_CC_SetFloatValue("Gain", 0.0)
if ret != 0:
print(f"设置增益失败! ret = {self.to_hex_str(ret)}")
else:
self.current_gain = 0.0
print("初始增益设置为: 0dB")
# 关闭自动白平衡识别绿色LED时必需
ret = self.cam.MV_CC_SetEnumValue("BalanceWhiteAuto", MV_BALANCE_WHITE_AUTO_OFF)
if ret != 0:
print(f"设置白平衡自动模式失败! ret = {self.to_hex_str(ret)}")
else:
print("自动白平衡已关闭")
def set_exposure_time(self, microseconds):
"""
设置曝光时间(微秒)
Args:
microseconds: 曝光时间(微秒)
Returns:
int: 错误码
"""
if not self.is_open:
return MV_E_CALLORDER
ret = self.cam.MV_CC_SetFloatValue("ExposureTime", microseconds)
if ret != 0:
print(f"设置曝光时间失败! ret = {self.to_hex_str(ret)}")
return ret
self.current_exp = microseconds
return MV_OK
def set_gain(self, db):
"""
设置增益dB
Args:
db: 增益值dB
Returns:
int: 错误码
"""
if not self.is_open:
return MV_E_CALLORDER
ret = self.cam.MV_CC_SetFloatValue("Gain", db)
if ret != 0:
print(f"设置增益失败! ret = {self.to_hex_str(ret)}")
return ret
self.current_gain = db
return MV_OK
def set_frame_rate(self, fps):
"""
设置帧率
Args:
fps: 帧率值
Returns:
int: 错误码
"""
if not self.is_open:
return MV_E_CALLORDER
ret = self.cam.MV_CC_SetFloatValue("AcquisitionFrameRate", fps)
if ret != 0:
print(f"设置帧率失败! ret = {self.to_hex_str(ret)}")
return ret
self.current_fps = fps
return MV_OK
def set_roi(self, x, y, w, h):
"""
设置ROI
Args:
x: X坐标
y: Y坐标
w: 宽度
h: 高度
Returns:
int: 错误码
"""
if not self.is_open:
return MV_E_CALLORDER
# 停止取流
if self.is_grabbing:
self.stop_grabbing()
# 设置ROI参数
ret = self.cam.MV_CC_SetIntValue("OffsetX", x)
if ret != 0:
print(f"设置OffsetX失败! ret = {self.to_hex_str(ret)}")
return ret
ret = self.cam.MV_CC_SetIntValue("OffsetY", y)
if ret != 0:
print(f"设置OffsetY失败! ret = {self.to_hex_str(ret)}")
return ret
ret = self.cam.MV_CC_SetIntValue("Width", w)
if ret != 0:
print(f"设置Width失败! ret = {self.to_hex_str(ret)}")
return ret
ret = self.cam.MV_CC_SetIntValue("Height", h)
if ret != 0:
print(f"设置Height失败! ret = {self.to_hex_str(ret)}")
return ret
# 重新开始取流
if self.is_grabbing:
self.start_grabbing()
return MV_OK
def start_grabbing(self):
"""
开始取流
Returns:
int: 错误码
"""
if not self.is_open:
print("相机未打开!")
return MV_E_CALLORDER
if self.is_grabbing:
print("已经开始取流!")
return MV_E_CALLORDER
self.b_exit = False
ret = self.cam.MV_CC_StartGrabbing()
if ret != 0:
print(f"开始取流失败! ret = {self.to_hex_str(ret)}")
return ret
self.is_grabbing = True
print("开始取流成功!")
# 启动取图线程
self.work_thread = threading.Thread(target=self.work_thread_func)
self.work_thread.daemon = True
self.work_thread.start()
return MV_OK
def stop_grabbing(self):
"""
停止取流
Returns:
int: 错误码
"""
if not self.is_open:
print("相机未打开!")
return MV_E_CALLORDER
if not self.is_grabbing:
print("未开始取流!")
return MV_E_CALLORDER
self.b_exit = True
time.sleep(0.1)
ret = self.cam.MV_CC_StopGrabbing()
if ret != 0:
print(f"停止取流失败! ret = {self.to_hex_str(ret)}")
return ret
self.is_grabbing = False
print("停止取流成功!")
return MV_OK
def close_device(self):
"""
关闭相机设备
"""
if self.is_grabbing:
self.stop_grabbing()
if self.is_open:
ret = self.cam.MV_CC_CloseDevice()
if ret != 0:
print(f"关闭设备失败! ret = {self.to_hex_str(ret)}")
# 销毁句柄
self.cam.MV_CC_DestroyHandle()
self.is_open = False
print("设备关闭成功!")
def work_thread_func(self):
"""
取图线程函数
"""
st_out_frame = MV_FRAME_OUT()
memset(byref(st_out_frame), 0, sizeof(st_out_frame))
timeout_count = 0 # 超时计数器
max_timeout_count = 3 # 最大超时次数
while not self.b_exit:
# 获取图像
ret = self.cam.MV_CC_GetImageBuffer(st_out_frame, 1000)
if ret == 0:
timeout_count = 0 # 重置超时计数器
# 打印图像信息
print(f"获取一帧图像: 宽度[{st_out_frame.stFrameInfo.nWidth}], 高度[{st_out_frame.stFrameInfo.nHeight}], 帧数[{st_out_frame.stFrameInfo.nFrameNum}]")
# 转换为 OpenCV 格式
try:
# 计算数据大小
data_size = st_out_frame.stFrameInfo.nWidth * st_out_frame.stFrameInfo.nHeight * 3
# 从缓冲区复制数据
frame_data = np.ctypeslib.as_array(st_out_frame.pBufAddr, shape=(data_size,))
frame = frame_data.reshape((st_out_frame.stFrameInfo.nHeight, st_out_frame.stFrameInfo.nWidth, 3))
# 保存最后一帧图像
self.last_frame = frame
# 显示图像
cv2.imshow("Camera", frame)
key = cv2.waitKey(1) & 0xFF
if key == ord('q'):
self.b_exit = True
break
except Exception as e:
print(f"处理图像失败: {e}")
finally:
# 释放缓存
try:
self.cam.MV_CC_FreeImageBuffer(st_out_frame)
except Exception as e:
print(f"释放图像缓存失败: {e}")
else:
print(f"获取图像失败! ret = {self.to_hex_str(ret)}")
timeout_count += 1
time.sleep(0.1)
# 连续超时3次触发重连
if timeout_count >= max_timeout_count:
print("连续超时,尝试重连相机...")
self._reconnect_camera()
timeout_count = 0
cv2.destroyAllWindows()
def _reconnect_camera(self):
"""
重连相机
"""
try:
# 停止取流
if self.is_grabbing:
print("停止取流...")
self.stop_grabbing()
# 关闭设备
print("关闭设备...")
self.close_device()
# 重新枚举设备
print("重新枚举设备...")
device_count = self.enum_devices()
if device_count == 0:
print("重连失败:未找到设备")
return
# 重新打开设备默认使用索引0
print("重新打开设备...")
ret = self.open_device(0)
if ret != 0:
print(f"重连失败:打开设备失败! ret = {self.to_hex_str(ret)}")
return
# 重新开始取流
print("重新开始取流...")
ret = self.start_grabbing()
if ret != 0:
print(f"重连失败:开始取流失败! ret = {self.to_hex_str(ret)}")
return
print("相机重连成功!")
except Exception as e:
print(f"重连相机失败: {e}")
def get_frame(self):
"""
获取当前帧图像
Returns:
tuple: (ret, frame)
"""
if not self.is_open or not self.is_grabbing:
return False, None
return True, self.last_frame
def __del__(self):
"""
析构函数,确保资源释放
"""
self.close_device()
# 反初始化SDK
try:
MvCamera.MV_CC_Finalize()
print("SDK反初始化成功")
except Exception as e:
print(f"SDK反初始化失败: {e}")
# SDK安装指南
"""
海康 MVS SDK 安装指南
1. 下载 MVS SDK
- 访问海康官网https://www.hikvision.com/cn/
- 搜索 "MVS SDK""机器视觉 SDK"
- 下载对应操作系统的最新版本
2. 安装 SDK
- 运行安装程序,按照默认设置安装
- 安装路径建议使用默认路径
3. 配置环境变量
- 将 SDK 安装目录下的 bin 文件夹添加到系统 PATH 环境变量
- 例如C:\\Program Files\\MVS\\Runtime\\bin\\win64_x64
4. 配置 Python 环境
- 复制 SDK 安装目录下的 Python 绑定文件
- 从C:\\Program Files\\MVS\\Development\\Samples\\Python\\MvImport
- 到:项目目录\\Python\\MvImport
5. 安装依赖
- pip install opencv-python
- pip install numpy
6. 测试安装
- 运行本脚本python camera.py
- 查看是否能成功枚举设备
7. 常见问题
- 找不到 MvCameraControl_class检查 MvImport 目录是否正确
- 相机无法打开:检查相机是否被其他程序占用
- 图像获取失败:检查相机连接和参数设置
"""
class CameraControlPanel:
"""
相机控制面板类
功能:
- 相机初始化和状态显示
- 曝光时间、增益、帧率等参数调节
- 相机控制(打开、关闭、开始取流、停止取流)
- 操作日志显示
"""
def __init__(self, root, camera):
"""
初始化相机控制面板
Args:
root: 父窗口
camera: 相机实例
"""
self.root = root
self.camera = camera
self.root.title("相机控制面板")
self.root.geometry("500x400")
self.root.resizable(True, True)
# 创建主框架
self.main_frame = ttk.Frame(self.root, padding="10")
self.main_frame.pack(fill=tk.BOTH, expand=True)
# 相机状态
self.status_var = tk.StringVar(value="未初始化")
# 曝光时间变量
self.exp_var = tk.DoubleVar(value=5000.0)
self.exp_var.trace("w", self.on_exp_change)
# 增益变量
self.gain_var = tk.DoubleVar(value=0.0)
self.gain_var.trace("w", self.on_gain_change)
# 帧率变量
self.fps_var = tk.DoubleVar(value=30.0)
self.fps_var.trace("w", self.on_fps_change)
# 状态标签
self.create_status_section()
# 相机控制
self.create_control_section()
# 参数调节
self.create_param_section()
# 日志区域
self.create_log_section()
# 初始化相机
self.init_camera()
def create_status_section(self):
"""
创建状态显示区域
"""
status_frame = ttk.LabelFrame(self.main_frame, text="相机状态", padding="10")
status_frame.pack(fill=tk.X, pady=5)
status_label = ttk.Label(status_frame, textvariable=self.status_var, font=("Arial", 12, "bold"))
status_label.pack(anchor=tk.W)
def create_control_section(self):
"""
创建相机控制区域
"""
control_frame = ttk.LabelFrame(self.main_frame, text="相机控制", padding="10")
control_frame.pack(fill=tk.X, pady=5)
# 按钮容器
button_frame = ttk.Frame(control_frame)
button_frame.pack(fill=tk.X)
# 初始化按钮
self.init_button = ttk.Button(button_frame, text="初始化相机", command=self.init_camera)
self.init_button.pack(side=tk.LEFT, padx=5)
# 打开按钮
self.open_button = ttk.Button(button_frame, text="打开相机", command=self.open_camera, state=tk.DISABLED)
self.open_button.pack(side=tk.LEFT, padx=5)
# 开始取流按钮
self.start_button = ttk.Button(button_frame, text="开始取流", command=self.start_grabbing, state=tk.DISABLED)
self.start_button.pack(side=tk.LEFT, padx=5)
# 停止取流按钮
self.stop_button = ttk.Button(button_frame, text="停止取流", command=self.stop_grabbing, state=tk.DISABLED)
self.stop_button.pack(side=tk.LEFT, padx=5)
# 关闭按钮
self.close_button = ttk.Button(button_frame, text="关闭相机", command=self.close_camera, state=tk.DISABLED)
self.close_button.pack(side=tk.LEFT, padx=5)
def create_param_section(self):
"""
创建参数调节区域
"""
param_frame = ttk.LabelFrame(self.main_frame, text="参数调节", padding="10")
param_frame.pack(fill=tk.X, pady=5)
# 曝光时间
exp_frame = ttk.Frame(param_frame)
exp_frame.pack(fill=tk.X, pady=5)
exp_label = ttk.Label(exp_frame, text="曝光时间 (μs):")
exp_label.pack(side=tk.LEFT, padx=5)
exp_scale = ttk.Scale(exp_frame, from_=100.0, to=50000.0, orient=tk.HORIZONTAL, variable=self.exp_var, length=200)
exp_scale.pack(side=tk.LEFT, padx=5, expand=True, fill=tk.X)
exp_entry = ttk.Entry(exp_frame, textvariable=self.exp_var, width=10)
exp_entry.pack(side=tk.LEFT, padx=5)
# 增益
gain_frame = ttk.Frame(param_frame)
gain_frame.pack(fill=tk.X, pady=5)
gain_label = ttk.Label(gain_frame, text="增益 (dB):")
gain_label.pack(side=tk.LEFT, padx=5)
gain_scale = ttk.Scale(gain_frame, from_=0.0, to=48.0, orient=tk.HORIZONTAL, variable=self.gain_var, length=200)
gain_scale.pack(side=tk.LEFT, padx=5, expand=True, fill=tk.X)
gain_entry = ttk.Entry(gain_frame, textvariable=self.gain_var, width=10)
gain_entry.pack(side=tk.LEFT, padx=5)
# 帧率
fps_frame = ttk.Frame(param_frame)
fps_frame.pack(fill=tk.X, pady=5)
fps_label = ttk.Label(fps_frame, text="帧率 (fps):")
fps_label.pack(side=tk.LEFT, padx=5)
fps_scale = ttk.Scale(fps_frame, from_=1.0, to=60.0, orient=tk.HORIZONTAL, variable=self.fps_var, length=200)
fps_scale.pack(side=tk.LEFT, padx=5, expand=True, fill=tk.X)
fps_entry = ttk.Entry(fps_frame, textvariable=self.fps_var, width=10)
fps_entry.pack(side=tk.LEFT, padx=5)
def create_log_section(self):
"""
创建日志显示区域
"""
log_frame = ttk.LabelFrame(self.main_frame, text="日志", padding="10")
log_frame.pack(fill=tk.BOTH, expand=True, pady=5)
# 创建文本框
self.log_text = tk.Text(log_frame, height=10, wrap=tk.WORD)
self.log_text.pack(fill=tk.BOTH, expand=True)
# 添加滚动条
scrollbar = ttk.Scrollbar(self.log_text, command=self.log_text.yview)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.log_text.config(yscrollcommand=scrollbar.set)
def log(self, message):
"""
添加日志信息
Args:
message: 日志消息
"""
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
log_message = f"[{timestamp}] {message}\n"
self.log_text.insert(tk.END, log_message)
self.log_text.see(tk.END)
def init_camera(self):
"""
初始化相机
"""
try:
self.log("正在初始化相机...")
self.status_var.set("初始化中")
# 枚举设备
device_count = self.camera.enum_devices()
if device_count == 0:
self.log("未找到相机设备")
self.status_var.set("未找到设备")
return
self.log(f"找到 {device_count} 台相机设备")
self.status_var.set("就绪")
# 启用打开按钮
self.open_button.config(state=tk.NORMAL)
self.log("相机初始化成功")
except Exception as e:
self.log(f"初始化失败: {e}")
self.status_var.set("初始化失败")
def open_camera(self):
"""
打开相机
"""
try:
self.log("正在打开相机...")
self.status_var.set("打开中")
# 打开设备默认使用索引0
ret = self.camera.open_device(0)
if ret != 0:
self.log(f"打开相机失败,错误码: {ret}")
self.status_var.set("打开失败")
return
self.log("相机打开成功")
self.status_var.set("已打开")
# 更新参数显示
self.exp_var.set(self.camera.current_exp)
self.gain_var.set(self.camera.current_gain)
self.fps_var.set(self.camera.current_fps)
# 启用控制按钮
self.start_button.config(state=tk.NORMAL)
self.close_button.config(state=tk.NORMAL)
self.open_button.config(state=tk.DISABLED)
except Exception as e:
self.log(f"打开相机失败: {e}")
self.status_var.set("打开失败")
def start_grabbing(self):
"""
开始取流
"""
try:
self.log("正在开始取流...")
self.status_var.set("取流中")
ret = self.camera.start_grabbing()
if ret != 0:
self.log(f"开始取流失败,错误码: {ret}")
self.status_var.set("取流失败")
return
self.log("开始取流成功")
self.status_var.set("正在取流")
# 更新按钮状态
self.start_button.config(state=tk.DISABLED)
self.stop_button.config(state=tk.NORMAL)
except Exception as e:
self.log(f"开始取流失败: {e}")
self.status_var.set("取流失败")
def stop_grabbing(self):
"""
停止取流
"""
try:
self.log("正在停止取流...")
ret = self.camera.stop_grabbing()
if ret != 0:
self.log(f"停止取流失败,错误码: {ret}")
return
self.log("停止取流成功")
self.status_var.set("已打开")
# 更新按钮状态
self.stop_button.config(state=tk.DISABLED)
self.start_button.config(state=tk.NORMAL)
except Exception as e:
self.log(f"停止取流失败: {e}")
def close_camera(self):
"""
关闭相机
"""
try:
self.log("正在关闭相机...")
self.camera.close_device()
self.log("相机关闭成功")
self.status_var.set("已关闭")
# 更新按钮状态
self.close_button.config(state=tk.DISABLED)
self.stop_button.config(state=tk.DISABLED)
self.start_button.config(state=tk.DISABLED)
self.open_button.config(state=tk.NORMAL)
except Exception as e:
self.log(f"关闭相机失败: {e}")
def on_exp_change(self, *args):
"""
曝光时间变化回调
"""
if self.camera.is_open:
try:
exp_value = self.exp_var.get()
ret = self.camera.set_exposure_time(exp_value)
if ret == 0:
self.log(f"曝光时间设置为: {exp_value} μs")
except Exception as e:
self.log(f"设置曝光时间失败: {e}")
def on_gain_change(self, *args):
"""
增益变化回调
"""
if self.camera.is_open:
try:
gain_value = self.gain_var.get()
ret = self.camera.set_gain(gain_value)
if ret == 0:
self.log(f"增益设置为: {gain_value} dB")
except Exception as e:
self.log(f"设置增益失败: {e}")
def on_fps_change(self, *args):
"""
帧率变化回调
"""
if self.camera.is_open:
try:
fps_value = self.fps_var.get()
ret = self.camera.set_frame_rate(fps_value)
if ret == 0:
self.log(f"帧率设置为: {fps_value} fps")
except Exception as e:
self.log(f"设置帧率失败: {e}")
if __name__ == "__main__":
"""
测试相机功能
"""
print("海康相机测试程序")
print("按 Q 退出显示窗口")
print("按 Ctrl+C 退出程序")
# 创建相机实例
camera = Camera()
try:
# 创建控制界面
root = tk.Tk()
app = CameraControlPanel(root, camera)
# 运行主循环
root.mainloop()
except KeyboardInterrupt:
print("\n用户中断程序")
except Exception as e:
print(f"程序异常: {e}")
finally:
# 清理资源
camera.close_device()
print("程序退出")