refimage 1.0.0-pre6

Imaging library. Provides image storage using CoW-like structures to avoid re-allocation in image-aquisition scenarios. Supports rich metadata and serdes.
Documentation
use std::path::PathBuf;
use std::time::Duration;

use chrono::Utc;
use image::DynamicImage;
use refimage::pipeline::Pipeline;
use refimage::OptimumExposureBuilder;
use refimage::{
    BayerPattern, ColorSpace, DemosaicMethod, DynamicImageRef, FitsCompression, FitsWrite,
    GenericImageRef, ImageProps, ImageRef, PixelData,
};

fn main() {
    // color_backtrace::install();
    let mut src = [
        229u8, 67, 95, 146, 232, 51, 229, 241, 169, 161, 15, 52, 45, 175, 98, 197,
    ];
    let expected = [
        229, 0, 0, 0, 67, 0, 95, 0, 0, 0, 146, 0, 0, 232, 0, 0, 0, 51, 0, 229, 0, 0, 0, 241, 169,
        0, 0, 0, 161, 0, 15, 0, 0, 0, 52, 0, 0, 45, 0, 0, 0, 175, 0, 98, 0, 0, 0, 197,
    ];
    let img = ImageRef::new(&mut src, 4, 4, BayerPattern::Rggb.into())
        .expect("Failed to create ImageRef");
    let img = DynamicImageRef::from(img);
    let mut img = GenericImageRef::new(Utc::now(), Duration::from_secs(1), img);
    img.insert_key("Camera", "Canon EOS 5D Mark III")
        .expect("Failed to insert key");
    img.insert_key("Lens", "EF24-70mm f/2.8L II USM")
        .expect("Failed to insert key");

    // Debayer through the pipeline, carrying the metadata onto the result.
    let debayered = Pipeline::new()
        .debayer(DemosaicMethod::None)
        .apply(&img)
        .expect("Failed to debayer");
    assert!(debayered.channels() == 3);
    assert!(debayered.width() == 4);
    assert!(debayered.height() == 4);
    assert!(debayered.color_space() == ColorSpace::Rgb);
    assert_eq!(debayered.as_raw_u8(), &expected);

    debayered
        .write_fits(PathBuf::from("./test.fits"), FitsCompression::NONE, true)
        .expect("Failed to write FITS");
    let dimg: DynamicImage = debayered
        .clone()
        .try_into()
        .expect("Failed to convert to DynamicImage");
    // Saving needs an encoder feature on the `image` crate; ignore if unavailable.
    match dimg.save("test.png") {
        Ok(()) => println!("wrote test.png"),
        Err(e) => println!("skipped PNG save: {e}"),
    }

    // A second pass to luminance, again metadata-preserving.
    let mut gray = Pipeline::new()
        .debayer(DemosaicMethod::None)
        .to_luma()
        .apply(&img)
        .expect("Failed to convert to luma");
    let eval = OptimumExposureBuilder::default()
        .pixel_exclusion(1)
        .build()
        .expect("Failed to build OptimumExposure");
    // `optimum_exposure` sources the reference exposure from the image's metadata.
    let res = gray
        .optimum_exposure(&eval, 1)
        .expect("Failed to calculate optimum exposure");
    println!("Optimum exposure: {:?} (bin {})", res.exposure, res.bin);
}