From bd32ecc2071de32537bd4abe9fe02414d8475dd1 Mon Sep 17 00:00:00 2001 From: Nikolay Tsoy Date: Sun, 25 Jun 2023 13:08:12 +0200 Subject: [PATCH 1/4] Make .create_camera just .camera (stereo, all_cameras) --- depthai_sdk/src/depthai_sdk/oak_camera.py | 113 +++++++++++++++++----- 1 file changed, 90 insertions(+), 23 deletions(-) diff --git a/depthai_sdk/src/depthai_sdk/oak_camera.py b/depthai_sdk/src/depthai_sdk/oak_camera.py index a701d194f..785e369cb 100644 --- a/depthai_sdk/src/depthai_sdk/oak_camera.py +++ b/depthai_sdk/src/depthai_sdk/oak_camera.py @@ -109,16 +109,16 @@ def __init__(self, self.replay = Replay(replay) self.replay.initPipeline(self.pipeline) logging.info(f'Available streams from recording: {self.replay.getStreams()}') - - def create_camera(self, - source: Union[str, dai.CameraBoardSocket], - resolution: Optional[Union[ - str, dai.ColorCameraProperties.SensorResolution, dai.MonoCameraProperties.SensorResolution - ]] = None, - fps: Optional[float] = None, - encode: Union[None, str, bool, dai.VideoEncoderProperties.Profile] = None, - name: Optional[str] = None, - ) -> CameraComponent: + + def camera(self, + source: Union[str, dai.CameraBoardSocket], + resolution: Optional[Union[ + str, dai.ColorCameraProperties.SensorResolution, dai.MonoCameraProperties.SensorResolution + ]] = None, + fps: Optional[float] = None, + encode: Union[None, str, bool, dai.VideoEncoderProperties.Profile] = None, + name: Optional[str] = None, + ) -> CameraComponent: """ Creates Camera component. This abstracts ColorCamera/MonoCamera nodes and supports mocking the camera when recording is passed during OakCamera initialization. Mocking the camera will send frames from the host to the @@ -144,13 +144,38 @@ def create_camera(self, self._components.append(comp) return comp - def create_all_cameras(self, - resolution: Optional[Union[ + def create_camera(self, + source: Union[str, dai.CameraBoardSocket], + resolution: Optional[Union[ str, dai.ColorCameraProperties.SensorResolution, dai.MonoCameraProperties.SensorResolution ]] = None, - fps: Optional[float] = None, - encode: Union[None, str, bool, dai.VideoEncoderProperties.Profile] = None, - ) -> List[CameraComponent]: + fps: Optional[float] = None, + encode: Union[None, str, bool, dai.VideoEncoderProperties.Profile] = None, + name: Optional[str] = None, + ) -> CameraComponent: + """ + Deprecated, use camera() instead. + + Creates Camera component. This abstracts ColorCamera/MonoCamera nodes and supports mocking the camera when + recording is passed during OakCamera initialization. Mocking the camera will send frames from the host to the + OAK device (via XLinkIn node). + + Args: + source (str / dai.CameraBoardSocket): Camera source + resolution (str/SensorResolution): Sensor resolution of the camera. + fps (float): Sensor FPS + encode (bool/str/Profile): Whether we want to enable video encoding (accessible via cameraComponent.out_encoded). If True, it will use MJPEG + name (str): Name used to identify the X-out stream. This name will also be associated with the frame in the callback function. + """ + return self.camera(source, resolution, fps, encode, name) + + def all_cameras(self, + resolution: Optional[Union[ + str, dai.ColorCameraProperties.SensorResolution, dai.MonoCameraProperties.SensorResolution + ]] = None, + fps: Optional[float] = None, + encode: Union[None, str, bool, dai.VideoEncoderProperties.Profile] = None, + ) -> List[CameraComponent]: """ Creates Camera component for each camera sensors on the OAK camera. @@ -177,6 +202,25 @@ def create_all_cameras(self, self._components.extend(components) return components + def create_all_cameras(self, + resolution: Optional[Union[ + str, dai.ColorCameraProperties.SensorResolution, + dai.MonoCameraProperties.SensorResolution + ]] = None, + fps: Optional[float] = None, + encode: Union[None, str, bool, dai.VideoEncoderProperties.Profile] = None, + ) -> List[CameraComponent]: + """ + Deprecated, use all_cameras() instead. + + Creates Camera component for each camera sensors on the OAK camera. + + Args: + resolution (str/SensorResolution): Sensor resolution of the camera. + fps (float): Sensor FPS + encode (bool/str/Profile): Whether we want to enable video encoding (accessible via cameraComponent.out_encoded). If True, it will use MJPEG + """ + return self.all_cameras(resolution, fps, encode) def create_nn(self, model: Union[str, Dict, Path], @@ -213,14 +257,14 @@ def create_nn(self, self._components.append(comp) return comp - def create_stereo(self, - resolution: Union[None, str, dai.MonoCameraProperties.SensorResolution] = None, - fps: Optional[float] = None, - left: Union[None, dai.Node.Output, CameraComponent] = None, # Left mono camera - right: Union[None, dai.Node.Output, CameraComponent] = None, # Right mono camera - name: Optional[str] = None, - encode: Union[None, str, bool, dai.VideoEncoderProperties.Profile] = None - ) -> StereoComponent: + def stereo(self, + resolution: Union[None, str, dai.MonoCameraProperties.SensorResolution] = None, + fps: Optional[float] = None, + left: Union[None, dai.Node.Output, CameraComponent] = None, # Left mono camera + right: Union[None, dai.Node.Output, CameraComponent] = None, # Right mono camera + name: Optional[str] = None, + encode: Union[None, str, bool, dai.VideoEncoderProperties.Profile] = None + ) -> StereoComponent: """ Create Stereo camera component. If left/right cameras/component aren't specified they will get created internally. @@ -245,6 +289,29 @@ def create_stereo(self, self._components.append(comp) return comp + def create_stereo(self, + resolution: Union[None, str, dai.MonoCameraProperties.SensorResolution] = None, + fps: Optional[float] = None, + left: Union[None, dai.Node.Output, CameraComponent] = None, # Left mono camera + right: Union[None, dai.Node.Output, CameraComponent] = None, # Right mono camera + name: Optional[str] = None, + encode: Union[None, str, bool, dai.VideoEncoderProperties.Profile] = None + ) -> StereoComponent: + """ + Deprecated, use stereo() instead. + + Create Stereo camera component. If left/right cameras/component aren't specified they will get created internally. + + Args: + resolution (str/SensorResolution): If monochrome cameras aren't already passed, create them and set specified resolution + fps (float): If monochrome cameras aren't already passed, create them and set specified FPS + left (CameraComponent/dai.node.MonoCamera): Pass the camera object (component/node) that will be used for stereo camera. + right (CameraComponent/dai.node.MonoCamera): Pass the camera object (component/node) that will be used for stereo camera. + name (str): Name used to identify the X-out stream. This name will also be associated with the frame in the callback function. + encode (bool/str/Profile): Whether we want to enable video encoding (accessible via StereoComponent.out.encoded). If True, it will use h264 codec. + """ + return self.stereo(resolution, fps, left, right, name, encode) + def create_imu(self) -> IMUComponent: """ Create IMU component From 1e4895f4f64e6b5e83808a27a8e7068970f3c663 Mon Sep 17 00:00:00 2001 From: Nikolay Tsoy Date: Sun, 25 Jun 2023 14:21:47 +0200 Subject: [PATCH 2/4] Pick first color sensor when source is color or rgb --- depthai_sdk/src/depthai_sdk/components/camera_component.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/depthai_sdk/src/depthai_sdk/components/camera_component.py b/depthai_sdk/src/depthai_sdk/components/camera_component.py index 4122c9375..d304bfcae 100644 --- a/depthai_sdk/src/depthai_sdk/components/camera_component.py +++ b/depthai_sdk/src/depthai_sdk/components/camera_component.py @@ -106,6 +106,13 @@ def __init__(self, "Please specify sensor type with c/color or m/mono after the ','" " - eg. `cam = oak.create_camera('cama,c')`" ) + elif source in ["COLOR", "RGB"]: + for features in device.getConnectedCameraFeatures(): + if dai.CameraSensorType.COLOR in features.supportedTypes: + source = features.socket + break + if not isinstance(source, dai.CameraBoardSocket): + raise ValueError("Couldn't find a color camera!") socket = parse_camera_socket(source) sensor = [f for f in device.getConnectedCameraFeatures() if f.socket == socket][0] From ace35d91df249da2654c27fa47194e035f1049a1 Mon Sep 17 00:00:00 2001 From: Nikolay Tsoy Date: Sun, 25 Jun 2023 15:02:31 +0200 Subject: [PATCH 3/4] Return existing CameraComponent when same socket is specified --- depthai_sdk/src/depthai_sdk/oak_camera.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/depthai_sdk/src/depthai_sdk/oak_camera.py b/depthai_sdk/src/depthai_sdk/oak_camera.py index 785e369cb..c40769aa4 100644 --- a/depthai_sdk/src/depthai_sdk/oak_camera.py +++ b/depthai_sdk/src/depthai_sdk/oak_camera.py @@ -22,7 +22,7 @@ from depthai_sdk.components.component import Component from depthai_sdk.components.imu_component import IMUComponent from depthai_sdk.components.nn_component import NNComponent -from depthai_sdk.components.parser import parse_usb_speed +from depthai_sdk.components.parser import parse_usb_speed, parse_camera_socket from depthai_sdk.components.stereo_component import StereoComponent from depthai_sdk.components.pointcloud_component import PointcloudComponent from depthai_sdk.oak_device import OakDevice @@ -131,6 +131,13 @@ def camera(self, encode (bool/str/Profile): Whether we want to enable video encoding (accessible via cameraComponent.out_encoded). If True, it will use MJPEG name (str): Name used to identify the X-out stream. This name will also be associated with the frame in the callback function. """ + socket = source + if isinstance(source, str): + socket = parse_camera_socket(source.split(",")[0]) + for comp in self._components: + if isinstance(comp, CameraComponent) and comp.node.getBoardSocket() == socket: + return comp + comp = CameraComponent(self._oak.device, self.pipeline, source=source, From be14f7a53e42ebf275bb7c0b38a4e9f78378115b Mon Sep 17 00:00:00 2001 From: Nikolay Tsoy Date: Sun, 25 Jun 2023 15:12:05 +0200 Subject: [PATCH 4/4] Reuse left/right components in stereo --- depthai_sdk/src/depthai_sdk/components/stereo_component.py | 2 +- depthai_sdk/src/depthai_sdk/oak_camera.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/depthai_sdk/src/depthai_sdk/components/stereo_component.py b/depthai_sdk/src/depthai_sdk/components/stereo_component.py index 9f6f7608d..7367c34b5 100644 --- a/depthai_sdk/src/depthai_sdk/components/stereo_component.py +++ b/depthai_sdk/src/depthai_sdk/components/stereo_component.py @@ -127,7 +127,7 @@ def __init__(self, if self._device.getDeviceName() == 'OAK-D-LR': self._resolution = dai.MonoCameraProperties.SensorResolution.THE_1200_P - if not self.left: + if not self.left: # Should never happen self.left = CameraComponent(device, pipeline, 'left', self._resolution, self._fps, replay=self._replay) if not self.right: self.right = CameraComponent(device, pipeline, 'right', self._resolution, self._fps, diff --git a/depthai_sdk/src/depthai_sdk/oak_camera.py b/depthai_sdk/src/depthai_sdk/oak_camera.py index c40769aa4..562f3aebc 100644 --- a/depthai_sdk/src/depthai_sdk/oak_camera.py +++ b/depthai_sdk/src/depthai_sdk/oak_camera.py @@ -283,6 +283,11 @@ def stereo(self, name (str): Name used to identify the X-out stream. This name will also be associated with the frame in the callback function. encode (bool/str/Profile): Whether we want to enable video encoding (accessible via StereoComponent.out.encoded). If True, it will use h264 codec. """ + if left is None: + left = self.camera(source=dai.CameraBoardSocket.LEFT, resolution=resolution, fps=fps) + if right is None: + right = self.camera(source=dai.CameraBoardSocket.RIGHT, resolution=resolution, fps=fps) + comp = StereoComponent(self._oak.device, self.pipeline, resolution=resolution,