11use std:: { fs:: File , path:: Path } ;
22
3- use kornia_image:: Image ;
4- use png:: Decoder ;
3+ use kornia_image:: { Image , ImageSize } ;
4+ use png:: { BitDepth , ColorType , Decoder , Encoder } ;
55
66use crate :: error:: IoError ;
77
@@ -47,6 +47,48 @@ pub fn read_image_png_rgba8(file_path: impl AsRef<Path>) -> Result<Image<u8, 4>,
4747 Ok ( Image :: new ( size. into ( ) , buf) ?)
4848}
4949
50+ /// Read a PNG image with a three channels (rgb16).
51+ ///
52+ /// # Arguments
53+ ///
54+ /// * `file_path` - The path to the PNG file.
55+ ///
56+ /// # Returns
57+ ///
58+ /// A RGB image with three channels (rgb16).
59+ pub fn read_image_png_rgb16 ( file_path : impl AsRef < Path > ) -> Result < Image < u16 , 3 > , IoError > {
60+ let ( buf, size) = read_png_impl ( file_path) ?;
61+
62+ // convert the buffer to u16
63+ let mut buf_u16 = Vec :: with_capacity ( buf. len ( ) / 2 ) ;
64+ for chunk in buf. chunks_exact ( 2 ) {
65+ buf_u16. push ( u16:: from_be_bytes ( [ chunk[ 0 ] , chunk[ 1 ] ] ) ) ;
66+ }
67+
68+ Ok ( Image :: new ( size. into ( ) , buf_u16) ?)
69+ }
70+
71+ /// Read a PNG image with a four channels (rgba16).
72+ ///
73+ /// # Arguments
74+ ///
75+ /// * `file_path` - The path to the PNG file.
76+ ///
77+ /// # Returns
78+ ///
79+ /// A RGB image with four channels (rgb16).
80+ pub fn read_image_png_rgba16 ( file_path : impl AsRef < Path > ) -> Result < Image < u16 , 4 > , IoError > {
81+ let ( buf, size) = read_png_impl ( file_path) ?;
82+
83+ // convert the buffer to u16
84+ let mut buf_u16 = Vec :: with_capacity ( buf. len ( ) / 2 ) ;
85+ for chunk in buf. chunks_exact ( 2 ) {
86+ buf_u16. push ( u16:: from_be_bytes ( [ chunk[ 0 ] , chunk[ 1 ] ] ) ) ;
87+ }
88+
89+ Ok ( Image :: new ( size. into ( ) , buf_u16) ?)
90+ }
91+
5092/// Read a PNG image with a single channel (mono16).
5193///
5294/// # Arguments
@@ -98,10 +140,172 @@ fn read_png_impl(file_path: impl AsRef<Path>) -> Result<(Vec<u8>, [usize; 2]), I
98140 Ok ( ( buf, [ info. width as usize , info. height as usize ] ) )
99141}
100142
143+ /// Writes the given PNG _(rgb8)_ data to the given file path.
144+ ///
145+ /// # Arguments
146+ ///
147+ /// - `file_path` - The path to the PNG image.
148+ /// - `image` - The tensor containing the PNG image data.
149+ pub fn write_image_png_rgb8 (
150+ file_path : impl AsRef < Path > ,
151+ image : & Image < u8 , 3 > ,
152+ ) -> Result < ( ) , IoError > {
153+ write_png_impl (
154+ file_path,
155+ image. as_slice ( ) ,
156+ image. size ( ) ,
157+ BitDepth :: Eight ,
158+ ColorType :: Rgb ,
159+ )
160+ }
161+
162+ /// Writes the given PNG _(rgba8)_ data to the given file path.
163+ ///
164+ /// # Arguments
165+ ///
166+ /// - `file_path` - The path to the PNG image.
167+ /// - `image` - The tensor containing the PNG image data.
168+ pub fn write_image_png_rgba8 (
169+ file_path : impl AsRef < Path > ,
170+ image : & Image < u8 , 4 > ,
171+ ) -> Result < ( ) , IoError > {
172+ write_png_impl (
173+ file_path,
174+ image. as_slice ( ) ,
175+ image. size ( ) ,
176+ BitDepth :: Eight ,
177+ ColorType :: Rgba ,
178+ )
179+ }
180+
181+ /// Writes the given PNG _(grayscale 8-bit)_ data to the given file path.
182+ ///
183+ /// # Arguments
184+ ///
185+ /// - `file_path` - The path to the PNG image.
186+ /// - `image` - The tensor containing the PNG image data.
187+ pub fn write_image_png_gray8 (
188+ file_path : impl AsRef < Path > ,
189+ image : & Image < u8 , 1 > ,
190+ ) -> Result < ( ) , IoError > {
191+ write_png_impl (
192+ file_path,
193+ image. as_slice ( ) ,
194+ image. size ( ) ,
195+ BitDepth :: Eight ,
196+ ColorType :: Grayscale ,
197+ )
198+ }
199+
200+ /// Writes the given PNG _(rgb16)_ data to the given file path.
201+ ///
202+ /// # Arguments
203+ ///
204+ /// - `file_path` - The path to the PNG image.
205+ /// - `image` - The tensor containing the PNG image data.
206+ pub fn write_image_png_rgb16 (
207+ file_path : impl AsRef < Path > ,
208+ image : & Image < u16 , 3 > ,
209+ ) -> Result < ( ) , IoError > {
210+ let image_size = image. size ( ) ;
211+ let mut image_buf: Vec < u8 > = Vec :: with_capacity ( image_size. width * image_size. height * 2 ) ;
212+
213+ for buf in image. as_slice ( ) {
214+ let be_bytes = buf. to_be_bytes ( ) ;
215+ image_buf. extend_from_slice ( & be_bytes) ;
216+ }
217+
218+ write_png_impl (
219+ file_path,
220+ & image_buf,
221+ image_size,
222+ BitDepth :: Sixteen ,
223+ ColorType :: Rgb ,
224+ )
225+ }
226+
227+ /// Writes the given PNG _(rgba16)_ data to the given file path.
228+ ///
229+ /// # Arguments
230+ ///
231+ /// - `file_path` - The path to the PNG image.
232+ /// - `image` - The tensor containing the PNG image data.
233+ pub fn write_image_png_rgba16 (
234+ file_path : impl AsRef < Path > ,
235+ image : & Image < u16 , 4 > ,
236+ ) -> Result < ( ) , IoError > {
237+ let image_size = image. size ( ) ;
238+ let mut image_buf: Vec < u8 > = Vec :: with_capacity ( image_size. width * image_size. height * 2 ) ;
239+
240+ for buf in image. as_slice ( ) {
241+ let be_bytes = buf. to_be_bytes ( ) ;
242+ image_buf. extend_from_slice ( & be_bytes) ;
243+ }
244+
245+ write_png_impl (
246+ file_path,
247+ & image_buf,
248+ image_size,
249+ BitDepth :: Sixteen ,
250+ ColorType :: Rgba ,
251+ )
252+ }
253+
254+ /// Writes the given PNG _(grayscale 16-bit)_ data to the given file path.
255+ ///
256+ /// # Arguments
257+ ///
258+ /// - `file_path` - The path to the PNG image.
259+ /// - `image` - The tensor containing the PNG image data.
260+ pub fn write_image_png_gray16 (
261+ file_path : impl AsRef < Path > ,
262+ image : & Image < u16 , 1 > ,
263+ ) -> Result < ( ) , IoError > {
264+ let image_size = image. size ( ) ;
265+ let mut image_buf: Vec < u8 > = Vec :: with_capacity ( image_size. width * image_size. height * 2 ) ;
266+
267+ for buf in image. as_slice ( ) {
268+ let bug_be = buf. to_be_bytes ( ) ;
269+ image_buf. extend_from_slice ( & bug_be) ;
270+ }
271+
272+ write_png_impl (
273+ file_path,
274+ & image_buf,
275+ image_size,
276+ BitDepth :: Sixteen ,
277+ ColorType :: Grayscale ,
278+ )
279+ }
280+
281+ fn write_png_impl (
282+ file_path : impl AsRef < Path > ,
283+ image_data : & [ u8 ] ,
284+ image_size : ImageSize ,
285+ // Make sure you set `depth` correctly
286+ depth : BitDepth ,
287+ color_type : ColorType ,
288+ ) -> Result < ( ) , IoError > {
289+ let file = File :: create ( file_path) ?;
290+
291+ let mut encoder = Encoder :: new ( file, image_size. width as u32 , image_size. height as u32 ) ;
292+ encoder. set_color ( color_type) ;
293+ encoder. set_depth ( depth) ;
294+
295+ let mut writer = encoder
296+ . write_header ( )
297+ . map_err ( |e| IoError :: PngEncodingError ( e. to_string ( ) ) ) ?;
298+ writer
299+ . write_image_data ( image_data)
300+ . map_err ( |e| IoError :: PngEncodingError ( e. to_string ( ) ) ) ?;
301+ Ok ( ( ) )
302+ }
303+
101304#[ cfg( test) ]
102305mod tests {
306+ use super :: * ;
103307 use crate :: error:: IoError ;
104- use crate :: png :: read_image_png_mono8 ;
308+ use std :: fs :: create_dir_all ;
105309
106310 #[ test]
107311 fn read_png_mono8 ( ) -> Result < ( ) , IoError > {
@@ -110,4 +314,42 @@ mod tests {
110314 assert_eq ! ( image. size( ) . height, 195 ) ;
111315 Ok ( ( ) )
112316 }
317+
318+ #[ test]
319+ fn read_write_png_rgb8 ( ) -> Result < ( ) , IoError > {
320+ let tmp_dir = tempfile:: tempdir ( ) ?;
321+ create_dir_all ( tmp_dir. path ( ) ) ?;
322+
323+ let file_path = tmp_dir. path ( ) . join ( "dog-rgb8.png" ) ;
324+ let image_data = read_image_png_rgb8 ( "../../tests/data/dog-rgb8.png" ) ?;
325+ write_image_png_rgb8 ( & file_path, & image_data) ?;
326+
327+ let image_data_back = read_image_png_rgb8 ( & file_path) ?;
328+ assert ! ( file_path. exists( ) , "File does not exist: {:?}" , file_path) ;
329+
330+ assert_eq ! ( image_data_back. cols( ) , 258 ) ;
331+ assert_eq ! ( image_data_back. rows( ) , 195 ) ;
332+ assert_eq ! ( image_data_back. num_channels( ) , 3 ) ;
333+
334+ Ok ( ( ) )
335+ }
336+
337+ #[ test]
338+ fn read_write_png_rgb16 ( ) -> Result < ( ) , IoError > {
339+ let tmp_dir = tempfile:: tempdir ( ) ?;
340+ create_dir_all ( tmp_dir. path ( ) ) ?;
341+
342+ let file_path = tmp_dir. path ( ) . join ( "rgb16.png" ) ;
343+ let image_data = read_image_png_rgb16 ( "../../tests/data/rgb16.png" ) ?;
344+ write_image_png_rgb16 ( & file_path, & image_data) ?;
345+
346+ let image_data_back = read_image_png_rgb16 ( & file_path) ?;
347+ assert ! ( file_path. exists( ) , "File does not exist: {:?}" , file_path) ;
348+
349+ assert_eq ! ( image_data_back. cols( ) , 32 ) ;
350+ assert_eq ! ( image_data_back. rows( ) , 32 ) ;
351+ assert_eq ! ( image_data_back. num_channels( ) , 3 ) ;
352+
353+ Ok ( ( ) )
354+ }
113355}
0 commit comments