1- use booster:: LowState ;
2- use color_eyre:: Result ;
1+ use color_eyre:: { eyre:: bail, Result } ;
32use hulkz:: Session ;
3+ use image:: { error:: DecodingError , ImageError , RgbImage } ;
4+ use ros2:: sensor_msgs:: image:: Image ;
5+ use yuv:: { yuv_nv12_to_rgb, YuvBiPlanarImage , YuvConversionMode , YuvRange , YuvStandardMatrix } ;
46
57#[ tokio:: main]
68async fn main ( ) -> Result < ( ) > {
@@ -15,24 +17,65 @@ async fn main() -> Result<()> {
1517
1618 let session = Session :: new ( ) . await ?;
1719
18- let mut low_state = session
19- . stream :: < LowState > ( "HULK10/booster/low_state" )
20- . await ?;
20+ let mut stream = session. stream :: < Image > ( "booster/rectified_image" ) . await ?;
21+
22+ while let Ok ( image) = stream. recv_async ( ) . await {
23+ let y_plane_size = ( image. step * image. height ) as usize ;
24+ // UV plane is half height, but same stride as Y in NV12 (usually)
25+ let uv_plane_size = ( image. step * image. height / 2 ) as usize ;
26+
27+ if image. data . len ( ) < y_plane_size + uv_plane_size {
28+ bail ! ( "NV12: Source buffer is too small for the given dimensions" ) ;
29+ }
30+
31+ // 2. Prepare Output Buffer
32+ // RgbImage is a flattened Vec<u8> (R, G, B, R, G, B...)
33+ let mut rgb_image = RgbImage :: new ( image. width , image. height ) ;
34+
35+ // 3. Define Strides
36+ // ROS 'step' is the stride for the Y plane.
37+ let y_stride = image. step ;
38+ // NV12 UV plane usually has the same stride as Y
39+ let uv_stride = image. step ;
40+ // RGB output stride (3 bytes per pixel * width)
41+ let rgb_stride = image. width * 3 ;
42+
43+ // 4. Split Input Data into Planes
44+ let ( y_plane, remaining) = image. data . split_at ( y_plane_size) ;
45+ let uv_plane = & remaining[ ..uv_plane_size] ;
46+
47+ let yuv_bi_planar_image = YuvBiPlanarImage {
48+ y_plane,
49+ y_stride,
50+ uv_plane,
51+ uv_stride,
52+ width : image. width ,
53+ height : image. height ,
54+ } ;
55+
56+ yuv_nv12_to_rgb (
57+ & yuv_bi_planar_image,
58+ rgb_image. as_flat_samples_mut ( ) . as_mut_slice ( ) ,
59+ rgb_stride,
60+ YuvRange :: Limited , // Standard for video (16-235). Use 'Full' for JPEGs.
61+ YuvStandardMatrix :: Bt709 , // Standard for HD Video. Use Bt601 for SD/Webcams.
62+ YuvConversionMode :: Balanced ,
63+ )
64+ . map_err ( |e| {
65+ ImageError :: Decoding ( DecodingError :: from_format_hint (
66+ image:: error:: ImageFormatHint :: Name ( format ! ( "NV12: {e}" ) ) ,
67+ ) )
68+ } ) ?;
2169
22- while let Ok ( low_state) = low_state. recv_async ( ) . await {
23- let accelerometer = low_state. imu_state . linear_acceleration ;
24- rec. log (
25- "booster/imu/accelerometer" ,
26- & rerun:: archetypes:: Scalars :: new ( [
27- accelerometer. x ( ) ,
28- accelerometer. y ( ) ,
29- accelerometer. z ( ) ,
30- ] ) ,
31- ) ?;
32- let rpy = low_state. imu_state . roll_pitch_yaw ;
3370 rec. log (
34- "booster/imu/roll_pitch_yaw" ,
35- & rerun:: archetypes:: Scalars :: new ( [ rpy. x ( ) , rpy. y ( ) , rpy. z ( ) ] ) ,
71+ "booster/rectified_image" ,
72+ & rerun:: archetypes:: Image :: from_image ( rgb_image) ?,
73+ // &rerun::archetypes::Image::from_color_model_and_bytes(
74+ // rgb_image.as_raw(),
75+ // [image.width, image.height],
76+ // rerun::ColorModel::RGB,
77+ // rerun::ChannelDatatype::U8,
78+ // ),
3679 ) ?;
3780 }
3881
0 commit comments