pre uc480 integration

This commit is contained in:
Thomas Ales [M S E]
2026-05-22 09:38:39 -05:00
parent 43e7b99512
commit a0e0151b5d
30 changed files with 12557 additions and 41 deletions
+66 -12
View File
@@ -4,11 +4,14 @@ Driver for IDS/Thorlabs uEye uC480 cameras using pyueye library.
Provides camera control, live streaming, and image capture capabilities.
"""
import time
import numpy as np
from pyueye import ueye
from PyQt6.QtCore import QThread, pyqtSignal, QObject
from PyQt6.QtGui import QImage
import logging
import threading
from contextlib import contextmanager
from typing import Optional, Tuple
logger = logging.getLogger(__name__)
@@ -24,17 +27,21 @@ class UC480Camera(QObject):
frame_ready = pyqtSignal(QImage) # Emitted when a new frame is captured
error_occurred = pyqtSignal(str) # Emitted when an error occurs
def __init__(self, camera_id: int = 0):
def __init__(self, camera_id: int = 1):
"""
Initialize the uC480 camera driver.
Args:
camera_id: Camera ID (0 for first available camera)
camera_id: Camera ID (1-based; use is_GetCameraList to find IDs)
"""
super().__init__()
self.camera_id = camera_id
self.h_cam = ueye.HIDS(camera_id)
# IS_ALLOW_STARTER_FW_UPLOAD (0x10000): instructs the SDK to block
# inside is_InitCamera until firmware upload and USB re-enumeration
# finish. Without this flag, the handle becomes invalid the moment
# the device reconnects and the SDK segfaults on the very next call.
self.h_cam = ueye.HIDS(camera_id | 0x10000)
self.is_initialized = False
self.is_capturing = False
@@ -55,6 +62,9 @@ class UC480Camera(QObject):
self.bytes_per_pixel = 3
self.color_mode = ueye.IS_CM_BGR8_PACKED
# Lock to serialize parameter changes that require stopping live video
self._settings_lock = threading.Lock()
def initialize(self) -> bool:
"""
Initialize the camera and allocate memory.
@@ -63,12 +73,23 @@ class UC480Camera(QObject):
True if successful, False otherwise
"""
try:
# Initialize camera
ret = ueye.is_InitCamera(self.h_cam, None)
if ret != ueye.IS_SUCCESS:
logger.error(f"Failed to initialize camera: {ret}")
self.error_occurred.emit(f"Failed to initialize camera: {ret}")
return False
# Initialize camera. After is_ExitCamera the UI124x series
# resets and re-enumerates on USB (firmware reload), so retry
# for up to ~10 s if IS_CANT_OPEN_DEVICE is returned.
for attempt in range(20):
ret = ueye.is_InitCamera(self.h_cam, None)
if ret == ueye.IS_SUCCESS:
break
if ret == ueye.IS_CANT_OPEN_DEVICE and attempt < 19:
logger.debug(f"Camera not ready (IS_CANT_OPEN_DEVICE), retrying ({attempt+1}/20)…")
time.sleep(0.5)
# h_cam value is consumed by a failed init on some SDK
# versions; recreate it to avoid IS_INVALID_CAMERA_HANDLE
self.h_cam = ueye.HIDS(self.camera_id | 0x10000)
else:
logger.error(f"Failed to initialize camera: {ret}")
self.error_occurred.emit(f"Failed to initialize camera: {ret}")
return False
# Get sensor info
ret = ueye.is_GetSensorInfo(self.h_cam, self.sensor_info)
@@ -161,9 +182,12 @@ class UC480Camera(QObject):
self.mem_ptr = None
if self.is_initialized:
ueye.is_ExitCamera(self.h_cam)
ret = ueye.is_ExitCamera(self.h_cam)
self.is_initialized = False
logger.info("Camera resources released")
if ret == ueye.IS_SUCCESS:
logger.info("Camera resources released")
else:
logger.error(f"is_ExitCamera failed: {ret} — camera handle may still be held by daemon")
def start_capture(self) -> bool:
"""
@@ -201,14 +225,37 @@ class UC480Camera(QObject):
return True
ret = ueye.is_StopLiveVideo(self.h_cam, ueye.IS_WAIT)
self.is_capturing = False # Always reset, even if the call fails
if ret != ueye.IS_SUCCESS:
logger.error(f"Failed to stop capture: {ret}")
return False
self.is_capturing = False
logger.info("Video capture stopped")
return True
@contextmanager
def _capture_paused(self):
"""
Context manager that temporarily stops live video while a camera
parameter is being changed, then restarts it. Many IDS cameras
return IS_CANT_COMMUNICATE_WITH_DRIVER (17) or IS_NO_SUCCESS (-1)
when gain/exposure commands are issued during active capture.
"""
with self._settings_lock:
was_capturing = self.is_capturing
if was_capturing:
ueye.is_StopLiveVideo(self.h_cam, ueye.IS_WAIT)
self.is_capturing = False
try:
yield
finally:
if was_capturing:
ret = ueye.is_CaptureVideo(self.h_cam, ueye.IS_DONT_WAIT)
if ret == ueye.IS_SUCCESS:
self.is_capturing = True
else:
logger.error(f"Failed to restart capture after settings change: {ret}")
def get_frame(self) -> Optional[QImage]:
"""
Capture a single frame from the camera.
@@ -405,6 +452,13 @@ class UC480Camera(QObject):
if ret == ueye.IS_SUCCESS:
logger.debug(f"Master gain set to {master_gain}")
return True
elif ret == ueye.IS_CANT_COMMUNICATE_WITH_DRIVER:
logger.error(
f"Hardware gain not supported by this camera model "
f"(IS_CANT_COMMUNICATE_WITH_DRIVER). "
f"Consider using gain boost instead."
)
return False
else:
logger.error(f"Failed to set gain: {ret}")
return False