354 lines
12 KiB
Python
354 lines
12 KiB
Python
|
|
import sys
|
|||
|
|
import os
|
|||
|
|
import logging
|
|||
|
|
from ctypes import *
|
|||
|
|
|
|||
|
|
# 添加相机模块路径
|
|||
|
|
sys.path.append(os.path.join(os.path.dirname(os.path.dirname(__file__)), "camera"))
|
|||
|
|
|
|||
|
|
# 导入相机相关模块
|
|||
|
|
from camera.MvCameraControl_class import MvCamera
|
|||
|
|
from camera.CamOperation_class import CameraOperation
|
|||
|
|
from camera.MvErrorDefine_const import *
|
|||
|
|
from camera.CameraParams_header import *
|
|||
|
|
from camera.CameraParams_const import *
|
|||
|
|
|
|||
|
|
|
|||
|
|
class CameraManager:
|
|||
|
|
"""相机管理器单例类,确保整个应用中只有一个相机实例"""
|
|||
|
|
|
|||
|
|
_instance = None
|
|||
|
|
_initialized = False
|
|||
|
|
|
|||
|
|
@staticmethod
|
|||
|
|
def get_instance():
|
|||
|
|
"""获取相机管理器实例"""
|
|||
|
|
if CameraManager._instance is None:
|
|||
|
|
CameraManager._instance = CameraManager()
|
|||
|
|
return CameraManager._instance
|
|||
|
|
|
|||
|
|
def __init__(self):
|
|||
|
|
"""初始化相机管理器"""
|
|||
|
|
if CameraManager._instance is not None:
|
|||
|
|
raise Exception("相机管理器是单例类,请使用get_instance()方法获取实例")
|
|||
|
|
else:
|
|||
|
|
CameraManager._instance = self
|
|||
|
|
|
|||
|
|
# 初始化变量
|
|||
|
|
self.deviceList = None
|
|||
|
|
self.cam = MvCamera()
|
|||
|
|
self.nSelCamIndex = -1
|
|||
|
|
self.obj_cam_operation = None
|
|||
|
|
self.isOpen = False
|
|||
|
|
self.isGrabbing = False
|
|||
|
|
|
|||
|
|
# 初始化SDK (只在第一次时初始化)
|
|||
|
|
if not CameraManager._initialized:
|
|||
|
|
MvCamera.MV_CC_Initialize()
|
|||
|
|
CameraManager._initialized = True
|
|||
|
|
logging.info("相机SDK已初始化")
|
|||
|
|
|
|||
|
|
def enum_devices(self):
|
|||
|
|
"""枚举相机设备"""
|
|||
|
|
try:
|
|||
|
|
# 确保先关闭任何已打开的相机
|
|||
|
|
if self.isOpen:
|
|||
|
|
self.close_device()
|
|||
|
|
|
|||
|
|
# 枚举设备
|
|||
|
|
self.deviceList = MV_CC_DEVICE_INFO_LIST()
|
|||
|
|
n_layer_type = (MV_GIGE_DEVICE | MV_USB_DEVICE | MV_GENTL_CAMERALINK_DEVICE
|
|||
|
|
| MV_GENTL_CXP_DEVICE | MV_GENTL_XOF_DEVICE)
|
|||
|
|
ret = MvCamera.MV_CC_EnumDevices(n_layer_type, self.deviceList)
|
|||
|
|
if ret != 0:
|
|||
|
|
error_msg = f"枚举设备失败! 错误码: 0x{ret:x}"
|
|||
|
|
logging.error(error_msg)
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
if self.deviceList.nDeviceNum == 0:
|
|||
|
|
logging.info("未找到相机设备")
|
|||
|
|
return []
|
|||
|
|
|
|||
|
|
logging.info(f"找到 {self.deviceList.nDeviceNum} 个相机设备")
|
|||
|
|
|
|||
|
|
# 构造设备信息列表
|
|||
|
|
devices_info = []
|
|||
|
|
for i in range(0, self.deviceList.nDeviceNum):
|
|||
|
|
mvcc_dev_info = cast(self.deviceList.pDeviceInfo[i], POINTER(MV_CC_DEVICE_INFO)).contents
|
|||
|
|
|
|||
|
|
if mvcc_dev_info.nTLayerType == MV_GIGE_DEVICE:
|
|||
|
|
# GigE相机
|
|||
|
|
user_defined_name = ""
|
|||
|
|
for per in mvcc_dev_info.SpecialInfo.stGigEInfo.chUserDefinedName:
|
|||
|
|
if per == 0:
|
|||
|
|
break
|
|||
|
|
user_defined_name = user_defined_name + chr(per)
|
|||
|
|
|
|||
|
|
model_name = ""
|
|||
|
|
for per in mvcc_dev_info.SpecialInfo.stGigEInfo.chModelName:
|
|||
|
|
if per == 0:
|
|||
|
|
break
|
|||
|
|
model_name = model_name + chr(per)
|
|||
|
|
|
|||
|
|
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)
|
|||
|
|
ip = f"{nip1}.{nip2}.{nip3}.{nip4}"
|
|||
|
|
|
|||
|
|
device_info = {
|
|||
|
|
"index": i,
|
|||
|
|
"type": "GigE",
|
|||
|
|
"name": user_defined_name,
|
|||
|
|
"model": model_name,
|
|||
|
|
"ip": ip,
|
|||
|
|
"display": f"[{i}]GigE: {user_defined_name} {model_name} ({ip})"
|
|||
|
|
}
|
|||
|
|
devices_info.append(device_info)
|
|||
|
|
|
|||
|
|
elif mvcc_dev_info.nTLayerType == MV_USB_DEVICE:
|
|||
|
|
# USB相机
|
|||
|
|
user_defined_name = ""
|
|||
|
|
for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chUserDefinedName:
|
|||
|
|
if per == 0:
|
|||
|
|
break
|
|||
|
|
user_defined_name = user_defined_name + chr(per)
|
|||
|
|
|
|||
|
|
model_name = ""
|
|||
|
|
for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chModelName:
|
|||
|
|
if per == 0:
|
|||
|
|
break
|
|||
|
|
model_name = model_name + chr(per)
|
|||
|
|
|
|||
|
|
device_info = {
|
|||
|
|
"index": i,
|
|||
|
|
"type": "USB",
|
|||
|
|
"name": user_defined_name,
|
|||
|
|
"model": model_name,
|
|||
|
|
"display": f"[{i}]USB: {user_defined_name} {model_name}"
|
|||
|
|
}
|
|||
|
|
devices_info.append(device_info)
|
|||
|
|
|
|||
|
|
else:
|
|||
|
|
# 其他类型相机
|
|||
|
|
device_info = {
|
|||
|
|
"index": i,
|
|||
|
|
"type": "Other",
|
|||
|
|
"display": f"[{i}]Other"
|
|||
|
|
}
|
|||
|
|
devices_info.append(device_info)
|
|||
|
|
|
|||
|
|
return devices_info
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
error_msg = f"枚举设备时发生异常: {str(e)}"
|
|||
|
|
logging.error(error_msg)
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
def open_device(self, device_index):
|
|||
|
|
"""打开相机设备
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
device_index: 设备索引
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
bool: 是否成功打开设备
|
|||
|
|
"""
|
|||
|
|
# 检查是否已经打开
|
|||
|
|
if self.isOpen:
|
|||
|
|
logging.warning("相机已经打开!")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
# 检查设备索引是否有效
|
|||
|
|
if device_index < 0 or (self.deviceList and device_index >= self.deviceList.nDeviceNum):
|
|||
|
|
logging.error(f"无效的设备索引: {device_index}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
logging.debug(f"准备打开相机,设备索引: {device_index}")
|
|||
|
|
|
|||
|
|
self.nSelCamIndex = device_index
|
|||
|
|
|
|||
|
|
# 创建相机操作对象
|
|||
|
|
self.obj_cam_operation = CameraOperation(self.cam, self.deviceList, self.nSelCamIndex)
|
|||
|
|
ret = self.obj_cam_operation.Open_device()
|
|||
|
|
if ret != 0:
|
|||
|
|
error_msg = f"打开相机失败! 错误码: 0x{ret:x}"
|
|||
|
|
logging.error(error_msg)
|
|||
|
|
self.isOpen = False
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
# 设置连续模式
|
|||
|
|
self.obj_cam_operation.Set_trigger_mode(False)
|
|||
|
|
|
|||
|
|
# 获取参数
|
|||
|
|
self.obj_cam_operation.Get_parameter()
|
|||
|
|
|
|||
|
|
self.isOpen = True
|
|||
|
|
logging.info(f"相机已打开,设备索引: {device_index}")
|
|||
|
|
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
error_msg = f"打开相机时发生异常: {str(e)}"
|
|||
|
|
logging.error(error_msg)
|
|||
|
|
self.isOpen = False
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
def close_device(self):
|
|||
|
|
"""关闭相机设备
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
bool: 是否成功关闭设备
|
|||
|
|
"""
|
|||
|
|
if not self.isOpen:
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
# 确保停止取图
|
|||
|
|
if self.isGrabbing:
|
|||
|
|
self.stop_grabbing()
|
|||
|
|
|
|||
|
|
# 关闭设备
|
|||
|
|
ret = self.obj_cam_operation.Close_device()
|
|||
|
|
if ret != 0:
|
|||
|
|
error_msg = f"关闭相机失败! 错误码: 0x{ret:x}"
|
|||
|
|
logging.error(error_msg)
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
self.isOpen = False
|
|||
|
|
logging.info("相机已关闭")
|
|||
|
|
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
error_msg = f"关闭相机时发生异常: {str(e)}"
|
|||
|
|
logging.error(error_msg)
|
|||
|
|
self.isOpen = False # 强制设置为关闭状态
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
def start_grabbing(self, window_id):
|
|||
|
|
"""开始取图
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
window_id: 显示窗口句柄
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
bool: 是否成功开始取图
|
|||
|
|
"""
|
|||
|
|
if not self.isOpen:
|
|||
|
|
logging.error("相机未打开,无法开始取图")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
if self.isGrabbing:
|
|||
|
|
logging.warning("相机已经在取图")
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
ret = self.obj_cam_operation.Start_grabbing(window_id)
|
|||
|
|
if ret != 0:
|
|||
|
|
error_msg = f"开始取图失败! 错误码: 0x{ret:x}"
|
|||
|
|
logging.error(error_msg)
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
self.isGrabbing = True
|
|||
|
|
logging.info("开始图像采集")
|
|||
|
|
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
error_msg = f"开始取图时发生异常: {str(e)}"
|
|||
|
|
logging.error(error_msg)
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
def stop_grabbing(self):
|
|||
|
|
"""停止取图
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
bool: 是否成功停止取图
|
|||
|
|
"""
|
|||
|
|
if not self.isOpen:
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
if not self.isGrabbing:
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
ret = self.obj_cam_operation.Stop_grabbing()
|
|||
|
|
if ret != 0:
|
|||
|
|
error_msg = f"停止取图失败! 错误码: 0x{ret:x}"
|
|||
|
|
logging.error(error_msg)
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
self.isGrabbing = False
|
|||
|
|
logging.info("停止图像采集")
|
|||
|
|
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
error_msg = f"停止取图时发生异常: {str(e)}"
|
|||
|
|
logging.error(error_msg)
|
|||
|
|
self.isGrabbing = False # 强制设置为非取图状态
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
def get_parameters(self):
|
|||
|
|
"""获取相机参数
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
tuple: (曝光时间, 增益, 帧率) 或 None (如果失败)
|
|||
|
|
"""
|
|||
|
|
if not self.isOpen:
|
|||
|
|
logging.error("相机未打开,无法获取参数")
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
ret = self.obj_cam_operation.Get_parameter()
|
|||
|
|
if ret != 0:
|
|||
|
|
error_msg = f"获取相机参数失败! 错误码: 0x{ret:x}"
|
|||
|
|
logging.error(error_msg)
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
exposure_time = self.obj_cam_operation.exposure_time
|
|||
|
|
gain = self.obj_cam_operation.gain
|
|||
|
|
frame_rate = self.obj_cam_operation.frame_rate
|
|||
|
|
|
|||
|
|
logging.info(f"获取相机参数: 曝光={exposure_time}, 增益={gain}, 帧率={frame_rate}")
|
|||
|
|
|
|||
|
|
return (exposure_time, gain, frame_rate)
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
error_msg = f"获取相机参数时发生异常: {str(e)}"
|
|||
|
|
logging.error(error_msg)
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
def set_parameters(self, frame_rate, exposure_time, gain):
|
|||
|
|
"""设置相机参数
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
frame_rate: 帧率
|
|||
|
|
exposure_time: 曝光时间
|
|||
|
|
gain: 增益
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
bool: 是否成功设置参数
|
|||
|
|
"""
|
|||
|
|
if not self.isOpen:
|
|||
|
|
logging.error("相机未打开,无法设置参数")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
# 设置参数
|
|||
|
|
ret = self.obj_cam_operation.Set_parameter(str(frame_rate), str(exposure_time), str(gain))
|
|||
|
|
if ret != 0:
|
|||
|
|
error_msg = f"设置相机参数失败! 错误码: 0x{ret:x}"
|
|||
|
|
logging.error(error_msg)
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
logging.info(f"设置相机参数: 曝光={exposure_time}, 增益={gain}, 帧率={frame_rate}")
|
|||
|
|
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
error_msg = f"设置相机参数时发生异常: {str(e)}"
|
|||
|
|
logging.error(error_msg)
|
|||
|
|
return False
|