pre uc480 integration
This commit is contained in:
+66
-12
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user