diff options
author | Valentin Popov <valentin@popov.link> | 2024-01-08 00:21:28 +0300 |
---|---|---|
committer | Valentin Popov <valentin@popov.link> | 2024-01-08 00:21:28 +0300 |
commit | 1b6a04ca5504955c571d1c97504fb45ea0befee4 (patch) | |
tree | 7579f518b23313e8a9748a88ab6173d5e030b227 /vendor/exr/examples | |
parent | 5ecd8cf2cba827454317368b68571df0d13d7842 (diff) | |
download | fparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.tar.xz fparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.zip |
Initial vendor packages
Signed-off-by: Valentin Popov <valentin@popov.link>
Diffstat (limited to 'vendor/exr/examples')
20 files changed, 1189 insertions, 0 deletions
diff --git a/vendor/exr/examples/0a_write_rgba.rs b/vendor/exr/examples/0a_write_rgba.rs new file mode 100644 index 0000000..daa4fcd --- /dev/null +++ b/vendor/exr/examples/0a_write_rgba.rs @@ -0,0 +1,36 @@ +extern crate exr; + + +/// `exr` offers a few simplified functions for the most basic use cases. +/// `write_rgb_f32_file` is a such a function, which writes a plain rgba exr file. +/// +/// To write your image data, you need to specify how to retrieve a single pixel from it. +/// The closure may capture variables or generate data on the fly. +fn main() { + use exr::prelude::*; + + // write a file, with 32-bit float precision per channel + write_rgba_file( + + // this accepts paths or &str + "minimal_rgba.exr", + + // image resolution is 2k + 2048, 2048, + + // generate (or lookup in your own image) + // an f32 rgb color for each of the 2048x2048 pixels + // (you could also create f16 values here to save disk space) + |x,y| { + ( + x as f32 / 2048.0, // red + y as f32 / 2048.0, // green + 1.0 - (y as f32 / 2048.0), // blue + 1.0 // alpha + ) + } + + ).unwrap(); + + println!("created file minimal_rgb.exr"); +}
\ No newline at end of file diff --git a/vendor/exr/examples/0b_read_meta.rs b/vendor/exr/examples/0b_read_meta.rs new file mode 100644 index 0000000..a20527f --- /dev/null +++ b/vendor/exr/examples/0b_read_meta.rs @@ -0,0 +1,22 @@ + + +// exr imports +extern crate exr; + +/// Print the custom meta data of a file, excluding technical encoding meta data. +/// Prints compression method and tile size, but not purely technical data like chunk count. +fn main() { + use exr::prelude::*; + + let meta_data = MetaData::read_from_file( + "generated_rgba_with_meta.exr", + false // do not throw an error for invalid or missing attributes, skipping them instead + ).expect("run example `1_write_rgba_with_metadata` to generate the required file"); + + for (layer_index, image_layer) in meta_data.headers.iter().enumerate() { + println!( + "custom meta data of layer #{}:\n{:#?}", + layer_index, image_layer.own_attributes + ); + } +}
\ No newline at end of file diff --git a/vendor/exr/examples/0c_read_rgba.rs b/vendor/exr/examples/0c_read_rgba.rs new file mode 100644 index 0000000..a2126f8 --- /dev/null +++ b/vendor/exr/examples/0c_read_rgba.rs @@ -0,0 +1,30 @@ +extern crate exr; + +/// `exr` offers a few simplified functions for the most basic use cases. +/// `read_first_rgba_layer_from_file` is a such a function, which loads rgba exr files. +/// To load the pixel data, you need to specify +/// how to create and how to set the pixels of your image. +fn main() { + let image = exr::prelude::read_first_rgba_layer_from_file( + "generated_rgba.exr", + + // instantiate your image type with the size of the image in file + |resolution, _| { + let default_pixel = [0.0, 0.0, 0.0, 0.0]; + let empty_line = vec![ default_pixel; resolution.width() ]; + let empty_image = vec![ empty_line; resolution.height() ]; + empty_image + }, + + // transfer the colors from the file to your image type, + // requesting all values to be converted to f32 numbers (you can also directly use f16 instead) + // and you could also use `Sample` instead of `f32` to keep the original data type from the file + |pixel_vector, position, (r,g,b, a): (f32, f32, f32, f32)| { + pixel_vector[position.y()][position.x()] = [r, g, b, a] + }, + + ).expect("run the `1_write_rgba` example to generate the required file"); + + // printing all pixels might kill the console, so only print some meta data about the image + println!("opened file generated_rgba.exr: {:#?}", image.layer_data.attributes); +}
\ No newline at end of file diff --git a/vendor/exr/examples/1a_write_rgba_with_metadata.rs b/vendor/exr/examples/1a_write_rgba_with_metadata.rs new file mode 100644 index 0000000..394410f --- /dev/null +++ b/vendor/exr/examples/1a_write_rgba_with_metadata.rs @@ -0,0 +1,64 @@ + +// exr imports +extern crate exr; + +/// Write an rgba exr file, generating the pixel values on the fly. +/// This streams the generated pixel directly to the file, +/// never allocating the actual total pixel memory of the image. +fn main() { + use exr::prelude::*; + use exr::meta::attribute::*; + + // this function can generate a color for any pixel + let generate_pixels = |position: Vec2<usize>| ( + position.x() as f32 / 2048.0, // red + position.y() as f32 / 2048.0, // green + 1.0 - (position.y() as f32 / 2048.0), // blue + 1.0 // alpha + ); + + let mut layer_attributes = LayerAttributes::named("generated rgba main layer"); + layer_attributes.comments = Some(Text::from("This image was generated as part of an example")); + layer_attributes.owner = Some(Text::from("The holy lambda function")); + layer_attributes.software_name = Some(Text::from("EXRS Project")); + layer_attributes.exposure = Some(1.0); + layer_attributes.focus = Some(12.4); + layer_attributes.frames_per_second = Some((60, 1)); + layer_attributes.other.insert( + Text::from("Layer Purpose (Custom Layer Attribute)"), + AttributeValue::Text(Text::from("This layer contains the rgb pixel data")) + ); + + let layer = Layer::new( + (2*2048, 2*2048), + layer_attributes, + Encoding::SMALL_FAST_LOSSLESS, // use fast but lossy compression + + SpecificChannels::rgba(generate_pixels) + ); + + // crop away black and transparent pixels from the border, if any + let layer = layer + .crop_where_eq((0.0, 0.0, 0.0, 0.0)) + .or_crop_to_1x1_if_empty(); + + let mut image = Image::from_layer(layer); + image.attributes.pixel_aspect = 1.0; + + image.attributes.time_code = Some(TimeCode { + hours: 0, + minutes: 1, + seconds: 59, + frame: 29, + ..TimeCode::default() + }); + + image.attributes.other.insert( + Text::from("Mice Count (Custom Image Attribute)"), + AttributeValue::I32(23333) + ); + + // write it to a file with all cores in parallel + image.write().to_file("generated_rgba_with_meta.exr").unwrap(); + println!("created file generated_rgba_with_meta.exr"); +}
\ No newline at end of file diff --git a/vendor/exr/examples/1b_convert_exr_to_png.rs b/vendor/exr/examples/1b_convert_exr_to_png.rs new file mode 100644 index 0000000..c78716e --- /dev/null +++ b/vendor/exr/examples/1b_convert_exr_to_png.rs @@ -0,0 +1,53 @@ + +extern crate image as png; + +// exr imports +extern crate exr; + +/// Converts one rgba exr with one layer to one png, or fail. +fn main() { + use exr::prelude::*; + use exr::prelude as exrs; + + // read from the exr file directly into a new `png::RgbaImage` image without intermediate buffers + let reader = exrs::read() + .no_deep_data() + .largest_resolution_level() + .rgba_channels( + |resolution, _channels: &RgbaChannels| -> png::RgbaImage { + png::ImageBuffer::new( + resolution.width() as u32, + resolution.height() as u32 + ) + }, + + // set each pixel in the png buffer from the exr file + |png_pixels, position, (r,g,b,a): (f32,f32,f32,f32)| { // TODO implicit argument types! + png_pixels.put_pixel( + position.x() as u32, position.y() as u32, + png::Rgba([tone_map(r), tone_map(g), tone_map(b), (a * 255.0) as u8]) + ); + } + ) + .first_valid_layer() + .all_attributes(); + + // an image that contains a single layer containing an png rgba buffer + let image: Image<Layer<SpecificChannels<png::RgbaImage, RgbaChannels>>> = reader + .from_file("generated_rgba.exr") + .expect("run the `1_write_rgba` example to generate the required file"); + + + /// compress any possible f32 into the range of [0,1]. + /// and then convert it to an unsigned byte. + fn tone_map(linear: f32) -> u8 { + // TODO does the `image` crate expect gamma corrected data? + let clamped = (linear - 0.5).tanh() * 0.5 + 0.5; + (clamped * 255.0) as u8 + } + + // save the png buffer to a png file + let png_buffer = &image.layer_data.channel_data.pixels; + png_buffer.save("rgb.png").unwrap(); + println!("created image rgb.png") +}
\ No newline at end of file diff --git a/vendor/exr/examples/2_rgba_adjust_exposure.rs b/vendor/exr/examples/2_rgba_adjust_exposure.rs new file mode 100644 index 0000000..c4e6097 --- /dev/null +++ b/vendor/exr/examples/2_rgba_adjust_exposure.rs @@ -0,0 +1,80 @@ + +// exr imports +extern crate exr; + +/// Read an rgba image, increase the exposure, and then write it back. +/// Uses multi-core compression where appropriate. +/// +/// All non-rgba channels and all layers except the first rgba layers will not be present in the new file. +fn main() { + use exr::prelude::*; + + /// This is an example of a custom image type. + /// You use your own image struct here. + // This struct trades sub-optimal memory-efficiency for clarity, + // because this is an example, and does not have to be perfectly efficient. + #[derive(Debug, PartialEq)] + struct CustomPixels { lines: Vec<Vec<RgbaF32Pixel>> } + type RgbaF32Pixel = (f32, f32, f32, f32); + + // read the image from a file + let mut image = read().no_deep_data() + .largest_resolution_level() + .rgba_channels( + // create our custom image based on the file info + |resolution, _channels| -> CustomPixels { + let default_rgba_pixel = (0.0, 0.0, 0.0, 0.0); + let default_line = vec![default_rgba_pixel; resolution.width()]; + let lines = vec![default_line; resolution.height()]; + CustomPixels { lines } + }, + + // request pixels with red, green, blue, and optionally and alpha values. + // transfer each pixel from the file to our image + |image, position, (r,g,b,a): RgbaF32Pixel| { + + // insert the values into our custom image + image.lines[position.y()][position.x()] = (r,g,b,a); + } + ) + .first_valid_layer() + .all_attributes() + .from_file("generated_rgba.exr") + .expect("run the `1_write_rgba` example to generate the required file"); + + let exposure_multiplier = 2.0; + + { // increase exposure of all pixels + for line in &mut image.layer_data.channel_data.pixels.lines { + for (r,g,b,_) in line { + // you should probably check the color space and white points + // for high quality color adjustments + *r *= exposure_multiplier; + *g *= exposure_multiplier; + *b *= exposure_multiplier; + } + } + + // also update meta data after modifying the image + if let Some(exposure) = &mut image.layer_data.attributes.exposure { + println!("increased exposure from {}s to {}s", exposure, *exposure * exposure_multiplier); + *exposure *= exposure_multiplier; + } + } + + // enable writing our custom pixel storage to a file + // FIXME this should be passed as a closure to the `write_with(|x| y)` call + impl GetPixel for CustomPixels { + type Pixel = RgbaF32Pixel; + fn get_pixel(&self, position: Vec2<usize>) -> Self::Pixel { + self.lines[position.y()][position.x()] + } + } + + // write the image to a file + image + .write().to_file("rgba_exposure_adjusted.exr") + .unwrap(); + + println!("created file rgba_exposure_adjusted.exr"); +}
\ No newline at end of file diff --git a/vendor/exr/examples/3a_write_dynamic_channels_with_metadata.rs b/vendor/exr/examples/3a_write_dynamic_channels_with_metadata.rs new file mode 100644 index 0000000..aedbb75 --- /dev/null +++ b/vendor/exr/examples/3a_write_dynamic_channels_with_metadata.rs @@ -0,0 +1,78 @@ + +#[macro_use] +extern crate smallvec; +extern crate rand; +extern crate half; + +use rand::Rng; + +// exr imports +extern crate exr; + + +/// Generate a noisy image and write it to a file, +/// also attaching some meta data. +fn main() { + use exr::prelude::*; + + fn generate_f16_vector(size: Vec2<usize>) -> Vec<f16> { + let mut values = vec![ f16::from_f32(0.5); size.area() ]; + + for _ in 0..(1024*1024/3)/4 { + let index = rand::thread_rng().gen_range(0 .. values.len()); + let value = 1.0 / rand::random::<f32>() - 1.0; + let value = if !value.is_normal() || value > 1000.0 { 1000.0 } else { value }; + values[index] = f16::from_f32(value); + } + + values + } + + let size = (1024, 512); + + let r = AnyChannel::new( + "R", FlatSamples::F16(generate_f16_vector(size.into())) + ); + + let g = AnyChannel::new( + "G", FlatSamples::F16(generate_f16_vector(size.into())) + ); + + let b = AnyChannel::new( + "B", FlatSamples::F32(generate_f16_vector(size.into()).into_iter().map(f16::to_f32).collect()) + ); + + let a = AnyChannel::new( + "A", FlatSamples::F32(generate_f16_vector(size.into()).into_iter().map(f16::to_f32).collect()) + ); + + let mut layer_attributes = LayerAttributes::named("test-image"); + layer_attributes.owner = Some(Text::from("It's you!")); + layer_attributes.comments = Some(Text::from("This image was procedurally generated")); + + let layer = Layer::new( + size, + layer_attributes, + Encoding::default(), + AnyChannels::sort(smallvec![ r, g, b, a ]), + ); + + // crop away transparent pixels from the border + let layer = layer + + // channel order is (a,b,g,r), as channels are already sorted + .crop_where(|samples| samples[0].is_zero()) + + // throw error if the image is 100% transparent pixels and should be removed + .or_none_if_empty().expect("image is empty and cannot be cropped"); + + let image = Image::from_layer(layer); + + println!("writing image {:#?}", image); + + image.write() + .on_progress(|progress| println!("progress: {:.1}", progress*100.0)) + .to_file("noisy.exr").unwrap(); + + println!("created file noisy.exr"); +}
\ No newline at end of file diff --git a/vendor/exr/examples/3b_read_all_channels_with_metadata.rs b/vendor/exr/examples/3b_read_all_channels_with_metadata.rs new file mode 100644 index 0000000..cd9fc04 --- /dev/null +++ b/vendor/exr/examples/3b_read_all_channels_with_metadata.rs @@ -0,0 +1,35 @@ + +// exr imports +extern crate exr; + +/// Read an image and print information about the image into the console. +/// This example shows how to read an image with multiple layers and arbitrary channels. +/// For example, a layer with XYZ channels, and additionally a separate Depth layer. +/// This example does not include resolution levels (mipmaps or ripmaps). +fn main() { + use exr::prelude::*; + + let image = read().no_deep_data() + .largest_resolution_level().all_channels().all_layers().all_attributes() + .on_progress(|progress| println!("progress: {:.1}", progress*100.0)) + .from_file("generated_rgba_with_meta.exr") + .expect("run example `1_write_rgba_with_metadata` to generate this image file"); + + println!("image was read: {:#?}", image); + + // output the average value for each channel of each layer + for layer in &image.layer_data { + for channel in &layer.channel_data.list { + + let sample_vec = &channel.sample_data; + let average = sample_vec.values_as_f32().sum::<f32>() / sample_vec.len() as f32; + + if let Some(layer_name) = &layer.attributes.layer_name { + println!("Channel `{}` of Layer `{}` has an average value of {}", channel.name, layer_name, average); + } + else { + println!("Channel `{}` has an average value of {}", channel.name, average); + } + } + } +}
\ No newline at end of file diff --git a/vendor/exr/examples/4a_write_custom_fixed_channels.rs b/vendor/exr/examples/4a_write_custom_fixed_channels.rs new file mode 100644 index 0000000..4eb6f8d --- /dev/null +++ b/vendor/exr/examples/4a_write_custom_fixed_channels.rs @@ -0,0 +1,41 @@ + +// exr imports +extern crate exr; + +/// Create an image with strange channels and write it to a file. +fn main() { + use exr::prelude::*; + + let pixels = SpecificChannels::build() + .with_channel("Kharthanasus Korthus") + .with_channel("Y") + .with_channel("11023") + .with_channel("*?!") + .with_channel("`--\"") + .with_channel("\r\r\r\n\n") + .with_pixel_fn(|position|{ + if position.0 < 1000 { + (f16::from_f32(0.2), 0.666_f32, 4_u32, 1532434.0213_f32, 0.99999_f32, 3.142594_f32/4.0) + } + else { + (f16::from_f32(0.4), 0.777_f32, 8_u32, 102154.3_f32, 0.00001_f32, 3.142594_f32/4.0) + } + }); + + let image = Image::from_channels((2000, 1400), pixels); + + // print progress only if it advances more than 1% + let mut current_progress_percentage = 0; + + image.write() + .on_progress(|progress| { + let new_progress = (progress * 100.0) as usize; + if new_progress != current_progress_percentage { + current_progress_percentage = new_progress; + println!("progress: {}%", current_progress_percentage) + } + }) + .to_file("custom_channels.exr").unwrap(); + + println!("created file custom_channels.exr"); +}
\ No newline at end of file diff --git a/vendor/exr/examples/4b_read_custom_fixed_channels.rs b/vendor/exr/examples/4b_read_custom_fixed_channels.rs new file mode 100644 index 0000000..a5fd273 --- /dev/null +++ b/vendor/exr/examples/4b_read_custom_fixed_channels.rs @@ -0,0 +1,49 @@ + +// exr imports +extern crate exr; + +/// Read an image and print information about the image into the console. +/// This example shows how to read an image with multiple layers and specific channels. +/// This example does not include resolution levels (mipmaps or ripmaps). +fn main() { + use exr::prelude::*; + + let image = read().no_deep_data() + .largest_resolution_level() + + .specific_channels() + .optional("A", f16::ONE) + .required("Y") // TODO also accept a closure with a detailed selection mechanism + .optional("right.Y", 0.0) + .collect_pixels( + |resolution, (a_channel, y_channel, y_right_channel)| { + println!("image contains alpha channel? {}", a_channel.is_some()); + println!("image contains stereoscopic luma channel? {}", y_right_channel.is_some()); + println!("the type of luma samples is {:?}", y_channel.sample_type); + + vec![vec![(f16::ZERO, 0.0, 0.0); resolution.width()]; resolution.height()] + }, + + // all samples will be converted to f32 (you can also use the enum `Sample` instead of `f32` here to retain the original data type from the file) + |vec, position, (a,y,yr): (f16, f32, f32)| { + vec[position.y()][position.x()] = (a, y, yr) + } + ) + + .all_layers() + .all_attributes() + .on_progress(|progress| println!("progress: {:.1}", progress*100.0)) + .from_file("custom_channels.exr") + .expect("run example `4_write_custom_fixed_channels` to generate this image file"); + + // output a random color of each channel of each layer + for layer in &image.layer_data { + let (alpha, luma, luma_right) = layer.channel_data.pixels.first().unwrap().first().unwrap(); + + println!( + "top left color of layer `{}`: (a, y, yr) = {:?}", + layer.attributes.layer_name.clone().unwrap_or_default(), + (alpha.to_f32(), luma, luma_right) + ) + } +}
\ No newline at end of file diff --git a/vendor/exr/examples/5a_write_multiple_layers.rs b/vendor/exr/examples/5a_write_multiple_layers.rs new file mode 100644 index 0000000..51e2aed --- /dev/null +++ b/vendor/exr/examples/5a_write_multiple_layers.rs @@ -0,0 +1,45 @@ + +extern crate smallvec; +extern crate rand; +extern crate half; + + +// exr imports +extern crate exr; + +/// Writes multiple layers into one exr file +/// Note: this may not be supported by legacy software +fn main() { + use exr::prelude::*; + let size = Vec2(512, 512); + + + let layer1 = Layer::new( + size, + LayerAttributes::named("teal rgb"), + Encoding::FAST_LOSSLESS, + SpecificChannels::rgb(|_pos| (0_f32, 0.4_f32, 0.4_f32)), + ); + + let layer2 = Layer::new( + size, + LayerAttributes::named("orange rgba"), + Encoding::FAST_LOSSLESS, + SpecificChannels::rgba(|_pos| (0.8_f32, 0.5_f32, 0.1_f32, 1.0_f32)), + ); + + // define the visible area of the canvas + let attributes = ImageAttributes::new( + // the pixel section that should be shown + IntegerBounds::from_dimensions(size) + ); + + let image = Image::empty(attributes) + .with_layer(layer1) // add an rgb layer of type `SpecificChannels<ClosureA>` + .with_layer(layer2); // add an rgba layer of different type, `SpecificChannels<ClosureB>`, not possible with a vector + + println!("writing image..."); + image.write().to_file("layers.exr").unwrap(); + + println!("created file layers.exr"); +}
\ No newline at end of file diff --git a/vendor/exr/examples/5b_extract_exr_layers_as_pngs.rs b/vendor/exr/examples/5b_extract_exr_layers_as_pngs.rs new file mode 100644 index 0000000..1f14ea7 --- /dev/null +++ b/vendor/exr/examples/5b_extract_exr_layers_as_pngs.rs @@ -0,0 +1,74 @@ +extern crate image as png; +use std::cmp::Ordering; + +extern crate exr; + +/// For each layer in the exr file, +/// extract each channel as grayscale png, +/// including all multi-resolution levels. +// +// FIXME throws "access denied" sometimes, simply trying again usually works. +// +pub fn main() { + use exr::prelude::*; + + let path = "layers.exr"; + let now = ::std::time::Instant::now(); + + // load the exr file from disk with multi-core decompression + let image = read() + .no_deep_data().largest_resolution_level().all_channels().all_layers().all_attributes() + .from_file(path).expect("run example `5a_write_multiple_layers` to generate this image file"); + + // warning: highly unscientific benchmarks ahead! + println!("\nloaded file in {:?}s", now.elapsed().as_secs_f32()); + let _ = std::fs::create_dir_all("pngs/"); + println!("writing images..."); + + for (layer_index, layer) in image.layer_data.iter().enumerate() { + let layer_name = layer.attributes.layer_name.as_ref() + .map_or(String::from("main_layer"), Text::to_string); + + for channel in &layer.channel_data.list { + let data : Vec<f32> = channel.sample_data.values_as_f32().collect(); + save_f32_image_as_png(&data, layer.size, format!( + "pngs/{} ({}) {}_{}x{}.png", + layer_index, layer_name, channel.name, + layer.size.width(), layer.size.height(), + )) + } + } + + /// Save raw float data to a PNG file, doing automatic brightness adjustments per channel + fn save_f32_image_as_png(data: &[f32], size: Vec2<usize>, name: String) { + let mut png_buffer = png::GrayImage::new(size.width() as u32, size.height() as u32); + let mut sorted = Vec::from(data); + sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Less)); + + // percentile normalization + let max = sorted[7 * sorted.len() / 8]; + let min = sorted[1 * sorted.len() / 8]; + + // primitive tone mapping + let tone = |v: f32| (v - 0.5).tanh() * 0.5 + 0.5; + let max_toned = tone(*sorted.last().unwrap()); + let min_toned = tone(*sorted.first().unwrap()); + + // for each pixel, tone map the value + for (x, y, pixel) in png_buffer.enumerate_pixels_mut() { + let v = data[y as usize * size.0 + x as usize]; + let v = (v - min) / (max - min); + let v = tone(v); + + let v = (v - min_toned) / (max_toned - min_toned); + + // TODO does the `image` crate expect gamma corrected data? + *pixel = png::Luma([(v.max(0.0).min(1.0) * 255.0) as u8]); + } + + png_buffer.save(&name).unwrap(); + } + + println!("extracted all layers to folder `./pngs/*.png`"); +} + diff --git a/vendor/exr/examples/5c_write_mip_maps.rs b/vendor/exr/examples/5c_write_mip_maps.rs new file mode 100644 index 0000000..1096c4e --- /dev/null +++ b/vendor/exr/examples/5c_write_mip_maps.rs @@ -0,0 +1,73 @@ + +extern crate smallvec; +extern crate rand; +extern crate half; + + +// exr imports +extern crate exr; + + + +/// Writes two layers, each with multiple mip maps. +/// All mip maps have solid color for brevity. +fn main() { + use exr::prelude::*; + use exr::math::RoundingMode; + use smallvec::smallvec; + + let full_size = Vec2(512, 512); + let size_rounding = RoundingMode::Up; + + let mip_levels_sizes = exr::meta::mip_map_levels( + size_rounding, full_size + ).collect::<Vec<_>>(); + + let red_mip_levels = mip_levels_sizes.iter() + .map(|(_index, level_size)|{ + FlatSamples::F32(vec![0.1_f32; level_size.area() ]) + }) + .collect(); + + let green_mip_levels = mip_levels_sizes.iter() + .map(|(_index, level_size)|{ + FlatSamples::F32(vec![0.6_f32; level_size.area() ]) + }) + .collect(); + + let blue_mip_levels = mip_levels_sizes.iter() + .map(|(_index, level_size)|{ + FlatSamples::F32(vec![1.0_f32; level_size.area() ]) + }) + .collect(); + + let rgb_mip_maps = AnyChannels::sort(smallvec![ + AnyChannel::new("R", Levels::Mip { level_data: red_mip_levels, rounding_mode: size_rounding }), + AnyChannel::new("G", Levels::Mip { level_data: green_mip_levels, rounding_mode: size_rounding }), + AnyChannel::new("B", Levels::Mip { level_data: blue_mip_levels, rounding_mode: size_rounding }), + ]); + + let layer1 = Layer::new( + full_size, + LayerAttributes::named("teal rgb"), + Encoding::FAST_LOSSLESS, + rgb_mip_maps + ); + + let mut layer2 = layer1.clone(); + layer2.attributes.layer_name = Some("Copied Layer".into()); + layer2.encoding = Encoding::SMALL_FAST_LOSSLESS; + + // define the visible area of the canvas + let image_attributes = ImageAttributes::new( + IntegerBounds::from_dimensions(full_size) + ); + + let image = Image::empty(image_attributes) + .with_layer(layer1).with_layer(layer2); + + println!("writing image..."); + image.write().to_file("mip_maps.exr").unwrap(); + + println!("created file mip_maps.exr"); +}
\ No newline at end of file diff --git a/vendor/exr/examples/5d_write_legacy_layers.rs b/vendor/exr/examples/5d_write_legacy_layers.rs new file mode 100644 index 0000000..7276c7f --- /dev/null +++ b/vendor/exr/examples/5d_write_legacy_layers.rs @@ -0,0 +1,73 @@ + +#[macro_use] +extern crate smallvec; +extern crate rand; +extern crate half; + + +// exr imports +extern crate exr; + +// TODO create a dedicated reader and writer for this scenario + +/// Generate an image with channel groups and write it to a file. +/// Some legacy software may group layers that contain a `.` in the layer name. +/// +/// Note: This is an OpenEXR legacy strategy. OpenEXR supports layers natively since 2013. +/// Use the natively supported exrs `Layer` types instead, if possible. +/// +fn main() { + use exr::prelude::*; + // TODO simplify handling these types of layers using read() and write() + + let size = Vec2(512, 512); + + let create_channel = |name: &str| -> AnyChannel<FlatSamples> { + let color: f16 = f16::from_bits(rand::random::<u16>()); + + AnyChannel::new( + name, + FlatSamples::F16(vec![color; size.area() ]) + ) + }; + + + // The channels have the following structure: + // + // - Object + // - Red + // - Green + // - Blue + // - Alpha + + // - Background + // - Red + // - Green + // - Blue + + let foreground_r = create_channel("Object.R"); + let foreground_g = create_channel("Object.G"); + let foreground_b = create_channel("Object.B"); + let foreground_a = create_channel("Object.A"); + + let background_r = create_channel("Background.R"); + let background_g = create_channel("Background.G"); + let background_b = create_channel("Background.B"); + + let layer = Layer::new( + size, + LayerAttributes::named("test-image"), + Encoding::FAST_LOSSLESS, + AnyChannels::sort(smallvec![ // the order does not actually matter + foreground_r, foreground_g, foreground_b, foreground_a, + background_r, background_g, background_b + ]), + ); + + let image = Image::from_layer(layer); + + println!("writing image {:#?}", image); + image.write().to_file("groups.exr").unwrap(); + + println!("created file groups.exr"); +}
\ No newline at end of file diff --git a/vendor/exr/examples/6_extract_mip_map_pngs.rs b/vendor/exr/examples/6_extract_mip_map_pngs.rs new file mode 100644 index 0000000..152c7c1 --- /dev/null +++ b/vendor/exr/examples/6_extract_mip_map_pngs.rs @@ -0,0 +1,74 @@ + +extern crate image as png; +use std::cmp::Ordering; + +extern crate exr; + +/// Extract all exr pixel information into pngs. +/// Writes each channel of each mip map of each layer as one grayscale png. +/// May appear black for single-color images. +pub fn main() { + use exr::prelude::*; + + let path = "mip_maps.exr"; + let start_time = ::std::time::Instant::now(); + + // load the exr file from disk with multi-core decompression + let image = read_all_data_from_file(path) + .expect("run example `5c_write_mip_maps` to generate this image file"); + + // warning: highly unscientific benchmarks ahead! + println!("\nloaded file in {:?}s", start_time.elapsed().as_secs_f32()); + let _ = std::fs::create_dir_all("pngs/"); + println!("writing images..."); + + for (layer_index, layer) in image.layer_data.iter().enumerate() { + let layer_name = layer.attributes.layer_name.as_ref() + .map_or(String::from("1"), Text::to_string); + + for channel in &layer.channel_data.list { + for (level, level_size) in layer.levels_with_resolution(&channel.sample_data) { + let data : Vec<f32> = level.values_as_f32().collect(); + + save_f32_image_as_png(&data, level_size, format!( + "pngs/{} ({}) {}.{}x{}.png", + layer_index, layer_name, channel.name, + level_size.width(), level_size.height(), + )) + } + } + } + + /// Save raw float data to a PNG file, doing automatic brightness adjustments per channel + fn save_f32_image_as_png(data: &[f32], size: Vec2<usize>, name: String) { + let mut png_buffer = png::GrayImage::new(size.width() as u32, size.height() as u32); + let mut sorted = Vec::from(data); + sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Less)); + + // percentile normalization + let max = sorted[7 * sorted.len() / 8]; + let min = sorted[1 * sorted.len() / 8]; + + // primitive tone mapping + let tone = |v: f32| (v - 0.5).tanh() * 0.5 + 0.5; + let max_toned = tone(*sorted.last().unwrap()); + let min_toned = tone(*sorted.first().unwrap()); + + // for each pixel, tone map the value + for (x, y, pixel) in png_buffer.enumerate_pixels_mut() { + let v = data[y as usize * size.0 + x as usize]; + let v = (v - min) / (max - min); + let v = tone(v); + + let v = (v - min_toned) / (max_toned - min_toned); + + // TODO does the `image` crate expect gamma corrected data? + *pixel = png::Luma([(v.max(0.0).min(1.0) * 255.0) as u8]); + } + + png_buffer.save(&name).unwrap(); + } + + println!("extracted all layers to folder `./pngs/*.png`"); +} + diff --git a/vendor/exr/examples/7_crop_alpha_any_image.rs b/vendor/exr/examples/7_crop_alpha_any_image.rs new file mode 100644 index 0000000..726beda --- /dev/null +++ b/vendor/exr/examples/7_crop_alpha_any_image.rs @@ -0,0 +1,46 @@ + +extern crate image as png; + +extern crate exr; + +/// Read an arbitrary image, crop away transparent pixels, +/// then write the cropped result to another file. +pub fn main() { + use exr::prelude::*; + + let path = "tests/images/valid/custom/oh crop.exr"; + + // loads any image (excluding deep data) + let image: FlatImage = read_all_flat_layers_from_file(path) + .expect("this file exists in the exrs repository. download that?"); + + // construct a cropped image + let image = Image { + attributes: image.attributes, + + // crop each layer + layer_data: image.layer_data.into_iter().map(|layer|{ + println!("cropping layer {:#?}", layer); + + // find the alpha channel of the layer + let alpha_channel_index = layer.channel_data.list.iter() + .position(|channel| channel.name.eq_case_insensitive("A")); + + // if has alpha, crop it where alpha is zero + if let Some(alpha_channel_index) = alpha_channel_index { + layer.crop_where(|pixel: FlatSamplesPixel| pixel[alpha_channel_index].is_zero()) + .or_crop_to_1x1_if_empty() // do not remove empty layers from image, because it could result in an image without content + .reallocate_cropped() // actually perform the crop operation + } + else { + // return the original layer, as no alpha channel can be used for cropping + layer + } + + }).collect::<Layers<_>>(), + }; + + image.write().to_file("cropped.exr").unwrap(); + println!("cropped file to cropped.exr"); +} + diff --git a/vendor/exr/examples/7_crop_alpha_rgba.rs b/vendor/exr/examples/7_crop_alpha_rgba.rs new file mode 100644 index 0000000..18478d3 --- /dev/null +++ b/vendor/exr/examples/7_crop_alpha_rgba.rs @@ -0,0 +1,48 @@ + +extern crate image as png; + +extern crate exr; + + +/// Read an rgba image, or fail if none can be found. +/// Then crop away transparent pixels, +/// and write the cropped result to another file. +/// This retains only the rgb pixels, and no other layers. +pub fn main() { + use exr::prelude::*; + use exr::image::pixel_vec::*; // import predefined pixel storage + + let path = "tests/images/valid/custom/oh crop.exr"; + + type DynamicRgbaPixel = (Sample, Sample, Sample, Sample); // `Sample` is an enum containing the original data type (f16,f32, or u32) + + // load an rgba image + // this specific example discards all but the first valid rgb layers and converts all pixels to f32 values + // TODO optional alpha channel! + let image: PixelImage<PixelVec<DynamicRgbaPixel>, RgbaChannels> = read_first_rgba_layer_from_file( + path, + PixelVec::<DynamicRgbaPixel>::constructor, + + // use this predefined rgba pixel container from the exr crate, requesting any type of pixels with 3 or 4 values + PixelVec::set_pixel + ).expect("this file exists in the exrs repository. download that?"); + + // construct a ~simple~ cropped image + let image: Image<Layer<CroppedChannels<SpecificChannels<PixelVec<DynamicRgbaPixel>, RgbaChannels>>>> = Image { + attributes: image.attributes, + + // crop each layer + layer_data: { + println!("cropping layer {:#?}", image.layer_data); + + // if has alpha, crop it where alpha is zero + image.layer_data + .crop_where(|(_r, _g, _b, alpha)| alpha.is_zero()) + .or_crop_to_1x1_if_empty() // do not remove empty layers from image, because it could result in an image without content + }, + }; + + image.write().to_file("cropped_rgba.exr").unwrap(); + println!("cropped file to cropped_rgba.exr"); +} + diff --git a/vendor/exr/examples/7_write_raw_blocks.rs b/vendor/exr/examples/7_write_raw_blocks.rs new file mode 100644 index 0000000..4cb2646 --- /dev/null +++ b/vendor/exr/examples/7_write_raw_blocks.rs @@ -0,0 +1,115 @@ + +#[macro_use] +extern crate smallvec; +extern crate rand; +extern crate half; + +use std::convert::TryInto; +use std::io::BufWriter; +use std::fs::File; +use exr::block::{UncompressedBlock}; +use exr::block::writer::ChunksWriter; + +// exr imports +extern crate exr; + +/// Generate a striped image on the fly and directly write that to a file without allocating the whole image at once. +/// On my machine, this program produces a 3GB file while only ever allocating 4MB memory (takes a while though). +fn main() { + use exr::prelude::*; + use attribute::*; + use exr::math::*; + + // pre-compute a list of random values + let random_values: Vec<f32> = (0..64) + .map(|_| rand::random::<f32>()) + .collect(); + + // resulting resolution (268 megapixels for 3GB files) + let size = (2048*8, 2048*8); + + // define meta data header that will be written + let header = exr::meta::header::Header::new( + "test-image".try_into().unwrap(), + size, + smallvec![ + attribute::ChannelDescription::new("B", SampleType::F32, true), + attribute::ChannelDescription::new("G", SampleType::F32, true), + attribute::ChannelDescription::new("R", SampleType::F32, true), + attribute::ChannelDescription::new("Z", SampleType::F32, true), + ], + ); + + // define encoding that will be written + let mut header = header.with_encoding( + Compression::Uncompressed, + + exr::meta::BlockDescription::Tiles(TileDescription { + tile_size: Vec2(64, 64), + level_mode: LevelMode::Singular, + rounding_mode: RoundingMode::Down + }), + + LineOrder::Increasing + ); + + // add some random meta data + header.own_attributes.exposure = Some(1.0); + + + let headers = smallvec![ header ]; + + // specify output path, and buffer it for better performance + let file = BufWriter::new(File::create("3GB.exr").unwrap()); + + let start_time = ::std::time::Instant::now(); + + // finally write the image + exr::block::write( + file, headers, true, + |meta_data, chunk_writer|{ + + + let blocks = meta_data.collect_ordered_blocks(|block_index|{ + let channel_description = &meta_data.headers[block_index.layer].channels; + + // fill the image file contents with one of the precomputed random values, + // picking a different one per channel + UncompressedBlock::from_lines(channel_description, block_index, |line_mut|{ + // TODO iterate mut instead?? + + let chan = line_mut.location.channel; + + if chan == 3 { // write time as depth (could also check for _meta.channels[chan].name == "Z") + line_mut.write_samples(|_| start_time.elapsed().as_secs_f32()) + .expect("write to line bug"); + } + + else { // write rgba color + line_mut + .write_samples(|sample_index| random_values[(sample_index + chan) % random_values.len()]) + .expect("write to line bug"); + } + }) + }); + + // print progress only if it advances more than 1% + let mut current_progress_percentage = 0; + + chunk_writer + .on_progress(|progress|{ + let new_progress = (progress * 100.0) as usize; + if new_progress != current_progress_percentage { + current_progress_percentage = new_progress; + println!("progress: {}%", current_progress_percentage) + } + }) + .compress_all_blocks_parallel(&meta_data, blocks)?; + + Ok(()) + } + ).unwrap(); + + // warning: highly unscientific benchmarks ahead! + println!("\ncreated file 3GB.exr in {:?}s", start_time.elapsed().as_secs_f32()); +} diff --git a/vendor/exr/examples/8_read_raw_blocks.rs b/vendor/exr/examples/8_read_raw_blocks.rs new file mode 100644 index 0000000..4864c98 --- /dev/null +++ b/vendor/exr/examples/8_read_raw_blocks.rs @@ -0,0 +1,129 @@ + +extern crate rand; +extern crate half; + +use std::io::{BufReader}; +use std::fs::File; +use exr::block::reader::ChunksReader; + +// exr imports +extern crate exr; + + +/// Collects the average pixel value for each channel. +/// Does not load the whole image into memory at once: only processes the image block by block. +/// On my machine, this program analyzes a 3GB file while only allocating 1.1MB. +fn main() { + use exr::prelude::*; + + let file = BufReader::new( + File::open("3GB.exr") + .expect("run example `7_write_raw_blocks` to generate this image file") + ); + + + // -- the following structs will hold the collected data from the image -- + + /// Collect averages for each layer in the image + #[derive(Debug)] + struct Layer { + #[allow(unused)] // note: is used in Debug impl + layer_name: Option<Text>, + + data_window: IntegerBounds, + + /// Collect one average float per channel in the layer + channels: Vec<Channel>, + } + + /// A single channel in the layer, holds a single average value + #[derive(Debug)] + struct Channel { + #[allow(unused)] // note: is used in Debug impl + channel_name: Text, + + sample_type: SampleType, // f32, u32, or f16 + average: f32, + } + + let start_time = ::std::time::Instant::now(); + + + // -- read the file, summing up the average pixel values -- + + // start reading the file, extracting the meta data of the image + let reader = exr::block::read(file, true).unwrap(); + + // print progress only if it advances more than 1% + let mut current_progress_percentage = 0; + + // create the empty data structure that will collect the analyzed results, + // based on the extracted meta data of the file + let mut averages = reader.headers().iter() + // create a layer for each header in the file + .map(|header| Layer { + layer_name: header.own_attributes.layer_name.clone(), + data_window: header.data_window(), + + // create a averaging channel for each channel in the file + channels: header.channels.list.iter() + .map(|channel| Channel { + channel_name: channel.name.clone(), + sample_type: channel.sample_type, + average: 0.0 + }) + .collect() + }) + .collect::<Vec<_>>(); + + // create a reader that loads only relevant chunks from the file, and also prints something on progress + let reader = reader + + // do not worry about multi-resolution levels or deep data + .filter_chunks(true, |meta_data, tile, block| { + let header = &meta_data.headers[block.layer]; + !header.deep && tile.is_largest_resolution_level() + }).unwrap() + + .on_progress(|progress|{ + let new_progress = (progress * 100.0) as usize; + if new_progress != current_progress_percentage { + current_progress_percentage = new_progress; + println!("progress: {}%", current_progress_percentage) + } + }); + + // read all pixel blocks from the image, decompressing in parallel + reader.decompress_parallel(true, |meta_data, block|{ + let header = &meta_data.headers[block.index.layer]; + + // collect all pixel values from the pixel block + for line in block.lines(&header.channels) { + let layer = &mut averages[line.location.layer]; + let channel = &mut layer.channels[line.location.channel]; + let channel_sample_count = layer.data_window.size.area() as f32; + + // now sum the average based on the values in this line section of pixels + match channel.sample_type { + SampleType::F16 => for value in line.read_samples::<f16>() { + channel.average += value?.to_f32() / channel_sample_count; + }, + + SampleType::F32 => for value in line.read_samples::<f32>() { + channel.average += value? / channel_sample_count; + }, + + SampleType::U32 => for value in line.read_samples::<u32>() { + channel.average += (value? as f32) / channel_sample_count; + }, + } + } + + Ok(()) + }).unwrap(); + + println!("average values: {:#?}", averages); + + // warning: highly unscientific benchmarks ahead! + println!("\nprocessed file in {:?}s", start_time.elapsed().as_secs_f32()); +}
\ No newline at end of file diff --git a/vendor/exr/examples/README.md b/vendor/exr/examples/README.md new file mode 100644 index 0000000..7e9453e --- /dev/null +++ b/vendor/exr/examples/README.md @@ -0,0 +1,24 @@ +# Examples + +These are examples that demonstrate how to use `exrs`. + +Some of these examples read image files +that can be generated by running a different example beforehand. +The examples are named such that running all examples alphabetically +will generate any image before it is used. + +Only the cropping examples use images from the source repository's test folder. + +## Things you can find in the examples: +- Read image data into a custom data structure, without loosing any meta information: + `2_rgba_adjust_exposure` +- Access all pixel information in a file, fully dynamic: + `6_extract_mip_map_pngs` + + +## Older Versions +The examples for any specific `exrs` version can be found on the `docs.rs` page: +- [docs.rs/crate/exr/1.71.0/source/examples/](https://docs.rs/crate/exr/1.7.0/source/examples/) +- [docs.rs/crate/exr/1.7.0/source/examples/](https://docs.rs/crate/exr/1.7.0/source/examples/) +- [docs.rs/crate/exr/1.6.5/source/examples/](https://docs.rs/crate/exr/1.6.5/source/examples/) +- ...
\ No newline at end of file |