hotfix/camera not connected error
This commit is contained in:
+49
-31
@@ -31,7 +31,7 @@ from .signal_processing import (
|
||||
)
|
||||
from .capture import (
|
||||
CaptureConfig,
|
||||
CaptureWorker,
|
||||
CaptureSource,
|
||||
ReconnectPolicy,
|
||||
is_live_camera,
|
||||
normalize_source,
|
||||
@@ -76,7 +76,8 @@ class Sensor:
|
||||
config_name="prototype", cal_path=None, override=False, box=None, mirror=True,
|
||||
backend='auto', capture_config: Optional[CaptureConfig] = None,
|
||||
auto_reconnect: bool = False, capture_mode: str = "latest",
|
||||
capture_queue_size: int = 1, capture_overflow: str = "drop_oldest"):
|
||||
capture_queue_size: int = 1, capture_overflow: str = "drop_oldest",
|
||||
**_deprecated_capture_kwargs):
|
||||
"""
|
||||
Initialize Sensor with video source and configuration.
|
||||
|
||||
@@ -96,9 +97,9 @@ class Sensor:
|
||||
"cpu": Use CPU for computation
|
||||
capture_config: Optional ``CaptureConfig`` overriding capture kwargs below.
|
||||
auto_reconnect: Enable USB disconnect auto-reconnect for live cameras.
|
||||
capture_mode: ``"latest"`` (low latency, default) or ``"queue"``.
|
||||
capture_queue_size: Bounded queue size for queue mode (1..10).
|
||||
capture_overflow: ``drop_oldest`` | ``drop_newest`` | ``block``.
|
||||
capture_mode: Deprecated; ignored (latest-frame capture only).
|
||||
capture_queue_size: Deprecated; ignored.
|
||||
capture_overflow: Deprecated; ignored.
|
||||
"""
|
||||
|
||||
self.CUPY_AVAILABLE = check_cupy_available()
|
||||
@@ -120,9 +121,6 @@ class Sensor:
|
||||
self.mirror = mirror
|
||||
self._capture_config_override = capture_config
|
||||
self._auto_reconnect = bool(auto_reconnect)
|
||||
self._capture_mode = capture_mode
|
||||
self._capture_queue_size = int(capture_queue_size)
|
||||
self._capture_overflow = capture_overflow
|
||||
|
||||
# Load configuration
|
||||
self.__config_cam = None
|
||||
@@ -231,7 +229,7 @@ class Sensor:
|
||||
# self._av_stream = None
|
||||
# self._av_decoder = None
|
||||
self.cap = None # compat alias for _capture_worker.source.cap
|
||||
self._capture_worker: Optional[CaptureWorker] = None
|
||||
self._capture_source: Optional[CaptureSource] = None
|
||||
self._is_live_camera = (
|
||||
is_live_camera(self.vid_src) and not self._external_only
|
||||
)
|
||||
@@ -705,9 +703,9 @@ class Sensor:
|
||||
|
||||
def disconnect(self):
|
||||
"""Release resources and stop async threads."""
|
||||
if self._capture_worker is not None:
|
||||
self._capture_worker.close()
|
||||
self._capture_worker = None
|
||||
if self._capture_source is not None:
|
||||
self._capture_source.close()
|
||||
self._capture_source = None
|
||||
self.cap = None
|
||||
if self.verbose:
|
||||
print("Sensor disconnected.")
|
||||
@@ -934,25 +932,23 @@ class Sensor:
|
||||
capture_config = CaptureConfig.from_camera_dict(
|
||||
self.vid_src,
|
||||
self.__config_cam,
|
||||
mode=self._capture_mode,
|
||||
queue_size=self._capture_queue_size,
|
||||
overflow=self._capture_overflow,
|
||||
reconnect=ReconnectPolicy(enabled=self._auto_reconnect),
|
||||
verbose=self.verbose,
|
||||
start_worker=False,
|
||||
background=False,
|
||||
)
|
||||
|
||||
try:
|
||||
self._capture_worker = open_capture(capture_config, start_worker=False)
|
||||
self._capture_source = open_capture(capture_config, start_background=False)
|
||||
except Exception as exc:
|
||||
raise Exception("Failed to open video source.") from exc
|
||||
|
||||
self.cap = self._capture_worker.source.cap
|
||||
# self._use_pyav = False
|
||||
self.cap = self._capture_source.cap
|
||||
|
||||
if not self._is_source_open():
|
||||
raise Exception("Failed to open video source.")
|
||||
|
||||
self._capture_source.warmup_frames(3)
|
||||
|
||||
# Wait for first valid frame
|
||||
frame = None
|
||||
while frame is None:
|
||||
@@ -967,16 +963,24 @@ class Sensor:
|
||||
if box is not None:
|
||||
self.stitcher.isstitch = self.isstitch = False
|
||||
self.stitcher.boxes = box
|
||||
self.stitch_valid = bool(self.isstitch)
|
||||
img = self.frame_processor.process(frame)
|
||||
self.initial_img = self._make_initial_gray(img)
|
||||
else:
|
||||
print("Override=True but no box provided.")
|
||||
else:
|
||||
self._find_stitch_boxes(frame)
|
||||
if not self.stitch_valid:
|
||||
raise RuntimeError(
|
||||
f"Failed to detect {self._config_units} vision tiles for stitching. "
|
||||
"Check camera index, lighting, and that MJPG 1080p is negotiated."
|
||||
)
|
||||
img = self.frame_processor.process(frame)
|
||||
self.initial_img = self._make_initial_gray(img)
|
||||
|
||||
# For live cameras, start a capture worker to avoid latency buildup.
|
||||
if self._is_live_camera and self._capture_worker is not None:
|
||||
self._capture_worker.start()
|
||||
# For live cameras, start background capture to avoid latency buildup.
|
||||
if self._is_live_camera and self._capture_source is not None:
|
||||
self._capture_source.start_background()
|
||||
|
||||
def _make_initial_gray(self, img_bgr):
|
||||
gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY)
|
||||
@@ -990,6 +994,9 @@ class Sensor:
|
||||
def _find_stitch_boxes(self, img_src, show=False):
|
||||
"""Detect vision tile boxes with camera stabilization."""
|
||||
n_boxes, v_mean0, timeout = 0, 0, 5
|
||||
boxes = []
|
||||
best_n_boxes = 0
|
||||
best_boxes = []
|
||||
start_time = cv2.getTickCount()
|
||||
|
||||
while n_boxes < self._config_units:
|
||||
@@ -1000,12 +1007,18 @@ class Sensor:
|
||||
|
||||
if abs(v_mean - v_mean0) < 5:
|
||||
n_boxes, boxes = self.stitcher.find_stitch_boxes(frame, show=show)
|
||||
if n_boxes > best_n_boxes:
|
||||
best_n_boxes = n_boxes
|
||||
best_boxes = boxes
|
||||
|
||||
if (cv2.getTickCount() - start_time) / cv2.getTickFrequency() > timeout:
|
||||
print("Timeout finding vision units.")
|
||||
self.stitcher.isstitch = self.isstitch = False
|
||||
break
|
||||
v_mean0 = v_mean
|
||||
|
||||
if n_boxes < self._config_units and best_n_boxes >= self._config_units:
|
||||
n_boxes = best_n_boxes
|
||||
boxes = best_boxes
|
||||
|
||||
self.n_boxes = n_boxes
|
||||
self.boxes = boxes
|
||||
@@ -1023,8 +1036,8 @@ class Sensor:
|
||||
# return True
|
||||
|
||||
def _is_source_open(self):
|
||||
if self._capture_worker is not None:
|
||||
return self._capture_worker.source.is_open()
|
||||
if self._capture_source is not None:
|
||||
return self._capture_source.is_open()
|
||||
return False
|
||||
# Legacy PyAV / direct-cap fallbacks (unused after orisys.capture refactor).
|
||||
# if self._use_pyav:
|
||||
@@ -1032,13 +1045,13 @@ class Sensor:
|
||||
# return self.cap is not None and self.cap.isOpened()
|
||||
|
||||
def _read_frame(self):
|
||||
if self._capture_worker is not None and self._capture_worker.running:
|
||||
return self._capture_worker.read(copy=True, wait_s=1.0)
|
||||
if self._capture_source is not None and self._capture_source.running:
|
||||
return self._capture_source.read_latest(copy=True, wait_s=1.0)
|
||||
return self._read_frame_raw()
|
||||
|
||||
def _read_frame_raw(self):
|
||||
if self._capture_worker is not None:
|
||||
return self._capture_worker.source.read_raw()
|
||||
if self._capture_source is not None:
|
||||
return self._capture_source.read_raw()
|
||||
return False, None
|
||||
# Legacy PyAV / direct-cap fallbacks (unused after orisys.capture refactor).
|
||||
# if self._use_pyav:
|
||||
@@ -1058,9 +1071,14 @@ class Sensor:
|
||||
@property
|
||||
def capture_status(self):
|
||||
"""Return capture health snapshot, or None in external-frame mode."""
|
||||
if self._capture_worker is None:
|
||||
if self._capture_source is None:
|
||||
return None
|
||||
return self._capture_worker.source.status
|
||||
return self._capture_source.status
|
||||
|
||||
@property
|
||||
def _capture_worker(self):
|
||||
"""Deprecated alias for ``_capture_source``."""
|
||||
return self._capture_source
|
||||
|
||||
|
||||
# =========================================================================
|
||||
|
||||
Reference in New Issue
Block a user