343 lines
9.9 KiB
Python
343 lines
9.9 KiB
Python
# -*- coding: utf-8 -*-
|
||
import sys
|
||
import ctypes
|
||
import threading
|
||
import time
|
||
import os
|
||
|
||
# 添加SDK路径
|
||
sys.path.append('Python/MvImport')
|
||
from MvCameraControl_class import *
|
||
from MvErrorDefine_const import *
|
||
from CameraParams_header import *
|
||
import cv2
|
||
import numpy as np
|
||
|
||
# 初始化全局变量
|
||
global deviceList
|
||
deviceList = MV_CC_DEVICE_INFO_LIST()
|
||
global cam
|
||
cam = MvCamera()
|
||
global nSelCamIndex
|
||
nSelCamIndex = 0
|
||
global isOpen
|
||
isOpen = False
|
||
global isGrabbing
|
||
isGrabbing = False
|
||
global bExit
|
||
bExit = False
|
||
|
||
# 将返回的错误码转换为十六进制显示
|
||
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
|
||
|
||
# Decoding Characters
|
||
def decoding_char(ctypes_char_array):
|
||
"""
|
||
安全地从 ctypes 字符数组中解码出字符串。
|
||
适用于 Python 2.x 和 3.x,以及 32/64 位环境。
|
||
"""
|
||
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():
|
||
global deviceList
|
||
|
||
deviceList = MV_CC_DEVICE_INFO_LIST()
|
||
n_layer_type = (MV_GIGE_DEVICE | MV_USB_DEVICE)
|
||
ret = MvCamera.MV_CC_EnumDevices(n_layer_type, deviceList)
|
||
if ret != 0:
|
||
print(f"枚举设备失败! ret = :{ToHexStr(ret)}")
|
||
return ret
|
||
|
||
if deviceList.nDeviceNum == 0:
|
||
print("未找到设备")
|
||
return ret
|
||
print(f"找到 {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:
|
||
print(f"\ngige device: [{i}]")
|
||
user_defined_name = decoding_char(mvcc_dev_info.SpecialInfo.stGigEInfo.chUserDefinedName)
|
||
model_name = decoding_char(mvcc_dev_info.SpecialInfo.stGigEInfo.chModelName)
|
||
print(f"device user define name: {user_defined_name}")
|
||
print(f"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(f"current ip: {nip1}.{nip2}.{nip3}.{nip4} ")
|
||
devList.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 = decoding_char(mvcc_dev_info.SpecialInfo.stUsb3VInfo.chUserDefinedName)
|
||
model_name = decoding_char(mvcc_dev_info.SpecialInfo.stUsb3VInfo.chModelName)
|
||
print(f"device user define name: {user_defined_name}")
|
||
print(f"device model name: {model_name}")
|
||
|
||
strSerialNumber = ""
|
||
for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chSerialNumber:
|
||
if per == 0:
|
||
break
|
||
strSerialNumber = strSerialNumber + chr(per)
|
||
print(f"user serial number: {strSerialNumber}")
|
||
devList.append(f"[{i}]USB: {user_defined_name} {model_name}({strSerialNumber})")
|
||
|
||
# 打印设备列表
|
||
print("\n设备列表:")
|
||
for i, dev in enumerate(devList):
|
||
print(f"{i}: {dev}")
|
||
|
||
return deviceList.nDeviceNum
|
||
|
||
# 打开相机
|
||
def open_device(nIndex):
|
||
global cam
|
||
global deviceList
|
||
global isOpen
|
||
|
||
if isOpen:
|
||
print("相机已经打开!")
|
||
return MV_E_CALLORDER
|
||
|
||
if nIndex < 0 or nIndex >= deviceList.nDeviceNum:
|
||
print("请选择有效的相机索引!")
|
||
return MV_E_CALLORDER
|
||
|
||
# 选择设备并创建句柄
|
||
stDeviceList = cast(deviceList.pDeviceInfo[nIndex], POINTER(MV_CC_DEVICE_INFO)).contents
|
||
cam = MvCamera()
|
||
ret = cam.MV_CC_CreateHandle(stDeviceList)
|
||
if ret != 0:
|
||
print(f"创建设备句柄失败! ret = {ToHexStr(ret)}")
|
||
cam.MV_CC_DestroyHandle()
|
||
return ret
|
||
|
||
# 打开设备
|
||
ret = cam.MV_CC_OpenDevice()
|
||
if ret != 0:
|
||
print(f"打开设备失败! ret = {ToHexStr(ret)}")
|
||
cam.MV_CC_DestroyHandle()
|
||
return ret
|
||
|
||
print("设备打开成功!")
|
||
isOpen = True
|
||
|
||
# 设置触发模式为off
|
||
ret = cam.MV_CC_SetEnumValue("TriggerMode", MV_TRIGGER_MODE_OFF)
|
||
if ret != 0:
|
||
print(f"设置触发模式失败! ret = {ToHexStr(ret)}")
|
||
|
||
# 设置像素格式为 BGR8
|
||
ret = cam.MV_CC_SetEnumValue("PixelFormat", PixelType_Gvsp_BGR8_Packed)
|
||
if ret != 0:
|
||
print(f"设置像素格式失败! ret = {ToHexStr(ret)}")
|
||
|
||
# 设置连续采集模式
|
||
ret = cam.MV_CC_SetEnumValue("AcquisitionMode", 2) # Continuous
|
||
if ret != 0:
|
||
print(f"设置连续采集模式失败! ret = {ToHexStr(ret)}")
|
||
|
||
return MV_OK
|
||
|
||
# 开始取流
|
||
def start_grabbing():
|
||
global cam
|
||
global isGrabbing
|
||
global bExit
|
||
|
||
if not isOpen:
|
||
print("相机未打开!")
|
||
return MV_E_CALLORDER
|
||
|
||
if isGrabbing:
|
||
print("已经开始取流!")
|
||
return MV_E_CALLORDER
|
||
|
||
bExit = False
|
||
ret = cam.MV_CC_StartGrabbing()
|
||
if ret != 0:
|
||
print(f"开始取流失败! ret = {ToHexStr(ret)}")
|
||
return ret
|
||
|
||
isGrabbing = True
|
||
print("开始取流成功!")
|
||
|
||
# 启动取图线程
|
||
global hThreadHandle
|
||
hThreadHandle = threading.Thread(target=work_thread)
|
||
hThreadHandle.daemon = True
|
||
hThreadHandle.start()
|
||
|
||
return MV_OK
|
||
|
||
# 停止取流
|
||
def stop_grabbing():
|
||
global cam
|
||
global isGrabbing
|
||
global bExit
|
||
|
||
if not isOpen:
|
||
print("相机未打开!")
|
||
return MV_E_CALLORDER
|
||
|
||
if not isGrabbing:
|
||
print("未开始取流!")
|
||
return MV_E_CALLORDER
|
||
|
||
bExit = True
|
||
time.sleep(0.1)
|
||
|
||
ret = cam.MV_CC_StopGrabbing()
|
||
if ret != 0:
|
||
print(f"停止取流失败! ret = {ToHexStr(ret)}")
|
||
return ret
|
||
|
||
isGrabbing = False
|
||
print("停止取流成功!")
|
||
|
||
return MV_OK
|
||
|
||
# 关闭相机
|
||
def close_device():
|
||
global cam
|
||
global isOpen
|
||
global isGrabbing
|
||
|
||
if isGrabbing:
|
||
stop_grabbing()
|
||
|
||
if isOpen:
|
||
ret = cam.MV_CC_CloseDevice()
|
||
if ret != 0:
|
||
print(f"关闭设备失败! ret = {ToHexStr(ret)}")
|
||
|
||
# 销毁句柄
|
||
cam.MV_CC_DestroyHandle()
|
||
isOpen = False
|
||
print("设备关闭成功!")
|
||
|
||
# 取图线程函数
|
||
def work_thread():
|
||
global cam
|
||
global bExit
|
||
|
||
stOutFrame = MV_FRAME_OUT()
|
||
memset(byref(stOutFrame), 0, sizeof(stOutFrame))
|
||
|
||
while not bExit:
|
||
# 获取图像
|
||
ret = cam.MV_CC_GetImageBuffer(stOutFrame, 1000)
|
||
|
||
if ret == 0:
|
||
# 打印图像信息
|
||
print(f"获取一帧图像: 宽度[{stOutFrame.stFrameInfo.nWidth}], 高度[{stOutFrame.stFrameInfo.nHeight}], 帧数[{stOutFrame.stFrameInfo.nFrameNum}]")
|
||
|
||
# 转换为 OpenCV 格式
|
||
try:
|
||
# 计算数据大小
|
||
data_size = stOutFrame.stFrameInfo.nWidth * stOutFrame.stFrameInfo.nHeight * 3
|
||
|
||
# 从缓冲区复制数据
|
||
frame_data = np.ctypeslib.as_array(stOutFrame.pBufAddr, shape=(data_size,))
|
||
frame_data = frame_data.reshape((stOutFrame.stFrameInfo.nHeight, stOutFrame.stFrameInfo.nWidth, 3))
|
||
|
||
# 显示图像
|
||
cv2.imshow("Camera", frame_data)
|
||
key = cv2.waitKey(1) & 0xFF
|
||
if key == ord('q'):
|
||
bExit = True
|
||
break
|
||
except Exception as e:
|
||
print(f"处理图像失败: {e}")
|
||
finally:
|
||
# 释放缓存
|
||
cam.MV_CC_FreeImageBuffer(stOutFrame)
|
||
else:
|
||
print(f"获取图像失败! ret = {ToHexStr(ret)}")
|
||
time.sleep(0.1)
|
||
|
||
cv2.destroyAllWindows()
|
||
|
||
# 主函数
|
||
def main():
|
||
print("海康相机验证程序")
|
||
print("按 Q 退出显示窗口")
|
||
print("按 Ctrl+C 退出程序")
|
||
|
||
# 初始化 SDK
|
||
MvCamera.MV_CC_Initialize()
|
||
|
||
try:
|
||
# 枚举设备
|
||
device_count = enum_devices()
|
||
if device_count == 0:
|
||
print("未找到设备,程序退出!")
|
||
return
|
||
|
||
# 选择设备
|
||
nIndex = int(input("请输入要打开的相机索引: "))
|
||
|
||
# 打开设备
|
||
ret = open_device(nIndex)
|
||
if ret != MV_OK:
|
||
print("打开设备失败,程序退出!")
|
||
return
|
||
|
||
# 开始取流
|
||
ret = start_grabbing()
|
||
if ret != MV_OK:
|
||
print("开始取流失败,程序退出!")
|
||
close_device()
|
||
return
|
||
|
||
# 等待用户操作
|
||
while True:
|
||
time.sleep(1)
|
||
if not isGrabbing:
|
||
break
|
||
|
||
except KeyboardInterrupt:
|
||
print("\n用户中断程序")
|
||
except Exception as e:
|
||
print(f"程序异常: {e}")
|
||
finally:
|
||
# 清理资源
|
||
if isOpen:
|
||
close_device()
|
||
|
||
# 反初始化 SDK
|
||
MvCamera.MV_CC_Finalize()
|
||
print("SDK反初始化成功")
|
||
print("程序退出")
|
||
|
||
if __name__ == "__main__":
|
||
main()
|