TractorVision初期移植

This commit is contained in:
CHAMPION923
2025-05-30 16:30:37 +08:00
commit 2acf920a87
36 changed files with 3898 additions and 0 deletions

294
lib/camera/ArenaCamera.py Normal file
View File

@@ -0,0 +1,294 @@
import ctypes
import time
import numpy as np
import cv2
# 定义错误代码
ARENA_ACQ_SUCCESS = 0
# 打印错误信息
def print_error(operation, error):
print(f"{operation} failed with error code: {error}")
# 重连机制函数
def retry_operation(operation_func, handle, *args, max_retries=1, delay=1):
"""
重试机制函数
:param operation_func: 要执行的操作函数
:param handle: 相机句柄
:param args: 操作函数的参数
:param max_retries: 最大重试次数
:param delay: 重试间隔时间(秒)
:return: 操作结果
"""
for attempt in range(max_retries + 1):
err = operation_func(handle, *args)
if err == ARENA_ACQ_SUCCESS:
return True
else:
print(f"Attempt {attempt + 1}/{max_retries} failed. Retrying in {delay} seconds...")
time.sleep(delay)
print_error(operation_func.__name__, err)
return False
class ArenaCamera:
# 静态变量存储共享库
libArenaAcq = ctypes.CDLL('libArenaAcq.so')
sys_handle = ctypes.c_void_p()
functions_initialized = False # Class-level flag
def __init__(self, sn):
self.sn = sn
self.handle = ctypes.c_void_p()
self.count = 0
# Initialize function argument types and return types if not already done
if not ArenaCamera.functions_initialized:
self._initialize_functions()
ArenaCamera.functions_initialized = True
@classmethod
def _initialize_functions(cls):
# 定义 ArenaAcq_Initialize
cls.libArenaAcq.ArenaAcq_Initialize.argtypes = [ctypes.POINTER(ctypes.c_void_p)]
cls.libArenaAcq.ArenaAcq_Initialize.restype = ctypes.c_int
# 定义 ArenaAcq_Connect
cls.libArenaAcq.ArenaAcq_Connect.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p),ctypes.c_char_p]
cls.libArenaAcq.ArenaAcq_Connect.restype = ctypes.c_int
# 定义 ArenaAcq_SetProperty
cls.libArenaAcq.ArenaAcq_SetProperty.argtypes = [
ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p
]
cls.libArenaAcq.ArenaAcq_SetProperty.restype = ctypes.c_int
# 定义 ArenaAcq_GetProperty
cls.libArenaAcq.ArenaAcq_GetProperty.argtypes = [
ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.POINTER(ctypes.c_size_t)
]
cls.libArenaAcq.ArenaAcq_GetProperty.restype = ctypes.c_int
# 定义 ArenaAcq_StartAcquisitionStream
cls.libArenaAcq.ArenaAcq_StartAcquisitionStream.argtypes = [ctypes.c_void_p]
cls.libArenaAcq.ArenaAcq_StartAcquisitionStream.restype = ctypes.c_int
# 定义 ArenaAcq_GetDepthImage
cls.libArenaAcq.ArenaAcq_GetDepthImage.argtypes = [
ctypes.c_void_p,
ctypes.POINTER(ctypes.POINTER(ctypes.c_uint16)),
ctypes.POINTER(ctypes.POINTER(ctypes.c_uint16)),
ctypes.POINTER(ctypes.POINTER(ctypes.c_uint16)),
ctypes.POINTER(ctypes.c_size_t),
ctypes.POINTER(ctypes.c_size_t),
ctypes.POINTER(ctypes.c_size_t),
ctypes.POINTER(ctypes.c_size_t)
]
cls.libArenaAcq.ArenaAcq_GetDepthImage.restype = ctypes.c_int
# 定义 ArenaAcq_GetImage
cls.libArenaAcq.ArenaAcq_GetImage.argtypes = [
ctypes.c_void_p,
ctypes.POINTER(ctypes.POINTER(ctypes.c_uint16)),
ctypes.POINTER(ctypes.c_size_t),
ctypes.POINTER(ctypes.c_size_t),
ctypes.POINTER(ctypes.c_size_t),
ctypes.POINTER(ctypes.c_size_t)
]
cls.libArenaAcq.ArenaAcq_GetImage.restype = ctypes.c_int
# 定义 ArenaAcq_ReleaseImage
cls.libArenaAcq.ArenaAcq_ReleaseImage.argtypes = [ctypes.POINTER(ctypes.c_uint8)]
cls.libArenaAcq.ArenaAcq_ReleaseImage.restype = ctypes.c_int
cls.libArenaAcq.ArenaAcq_ReleaseHandle.argtypes = [ctypes.POINTER(ctypes.c_void_p)]
cls.libArenaAcq.ArenaAcq_ReleaseHandle.restype = ctypes.c_int
cls.libArenaAcq.ArenaAcq_ReleaseDevice.argtypes = [ctypes.POINTER(ctypes.c_void_p)]
cls.libArenaAcq.ArenaAcq_ReleaseDevice.restype = ctypes.c_int
# 定义 ArenaAcq_StopAcquisitionStream
cls.libArenaAcq.ArenaAcq_StopAcquisitionStream.argtypes = [ctypes.c_void_p]
cls.libArenaAcq.ArenaAcq_StopAcquisitionStream.restype = ctypes.c_int
# 定义 ArenaAcq_Disconnect
cls.libArenaAcq.ArenaAcq_Disconnect.argtypes = [ctypes.c_void_p,ctypes.c_void_p]
cls.libArenaAcq.ArenaAcq_Disconnect.restype = ctypes.c_int
# 定义 ArenaAcq_Cleanup
cls.libArenaAcq.ArenaAcq_Cleanup.argtypes = [ctypes.c_void_p]
cls.libArenaAcq.ArenaAcq_Cleanup.restype = ctypes.c_int
cls.libArenaAcq.ArenaAcq_SaveDepthImageAsPGM.argtypes = [ctypes.POINTER(ctypes.c_uint16),ctypes.c_size_t,ctypes.c_size_t,ctypes.c_char_p]
cls.libArenaAcq.ArenaAcq_SaveDepthImageAsPGM.restype = ctypes.c_int
# 初始化库
if not retry_operation(cls.libArenaAcq.ArenaAcq_Initialize, ctypes.byref(cls.sys_handle)):
print(f"Initialization failed")
cls.libArenaAcq.ArenaAcq_Cleanup(cls.sys_handle)
cls.libArenaAcq.ArenaAcq_ReleaseHandle(cls.sys_handle)
return
print(f"Library initialized successfully")
@classmethod
def shutdown(cls):
cls.libArenaAcq.ArenaAcq_Cleanup(cls.sys_handle)
print(f"Library shutdown successfully")
def create(self):
# 连接到相机
if not retry_operation(self.libArenaAcq.ArenaAcq_Connect,self.sys_handle, self.handle, self.sn.encode('utf-8')):
print(f"Connection failed for camera {self.sn}")
self.libArenaAcq.ArenaAcq_Cleanup(self.handle)
return
print(f"Connected to camera {self.sn} successfully")
# 开始采集流
if not retry_operation(self.libArenaAcq.ArenaAcq_StartAcquisitionStream, self.handle):
print(f"Start acquisition failed for camera {self.sn}")
self.destroy()
return
print(f"Acquisition started successfully for camera {self.sn}")
def destroy(self):
# 停止采集流
if not retry_operation(self.libArenaAcq.ArenaAcq_StopAcquisitionStream, self.handle):
print(f"Stop acquisition failed for camera {self.sn}")
self.libArenaAcq.ArenaAcq_Disconnect(self.sys_handle,self.handle)
self.libArenaAcq.ArenaAcq_Cleanup(self.handle)
return
print(f"Acquisition stopped successfully for camera {self.sn}")
# 断开与相机的连接
if not retry_operation(self.libArenaAcq.ArenaAcq_Disconnect,self.sys_handle,self.handle):
print(f"Disconnection failed for camera {self.sn}")
self.libArenaAcq.ArenaAcq_Cleanup(self.handle)
return
print(f"Disconnected from camera {self.sn} successfully")
# 清理资源
if not retry_operation(self.libArenaAcq.ArenaAcq_Cleanup, self.handle):
print(f"Cleanup failed for camera {self.sn}")
return
def read(self):
image_data = ctypes.POINTER(ctypes.c_uint16)()
image_size = ctypes.c_size_t()
width = ctypes.c_size_t()
height = ctypes.c_size_t()
bits_per_pixel = ctypes.c_size_t()
if not retry_operation(self.libArenaAcq.ArenaAcq_GetImage, self.handle, ctypes.byref(image_data),
ctypes.byref(image_size), ctypes.byref(width), ctypes.byref(height),
ctypes.byref(bits_per_pixel)):
print(f"Image acquisition failed for camera {self.sn}")
return None
if image_size.value<=0:
print(f"Image size is 0 for camera {self.sn}")
return None
# 将图像数据转换为OpenCV格式
image_array = ctypes.cast(image_data, ctypes.POINTER(ctypes.c_uint16 * (width.value * height.value))).contents
image_np = np.array(image_array).reshape(height.value, width.value)
image_np = np.copy(image_np)
self.count=self.count+1
# 释放图像资源
self.libArenaAcq.ArenaAcq_ReleaseImage(ctypes.cast(image_data, ctypes.POINTER(ctypes.c_uint8)))
return image_np
def capture(self):
image_data = ctypes.POINTER(ctypes.c_uint16)()
x_image_data = ctypes.POINTER(ctypes.c_uint16)()
y_image_data = ctypes.POINTER(ctypes.c_uint16)()
image_size = ctypes.c_size_t()
width = ctypes.c_size_t()
height = ctypes.c_size_t()
bits_per_pixel = ctypes.c_size_t()
if not retry_operation(self.libArenaAcq.ArenaAcq_GetDepthImage, self.handle, ctypes.byref(image_data),
ctypes.byref(x_image_data), ctypes.byref(y_image_data),
ctypes.byref(image_size), ctypes.byref(width), ctypes.byref(height),
ctypes.byref(bits_per_pixel)):
print(f"Image acquisition failed for camera {self.sn}")
return None, None, None
if image_size.value<=0:
return None,None,None
# 将图像数据转换为OpenCV格式
depth_image_array = ctypes.cast(image_data, ctypes.POINTER(ctypes.c_uint16 * (width.value * height.value))).contents
x_image_array = ctypes.cast(x_image_data, ctypes.POINTER(ctypes.c_uint16 * (width.value * height.value))).contents
y_image_array = ctypes.cast(y_image_data, ctypes.POINTER(ctypes.c_uint16 * (width.value * height.value))).contents
depth_image_np = np.array(depth_image_array).reshape(height.value, width.value)
x_image_np = np.array(x_image_array).reshape(height.value, width.value)
y_image_np = np.array(y_image_array).reshape(height.value, width.value)
depth_image_np = np.copy(depth_image_np)
x_image_np = np.copy(x_image_np)
y_image_np = np.copy(y_image_np)
# print("copied.")
# self.libArenaAcq.ArenaAcq_SaveDepthImageAsPGM(image_data,width,height,f'dbg_{self.sn}_{self.count}'.encode('utf-8'))
self.count=self.count+1
# 释放图像资源
self.libArenaAcq.ArenaAcq_ReleaseImage(ctypes.cast(image_data, ctypes.POINTER(ctypes.c_uint8)))
self.libArenaAcq.ArenaAcq_ReleaseImage(ctypes.cast(x_image_data, ctypes.POINTER(ctypes.c_uint8)))
self.libArenaAcq.ArenaAcq_ReleaseImage(ctypes.cast(y_image_data, ctypes.POINTER(ctypes.c_uint8)))
return x_image_np, y_image_np, depth_image_np
if __name__ == '__main__':
cam2 = ArenaCamera("233801341") # Hypothetical second camera serial number
cam2.create()
cam1 = ArenaCamera("233800056")
cam1.create()
# cv2.namedWindow("Z1",cv2.WINDOW_AUTOSIZE)
# cv2.namedWindow("Z2",cv2.WINDOW_AUTOSIZE)
i=0
while i<20:
raw1 = cam1.read()
X2, Y2, Z2 = cam2.capture()
# X1, Y1, Z1 = cam1.capture()
# Display images for the first camera
# cv2.imshow("X1", X1)
# cv2.imshow("Y1", Y1)
# cv2.imshow("Z1", Z1)
# Display images for the second camera
# cv2.imshow("X2", X2)
# cv2.imshow("Y2", Y2)
# cv2.imshow("Z2", Z2)
# if cv2.waitKey(0) & 0xFF == ord('q'):
# break
# cv2.imwrite("z1.png",Z1)
cv2.imwrite("z2.png",Z2)
# time.sleep(3)
i=i+1
cv2.imwrite("raw1.png",raw1)
print(f'read {i}')
cv2.destroyAllWindows()
cam1.destroy()
cam2.destroy()
ArenaCamera.shutdown()

