Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement png read routines #222

Merged
merged 2 commits into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/kornia-io/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ all-features = true
[dependencies]
image = "0.25"
kornia-image = { workspace = true }
png = "0.17"
log = { workspace = true }
thiserror = { workspace = true }

Expand Down
4 changes: 4 additions & 0 deletions crates/kornia-io/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,8 @@ pub enum IoError {
/// Error to decode the image.
#[error("Failed to decode the image")]
ImageDecodeError(#[from] image::ImageError),

/// Error to decode the PNG image.
#[error("Failed to decode the image")]
PngDecodeError(String),
}
3 changes: 3 additions & 0 deletions crates/kornia-io/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ pub mod functional;
#[cfg(feature = "jpegturbo")]
pub mod jpeg;

/// PNG image encoding and decoding.
pub mod png;

/// GStreamer video module for real-time video processing.
#[cfg(feature = "gstreamer")]
pub mod stream;
Expand Down
113 changes: 113 additions & 0 deletions crates/kornia-io/src/png.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
use std::{fs::File, path::Path};

use kornia_image::Image;
use png::Decoder;

use crate::error::IoError;

/// Read a PNG image with a single channel (mono8).
///
/// # Arguments
///
/// * `file_path` - The path to the PNG file.
///
/// # Returns
///
/// A grayscale image with a single channel (mono8).
pub fn read_image_png_mono8(file_path: impl AsRef<Path>) -> Result<Image<u8, 1>, IoError> {
let (buf, size) = read_png_impl(file_path)?;
Ok(Image::new(size.into(), buf)?)
}

/// Read a PNG image with a three channels (rgb8).
///
/// # Arguments
///
/// * `file_path` - The path to the PNG file.
///
/// # Returns
///
/// A RGB image with three channels (rgb8).
pub fn read_image_png_rgb8(file_path: impl AsRef<Path>) -> Result<Image<u8, 3>, IoError> {
let (buf, size) = read_png_impl(file_path)?;
Ok(Image::new(size.into(), buf)?)
}

/// Read a PNG image with a four channels (rgba8).
///
/// # Arguments
///
/// * `file_path` - The path to the PNG file.
///
/// # Returns
///
/// A RGBA image with four channels (rgba8).
pub fn read_image_png_rgba8(file_path: impl AsRef<Path>) -> Result<Image<u8, 4>, IoError> {
let (buf, size) = read_png_impl(file_path)?;
Ok(Image::new(size.into(), buf)?)
}

/// Read a PNG image with a single channel (mono16).
///
/// # Arguments
///
/// * `file_path` - The path to the PNG file.
///
/// # Returns
///
/// A grayscale image with a single channel (mono16).
pub fn read_image_png_mono16(file_path: impl AsRef<Path>) -> Result<Image<u16, 1>, IoError> {
let (buf, size) = read_png_impl(file_path)?;

// convert the buffer to u16
let mut buf_u16 = Vec::with_capacity(buf.len() / 2);
for chunk in buf.chunks_exact(2) {
buf_u16.push(u16::from_be_bytes([chunk[0], chunk[1]]));
}

Ok(Image::new(size.into(), buf_u16)?)
}

// utility function to read the png file
fn read_png_impl(file_path: impl AsRef<Path>) -> Result<(Vec<u8>, [usize; 2]), IoError> {
// verify the file exists
let file_path = file_path.as_ref();
if !file_path.exists() {
return Err(IoError::FileDoesNotExist(file_path.to_path_buf()));
}

// verify the file extension
if let Some(extension) = file_path.extension() {
if extension != "png" {
return Err(IoError::InvalidFileExtension(file_path.to_path_buf()));
}
} else {
return Err(IoError::InvalidFileExtension(file_path.to_path_buf()));
}

let file = File::open(file_path)?;
let mut reader = Decoder::new(file)
.read_info()
.map_err(|e| IoError::PngDecodeError(e.to_string()))?;

let mut buf = vec![0; reader.output_buffer_size()];
let info = reader
.next_frame(&mut buf)
.map_err(|e| IoError::PngDecodeError(e.to_string()))?;

Ok((buf, [info.width as usize, info.height as usize]))
}

#[cfg(test)]
mod tests {
use crate::error::IoError;
use crate::png::read_image_png_mono8;

#[test]
fn read_png_mono8() -> Result<(), IoError> {
let image = read_image_png_mono8("../../tests/data/dog.png")?;
assert_eq!(image.size().width, 258);
assert_eq!(image.size().height, 195);
Ok(())
}
}
Binary file added tests/data/dog.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading