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/6_extract_mip_map_pngs.rs | |
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/6_extract_mip_map_pngs.rs')
-rw-r--r-- | vendor/exr/examples/6_extract_mip_map_pngs.rs | 74 |
1 files changed, 74 insertions, 0 deletions
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`"); +} + |