View File

@@ -0,0 +1,153 @@
import sys
import platform
from PySide6.QtWidgets import QApplication, QMainWindow, QLabel
from PySide6.QtCore import Qt
from PySide6.QtGui import QImage, QPixmap
from PySide6.QtCore import QTimer
import numpy as np
import ctypes
from lib.camera.mvsdk import *
from lib.camera import mvsdk
class MindVisionCamera:
def __init__(self, sn):
self.sn = sn # 相机序列号
self.hCamera = None # 相机句柄
self.FrameBufferSize = None # 帧缓冲区大小
self.pFrameBuffer = None # 帧缓冲区指针
self.monoCamera = None # 是否为单色相机标志
self.cap = None # 相机能力参数
# Initialize camera
self._initialize_camera()
def _initialize_camera(self):
# Enumerate cameras
DevList = CameraEnumerateDevice()
nDev = len(DevList)
if nDev < 1:
print("No camera was found!")
return
for i, DevInfo in enumerate(DevList):
# 检索指定sn相机
if DevInfo.GetSn() == self.sn:
print(f"Found camera with SN: {self.sn}")
break
else:
print(f"Camera with SN: {self.sn} not found!")
return
# Open camera
try:
self.hCamera = CameraInit(DevInfo, -1, -1)
except CameraException as e:
print("CameraInit Failed({}): {}".format(e.error_code, e.message))
return
# 获取相机参数
self.cap = CameraGetCapability(self.hCamera)
# 判断是否为单色相机
self.monoCamera = self.cap.sIspCapacity.bMonoSensor != 0
if self.monoCamera:
CameraSetIspOutFormat(self.hCamera, CAMERA_MEDIA_TYPE_MONO8)
CameraSetTriggerMode(self.hCamera, 0) # 设置触发模式为连续采集
CameraSetAeState(self.hCamera, 1) # 关闭自动曝光
# CameraSetExposureTime(self.hCamera, 30 * 1000) # 设置曝光时间为30ms
CameraPlay(self.hCamera) # 开始采集
# 分配帧缓冲区
self.FrameBufferSize = (
self.cap.sResolutionRange.iWidthMax
* self.cap.sResolutionRange.iHeightMax
* (1 if self.monoCamera else 3)
)
self.pFrameBuffer = CameraAlignMalloc(self.FrameBufferSize, 16)
def capture(self):
try:
# 从相机获取图像数据2000ms超时返回原始数据指针和帧头信息
pRawData, FrameHead = CameraGetImageBuffer(self.hCamera, 2000)
CameraImageProcess(self.hCamera, pRawData, self.pFrameBuffer, FrameHead)
# 反转
if platform.system() == "Windows":
mvsdk.CameraFlipFrameBuffer(self.pFrameBuffer, FrameHead, 1)
# 此时图片已经存储在pFrameBuffer中 对于彩色相机pFrameBuffer=RGB数据 黑白相机pFrameBuffer=8位灰度数据
# 将图像数据转换为OpenCV格式
image_array = (mvsdk.c_ubyte * FrameHead.uBytes).from_address(
self.pFrameBuffer
)
if self.monoCamera:
image_np = np.array(image_array).reshape(
FrameHead.iHeight, FrameHead.iWidth
)
else:
image_np = np.array(image_array).reshape(
FrameHead.iHeight, FrameHead.iWidth, 3
)
# Convert to OpenCV format
if FrameHead.uiMediaType == CAMERA_MEDIA_TYPE_MONO8:
image = np.frombuffer(image_np, dtype=np.uint8).reshape(
FrameHead.iHeight, FrameHead.iWidth
)
else:
image = np.frombuffer(image_np, dtype=np.uint8).reshape(
FrameHead.iHeight, FrameHead.iWidth, 3
)
CameraReleaseImageBuffer(self.hCamera, pRawData)
return image
except CameraException as e:
print("CameraGetImageBuffer failed({}): {}".format(e.error_code, e.message))
return None
def __del__(self):
if self.hCamera:
CameraUnInit(self.hCamera)
if self.pFrameBuffer:
CameraAlignFree(self.pFrameBuffer)
# class MainWindow(QMainWindow):
# def __init__(self, camera):
# super().__init__()
# self.setWindowTitle("MindVision Camera")
# self.setGeometry(100, 100, 800, 600)
# self.label = QLabel(self)
# self.label.setAlignment(Qt.AlignCenter)
# self.label.setScaledContents(True) # 自动缩放图像以适应标签大小
# self.camera = camera
# self.timer = QTimer(self)
# self.timer.timeout.connect(self.update_frame)
# self.timer.start(30) # Update frame every 30ms
# def closeEvent(self, event):
# self.timer.stop()
# self.camera.__del__()
# event.accept()
# def resizeEvent(self, event):
# super().resizeEvent(event)
# self.update_frame() # 窗口大小改变时更新图像
# def update_frame(self):
# image = self.camera.capture()
# if image is not None:
# if self.camera.monoCamera:
# qimage = QImage(image.data, image.shape[1], image.shape[0], QImage.Format_Grayscale8)
# else:
# qimage = QImage(image.data, image.shape[1], image.shape[0], QImage.Format_BGR888)
# pixmap = QPixmap.fromImage(qimage)
# self.label.setPixmap(pixmap)
# self.label.resize(self.size()) # 调整标签大小以适应窗口
# if __name__ == '__main__':
# app = QApplication(sys.argv)
# cam = MindVisionCamera("054042423002")
# window = MainWindow(cam)
# window.show()
# sys.exit(app.exec())

0
lib/camera/__init__.py Normal file
View File

2394
lib/camera/mvsdk.py Normal file

File diff suppressed because it is too large Load Diff