From acd0c144f5e97d2accfe1830a6806da618beb74d Mon Sep 17 00:00:00 2001 From: cc Date: Mon, 19 Oct 2026 21:50:28 -0700 Subject: [PATCH 1/2] refactor: migrate ZED producer values and frame conversions to CDR --- .github/workflows/message-codegen.yml | 5 + dimos/hardware/sensors/camera/zed/camera.py | 201 +++++++----------- dimos/hardware/sensors/camera/zed/compat.py | 2 +- dimos/hardware/sensors/camera/zed/messages.py | 112 ++++++++++ .../sensors/camera/zed/test_messages.py | 128 +++++++++++ 5 files changed, 319 insertions(+), 129 deletions(-) create mode 100644 dimos/hardware/sensors/camera/zed/messages.py create mode 100644 dimos/hardware/sensors/camera/zed/test_messages.py diff --git a/.github/workflows/message-codegen.yml b/.github/workflows/message-codegen.yml index a50d298ec43..11ad11f84fa 100644 --- a/.github/workflows/message-codegen.yml +++ b/.github/workflows/message-codegen.yml @@ -202,3 +202,8 @@ jobs: run: | .native-review/bin/python -I -c 'import dimos; import pytest; import sys; raise SystemExit(pytest.main(sys.argv[1:]))' --noconftest --import-mode=importlib -o addopts='' -q "$PWD/dimos/perception/experimental/temporal_memory/test_temporal_memory_module.py" "$PWD/dimos/perception/experimental/test_perceive_loop_skill.py" "$PWD/dimos/visualization/rerun/test_cdr.py" MYPYPATH="$PWD/build/message-source/python" .native-review/bin/python -m mypy --follow-imports=silent dimos/perception/experimental/perceive_loop_skill.py dimos/perception/experimental/temporal_memory/clip_filter.py dimos/perception/experimental/temporal_memory/entity_graph_db.py dimos/perception/experimental/temporal_memory/frame_window_accumulator.py dimos/perception/experimental/temporal_memory/temporal_memory.py dimos/perception/experimental/temporal_memory/temporal_utils/helpers.py dimos/perception/experimental/temporal_memory/window_analyzer.py dimos/visualization/rerun/cdr.py + - name: Verify generated ZED frames without the proprietary SDK + if: runner.os == 'Linux' + run: | + .native-review/bin/python -I -c 'import dimos; import pytest; import sys; raise SystemExit(pytest.main(sys.argv[1:]))' --noconftest --import-mode=importlib -o addopts='' -q "$PWD/dimos/hardware/sensors/camera/zed/test_messages.py" + MYPYPATH="$PWD/build/message-source/python" .native-review/bin/python -m mypy --follow-imports=silent dimos/hardware/sensors/camera/zed/camera.py dimos/hardware/sensors/camera/zed/compat.py dimos/hardware/sensors/camera/zed/messages.py diff --git a/dimos/hardware/sensors/camera/zed/camera.py b/dimos/hardware/sensors/camera/zed/camera.py index fd777339dfc..6308b700503 100644 --- a/dimos/hardware/sensors/camera/zed/camera.py +++ b/dimos/hardware/sensors/camera/zed/camera.py @@ -18,8 +18,9 @@ import threading import time -from pydantic import Field -import pyzed.sl as sl +import numpy as np +from pydantic import Field, InstanceOf +from pyzed import sl import reactivex as rx from dimos.constants import DEFAULT_THREAD_JOIN_TIMEOUT @@ -29,17 +30,23 @@ from dimos.core.stream import Out from dimos.core.transport_factory import make_transport from dimos.hardware.sensors.camera.spec import ( - OPTICAL_ROTATION, DepthCameraConfig, DepthCameraHardware, ) -from dimos.msgs.geometry_msgs.Quaternion import Quaternion -from dimos.msgs.geometry_msgs.Transform import Transform -from dimos.msgs.geometry_msgs.Vector3 import Vector3 -from dimos.msgs.sensor_msgs.CameraInfo import CameraInfo -from dimos.msgs.sensor_msgs.Image import Image, ImageFormat -from dimos.msgs.sensor_msgs.PointCloud2 import PointCloud2 -from dimos.msgs.tf2_msgs.TFMessage import TFMessage +from dimos.hardware.sensors.camera.zed.messages import ( + frame_message, + tracking_to_base, + transform_from_arrays, +) +from dimos.messages.builtin_interfaces.msg import Time +from dimos.messages.geometry_msgs.msg import Quaternion, Transform, TransformStamped, Vector3 +from dimos.messages.sensor_msgs.msg import CameraInfo, Image, PointCloud2, RegionOfInterest +from dimos.messages.std_msgs.msg import Header +from dimos.messages.tf2_msgs.msg import TFMessage +from dimos.msgs.camera_info import camera_info_with_stamp +from dimos.msgs.image import image_from_array +from dimos.msgs.pointcloud import pointcloud_from_rgbd, voxel_downsample_cloud +from dimos.msgs.time import time_from_nanoseconds from dimos.spec import perception from dimos.utils.reactive import backpressure @@ -47,8 +54,8 @@ def default_base_transform() -> Transform: """Default identity transform for camera mounting.""" return Transform( - translation=Vector3(0.0, 0.0, 0.0), - rotation=Quaternion(0.0, 0.0, 0.0, 1.0), + translation=Vector3(x=0.0, y=0.0, z=0.0), + rotation=Quaternion(w=1.0, x=0.0, y=0.0, z=0.0), ) @@ -58,7 +65,7 @@ class ZEDCameraConfig(ModuleConfig, DepthCameraConfig): fps: int = 15 camera_name: str = "camera" base_frame_id: str = "base_link" - base_transform: Transform | None = Field(default_factory=default_base_transform) + base_transform: InstanceOf[Transform] | None = Field(default_factory=default_base_transform) align_depth_to_color: bool = True enable_depth: bool = True enable_pointcloud: bool = False @@ -129,13 +136,11 @@ def __init__(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def] self._sl_camera_info: sl.CameraInformation | None = None def _publish_camera_info(self) -> None: - ts = time.time() + stamp = time_from_nanoseconds(time.time_ns()) if self._color_camera_info: - self._color_camera_info.ts = ts - self.camera_info.publish(self._color_camera_info) + self.camera_info.publish(camera_info_with_stamp(self._color_camera_info, stamp)) if self._depth_camera_info: - self._depth_camera_info.ts = ts - self.depth_camera_info.publish(self._depth_camera_info) + self.depth_camera_info.publish(camera_info_with_stamp(self._depth_camera_info, stamp)) @rpc def start(self) -> None: @@ -232,10 +237,14 @@ def _intrinsics_to_camera_info( height=self._stream_height, width=self._stream_width, distortion_model="plumb_bob", - D=D, - K=K, - P=P, - frame_id=frame_id, + d=np.asarray(D, dtype=np.float64), + k=np.asarray(K, dtype=np.float64), + r=np.array([1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0], dtype=np.float64), + p=np.asarray(P, dtype=np.float64), + header=Header(frame_id=frame_id, stamp=Time(sec=0, nanosec=0)), + binning_x=0, + binning_y=0, + roi=RegionOfInterest(x_offset=0, y_offset=0, height=0, width=0, do_rectify=False), ) def _get_extrinsics(self) -> None: @@ -250,16 +259,14 @@ def _extrinsics_to_transform( extrinsics: sl.Transform, frame_id: str, child_frame_id: str, - ts: float, - ) -> Transform: - translation = extrinsics.get_translation().get() - quat = extrinsics.get_orientation().get() # [x, y, z, w] - return Transform( - translation=Vector3(*translation), - rotation=Quaternion(quat[0], quat[1], quat[2], quat[3]), - frame_id=frame_id, - child_frame_id=child_frame_id, - ts=ts, + stamp: Time, + ) -> TransformStamped: + return transform_from_arrays( + extrinsics.get_translation().get(), + extrinsics.get_orientation().get(), + parent=frame_id, + child=child_frame_id, + stamp=stamp, ) def _enable_tracking(self) -> None: @@ -292,7 +299,7 @@ def _capture_loop(self) -> None: time.sleep(0.001) continue - ts = time.time() + stamp = time_from_nanoseconds(time.time_ns()) color_img = None if self._image_left is not None: @@ -301,11 +308,10 @@ def _capture_loop(self) -> None: if color_data.ndim == 3 and color_data.shape[2] == 4: color_data = color_data[:, :, :3] color_data = cv2.cvtColor(color_data, cv2.COLOR_BGR2RGB) - color_img = Image( - data=color_data, - format=ImageFormat.RGB, - frame_id=self._color_optical_frame, - ts=ts, + color_img = image_from_array( + color_data, + encoding="rgb8", + header=Header(frame_id=self._color_optical_frame, stamp=stamp), ) self.color_image.publish(color_img) @@ -320,11 +326,10 @@ def _capture_loop(self) -> None: if self.config.align_depth_to_color else self._depth_optical_frame ) - depth_img = Image( - data=depth_data, - format=ImageFormat.DEPTH, - frame_id=depth_frame_id, - ts=ts, + depth_img = image_from_array( + depth_data, + encoding="32FC1", + header=Header(frame_id=depth_frame_id, stamp=stamp), ) self.depth_image.publish(depth_img) @@ -333,99 +338,39 @@ def _capture_loop(self) -> None: self._latest_color_img = color_img self._latest_depth_img = depth_img - self._publish_tf(ts) + self._publish_tf(stamp) - def _tracking_transform(self, ts: float) -> Transform | None: + def _tracking_transform(self, stamp: Time) -> TransformStamped | None: if not self._tracking_enabled or self._zed is None or self._pose is None: return None state = self._zed.get_position(self._pose, sl.REFERENCE_FRAME.WORLD) if state != sl.POSITIONAL_TRACKING_STATE.OK: return None - - translation = self._pose.get_translation().get().tolist() - rotation = self._pose.get_orientation().get().tolist() - world_to_camera = Transform( - translation=Vector3(*translation), - rotation=Quaternion(*rotation), - frame_id=self.config.world_frame, - child_frame_id=self._camera_link, - ts=ts, - ) - if self.config.base_transform is None: - return world_to_camera - - base_to_camera = Transform( - translation=self.config.base_transform.translation, - rotation=self.config.base_transform.rotation, - frame_id=self.config.base_frame_id, - child_frame_id=self._camera_link, - ts=ts, - ) - camera_to_base = base_to_camera.inverse() - world_to_base = world_to_camera + camera_to_base - world_to_base.frame_id = self.config.world_frame - world_to_base.child_frame_id = self.config.base_frame_id - world_to_base.ts = ts - return world_to_base - - def _publish_tf(self, ts: float) -> None: - transforms = [] - - if self.config.base_transform is not None: - base_to_camera = Transform( - translation=self.config.base_transform.translation, - rotation=self.config.base_transform.rotation, - frame_id=self.config.base_frame_id, - child_frame_id=self._camera_link, - ts=ts, - ) - transforms.append(base_to_camera) - - # camera_imu_transform is IMU -> left_camera (coordinate transform), - # we need to invert to get the pose of left camera in camera_link frame - camera_link_to_depth = self._extrinsics_to_transform( - self._camera_link_to_color_extrinsics, + pose = self._extrinsics_to_transform( + self._pose, + self.config.world_frame, self._camera_link, - self._depth_frame, - ts, - ).inverse() - camera_link_to_depth.frame_id = self._camera_link - camera_link_to_depth.child_frame_id = self._depth_frame - transforms.append(camera_link_to_depth) - - depth_to_depth_optical = Transform( - translation=Vector3(0.0, 0.0, 0.0), - rotation=OPTICAL_ROTATION, - frame_id=self._depth_frame, - child_frame_id=self._depth_optical_frame, - ts=ts, + stamp, ) - transforms.append(depth_to_depth_optical) + return tracking_to_base(pose, self.config.base_transform, self.config.base_frame_id) - color_tf = self._extrinsics_to_transform( + def _publish_tf(self, stamp: Time) -> None: + extrinsics = self._extrinsics_to_transform( self._camera_link_to_color_extrinsics, self._camera_link, self._color_frame, - ts, - ).inverse() - color_tf.frame_id = self._camera_link - color_tf.child_frame_id = self._color_frame - transforms.append(color_tf) - - color_to_color_optical = Transform( - translation=Vector3(0.0, 0.0, 0.0), - rotation=OPTICAL_ROTATION, - frame_id=self._color_frame, - child_frame_id=self._color_optical_frame, - ts=ts, + stamp, + ) + self.tf.publish( + frame_message( + camera_name=self.config.camera_name, + base_frame=self.config.base_frame_id, + base_transform=self.config.base_transform, + sdk_extrinsics=extrinsics.transform, + stamp=stamp, + tracking=self._tracking_transform(stamp), + ) ) - transforms.append(color_to_color_optical) - - tracking_tf = self._tracking_transform(ts) - if tracking_tf is not None: - transforms.append(tracking_tf) - - self.tf.publish(TFMessage(*transforms)) def _generate_pointcloud(self) -> None: with self._pointcloud_lock: @@ -436,13 +381,13 @@ def _generate_pointcloud(self) -> None: return try: - pcd = PointCloud2.from_rgbd( - color_image=color_img, - depth_image=depth_img, - camera_info=self._color_camera_info, + pcd = pointcloud_from_rgbd( + color=color_img, + depth=depth_img, + calibration=self._color_camera_info, depth_scale=self._depth_scale, ) - pcd = pcd.voxel_downsample(0.005) + pcd = voxel_downsample_cloud(pcd, 0.005) self.pointcloud.publish(pcd) except Exception as e: print(f"Pointcloud generation error: {e}") diff --git a/dimos/hardware/sensors/camera/zed/compat.py b/dimos/hardware/sensors/camera/zed/compat.py index f7e78fc5d62..2ee92c2341f 100644 --- a/dimos/hardware/sensors/camera/zed/compat.py +++ b/dimos/hardware/sensors/camera/zed/compat.py @@ -16,7 +16,7 @@ from pathlib import Path -from dimos.msgs.sensor_msgs.CameraInfo import CalibrationProvider +from dimos.msgs.camera_info import CalibrationProvider try: import pyzed.sl # noqa: F401 diff --git a/dimos/hardware/sensors/camera/zed/messages.py b/dimos/hardware/sensors/camera/zed/messages.py new file mode 100644 index 00000000000..389d902e468 --- /dev/null +++ b/dimos/hardware/sensors/camera/zed/messages.py @@ -0,0 +1,112 @@ +# Copyright 2025-2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""SDK-independent CDR frame conversions used by the ZED producer.""" + +from __future__ import annotations + +from collections.abc import Sequence + +from dimos.hardware.sensors.camera.spec import OPTICAL_ROTATION +from dimos.messages.builtin_interfaces.msg import Time +from dimos.messages.geometry_msgs.msg import Quaternion, Transform, TransformStamped, Vector3 +from dimos.messages.std_msgs.msg import Header +from dimos.messages.tf2_msgs.msg import TFMessage +from dimos.msgs.geometry import compose_transforms, inverse_transform + + +def transform_from_arrays( + translation: Sequence[float], + rotation_xyzw: Sequence[float], + *, + parent: str, + child: str, + stamp: Time, +) -> TransformStamped: + if len(translation) != 3 or len(rotation_xyzw) != 4: + raise ValueError("ZED pose requires three translation and four quaternion components") + return TransformStamped( + header=Header(frame_id=parent, stamp=stamp), + child_frame_id=child, + transform=Transform( + translation=Vector3( + x=float(translation[0]), y=float(translation[1]), z=float(translation[2]) + ), + rotation=Quaternion( + x=float(rotation_xyzw[0]), + y=float(rotation_xyzw[1]), + z=float(rotation_xyzw[2]), + w=float(rotation_xyzw[3]), + ), + ), + ) + + +def tracking_to_base( + world_to_camera: TransformStamped, + base_transform: Transform | None, + base_frame: str, +) -> TransformStamped: + if base_transform is None: + return world_to_camera + mount = TransformStamped( + header=Header(frame_id=base_frame, stamp=world_to_camera.header.stamp), + child_frame_id=world_to_camera.child_frame_id, + transform=base_transform, + ) + return compose_transforms(world_to_camera, inverse_transform(mount)) + + +def frame_message( + *, + camera_name: str, + base_frame: str, + base_transform: Transform | None, + sdk_extrinsics: Transform, + stamp: Time, + tracking: TransformStamped | None = None, +) -> TFMessage: + camera_link = camera_name + "_link" + transforms = [] + if base_transform is not None: + transforms.append( + TransformStamped( + header=Header(frame_id=base_frame, stamp=stamp), + child_frame_id=camera_link, + transform=base_transform, + ) + ) + for kind in ["depth", "color"]: + frame = camera_name + "_" + kind + "_frame" + optical = camera_name + "_" + kind + "_optical_frame" + # Preserve the producer's existing inverse SDK extrinsics convention. + edge = inverse_transform( + TransformStamped( + header=Header(frame_id=camera_link, stamp=stamp), + child_frame_id=frame, + transform=sdk_extrinsics, + ) + ) + edge.header.frame_id, edge.child_frame_id = camera_link, frame + transforms.append(edge) + transforms.append( + TransformStamped( + header=Header(frame_id=frame, stamp=stamp), + child_frame_id=optical, + transform=Transform(rotation=OPTICAL_ROTATION, translation=Vector3(x=0, y=0, z=0)), + ) + ) + if tracking is not None: + transforms.append(tracking) + return TFMessage(transforms=transforms) diff --git a/dimos/hardware/sensors/camera/zed/test_messages.py b/dimos/hardware/sensors/camera/zed/test_messages.py new file mode 100644 index 00000000000..07aefe3c18e --- /dev/null +++ b/dimos/hardware/sensors/camera/zed/test_messages.py @@ -0,0 +1,128 @@ +# Copyright 2025-2026 Dimensional Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import math + +from dimos_cdrgen.registry import decode, encode +import numpy as np +import pytest + +from dimos.hardware.sensors.camera.zed.compat import CameraInfo as ZedCalibration +from dimos.hardware.sensors.camera.zed.messages import ( + frame_message, + tracking_to_base, + transform_from_arrays, +) +from dimos.messages.geometry_msgs.msg import Transform, Vector3 +from dimos.messages.tf2_msgs.msg import TFMessage +from dimos.msgs.geometry import quaternion_from_euler, transform_matrix +from dimos.msgs.time import time_from_nanoseconds, to_nanoseconds + + +@pytest.mark.parametrize("ns", [0, -1, 1700000000123456789]) +def test_frames_roundtrip_cdr_preserves_exact_stamp_and_extrinsics(ns): + stamp = time_from_nanoseconds(ns) + extrinsics = Transform( + translation=Vector3(x=1, y=2, z=3), rotation=quaternion_from_euler(0, 0, math.pi / 2) + ) + mount = Transform(translation=Vector3(x=4, y=5, z=6), rotation=quaternion_from_euler(0.2, 0, 0)) + + message = frame_message( + camera_name="zed", + base_frame="base", + base_transform=mount, + sdk_extrinsics=extrinsics, + stamp=stamp, + ) + actual = decode(encode(message), TFMessage) + + assert [(edge.header.frame_id, edge.child_frame_id) for edge in actual.transforms] == [ + ("base", "zed_link"), + ("zed_link", "zed_depth_frame"), + ("zed_depth_frame", "zed_depth_optical_frame"), + ("zed_link", "zed_color_frame"), + ("zed_color_frame", "zed_color_optical_frame"), + ] + assert [to_nanoseconds(edge.header.stamp) for edge in actual.transforms] == [ns] * 5 + np.testing.assert_allclose( + transform_matrix(actual.transforms[0].transform), transform_matrix(mount) + ) + for edge in [actual.transforms[1], actual.transforms[3]]: + np.testing.assert_allclose( + transform_matrix(edge.transform), + np.linalg.inv(transform_matrix(extrinsics)), + atol=1e-12, + ) + for edge in [actual.transforms[2], actual.transforms[4]]: + optical_to_frame = transform_matrix(edge.transform) + # Optical right/down/forward maps to camera forward/left/up. + np.testing.assert_allclose(optical_to_frame @ [1, 2, 3, 1], [3, -1, -2, 1], atol=1e-12) + + +def test_tracking_removes_rotated_mount_and_is_included_without_mount_edge(): + stamp = time_from_nanoseconds(1700000000123456789) + world_camera = transform_from_arrays( + [5, 6, 7], + [0, 0, math.sqrt(0.5), math.sqrt(0.5)], + parent="world", + child="zed_link", + stamp=stamp, + ) + mount = Transform( + translation=Vector3(x=1, y=2, z=3), rotation=quaternion_from_euler(0, 0, -math.pi / 2) + ) + + tracking = tracking_to_base(world_camera, mount, "base") + message = frame_message( + camera_name="zed", + base_frame="base", + base_transform=None, + sdk_extrinsics=mount, + stamp=stamp, + tracking=tracking, + ) + actual = decode(encode(message), TFMessage) + + assert tracking.header.frame_id == "world" + assert tracking.child_frame_id == "base" + assert to_nanoseconds(tracking.header.stamp) == 1700000000123456789 + np.testing.assert_allclose( + transform_matrix(tracking.transform), + transform_matrix(world_camera.transform) @ np.linalg.inv(transform_matrix(mount)), + atol=1e-12, + ) + assert len(actual.transforms) == 5 + assert actual.transforms[-1] == tracking + assert actual.transforms[0].header.frame_id == "zed_link" + assert tracking_to_base(world_camera, None, "base") == world_camera + + +@pytest.mark.parametrize("translation,rotation", [([1, 2], [0, 0, 0, 1]), ([1, 2, 3], [0, 0, 1])]) +def test_incomplete_sdk_pose_is_rejected(translation, rotation): + with pytest.raises(ValueError, match="three translation and four quaternion"): + transform_from_arrays( + translation, rotation, parent="world", child="camera", stamp=time_from_nanoseconds(0) + ) + + +def test_shipped_zed_calibration_is_generated_and_roundtrips_cdr(): + calibration = ZedCalibration.SingleWebcam + + actual = decode(encode(calibration), type(calibration)) + + assert (actual.width, actual.height) == (640, 376) + assert actual.header.frame_id == "camera_optical" + assert to_nanoseconds(actual.header.stamp) == 0 + assert actual.k[0] == pytest.approx(379.45267) + assert actual.d[0] == pytest.approx(-0.309435) From 4dcda64a025d74900602e2e85612af1de3f6a6c0 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 20 Oct 2026 04:51:17 +0000 Subject: [PATCH 2/2] [autofix.ci] apply automated fixes --- dimos/models/segmentation/edge_tam.py | 2 +- dimos/models/segmentation/test_yoloe.py | 4 ++-- dimos/models/segmentation/yoloe.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dimos/models/segmentation/edge_tam.py b/dimos/models/segmentation/edge_tam.py index e240a6529d1..b65977283c0 100644 --- a/dimos/models/segmentation/edge_tam.py +++ b/dimos/models/segmentation/edge_tam.py @@ -20,7 +20,6 @@ import tempfile from typing import TYPE_CHECKING, Any, Protocol, TypedDict -from dimos.messages.sensor_msgs.msg import Image from hydra.utils import instantiate import numpy as np from numpy.typing import NDArray @@ -28,6 +27,7 @@ from PIL import Image as PILImage import torch +from dimos.messages.sensor_msgs.msg import Image from dimos.models.base import default_torch_device from dimos.msgs.image import image_to_bgr, image_to_jpeg from dimos.perception.detection.detectors.base import Detector diff --git a/dimos/models/segmentation/test_yoloe.py b/dimos/models/segmentation/test_yoloe.py index 345fc79ac1c..5d2efaffa50 100644 --- a/dimos/models/segmentation/test_yoloe.py +++ b/dimos/models/segmentation/test_yoloe.py @@ -16,11 +16,11 @@ from typing import Any from unittest.mock import MagicMock -from dimos.messages.sensor_msgs.msg import Image -from dimos.messages.std_msgs.msg import Header import numpy as np from ultralytics.models.yolo.yoloe import YOLOEVPSegPredictor +from dimos.messages.sensor_msgs.msg import Image +from dimos.messages.std_msgs.msg import Header from dimos.models.segmentation.yoloe import YoloeBoxSegmenter from dimos.msgs.image import image_from_array, image_to_bgr from dimos.msgs.time import time_from_seconds, to_seconds diff --git a/dimos/models/segmentation/yoloe.py b/dimos/models/segmentation/yoloe.py index d19ecc15701..fd075227d27 100644 --- a/dimos/models/segmentation/yoloe.py +++ b/dimos/models/segmentation/yoloe.py @@ -19,11 +19,11 @@ from dataclasses import replace from typing import Protocol -from dimos.messages.sensor_msgs.msg import Image import numpy as np from numpy.typing import NDArray from ultralytics.models.yolo.yoloe import YOLOEVPSegPredictor +from dimos.messages.sensor_msgs.msg import Image from dimos.msgs.image import image_to_bgr from dimos.perception.detection.detectors.yoloe import Yoloe2DDetector, YoloePromptMode from dimos.perception.detection.type.detection2d.bbox import Bbox, Detection2DBBox