Image loader (#46)

* imageLoader branch created
imageLoader file created
imageLoader function created (placeholder)
included imageLoader into lib.rs
added a new folder with 2 test img

* added the decoder and began to configure it

created new variables and initiated them with calues from the .info

added some questions

* added Debug to the the derive at the image mod.rs

added a few more test pngs

added functionality to the function imageLoader

added four functions which decode the IDAT chunk into the correspondig rgba values

added test functions

added documentation

---------

Co-authored-by: Schwingel0206 <Elias.alexander@unity-mail.de>
Co-authored-by: Schwingel0206 <132705455+Schwingel0206@users.noreply.github.com>
This commit is contained in:
teridax 2023-06-17 20:02:27 +00:00 committed by GitHub
parent ea023a8c39
commit d83c89c8f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 348 additions and 1 deletions

View File

@ -40,7 +40,7 @@ pub trait Sample: Into<f32> + PartialEq + Default + Copy + From<u8> + PartialOrd
impl<T: Into<f32> + PartialEq + Default + Copy + From<u8> + PartialOrd> Sample for T {}
#[allow(unused)]
#[derive(Default)]
#[derive(Default, Debug)]
pub struct Image<T>
where
T: Sample,
@ -91,6 +91,9 @@ where
pub fn path(&self) -> &PathBuf {
&self.path
}
pub fn pixels(&self) -> &Vec<(T, T, T, T)> {
&self.pixels
}
/// Returns the iterator of the pixels vector
pub fn iter(&self) -> Iter<'_, (T, T, T, T)> {

343
src/image_loader/mod.rs Normal file
View File

@ -0,0 +1,343 @@
use crate::image::Image;
use png::BitDepth;
use std::fs::File;
use std::path::Path;
///# Image Loader
/// The image_loader function is a function which can use a path of an image to return some Metadata from the image.<br>
///It can also retrieve the rgba values of every pixel from the image and the path.<br>
///<br>
///# IMPORTANT<br>
/// Doesn't support pictures with the color type indexed!<br>
///<br>
///# Parameter
///The function has the parameter path with the type &Path.<br>
///<br>
///# Return variables
///The return value is a struct which includes four variables.<br>
///1. width [u32]: width contains the number of pixels in a row of the given png.<br>
///2. height [u32]: height contains the height of the picture in measured pixels.<br>
///3. pixel_vec [Vec<(f32, f32, f32, f32)>]: pixel_vec contains the rgba values of every pixel in the picture, saved in a vec themselves.<br>
///4. path [path.to_path_buf()]: is the path from the parameter.<br>
pub fn image_loader(path: &Path) -> Result<Image<f32>, &'static str> {
let decoder = match File::open(path) {
Ok(file) => png::Decoder::new(file),
Err(_) => return Err("failed the decoder"),
};
let mut reader = match decoder.read_info() {
Ok(reader) => reader,
Err(_) => return Err("Failed to read PNG info"),
};
let mut buf = vec![0; reader.output_buffer_size()];
let info = match reader.next_frame(&mut buf) {
Ok(info) => info,
Err(_) => return Err("Failed to read PNG frame"),
};
let bit_depth = reader.info().bit_depth;
let color_type = reader.info().color_type;
let width = reader.info().width;
let height = reader.info().height;
let palette = &reader.info().palette;
let idat = &buf[..info.buffer_size()];
println!("idat {:?} idat ", idat);
println!("palette {:?} palette", palette);
println!("depth {:?} depth", bit_depth);
let pixel_vec = match color_type {
png::ColorType::Grayscale => grayscale_to_rgba(idat, bit_depth),
png::ColorType::GrayscaleAlpha => grayscale_alpha_to_rgba(idat, bit_depth),
png::ColorType::Rgb => rgb_to_rgba(idat, bit_depth),
png::ColorType::Rgba => decode_rgba(idat, bit_depth),
_ => panic!("Unsupported color type or bit depth"),
}?;
let image: Image<f32> = Image::new(
width as usize,
height as usize,
pixel_vec,
path.to_path_buf(),
);
Ok(image)
}
///# Grayscale to RGBA
///The grayscale_to_rgba function converts a IDAT chunk from an picture with the color type grayscale and the bit depth into an RGBA value.<br>
/// <br>
///# Parameter
///The function has the following two parameters:<br>
///1. IDAT [&[u8]]: This array cointans the IDAT chunk of the given image.<br>
///2. bit_depth [BitDepth]: This variable contains the bit depth of the given image.<br>
///<br>
///# Return variables
///This function returns a vector filled with vectors filled with four [f32] variables, which are the RGBA values.<br>
///Also this function sets the alpha channel of the RGBA value to 255.
fn grayscale_to_rgba(
idat: &[u8],
bit_depth: BitDepth,
) -> Result<Vec<(f32, f32, f32, f32)>, &'static str> {
let mut rgba_values = Vec::new();
let max_value: u32 = (1 << bit_depth as u32) - 1;
for byte in idat {
let grayscale = match bit_depth {
BitDepth::One => ((*byte & 0x01) as u32 * max_value) as f32,
BitDepth::Two => ((*byte & 0x03) as u32 * max_value / 3) as f32,
BitDepth::Four => ((*byte & 0x0F) as u32 * max_value / 15) as f32,
BitDepth::Eight => *byte as f32,
BitDepth::Sixteen => ((*byte as u32 * 255) / max_value) as f32,
};
let rgba = (grayscale, grayscale, grayscale, 255_f32);
rgba_values.push(rgba);
}
Ok(rgba_values)
}
///# Grayscale Alpha to RGBA
///The grayscale_alpha_to_rgba function converts a IDAT chunk from a picture with the color type grayscale alpha and the bit depth into an RGBA value.<br>
/// <br>
///# Parameter
///The function has the following two parameters:<br>
///1. IDAT [&[u8]]: This array contains the IDAT chunk of the given image.<br>
///2. bit_depth [BitDepth]: This variable contains the bit depth of the given image.<br>
///<br>
///# Return variables
///This function returns a vector filled with vectors filled with four [f32] variables, which are the RGBA values.
fn grayscale_alpha_to_rgba(
idat: &[u8],
bit_depth: BitDepth,
) -> Result<Vec<(f32, f32, f32, f32)>, &'static str> {
let mut rgba_values = Vec::new();
let chunk_size;
if bit_depth == BitDepth::Eight {
chunk_size = 2;
} else if bit_depth == BitDepth::Sixteen {
chunk_size = 4;
} else {
return Err("Invalid Bit Depth");
}
for pair in idat.chunks(chunk_size) {
if pair.len() < 2 {
return Err("Insufficient data");
}
let grayscale = match bit_depth {
BitDepth::Eight => pair[0] as f32,
BitDepth::Sixteen => ((pair[0] as u16) << 8 | pair[1] as u16) as f32,
_ => return Err("Unsupported bit depth"),
};
let alpha = match bit_depth {
BitDepth::Eight => pair[1] as f32,
BitDepth::Sixteen => ((pair[2] as u16) << 8 | pair[3] as u16) as f32,
_ => return Err("Unsupported bit depth"),
};
let rgba = (grayscale, grayscale, grayscale, alpha);
rgba_values.push(rgba);
}
Ok(rgba_values)
}
///# RGB to RGBA
///The tgb_to_rgba function converts a IDAT chunk from a picture with the color type rgb and the bit depth into an RGBA value.<br>
/// <br>
///# Parameter
///The function has the following two parameters:<br>
///1. IDAT [&[u8]]: This array contains the IDAT chunk of the given image.<br>
///2. bit_depth [BitDepth]: This variable contains the bit depth of the given image.<br>
///<br>
///# Return variables
///This function returns a vector filled with vectors filled with four [f32] variables, which are the RGBA values.
///Also this function sets the alpha channel of the RGBA value to 255.
fn rgb_to_rgba(
idat: &[u8],
bit_depth: BitDepth,
) -> Result<Vec<(f32, f32, f32, f32)>, &'static str> {
let mut rgba_values = Vec::new();
let chunk_size;
if bit_depth == BitDepth::Eight {
chunk_size = 3;
} else if bit_depth == BitDepth::Sixteen {
chunk_size = 6;
} else {
return Err("Invalid Bit Depth");
}
for group in idat.chunks(chunk_size) {
if group.len() < 3 {
return Err("Insufficient data");
}
let red = match bit_depth {
BitDepth::Eight => group[0] as f32,
BitDepth::Sixteen => ((group[0] as u16) << 8 | group[1] as u16) as f32,
_ => return Err("Unsupported bit depth"),
};
let green = match bit_depth {
BitDepth::Eight => group[1] as f32,
BitDepth::Sixteen => ((group[2] as u16) << 8 | group[3] as u16) as f32,
_ => return Err("Unsupported bit depth"),
};
let blue = match bit_depth {
BitDepth::Eight => group[2] as f32,
BitDepth::Sixteen => ((group[4] as u16) << 8 | group[5] as u16) as f32,
_ => return Err("Unsupported bit depth"),
};
let rgba = (red, green, blue, 255_f32);
rgba_values.push(rgba);
}
Ok(rgba_values)
}
///# Decode RGBA
///The decode_rgba function converts a IDAT chunk from a picture with the color type rgba and the bit depth into an RGBA value.<br>
/// <br>
///# Parameter
///The function has the following two parameters:<br>
///1. IDAT [&[u8]]: This array contains the IDAT chunk of the given image.<br>
///2. bit_depth [BitDepth]: This variable contains the bit depth of the given image.<br>
///<br>
///# Return variables
///This function returns a vector filled with vectors filled with four [f32] variables, which are the RGBA values.
fn decode_rgba(
idat: &[u8],
bit_depth: BitDepth,
) -> Result<Vec<(f32, f32, f32, f32)>, &'static str> {
let mut rgba_values = Vec::new();
let chunk_size;
if bit_depth == BitDepth::Eight {
chunk_size = 4;
} else if bit_depth == BitDepth::Sixteen {
chunk_size = 8;
} else {
return Err("Invalid Bit Depth");
}
for group in idat.chunks(chunk_size) {
if group.len() < 4 {
return Err("Insufficient data");
}
let red = match bit_depth {
BitDepth::Eight => group[0] as f32,
BitDepth::Sixteen => ((group[0] as u16) << 8 | group[1] as u16) as f32,
_ => return Err("Unsupported bit depth"),
};
let green = match bit_depth {
BitDepth::Eight => group[1] as f32,
BitDepth::Sixteen => ((group[2] as u16) << 8 | group[3] as u16) as f32,
_ => return Err("Unsupported bit depth"),
};
let blue = match bit_depth {
BitDepth::Eight => group[2] as f32,
BitDepth::Sixteen => ((group[4] as u16) << 8 | group[5] as u16) as f32,
_ => return Err("Unsupported bit depth"),
};
let alpha = match bit_depth {
BitDepth::Eight => group[3] as f32,
BitDepth::Sixteen => ((group[6] as u16) << 8 | group[7] as u16) as f32,
_ => return Err("Unsupported bit depth"),
};
let rgba = (red, green, blue, alpha);
rgba_values.push(rgba);
}
Ok(rgba_values)
}
#[cfg(test)]
mod test {
use std::path::PathBuf;
use super::*;
#[test]
fn test_image_loader() {
let path = Path::new("test_img/red_image.png");
let test = image_loader(path);
let image = Image::new(
4,
4,
vec![
(255., 0., 0., 255.),
(0., 255., 0., 255.),
(0., 0., 255., 255.),
(255., 255., 255., 255.),
(127., 127., 127., 255.),
(0., 255., 255., 255.),
(255., 255., 0., 255.),
(255., 0., 0., 255.),
(127., 127., 127., 255.),
(255., 0., 255., 255.),
(255., 0., 0., 255.),
(255., 0., 0., 255.),
(255., 0., 0., 255.),
(255., 0., 0., 255.),
(255., 0., 0., 255.),
(0., 0., 0., 255.),
],
PathBuf::default(),
);
assert_eq!(test.unwrap().pixels(), image.pixels())
}
#[test]
#[should_panic]
fn test_wrong_img() {
let path = Path::new("test_img/wrong pixel count.png");
let test = image_loader(path);
let image = Image::new(
4,
4,
vec![
(255., 0., 0., 255.),
(255., 0., 0., 255.),
(255., 0., 0., 255.),
(255., 0., 0., 255.),
(255., 0., 0., 255.),
(255., 0., 0., 255.),
(255., 0., 0., 255.),
(255., 0., 0., 255.),
(255., 0., 0., 255.),
(255., 0., 0., 255.),
(255., 0., 0., 255.),
(255., 0., 0., 255.),
(255., 0., 0., 255.),
(255., 0., 0., 255.),
(255., 0., 0., 255.),
(255., 0., 0., 255.),
],
PathBuf::default(),
);
//should panic because we are looking at a corrupt picture
assert_eq!(test.unwrap().pixels(), image.pixels())
}
}

View File

@ -1,6 +1,7 @@
extern crate core;
pub mod image;
pub mod image_loader;
pub mod multithreading;
pub fn add(left: usize, right: usize) -> usize {

BIN
test_img/gray_image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 B

BIN
test_img/hut.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

BIN
test_img/indexed_image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 711 B

BIN
test_img/red_image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 539 B

BIN
test_img/rot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 540 B

BIN
test_img/town_blue.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

BIN
test_img/wrong size.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB