import sys
import cv2
import numpy as np
import time
import os
import threading
import json
from datetime import datetime
from ctypes import *
from MvImport.MvErrorDefine_const import MV_OK
sys.path.append("C:\\Program Files (x86)\\MVS\\Development\\Samples\\Python\\MvImport")
from MvImport.MvCameraControl_class import *
from MvImport.CamOperation_class import CameraOperation
# 全局变量
takephoto = False # 拍照触发标志
camera_lock = threading.Lock() # 相机操作锁
stop_flag = False # 程序停止标志
global deviceList
# class HikCamera:
# def __init__(self):
# self.cam = None
# self.data_buf = None
# self.nPayloadSize = 0
# self.initialized = False
#
# def initialize(self):
# """初始化相机"""
# try:
# # 枚举设备
# deviceList = MV_CC_DEVICE_INFO_LIST()
# tlayerType = MV_GIGE_DEVICE | MV_USB_DEVICE
# ret = MvCamera.MV_CC_EnumDevices(tlayerType, deviceList)
# if deviceList.nDeviceNum == 0:
# print("错误:未检测到相机设备!")
# return False
#
# # 创建相机实例
# self.cam = MvCamera()
# stDeviceInfo = cast(deviceList.pDeviceInfo[0], POINTER(MV_CC_DEVICE_INFO)).contents
# ret = self.cam.MV_CC_CreateHandle(stDeviceInfo)
# if ret != 0:
# print(f"创建相机句柄失败,错误码: {ret}")
# return False
#
# # 打开设备
# ret = self.cam.MV_CC_OpenDevice(MV_ACCESS_Exclusive, 0)
# if ret != 0:
# print(f"打开相机失败,错误码: {ret}")
# return False
#
# # 配置相机参数
# # 设置连续采集模式
# ret = self.cam.MV_CC_SetEnumValue("TriggerMode", MV_TRIGGER_MODE_OFF)
# if ret != 0:
# print(f"设置连续采集模式失败,错误码: {ret}")
#
# # 设置自动曝光
# ret = self.cam.MV_CC_SetEnumValue("ExposureAuto", 1) # 1=连续自动曝光
# if ret != 0:
# print(f"设置自动曝光失败,错误码: {ret}")
# # 尝试手动设置曝光
# self.cam.MV_CC_SetFloatValue("ExposureTime", 9000.0) # 10ms
#
# # 设置自动增益
# ret = self.cam.MV_CC_SetEnumValue("GainAuto", 1) # 1=连续自动增益
# if ret != 0:
# print(f"设置自动增益失败,错误码: {ret}")
# self.cam.MV_CC_SetFloatValue("Gain", 12.0) # 12dB增益
#
# # 获取有效载荷大小
# stParam = MVCC_INTVALUE()
# self.cam.MV_CC_GetIntValue("PayloadSize", stParam)
# self.nPayloadSize = stParam.nCurValue
# self.data_buf = (c_ubyte * self.nPayloadSize)()
#
# # 开始取流
# ret = self.cam.MV_CC_StartGrabbing()
# if ret != 0:
# print(f"开始取流失败,错误码: {ret}")
# return False
#
# self.initialized = True
# print("相机初始化成功")
# return True
#
# except Exception as e:
# print(f"相机初始化异常: {str(e)}")
# return False
#
# def capture_image(self):
# """捕获一帧图像"""
# if not self.initialized:
# print("相机未初始化")
# return None
#
# try:
# # 获取一帧数据
# stFrameInfo = MV_FRAME_OUT_INFO_EX()
# ret = self.cam.MV_CC_GetOneFrameTimeout(self.data_buf, self.nPayloadSize, stFrameInfo, 1000)
#
# if ret != 0:
# print(f"获取帧失败,错误码: {ret}")
# return None
#
# # 直接处理BGR格式
# if stFrameInfo.enPixelType == PixelType_Gvsp_BGR8_Packed:
# img = np.frombuffer(self.data_buf, dtype=np.uint8, count=stFrameInfo.nFrameLen)
# img = img.reshape((stFrameInfo.nHeight, stFrameInfo.nWidth, 3))
# return img
#
# # 尝试转换其他格式
# convert_param = MV_CC_PIXEL_CONVERT_PARAM()
# memset(byref(convert_param), 0, sizeof(convert_param))
#
# # 关键修复:计算目标缓冲区大小
# nDstBufferSize = stFrameInfo.nWidth * stFrameInfo.nHeight * 3 + 2048 # 额外增加2KB缓冲区
# pDstBuffer = (c_ubyte * nDstBufferSize)()
#
# # 设置转换参数
# convert_param.nWidth = stFrameInfo.nWidth
# convert_param.nHeight = stFrameInfo.nHeight
# convert_param.pSrcData = self.data_buf
# convert_param.nSrcDataLen = stFrameInfo.nFrameLen
# convert_param.enSrcPixelType = stFrameInfo.enPixelType
# convert_param.enDstPixelType = PixelType_Gvsp_BGR8_Packed
# convert_param.pDstBuffer = cast(pDstBuffer, POINTER(c_ubyte))
# convert_param.nDstBufferSize = nDstBufferSize
#
# ret_convert = self.cam.MV_CC_ConvertPixelType(convert_param)
#
# if ret_convert == 0 and convert_param.nDstLen > 0:
# img = np.frombuffer(pDstBuffer, dtype=np.uint8, count=convert_param.nDstLen)
# img = img.reshape((stFrameInfo.nHeight, stFrameInfo.nWidth, 3))
# return img
# else:
# print(f"图像转换失败,错误码: {ret_convert}")
# print(f"源格式: {stFrameInfo.enPixelType}, 目标大小: {convert_param.nDstLen}/{nDstBufferSize}")
# return None
#
# except Exception as e:
# print(f"捕获图像异常: {str(e)}")
# return None
# def release(self):
# """释放相机资源"""
# if self.initialized:
# try:
# self.cam.MV_CC_StopGrabbing()
# self.cam.MV_CC_CloseDevice()
# print("相机资源已释放")
# except:
# print("释放相机资源时出错")
# finally:
# self.initialized = False
# 获取选取设备信息的索引,通过[]之间的字符去解析
def TxtWrapBy(start_str, end, all):
start = all.find(start_str)
if start >= 0:
start += len(start_str)
end = all.find(end, start)
if end >= 0:
return all[start:end].strip()
# 将返回的错误码转换为十六进制显示
def ToHexStr(num):
chaDic = {10: 'a', 11: 'b', 12: 'c', 13: 'd', 14: 'e', 15: 'f'}
hexStr = ""
if num < 0:
num = num + 2 ** 32
while num >= 16:
digit = num % 16
hexStr = chaDic.get(digit, str(digit)) + hexStr
num //= 16
hexStr = chaDic.get(num, str(num)) + hexStr
return hexStr
def decoding_char(c_ubyte_value):
c_char_p_value = ctypes.cast(c_ubyte_value, ctypes.c_char_p)
try:
decode_str = c_char_p_value.value.decode('gbk') # Chinese characters
except UnicodeDecodeError:
decode_str = str(c_char_p_value.value)
return decode_str
class HikCamera:
def __init__(self, device_index=0): # 添加设备索引参数
self.cam = None
self.data_buf = None
self.nPayloadSize = 0
self.initialized = False
self.device_index = device_index # 保存设备索引\
self.initialize(device_index)
self.cam_op = None
def enum_devices(self):
global obj_cam_operation
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, deviceList)
if ret != 0:
strError = "Enum devices fail! ret = :" + ToHexStr(ret)
return ret
if deviceList.nDeviceNum == 0:
return ret
print("Find %d devices!" % deviceList.nDeviceNum)
devList = []
for i in range(0, deviceList.nDeviceNum):
mvcc_dev_info = cast(deviceList.pDeviceInfo[i], POINTER(MV_CC_DEVICE_INFO)).contents
if mvcc_dev_info.nTLayerType == MV_GIGE_DEVICE or mvcc_dev_info.nTLayerType == MV_GENTL_GIGE_DEVICE:
print("\ngige device: [%d]" % i)
user_defined_name = decoding_char(mvcc_dev_info.SpecialInfo.stGigEInfo.chUserDefinedName)
model_name = decoding_char(mvcc_dev_info.SpecialInfo.stGigEInfo.chModelName)
print("device user define name: " + user_defined_name)
print("device model name: " + model_name)
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("current ip: %d.%d.%d.%d " % (nip1, nip2, nip3, nip4))
devList.append(
"[" + str(i) + "]GigE: " + user_defined_name + " " + model_name + "(" + str(nip1) + "." + str(
nip2) + "." + str(nip3) + "." + str(nip4) + ")")
return deviceList
def open_device(self, deviceList, nSelCamIndex):
global obj_cam_operation
cam = MvCamera() # 实例化
if nSelCamIndex < 0:
return MV_E_CALLORDER
obj_cam_operation = CameraOperation(cam, deviceList, nSelCamIndex) # 实例化相机操作类
ret = obj_cam_operation.Open_device()
if 0 != ret:
strError = "Open device failed ret:" + ToHexStr(ret)
return obj_cam_operation
def initialize(self, device_index):
# 加载一个json文件(相机id,ip,name,mfs path)
with open('config/settings.json', 'r', encoding='utf-8') as f: # 注意文件路径
settings = json.load(f) # 解析 JSON 为 Python 字典
# 根据传入的相机ID加载path
path = settings[str(device_index)]['path']
# 搜索相机列表
deviceList = self.enum_devices()
# 打开相机
# 1.实例化一个相机
# 2.实例化相机操作类(cam, list, index)
self.cam_op = self.open_device(deviceList, device_index)
# 设置相机参数
self.cam_op.cam_settings(path)
self.initialized = True
# ret = obj_cam_operation.Open_device()
# # 加载参数
# if 0 != ret:
# print(f"打开相机失败,错误码: {ret}")
# return False
# ret = self.cam.MV_CC_FeatureLoad("mfs path")
# if MV_OK != ret:
# print("load feature fail! ret [0x%x]" % ret)
# print("finish import the camera properties from the file")
# self.initialized = True
# try:
# # 枚举设备
# deviceList = MV_CC_DEVICE_INFO_LIST()
# tlayerType = MV_GIGE_DEVICE | MV_USB_DEVICE
# ret = MvCamera.MV_CC_EnumDevices(tlayerType, deviceList)
# if deviceList.nDeviceNum == 0:
# print("错误:未检测到相机设备!")
# return False
#
# # 检查设备索引是否有效
# if self.device_index >= deviceList.nDeviceNum:
# print(f"错误:设备索引{self.device_index}超出范围,最大设备数={deviceList.nDeviceNum}")
# return False
#
# # 创建相机实例(使用指定的设备索引)
# self.cam = MvCamera()
# stDeviceInfo = cast(deviceList.pDeviceInfo[self.device_index], POINTER(MV_CC_DEVICE_INFO)).contents
# ret = self.cam.MV_CC_CreateHandle(stDeviceInfo)
# if ret != 0:
# print(f"创建相机{self.device_index}句柄失败,错误码: {ret}")
# return False
#
# # 打开设备
# ret = self.cam.MV_CC_OpenDevice(MV_ACCESS_Exclusive, 0)
# if ret != 0:
# print(f"打开相机失败,错误码: {ret}")
# return False
#
# # 配置相机参数
# # 设置连续采集模式
# ret = self.cam.MV_CC_SetEnumValue("TriggerMode", MV_TRIGGER_MODE_OFF)
# if ret != 0:
# print(f"设置连续采集模式失败,错误码: {ret}")
#
# # 设置自动曝光
# ret = self.cam.MV_CC_SetEnumValue("ExposureAuto", 1) # 1=连续自动曝光
# if ret != 0:
# print(f"设置自动曝光失败,错误码: {ret}")
# # 尝试手动设置曝光
# self.cam.MV_CC_SetFloatValue("ExposureTime", 9000.0) # 10ms
#
# # 设置自动增益
# ret = self.cam.MV_CC_SetEnumValue("GainAuto", 1) # 1=连续自动增益
# if ret != 0:
# print(f"设置自动增益失败,错误码: {ret}")
# self.cam.MV_CC_SetFloatValue("Gain", 12.0) # 12dB增益
#
# # 获取有效载荷大小
# stParam = MVCC_INTVALUE()
# self.cam.MV_CC_GetIntValue("PayloadSize", stParam)
# self.nPayloadSize = stParam.nCurValue
# self.data_buf = (c_ubyte * self.nPayloadSize)()
#
# # 开始取流
# ret = self.cam.MV_CC_StartGrabbing()
# if ret != 0:
# print(f"开始取流失败,错误码: {ret}")
# return False
#
# self.initialized = True
# print(f"相机{self.device_index}初始化成功")
# return True
#
# except Exception as e:
# print(f"相机{self.device_index}初始化异常: {str(e)}")
# return False
def capture_image(self):
"""捕获一帧图像"""
if not self.initialized:
print("相机未初始化")
return None
try:
# 获取一帧数据
stFrameInfo = MV_FRAME_OUT_INFO_EX()
ret = self.cam.MV_CC_GetOneFrameTimeout(self.data_buf, self.nPayloadSize, stFrameInfo, 1000)
if ret != 0:
print(f"获取帧失败,错误码: {ret}")
return None
# 直接处理BGR格式
if stFrameInfo.enPixelType == PixelType_Gvsp_BGR8_Packed:
img = np.frombuffer(self.data_buf, dtype=np.uint8, count=stFrameInfo.nFrameLen)
img = img.reshape((stFrameInfo.nHeight, stFrameInfo.nWidth, 3))
return img
# 尝试转换其他格式
convert_param = MV_CC_PIXEL_CONVERT_PARAM()
memset(byref(convert_param), 0, sizeof(convert_param))
# 关键修复:计算目标缓冲区大小
nDstBufferSize = stFrameInfo.nWidth * stFrameInfo.nHeight * 3 + 2048 # 额外增加2KB缓冲区
pDstBuffer = (c_ubyte * nDstBufferSize)()
# 设置转换参数
convert_param.nWidth = stFrameInfo.nWidth
convert_param.nHeight = stFrameInfo.nHeight
convert_param.pSrcData = self.data_buf
convert_param.nSrcDataLen = stFrameInfo.nFrameLen
convert_param.enSrcPixelType = stFrameInfo.enPixelType
convert_param.enDstPixelType = PixelType_Gvsp_BGR8_Packed
convert_param.pDstBuffer = cast(pDstBuffer, POINTER(c_ubyte))
convert_param.nDstBufferSize = nDstBufferSize
ret_convert = self.cam.MV_CC_ConvertPixelType(convert_param)
if ret_convert == 0 and convert_param.nDstLen > 0:
img = np.frombuffer(pDstBuffer, dtype=np.uint8, count=convert_param.nDstLen)
img = img.reshape((stFrameInfo.nHeight, stFrameInfo.nWidth, 3))
return img
else:
print(f"图像转换失败,错误码: {ret_convert}")
print(f"源格式: {stFrameInfo.enPixelType}, 目标大小: {convert_param.nDstLen}/{nDstBufferSize}")
return None
except Exception as e:
print(f"捕获图像异常: {str(e)}")
return None
def release(self):
"""释放相机资源"""
if self.initialized:
try:
self.cam.MV_CC_StopGrabbing()
self.cam.MV_CC_CloseDevice()
print("相机资源已释放")
except:
print("释放相机资源时出错")
finally:
self.initialized = False
def timer1_thread():
"""定时器线程,每分钟设置一次拍照标志"""
global takephoto
while not stop_flag:
# 每分钟触发一次拍照
time.sleep(30)
takephoto = True
print(f"[{datetime.now().strftime('%H:%M:%S')}] 触发拍照标志")
# def timer2_thread():
# """定时器线程,每分钟设置一次拍照标志"""
# global takephoto
# while not stop_flag:
# # 每分钟触发一次拍照
# time.sleep(30)
# takephoto = True
# print(f"[{datetime.now().strftime('%H:%M:%S')}] 触发拍照标志")
def photo_task(output_dir1, output_dir2):
"""拍照任务处理线程"""
global takephoto
camera1 = HikCamera(device_index=0)
# camera2 = HikCamera(device_index=1)
# 确保输出目录存在
os.makedirs(output_dir1, exist_ok=True)
# os.makedirs(output_dir2, exist_ok=True)
while not stop_flag:
if takephoto:
with camera_lock:
takephoto = False # 重置标志
# # 初始化相机(如果需要)
# if not camera1.initialized and camera2.initialized:
# if not camera1.initialize() and camera2.initialized:
# print("无法初始化相机,跳过本次拍照")
# continue
# 捕获图像
print("开始捕获图像...")
img1 = camera1.capture_image()
# img2 = camera2.capture_image()
# if img1 and img2 is not None:
if img1 is not None:
# 生成文件名
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename1 = os.path.join(output_dir1, f"photo1_{timestamp}.jpg")
# filename2 = os.path.join(output_dir2, f"photo2_{timestamp}.jpg")
# 保存图像
cv2.imwrite(filename1, img1)
# cv2.imwrite(filename2, img2)
# print(f"照片已保存至: {filename1, filename2}")
print(f"照片已保存至: {filename1}")
else:
print("捕获图像失败")
# 短暂延迟,避免过于频繁访问标志位
time.sleep(0.1)
# 短暂睡眠以降低CPU占用
time.sleep(0.1)
# 释放相机资源
camera1.release()
# camera2.release()
if __name__ == "__main__":
# 配置参数
output_directory1 = "C:/Users/25770/Desktop/photo1" # 更改为你的存储路径
output_directory2 = "C:/Users/25770/Desktop/photo2" # 更改为你的存储路径
#
#
# # 索引相机
# # ch:初始化SDK | en: initialize SDK
# MvCamera.MV_CC_Initialize()
#
# deviceList = MV_CC_DEVICE_INFO_LIST()
# tlayerType = MV_GIGE_DEVICE | MV_USB_DEVICE
# 创建并启动线程
timer1 = threading.Thread(target=timer1_thread, daemon=True)
photo_processor = threading.Thread(target=photo_task, args=(output_directory1, output_directory2), daemon=True)
timer1.start()
photo_processor.start()
try:
# 主线程保持运行
while True:
time.sleep(1)
except KeyboardInterrupt:
print("程序被用户中断")
stop_flag = True
timer1.join()
photo_processor.join()
print("程序已退出")
运行后报错:
gige device: [0]
device user define name:
device model name: MV-CS050-10GC
current ip: 169.254.199.38
open device successfully!
finish import the camera properties from the file
[13:56:48] 触发拍照标志
开始捕获图像...
捕获图像异常: 'NoneType' object has no attribute 'MV_CC_GetOneFrameTimeout'
捕获图像失败
最新发布