From 7553dce9dccb7e93efc2ac14e004163e5d2862d4 Mon Sep 17 00:00:00 2001 From: gennyble Date: Fri, 17 Nov 2023 07:59:54 -0600 Subject: prism produces reasonable images --- Cargo.lock | 1 + LRI.md | 4 +- lri-rs/src/block.rs | 20 ++++- lri-rs/src/lib.rs | 217 +++------------------------------------------- lri-rs/src/types.rs | 235 ++++++++++++++++++++++++++++++++++++++++++++++++++ lri-study/src/main.rs | 35 +++++--- prism/Cargo.toml | 1 + prism/src/main.rs | 123 ++++++++++---------------- 8 files changed, 337 insertions(+), 299 deletions(-) create mode 100644 lri-rs/src/types.rs diff --git a/Cargo.lock b/Cargo.lock index 32a9ec3..d44e79c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -507,6 +507,7 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" name = "prism" version = "0.1.0" dependencies = [ + "camino", "lri-rs", "mozjpeg", "nalgebra", diff --git a/LRI.md b/LRI.md index bb5f324..f4668be 100644 --- a/LRI.md +++ b/LRI.md @@ -3,10 +3,8 @@ The file is made up of many blocks, usually 10 or 11 but cases of 40 have occurr Blocks start with a header and contain some data. There is always a protobuf message within that data, and sometimes stuff like the images themselves. -Little-endian. - ## Block Header -The header is 32 bytes long. and goes as follows: +The header is 32 bytes long, uses little-endian, and goes as follows: | bytes | type | meaning | | ----- | -----| ------- | | 4 | - | Signature: "LELR" | diff --git a/lri-rs/src/block.rs b/lri-rs/src/block.rs index d973430..e3d94a5 100644 --- a/lri-rs/src/block.rs +++ b/lri-rs/src/block.rs @@ -6,7 +6,7 @@ use lri_proto::{ }; use crate::{ - fine::Signature, CameraId, CameraInfo, ColorInfo, DataFormat, HdrMode, RawData, RawImage, + AwbGain, AwbMode, CameraId, CameraInfo, ColorInfo, DataFormat, HdrMode, RawData, RawImage, SceneMode, SensorModel, }; @@ -17,15 +17,18 @@ pub(crate) struct Block<'lri> { } impl<'lri> Block<'lri> { + /// Get a slice to the entire body of this block pub fn body(&self) -> &[u8] { &self.data[32..] } + /// Get a slice to this block's messge data pub fn message_data(&self) -> &[u8] { let end = self.header.message_offset + self.header.message_length; &self.data[self.header.message_offset..end] } + /// Parse the message pub fn message(&self) -> Message { match self.header.kind { BlockType::LightHeader => { @@ -46,7 +49,6 @@ impl<'lri> Block<'lri> { images: &mut Vec>, colors: &mut Vec, infos: &mut Vec, - sig: &mut Signature, ) { let LightHeader { mut hw_info, @@ -59,7 +61,6 @@ impl<'lri> Block<'lri> { mut view_preferences, .. } = if let Message::LightHeader(lh) = self.message() { - sig.merge(&lh); lh } else if let Message::ViewPreferences(vp) = self.message() { self.extract_view(vp, ext); @@ -228,6 +229,8 @@ impl<'lri> Block<'lri> { hdr_mode, scene_mode, is_on_tripod, + awb_mode, + awb_gains, .. } = vp; @@ -250,6 +253,14 @@ impl<'lri> Block<'lri> { if let Some(tri) = is_on_tripod { ext.on_tripod = Some(tri); } + + if let Some(Ok(awbmode)) = awb_mode.map(|ev| ev.enum_value()) { + ext.awb = Some(awbmode.into()); + } + + if let Some(gain) = awb_gains.into_option() { + ext.awb_gain = Some(gain.into()); + } } } @@ -265,6 +276,9 @@ pub(crate) struct ExtractedData { pub hdr: Option, pub scene: Option, pub on_tripod: Option, + + pub awb: Option, + pub awb_gain: Option, } pub enum Message { diff --git a/lri-rs/src/lib.rs b/lri-rs/src/lib.rs index c136669..342248a 100644 --- a/lri-rs/src/lib.rs +++ b/lri-rs/src/lib.rs @@ -1,15 +1,11 @@ -use std::{fmt, time::Duration}; +use std::time::Duration; use block::{Block, ExtractedData, Header}; -use fine::Signature; -use lri_proto::{ - camera_id::CameraID as PbCameraID, camera_module::camera_module::surface::FormatType, - color_calibration::color_calibration::IlluminantType, - view_preferences::view_preferences::HDRMode, -}; mod block; -mod fine; +mod types; + +pub use types::*; pub struct LriFile<'lri> { pub image_reference_camera: Option, @@ -25,7 +21,8 @@ pub struct LriFile<'lri> { pub hdr: Option, pub scene: Option, pub on_tripod: Option, - pub sig: Signature, + pub awb: Option, + pub awb_gain: Option, } impl<'lri> LriFile<'lri> { @@ -36,11 +33,10 @@ impl<'lri> LriFile<'lri> { let mut camera_infos = vec![]; let mut ext = ExtractedData::default(); - let mut sig = Signature::new(); // Read data blocks and extract informtion we care about loop { - if data.len() == 0 { + if data.is_empty() { break; } @@ -55,13 +51,7 @@ impl<'lri> LriFile<'lri> { data: block_data, }; - block.extract_meaningful_data( - &mut ext, - &mut images, - &mut colors, - &mut camera_infos, - &mut sig, - ); + block.extract_meaningful_data(&mut ext, &mut images, &mut colors, &mut camera_infos); } // Further fill in the RawImage's we extracted @@ -93,7 +83,8 @@ impl<'lri> LriFile<'lri> { hdr: ext.hdr, scene: ext.scene, on_tripod: ext.on_tripod, - sig, + awb: ext.awb, + awb_gain: ext.awb_gain, } } @@ -232,191 +223,3 @@ pub struct CameraInfo { camera: CameraId, sensor: SensorModel, } - -#[derive(Copy, Clone, Debug, PartialEq)] -/// The representation of the raw data in the LRI file -pub enum DataFormat { - // I'm not sure what this is?? Do we ever see it??? - BayerJpeg, - Packed10bpp, - // Never seen - //Packed12bpp, - //Packed14bpp, -} - -impl fmt::Display for DataFormat { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let str = match self { - Self::BayerJpeg => "BayerJpeg", - Self::Packed10bpp => "Packed10bpp", - //Self::Packed12bpp => "Packed12bpp", - //Self::Packed14bpp => "Packed14bpp", - }; - - write!(f, "{str}") - } -} - -impl From for DataFormat { - fn from(proto: FormatType) -> Self { - match proto { - FormatType::RAW_BAYER_JPEG => Self::BayerJpeg, - FormatType::RAW_PACKED_10BPP => Self::Packed10bpp, - FormatType::RAW_PACKED_12BPP => unreachable!(), - FormatType::RAW_PACKED_14BPP => unreachable!(), - FormatType::RAW_RESERVED_0 - | FormatType::RAW_RESERVED_1 - | FormatType::RAW_RESERVED_2 - | FormatType::RAW_RESERVED_3 - | FormatType::RAW_RESERVED_4 - | FormatType::RAW_RESERVED_5 => unimplemented!(), - } - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub enum CameraId { - A1, - A2, - A3, - A4, - A5, - B1, - B2, - B3, - B4, - B5, - C1, - C2, - C3, - C4, - C5, - C6, -} - -impl From for CameraId { - fn from(pbid: PbCameraID) -> Self { - match pbid { - PbCameraID::A1 => Self::A1, - PbCameraID::A2 => Self::A2, - PbCameraID::A3 => Self::A3, - PbCameraID::A4 => Self::A4, - PbCameraID::A5 => Self::A5, - PbCameraID::B1 => Self::B1, - PbCameraID::B2 => Self::B2, - PbCameraID::B3 => Self::B3, - PbCameraID::B4 => Self::B4, - PbCameraID::B5 => Self::B5, - PbCameraID::C1 => Self::C1, - PbCameraID::C2 => Self::C2, - PbCameraID::C3 => Self::C3, - PbCameraID::C4 => Self::C4, - PbCameraID::C5 => Self::C5, - PbCameraID::C6 => Self::C6, - } - } -} - -impl fmt::Display for CameraId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // this is good; i write good code - write!(f, "{self:?}") - } -} - -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum Whitepoint { - A, - D50, - D65, - D75, - F2, - F7, - F11, - TL84, -} - -impl From for Whitepoint { - fn from(it: IlluminantType) -> Self { - match it { - IlluminantType::A => Self::A, - IlluminantType::D50 => Self::D50, - IlluminantType::D65 => Self::D65, - IlluminantType::D75 => Self::D75, - IlluminantType::F2 => Self::F2, - IlluminantType::F7 => Self::F7, - IlluminantType::F11 => Self::F11, - IlluminantType::TL84 => Self::TL84, - IlluminantType::UNKNOWN => unimplemented!(), - } - } -} - -#[derive(Copy, Clone, Debug)] -pub enum SensorModel { - Unknown, - Ar835, - Ar1335, - Ar1335Mono, - Imx386, - Imx386Mono, -} - -impl From for SensorModel { - fn from(pbst: lri_proto::sensor_type::SensorType) -> Self { - use lri_proto::sensor_type::SensorType as ProtoSt; - - match pbst { - ProtoSt::SENSOR_UNKNOWN => Self::Unknown, - ProtoSt::SENSOR_AR835 => Self::Ar835, - ProtoSt::SENSOR_AR1335 => Self::Ar1335, - ProtoSt::SENSOR_AR1335_MONO => Self::Ar1335Mono, - ProtoSt::SENSOR_IMX386 => Self::Imx386, - ProtoSt::SENSOR_IMX386_MONO => Self::Imx386Mono, - } - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum HdrMode { - None, - Default, - Natural, - Surreal, -} - -impl From for HdrMode { - fn from(h: HDRMode) -> Self { - match h { - HDRMode::HDR_MODE_NONE => Self::None, - HDRMode::HDR_MODE_DEFAULT => Self::Default, - HDRMode::HDR_MODE_NATURAL => Self::Natural, - HDRMode::HDR_MODE_SURREAL => Self::Surreal, - } - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum SceneMode { - Portrait, - Landscape, - Sport, - Macro, - Night, - None, -} - -impl From for SceneMode { - fn from(sm: lri_proto::view_preferences::view_preferences::SceneMode) -> Self { - use lri_proto::view_preferences::view_preferences::SceneMode as PbSceneMode; - - match sm { - PbSceneMode::SCENE_MODE_PORTRAIT => Self::Portrait, - PbSceneMode::SCENE_MODE_LANDSCAPE => Self::Landscape, - PbSceneMode::SCENE_MODE_SPORT => Self::Sport, - PbSceneMode::SCENE_MODE_MACRO => Self::Macro, - PbSceneMode::SCENE_MODE_NIGHT => Self::Night, - PbSceneMode::SCENE_MODE_NONE => Self::None, - } - } -} diff --git a/lri-rs/src/types.rs b/lri-rs/src/types.rs new file mode 100644 index 0000000..8b759ac --- /dev/null +++ b/lri-rs/src/types.rs @@ -0,0 +1,235 @@ +/// Responsible for mapping generated protobuf enums to enums defined here. It +/// seemed like a bad idea to rexport from lri-proto. +use std::fmt; + +use lri_proto::{ + camera_id::CameraID as PbCameraID, camera_module::camera_module::surface::FormatType, + color_calibration::color_calibration::IlluminantType, + view_preferences::view_preferences::HDRMode, +}; + +#[derive(Copy, Clone, Debug, PartialEq)] +/// The representation of the raw data in the LRI file +pub enum DataFormat { + BayerJpeg, + Packed10bpp, + // Never seen + //Packed12bpp, + //Packed14bpp, +} + +impl fmt::Display for DataFormat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let str = match self { + Self::BayerJpeg => "BayerJpeg", + Self::Packed10bpp => "Packed10bpp", + //Self::Packed12bpp => "Packed12bpp", + //Self::Packed14bpp => "Packed14bpp", + }; + + write!(f, "{str}") + } +} + +impl From for DataFormat { + fn from(proto: FormatType) -> Self { + match proto { + FormatType::RAW_BAYER_JPEG => Self::BayerJpeg, + FormatType::RAW_PACKED_10BPP => Self::Packed10bpp, + FormatType::RAW_PACKED_12BPP => unreachable!(), + FormatType::RAW_PACKED_14BPP => unreachable!(), + FormatType::RAW_RESERVED_0 + | FormatType::RAW_RESERVED_1 + | FormatType::RAW_RESERVED_2 + | FormatType::RAW_RESERVED_3 + | FormatType::RAW_RESERVED_4 + | FormatType::RAW_RESERVED_5 => unimplemented!(), + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum CameraId { + A1, + A2, + A3, + A4, + A5, + B1, + B2, + B3, + B4, + B5, + C1, + C2, + C3, + C4, + C5, + C6, +} + +impl From for CameraId { + fn from(pbid: PbCameraID) -> Self { + match pbid { + PbCameraID::A1 => Self::A1, + PbCameraID::A2 => Self::A2, + PbCameraID::A3 => Self::A3, + PbCameraID::A4 => Self::A4, + PbCameraID::A5 => Self::A5, + PbCameraID::B1 => Self::B1, + PbCameraID::B2 => Self::B2, + PbCameraID::B3 => Self::B3, + PbCameraID::B4 => Self::B4, + PbCameraID::B5 => Self::B5, + PbCameraID::C1 => Self::C1, + PbCameraID::C2 => Self::C2, + PbCameraID::C3 => Self::C3, + PbCameraID::C4 => Self::C4, + PbCameraID::C5 => Self::C5, + PbCameraID::C6 => Self::C6, + } + } +} + +impl fmt::Display for CameraId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // this is good; i write good code + write!(f, "{self:?}") + } +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum Whitepoint { + A, + D50, + D65, + D75, + F2, + F7, + F11, + TL84, +} + +impl From for Whitepoint { + fn from(it: IlluminantType) -> Self { + match it { + IlluminantType::A => Self::A, + IlluminantType::D50 => Self::D50, + IlluminantType::D65 => Self::D65, + IlluminantType::D75 => Self::D75, + IlluminantType::F2 => Self::F2, + IlluminantType::F7 => Self::F7, + IlluminantType::F11 => Self::F11, + IlluminantType::TL84 => Self::TL84, + IlluminantType::UNKNOWN => unimplemented!(), + } + } +} + +#[derive(Copy, Clone, Debug)] +pub enum SensorModel { + Unknown, + Ar835, + Ar1335, + Ar1335Mono, + Imx386, + Imx386Mono, +} + +impl From for SensorModel { + fn from(pbst: lri_proto::sensor_type::SensorType) -> Self { + use lri_proto::sensor_type::SensorType as ProtoSt; + + match pbst { + ProtoSt::SENSOR_UNKNOWN => Self::Unknown, + ProtoSt::SENSOR_AR835 => Self::Ar835, + ProtoSt::SENSOR_AR1335 => Self::Ar1335, + ProtoSt::SENSOR_AR1335_MONO => Self::Ar1335Mono, + ProtoSt::SENSOR_IMX386 => Self::Imx386, + ProtoSt::SENSOR_IMX386_MONO => Self::Imx386Mono, + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum HdrMode { + None, + Default, + Natural, + Surreal, +} + +impl From for HdrMode { + fn from(h: HDRMode) -> Self { + match h { + HDRMode::HDR_MODE_NONE => Self::None, + HDRMode::HDR_MODE_DEFAULT => Self::Default, + HDRMode::HDR_MODE_NATURAL => Self::Natural, + HDRMode::HDR_MODE_SURREAL => Self::Surreal, + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum SceneMode { + Portrait, + Landscape, + Sport, + Macro, + Night, + None, +} + +impl From for SceneMode { + fn from(sm: lri_proto::view_preferences::view_preferences::SceneMode) -> Self { + use lri_proto::view_preferences::view_preferences::SceneMode as PbSceneMode; + + match sm { + PbSceneMode::SCENE_MODE_PORTRAIT => Self::Portrait, + PbSceneMode::SCENE_MODE_LANDSCAPE => Self::Landscape, + PbSceneMode::SCENE_MODE_SPORT => Self::Sport, + PbSceneMode::SCENE_MODE_MACRO => Self::Macro, + PbSceneMode::SCENE_MODE_NIGHT => Self::Night, + PbSceneMode::SCENE_MODE_NONE => Self::None, + } + } +} + +/// Auto White Balance Mode +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum AwbMode { + Auto, + Daylight, +} + +impl From for AwbMode { + fn from(awb: lri_proto::view_preferences::view_preferences::AWBMode) -> Self { + use lri_proto::view_preferences::view_preferences::AWBMode as PbAwbMode; + + match awb { + PbAwbMode::AWB_MODE_AUTO => Self::Auto, + PbAwbMode::AWB_MODE_DAYLIGHT => Self::Daylight, + _ => panic!("{awb:?}"), + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct AwbGain { + pub r: f32, + pub gr: f32, + pub gb: f32, + pub b: f32, +} + +impl From for AwbGain { + fn from(gain: lri_proto::view_preferences::view_preferences::ChannelGain) -> Self { + // all fields in ChannelGain are marked as required + Self { + r: gain.r.unwrap(), + gr: gain.g_r.unwrap(), + gb: gain.g_b.unwrap(), + b: gain.b.unwrap(), + } + } +} diff --git a/lri-study/src/main.rs b/lri-study/src/main.rs index 962fc26..2b4c7cd 100644 --- a/lri-study/src/main.rs +++ b/lri-study/src/main.rs @@ -4,10 +4,11 @@ use std::{ }; use camino::Utf8PathBuf; -use lri_rs::{DataFormat, HdrMode, LriFile, SceneMode, SensorModel}; +use lri_rs::{AwbMode, DataFormat, HdrMode, LriFile, SceneMode, SensorModel}; use owo_colors::OwoColorize; fn main() { + #[allow(clippy::single_match)] match std::env::args().nth(1).as_deref() { Some("gather") => gather(), _ => (), @@ -31,15 +32,15 @@ fn gather() -> ! { Some("jpg") => files .entry(stub.clone()) .and_modify(|e| e.jpg = Some(path.to_owned())) - .or_insert(Photo::new_jpg(&path)), + .or_insert(Photo::new_jpg(path)), Some("lri") => files .entry(stub.clone()) .and_modify(|e| e.lri = Some(path.to_owned())) - .or_insert(Photo::new_lri(&path)), + .or_insert(Photo::new_lri(path)), Some("lris") => files .entry(stub.clone()) .and_modify(|e| e.lris = Some(path.to_owned())) - .or_insert(Photo::new_lris(&path)), + .or_insert(Photo::new_lris(path)), None | Some(_) => continue, }; } @@ -50,7 +51,7 @@ fn gather() -> ! { let mut photos: Vec = files.into_values().collect(); photos.sort_by(|a, b| a.lri.as_deref().unwrap().cmp(b.lri.as_deref().unwrap())); - for (idx, photo) in photos.into_iter().enumerate() { + for (_idx, photo) in photos.into_iter().enumerate() { let lri_path = match photo.lri { Some(p) => p, None => continue, @@ -66,10 +67,6 @@ fn gather() -> ! { print!("{} - ", lri_path.file_stem().unwrap()); - let path = format!("{}_{idx}", lri_path.file_stem().unwrap_or_default()); - let dbg = format!("{:#?}", lri.sig); - std::fs::write(path, dbg.as_bytes()).unwrap(); - if let Some(fwv) = lri.firmware_version.as_ref() { print!( "[{}] focal:{:<3} iit:{:>2}ms gain:{:2.0} ", @@ -110,6 +107,24 @@ fn gather() -> ! { Some(false) => print!("{} - ", "af".red()), Some(true) => print!("{} - ", "af".green()), } + + match lri.awb { + None => print!("{}:", "awb".dimmed()), + Some(AwbMode::Auto) => print!("{}:", "awb".white()), + Some(AwbMode::Daylight) => print!("{}:", "awb".yellow()), + } + + match lri.awb_gain { + None => print!("{} - ", "gain".dimmed()), + Some(gain) => print!( + "{} - [{:.2},{:.2},{:.2},{:.2}] ", + "gain".white(), + gain.r, + gain.gr, + gain.gr, + gain.b + ), + } } for img in lri.images() { @@ -127,7 +142,7 @@ fn gather() -> ! { DataFormat::Packed10bpp => print!("{} ", sens.yellow()), } } - println!(""); + println!(); } println!(" ---\nTook {:.2}s", start.elapsed().as_secs_f32()); diff --git a/prism/Cargo.toml b/prism/Cargo.toml index d296b79..6861a34 100644 --- a/prism/Cargo.toml +++ b/prism/Cargo.toml @@ -13,3 +13,4 @@ rawloader = "0.37.1" nalgebra = "0.31.4" mozjpeg = "0.10.1" zune-jpeg = "0.3.17" +camino = "1.1.6" diff --git a/prism/src/main.rs b/prism/src/main.rs index 291d725..43278a6 100644 --- a/prism/src/main.rs +++ b/prism/src/main.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; -use lri_rs::{CameraId, DataFormat, LriFile, RawData, RawImage, SensorModel, Whitepoint}; +use camino::Utf8PathBuf; +use lri_rs::{AwbGain, CameraId, LriFile, RawData, RawImage, SensorModel, Whitepoint}; use nalgebra::{Matrix3, Matrix3x1}; mod rotate; @@ -12,14 +13,29 @@ pub struct Entry { } fn main() { + let args = std::env::args().skip(1); + + if args.len() != 2 { + eprintln!("Usage: prism "); + std::process::exit(1); + } + let file_name = std::env::args().nth(1).unwrap(); + let directory = Utf8PathBuf::from(std::env::args().nth(2).unwrap()); + + if !directory.exists() { + std::fs::create_dir_all(&directory).unwrap(); + } + let bytes = std::fs::read(file_name).unwrap(); let lri = LriFile::decode(&bytes); + let gain = lri.awb_gain.unwrap(); println!("{} images", lri.image_count()); - lri.reference_image() - .map(|raw| make(raw, String::from("reference.png"))); + if let Some(refimg) = lri.reference_image() { + make(refimg, directory.join("reference.png"), gain); + } let mut set: HashMap = HashMap::new(); @@ -32,36 +48,16 @@ fn main() { }); } - set.into_iter().for_each(|kv| { + /*set.into_iter().for_each(|kv| { println!("{} {:?} {}", kv.0, kv.1.sensor, kv.1.count); - }); + });*/ for (idx, img) in lri.images().enumerate() { - /*for color in &img.color { - println!( - "{:?} rg = {} bg = {}", - color.whitepoint, color.rg, color.bg - ); - - let white = - Matrix3::from_row_slice(&color.forward_matrix) * Matrix3x1::new(1.0, 1.0, 1.0); - - let white_x = white[0] / (white[0] + white[1] + white[2]); - let white_y = white[1] / (white[0] + white[1] + white[2]); - let white_z = 1.0 - white_x - white_y; - - println!("\twhite: x = {} y = {} z = {}", white_x, white_y, white_z); - - println!("\t{:?}", color.forward_matrix); - }*/ - //std::process::exit(0); - - make(img, format!("image_{idx}.png")); - //return; + make(img, directory.join(format!("image_{idx}.png")), gain); } } -fn make(img: &RawImage, path: String) { +fn make(img: &RawImage, path: Utf8PathBuf, awb_gain: AwbGain) { use rawproc::image::RawMetadata; use rawproc::{colorspace::BayerRgb, image::Image}; @@ -81,14 +77,7 @@ fn make(img: &RawImage, path: String) { sbro.0, sbro.1 ); - let mut bayered = bayer( - data, - *width, - *height, - format!("{}_bjpg", &path[..path.len() - 4]), - ); - - //bayered.iter_mut().for_each(|p| *p = p.saturating_sub(42)); + let bayered = bayer(data, *width, *height); let (mut rgb, color_format) = match img.cfa_string() { Some(cfa_string) => { @@ -114,56 +103,35 @@ fn make(img: &RawImage, path: String) { }; rotate::rotate_180(rgb.as_mut_slice()); - let mut floats: Vec = rgb.into_iter().map(|p| p as f32 / 255.0).collect(); - if color.len() > 0 { - print!("\t"); + if !color.is_empty() { + print!("\tAvailable whitepoints: "); color.iter().for_each(|c| print!("{:?} ", c.whitepoint)); println!(); } - match img.color_info(Whitepoint::F11) { + match img.color_info(Whitepoint::D65) { Some(c) => { - //println!("\tApplying color profile: {:?}", c.color_matrix); + println!("\tUsing D65"); let to_xyz = Matrix3::from_row_slice(&c.forward_matrix); - let to_srgb = Matrix3::from_row_slice(&BRUCE_XYZ_RGB_D65); - let color = Matrix3::from_row_slice(&c.color_matrix); - let d50_d65 = Matrix3::from_row_slice(&BRADFORD_D50_D65); - - let xyz_d65 = to_xyz * d50_d65; - - //println!("{color}"); - - let white = xyz_d65 * Matrix3x1::new(1.0, 1.0, 1.0); - - let white_x = white[0] / (white[0] + white[1] + white[2]); - let white_y = white[1] / (white[0] + white[1] + white[2]); - let white_z = 1.0 - white_x - white_y; - - /*println!( - "\t{:?} ||| white: x = {} y = {} z = {}", - c.whitepoint, white_x, white_y, white_z - );*/ + // We're using Whitepoint::D65, but there is no D50 profile. + // If we use the BRUCE_XYZ_RGB_D65 matrix the image + // comes out too warm. + let to_srgb = Matrix3::from_row_slice(&BRUCE_XYZ_RGB_D50); let premul = to_xyz * to_srgb; - let prenorm = premul.normalize(); - //println!("{prenorm}"); - for chnk in floats.chunks_mut(3) { - let r = chnk[0] * (1.0 / c.rg); + /*let r = chnk[0] * (1.0 / c.rg); + let g = chnk[1]; + let b = chnk[2] * (1.0 / c.bg);*/ + let r = chnk[0] * awb_gain.r; let g = chnk[1]; - let b = chnk[2] * (1.0 / c.bg); + let b = chnk[2] * awb_gain.b; let px = Matrix3x1::new(r, g, b); - - //let rgb = premul * px; - //let px = color * px; - let xyz = to_xyz * px; - //let xyz = d50_d65 * xyz; - //let xyz_white = color * xyz; - let rgb = to_srgb * xyz; + let rgb = premul * px; chnk[0] = srgb_gamma(rgb[0]) * 255.0; chnk[1] = srgb_gamma(rgb[1]) * 255.0; @@ -171,14 +139,14 @@ fn make(img: &RawImage, path: String) { } } None => { - println!("\tno color profile found"); + println!("\tColor profile for D65 not found. Doing gamma and nothing else!"); floats.iter_mut().for_each(|f| *f = srgb_gamma(*f) * 255.0); } } let bytes: Vec = floats.into_iter().map(|f| f as u8).collect(); - println!("Writing {}", &path); + println!("\tWriting {}", &path); make_png(path, *width, *height, &bytes, color_format) } @@ -215,19 +183,22 @@ pub fn srgb_gamma(mut float: f32) -> f32 { float.clamp(0.0, 1.0) } -fn bayer(data: &RawData<'_>, width: usize, height: usize, path: String) -> Vec { +fn bayer(data: &RawData<'_>, width: usize, height: usize) -> Vec { match data { RawData::Packed10bpp { data } => { - // Assume 10-bit let size = width * height; let mut ten_data = vec![0; size]; unpack::tenbit(data, width * height, ten_data.as_mut_slice()); // I've only seen it on one color defintion or - // something, but there's a black level of 42, so subtract it + // something, but there's a black level of 42, so subtract it. + // without it the image is entirely too red. //ten_data.iter_mut().for_each(|p| *p = p.saturating_sub(42)); - ten_data.into_iter().map(|p| (p >> 2) as u8).collect() + ten_data + .into_iter() + .map(|p| ((p.saturating_sub(42)) >> 2) as u8) + .collect() } RawData::BayerJpeg { header: _, -- cgit 1.4.1-3-g733a5