1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
use std::time::Duration;
use depthai::camera::{CameraNode, CameraOutputConfig};
use depthai::common::{CameraBoardSocket, ImageFrameType, ResizeMode};
use depthai::{
Colormap, ImageManipNode, Pipeline, RerunHostNode, RerunHostNodeConfig, RerunViewer,
RerunWebConfig, Result,
};
/// Example demonstrating the `ImageManip` node, visualized using `RerunHostNode`.
///
/// This example:
/// - captures a camera stream
/// - applies an ImageManip initial configuration (crop/resize/format)
/// - logs the manipulated frames to Rerun (Web viewer by default)
fn main() -> Result<()> {
const CAM_W: u32 = 640;
const CAM_H: u32 = 480;
// Use an implicit/default device (keeps the example short).
let pipeline = Pipeline::new().build()?;
// Camera (built immediately by create_with).
let cam = pipeline.create_with::<CameraNode, _>(CameraBoardSocket::CamA)?;
// IMPORTANT: don't feed RAW frames into ImageManip.
//
// `cam.raw()` is typically RAW10 (ImageFrameType value 17), and many devices/firmware
// variants don't support RAW inputs for ImageManip (they error with "Frame type 17 not supported").
//
// Instead, request a processed stream from the camera (NV12 is widely supported) and then
// do crop/resize/format conversion via the explicit `ImageManip` node.
//
// We also request a modest output size to keep device-side buffer requirements reasonable.
let cam_out = cam.request_output(CameraOutputConfig {
// Keep ImageManip output at the same resolution as this input stream.
size: (CAM_W, CAM_H),
frame_type: Some(ImageFrameType::NV12),
resize_mode: ResizeMode::Crop,
fps: Some(30.0),
enable_undistortion: None,
})?;
// Image manipulation node.
let manip = pipeline.create::<ImageManipNode>()?;
manip.set_num_frames_pool(4);
// DepthAI allocates output buffers based on this limit.
// If it's larger than the actual image payload, some backends will report the full
// allocated buffer length, which then triggers RerunHostNode's "buffer larger than expected"
// truncation note.
//
// Our output is CAM_W x CAM_H RGB888i => CAM_W * CAM_H * 3 bytes.
// Keeping this equal to the expected payload helps avoid host-side
// warnings caused by padded allocations.
manip.set_max_output_frame_size((CAM_W * CAM_H * 3) as i32);
// Configure the node's initial config.
// (This handle is shared with the node; mutations affect the pipeline.)
let mut cfg = manip.initial_config()?;
cfg.clear_ops()
// Rotate the image 180 degrees.
.add_rotate_deg(180.0)
// Set output frame format.
// NOTE: Some devices/firmware variants don't support BGR888i output from ImageManip.
// RGB888i is widely supported and works with the RerunHostNode.
.set_frame_type(ImageFrameType::RGB888i)
// Optional visualization: apply a colormap (mostly useful for grayscale).
.set_colormap(Colormap::None)
.set_undistort(false);
// Link camera -> ImageManip.
cam_out.link(&manip.inputImage()?)?;
// Visualize using Rerun.
let host = pipeline.create_with::<RerunHostNode, _>(RerunHostNodeConfig {
entity_path: "image_manip".to_string(),
viewer: RerunViewer::Web(RerunWebConfig {
// In VS Code remote/containers, auto-opening a browser is often confusing.
// We'll print the URL instead.
open_browser: false,
..Default::default()
}),
..Default::default()
})?;
manip.out()?.link(&host.input("in")?)?;
// Start the pipeline.
pipeline.start()?;
eprintln!("image_manip running (press Ctrl-C to stop)...");
eprintln!("If the web viewer can't fetch data, make sure the gRPC /proxy port (default 9876) is reachable from your browser (e.g. port-forward it if you're remote).");
loop {
std::thread::sleep(Duration::from_secs(1));
}
}