Initial vendor packages

Signed-off-by: Valentin Popov <valentin@popov.link>
This commit is contained in:
2024-01-08 01:21:28 +04:00
parent 5ecd8cf2cb
commit 1b6a04ca55
7309 changed files with 2160054 additions and 0 deletions

36
vendor/exr/examples/0a_write_rgba.rs vendored Normal file
View File

@ -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");
}

22
vendor/exr/examples/0b_read_meta.rs vendored Normal file
View File

@ -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
);
}
}

30
vendor/exr/examples/0c_read_rgba.rs vendored Normal file
View File

@ -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);
}

View File

@ -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");
}

View File

@ -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")
}

View File

@ -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");
}

View File

@ -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");
}

View File

@ -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);
}
}
}
}

View File

@ -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");
}

View File

@ -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)
)
}
}

View File

@ -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");
}

View File

@ -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`");
}

View File

@ -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");
}

View File

@ -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");
}

View File

@ -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`");
}

View File

@ -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");
}

View File

@ -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");
}

View File

@ -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());
}

129
vendor/exr/examples/8_read_raw_blocks.rs vendored Normal file
View File

@ -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());
}

24
vendor/exr/examples/README.md vendored Normal file
View File

@ -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/)
- ...