Depth camera helpersΒΆ
Use calibrated RGB-D data to parse camera intrinsics, sample depth at a pixel,
and deproject into the ROS optical camera frame. Deprojected points use +x
right, +y down, and +z forward.
from olo import Client
from olo.vision import (
Pixel,
camera_info_to_intrinsics,
deproject_pixel,
project,
sample_depth,
sample_depth_point,
)
# @olo-playground: autofill-sim-topics
with Client() as client:
robot = client.robot()
depth_topic = robot.core.topic("wrist_rgbd/depth_image", "sensor_msgs/msg/Image")
camera_info_topic = robot.core.topic("wrist_rgbd/camera_info", "sensor_msgs/msg/CameraInfo")
depth = robot.core.get_image(depth_topic)
camera_info = robot.core.get_latest(camera_info_topic)
intrinsics = camera_info_to_intrinsics(camera_info)
# Pick a pixel of interest (e.g. from caller-owned detection).
height, width = depth.array.shape[:2]
pixel = Pixel(x=(width - 1) / 2.0, y=(height - 1) / 2.0)
# sample_depth returns a depth scalar in metres; use a median window on noisy data.
depth_metres = sample_depth(depth, pixel, window_size=3)
if depth_metres is None:
print(f"No valid depth at pixel ({pixel.x:.0f}, {pixel.y:.0f})")
else:
print(f"Depth at centre pixel: {depth_metres:.3f} m")
# sample_depth_point combines sampling and deprojection into a stamped observation.
observation = sample_depth_point(depth, pixel, intrinsics, window_size=3)
if observation is None:
print("Could not build a 3D observation for the centre pixel.")
else:
point = observation.point
print(f"3D point in {observation.frame_id}: ({point.x:.3f}, {point.y:.3f}, {point.z:.3f})")
# deproject_pixel and project are available for manual workflows.
if depth_metres is not None:
point = deproject_pixel(pixel, depth_metres, intrinsics)
round_trip = project(point, intrinsics)
print(f"Round-trip pixel: ({round_trip.x:.1f}, {round_trip.y:.1f})")
import { connect } from "olo/web";
import {
cameraInfoToIntrinsics,
deprojectPixel,
project,
sampleDepth,
sampleDepthPoint,
type Pixel,
} from "olo/vision";
// @olo-playground: autofill-sim-topics
const client = connect();
const robot = await client.robot();
const depthTopic = robot.core.topic("wrist_rgbd/depth_image", "sensor_msgs/msg/Image");
const cameraInfoTopic = robot.core.topic("wrist_rgbd/camera_info", "sensor_msgs/msg/CameraInfo");
const depth = await robot.core.getImage(depthTopic);
const cameraInfo = await robot.core.getLatest(cameraInfoTopic);
const intrinsics = cameraInfoToIntrinsics(cameraInfo);
// Pick a pixel of interest (e.g. from caller-owned detection).
const pixel: Pixel = {
x: (depth.width - 1) / 2,
y: (depth.height - 1) / 2,
};
// sampleDepth returns a depth scalar in metres; use a median window on noisy data.
const depthMetres = sampleDepth(depth, pixel, { windowSize: 3 });
if (depthMetres === undefined) {
console.log(`No valid depth at pixel (${pixel.x.toFixed(0)}, ${pixel.y.toFixed(0)})`);
} else {
console.log(`Depth at centre pixel: ${depthMetres.toFixed(3)} m`);
}
// sampleDepthPoint combines sampling and deprojection into a stamped observation.
const observation = sampleDepthPoint(depth, pixel, intrinsics, { windowSize: 3 });
if (observation === undefined) {
console.log("Could not build a 3D observation for the centre pixel.");
} else {
const { point, frameId } = observation;
console.log(
`3D point in ${frameId}: (${point.x.toFixed(3)}, ${point.y.toFixed(3)}, ${point.z.toFixed(3)})`,
);
}
// deprojectPixel and project are available for manual workflows.
if (depthMetres !== undefined) {
const point = deprojectPixel(pixel, depthMetres, intrinsics);
const roundTrip = project(point, intrinsics);
console.log(`Round-trip pixel: (${roundTrip.x.toFixed(1)}, ${roundTrip.y.toFixed(1)})`);
}