fix: acquired frames own their arrays, not views on SDK buffers
Since the acquisition thread, frames outlive the grab (the latest frame, the 100 ms cache, the 4 shared encodings). rgb/depth were views on the SDK frame memory (np.asanyarray of get_data), so each kept an SDK or rs.align pool frame busy; on the Jetson an exhausted pool shows as wait_for_frames timeouts. _grab_once now copies them (_owned: only arrays that are views; the YUYV decode, USB 2 resize and scaled depth already own theirs). New phase "copy" in the timings, to see its cost on PC2. capture() docstring: the frame is the next one COMPLETED after the call; a grab already in progress may have started before it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2e80447264
commit
55e0a991b4
1 file changed
+14
-4
@@ -47,6 +47,11 @@ class CameraFrame:
|
||||
timestamp: float
|
||||
|
||||
|
||||
def _owned(array: np.ndarray) -> np.ndarray:
|
||||
"""`array` if it owns its memory, else a copy (a view on an SDK buffer has a `base`)."""
|
||||
return array if array.base is None else array.copy()
|
||||
|
||||
|
||||
@dataclass
|
||||
class CameraIntrinsics:
|
||||
"""Per-sensor intrinsics. `*_ir` = depth, `*_rgb` = color."""
|
||||
@@ -389,10 +394,11 @@ class RealSenseD435:
|
||||
# Frame capture
|
||||
# ------------------------------------------------------------------
|
||||
def capture(self, timeout_ms: int = 5000) -> Optional[CameraFrame]:
|
||||
"""The next frame acquired after this call (None on timeout or when closed).
|
||||
"""The next frame COMPLETED after this call (None on timeout or when closed).
|
||||
|
||||
Does not touch the pipeline: it waits for the acquisition thread.
|
||||
Requests waiting at the same moment get the same frame.
|
||||
A grab already in progress when the call arrives may have started
|
||||
before it. Does not touch the pipeline: it waits for the acquisition
|
||||
thread. Requests waiting at the same moment get the same frame.
|
||||
"""
|
||||
acquisition = self._acquisition
|
||||
if not self._open or acquisition is None:
|
||||
@@ -423,7 +429,11 @@ class RealSenseD435:
|
||||
return None
|
||||
|
||||
with timer.phase("convert"):
|
||||
return self._frame_from(color_frame, depth_frame)
|
||||
frame = self._frame_from(color_frame, depth_frame)
|
||||
# Own copies: frames now live on after the grab (latest frame, 100 ms cache,
|
||||
# shared encodings), and views would keep the SDK's frame pool busy.
|
||||
with timer.phase("copy"):
|
||||
return CameraFrame(rgb=_owned(frame.rgb), depth=_owned(frame.depth), timestamp=frame.timestamp)
|
||||
|
||||
def _frame_from(self, color_frame, depth_frame) -> CameraFrame:
|
||||
"""numpy BGR + depth in mm from the SDK frames (YUYV decode, USB 2 resize, depth scale)."""
|
||||
|
||||
Reference in new issue
Block a user