@@ -7,7 +7,8 @@ use std::{
77
88use color_eyre:: eyre:: { self , WrapErr } ;
99use geometry:: circle:: Circle ;
10- use image:: { ImageReader , RgbImage } ;
10+ use image:: { ImageError , ImageReader , RgbImage , error:: DecodingError } ;
11+ use num_traits:: Euclid ;
1112use serde:: { Deserialize , Serialize } ;
1213
1314use coordinate_systems:: Pixel ;
@@ -33,12 +34,12 @@ pub struct YCbCr422Image {
3334 buffer : Arc < Vec < YCbCr422 > > ,
3435}
3536
36- impl From < RgbImage > for YCbCr422Image {
37- fn from ( rgb_image : RgbImage ) -> Self {
37+ impl From < & RgbImage > for YCbCr422Image {
38+ fn from ( rgb_image : & RgbImage ) -> Self {
3839 let width_422 = rgb_image. width ( ) / 2 ;
3940 let height = rgb_image. height ( ) ;
4041 let data = rgb_image
41- . into_vec ( )
42+ . to_vec ( )
4243 . chunks ( 6 )
4344 . map ( |pixel| {
4445 let left_color: YCbCr444 = Rgb {
@@ -110,15 +111,17 @@ impl From<YCbCr422Image> for RgbImage {
110111 }
111112}
112113
113- impl From < & Ros2Image > for YCbCr422Image {
114- fn from ( ros2_image : & Ros2Image ) -> Self {
114+ impl TryFrom < & Ros2Image > for YCbCr422Image {
115+ type Error = ImageError ;
116+
117+ fn try_from ( ros2_image : & Ros2Image ) -> Result < Self , ImageError > {
115118 let width_422 = ros2_image. width / 2 ;
116119 let height = ros2_image. height ;
117120
118- let data = match ros2_image. encoding . as_str ( ) {
121+ let data: Vec < YCbCr422 > = match ros2_image. encoding . as_str ( ) {
119122 "rgb8" => ros2_image
120123 . data
121- . chunks ( 6 )
124+ . chunks_exact ( 6 )
122125 . map ( |pixel| {
123126 let left_color: YCbCr444 = Rgb {
124127 red : pixel[ 0 ] ,
@@ -135,14 +138,61 @@ impl From<&Ros2Image> for YCbCr422Image {
135138 [ left_color, right_color] . into ( )
136139 } )
137140 . collect ( ) ,
138- _ => unimplemented ! ( "image encoding not supported" ) ,
141+ "nv12" => {
142+ let y_plane_size = ( ros2_image. width * ros2_image. height ) as usize ;
143+ let uv_plane_size = ( ros2_image. width * ros2_image. height / 2 ) as usize ;
144+
145+ if ros2_image. data . len ( ) < y_plane_size + uv_plane_size {
146+ return Err ( ImageError :: Decoding ( DecodingError :: from_format_hint (
147+ image:: error:: ImageFormatHint :: Name (
148+ "NV12: Source buffer is too small for the given dimensions" . to_string ( ) ,
149+ ) ,
150+ ) ) ) ;
151+ }
152+
153+ let y_stride = ros2_image. width ;
154+ let chunked_y_stride = y_stride / 2 ;
155+
156+ let ( y_plane, uv_plane) = ros2_image. data . split_at ( y_plane_size) ;
157+
158+ assert_eq ! ( uv_plane. len( ) , uv_plane_size) ;
159+
160+ y_plane
161+ . chunks_exact ( 2 )
162+ . enumerate ( )
163+ . map ( |( i, y_chunk) | {
164+ let ( y_row, column) = ( i as u32 ) . div_rem_euclid ( & chunked_y_stride) ;
165+ let uv_row = y_row / 2 ;
166+
167+ assert ! ( uv_row <= ros2_image. height / 2 ) ;
168+ assert ! ( column <= ros2_image. width / 2 ) ;
169+
170+ // don't forget sunscreen
171+ let uv_index = uv_row * chunked_y_stride + column;
172+ let uv_byte_index = uv_index as usize * 2 ;
173+
174+ assert ! ( uv_byte_index < uv_plane_size) ;
175+
176+ let cb = uv_plane[ uv_byte_index] ;
177+ let cr = uv_plane[ uv_byte_index + 1 ] ;
178+
179+ YCbCr422 {
180+ y1 : y_chunk[ 0 ] ,
181+ cb,
182+ y2 : y_chunk[ 1 ] ,
183+ cr,
184+ }
185+ } )
186+ . collect ( )
187+ }
188+ encoding => unimplemented ! ( r#"image encoding "{encoding}" not supported"# ) ,
139189 } ;
140190
141- Self {
191+ Ok ( Self {
142192 width_422,
143193 height,
144194 buffer : Arc :: new ( data) ,
145- }
195+ } )
146196 }
147197}
148198
@@ -225,7 +275,7 @@ impl YCbCr422Image {
225275
226276 pub fn load_from_rgb_file ( path : impl AsRef < Path > ) -> eyre:: Result < Self > {
227277 let rgb_image = ImageReader :: open ( path) ?. decode ( ) ?. into_rgb8 ( ) ;
228- Ok ( Self :: from ( rgb_image) )
278+ Ok ( Self :: from ( & rgb_image) )
229279 }
230280
231281 pub fn save_to_rgb_file ( & self , file : impl AsRef < Path > + Debug ) -> eyre:: Result < ( ) > {
0 commit comments