From 4b0f26eac1ad6de9526687fdf2999eeae2bc67a6 Mon Sep 17 00:00:00 2001 From: gennyble Date: Tue, 12 Sep 2023 04:13:13 -0500 Subject: please don't hate me --- .gitignore | 4 +- Cargo.lock | 106 +++++++++++++++++++++++++ Cargo.toml | 2 +- bayer_jpeg.md | 29 +++++++ lri-rs/src/block.rs | 106 ++++++++++++++++++++++--- lri-rs/src/lib.rs | 68 ++++++++++------ lri-study/Cargo.toml | 11 +++ lri-study/src/main.rs | 139 ++++++++++++++++++++++++++++++++ prism/src/main.rs | 215 ++++++++++++++++++++++++++------------------------ 9 files changed, 542 insertions(+), 138 deletions(-) create mode 100644 bayer_jpeg.md create mode 100644 lri-study/Cargo.toml create mode 100644 lri-study/src/main.rs diff --git a/.gitignore b/.gitignore index 0036efe..4064ec8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ /target *.png -lri-study \ No newline at end of file +*.jpg +*.bjp +**/.DS_Store \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index c91bb1d..32a9ec3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -32,6 +32,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "autocfg" version = "1.1.0" @@ -68,12 +74,19 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +[[package]] +name = "camino" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" + [[package]] name = "cc" version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ + "jobserver", "libc", ] @@ -135,6 +148,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + [[package]] name = "either" version = "1.9.0" @@ -246,6 +265,15 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "jobserver" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +dependencies = [ + "libc", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -285,6 +313,15 @@ dependencies = [ "lri-proto", ] +[[package]] +name = "lri-study" +version = "0.1.0" +dependencies = [ + "camino", + "lri-rs", + "owo-colors", +] + [[package]] name = "matrixmultiply" version = "0.3.7" @@ -320,6 +357,30 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "mozjpeg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84edaffd54775e831923ce65f73288793c80fe98771b0be9eadaf753b29792fc" +dependencies = [ + "arrayvec", + "libc", + "mozjpeg-sys", + "rgb", +] + +[[package]] +name = "mozjpeg-sys" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dab8f5496b7f0e8c593d33dbbbc16c6eefd3a6991d794f56e96bebc5228cfd29" +dependencies = [ + "cc", + "dunce", + "libc", + "nasm-rs", +] + [[package]] name = "nalgebra" version = "0.31.4" @@ -347,6 +408,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "nasm-rs" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4d98d0065f4b1daf164b3eafb11974c94662e5e2396cf03f32d0bb5c17da51" +dependencies = [ + "rayon", +] + [[package]] name = "num-complex" version = "0.4.4" @@ -402,6 +472,12 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + [[package]] name = "paste" version = "1.0.14" @@ -432,10 +508,12 @@ name = "prism" version = "0.1.0" dependencies = [ "lri-rs", + "mozjpeg", "nalgebra", "png", "rawloader", "rawproc", + "zune-jpeg", ] [[package]] @@ -631,6 +709,15 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +[[package]] +name = "rgb" +version = "0.8.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20ec2d3e3fc7a92ced357df9cebd5a10b6fb2aa1ee797bf7e9ce2f17dffc8f59" +dependencies = [ + "bytemuck", +] + [[package]] name = "rustc_version" version = "0.4.0" @@ -882,3 +969,22 @@ name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "zune-core" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ca36c2e02af0d8d7ee977542bfe33ed1c516be73d3c1faa4420af46e96ceee" +dependencies = [ + "bitflags 2.4.0", +] + +[[package]] +name = "zune-jpeg" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2848e8f4f29dbdcc79910ab3abdff22bb0bacef8556f2a983b5ca950d8b4991e" +dependencies = [ + "log", + "zune-core", +] diff --git a/Cargo.toml b/Cargo.toml index a33de68..49e3aa4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,3 @@ [workspace] -members = ["lri-proto", "lri-rs", "prism"] +members = ["lri-proto", "lri-rs", "prism", "lri-study"] resolver = "2" diff --git a/bayer_jpeg.md b/bayer_jpeg.md new file mode 100644 index 0000000..94f7a6b --- /dev/null +++ b/bayer_jpeg.md @@ -0,0 +1,29 @@ +# BayerJPEG +Th BayerJPEG is a strange format used by the Light L16... *sometimes*. We don't yet know when it switches from it's normal packed 10-bit raw format. + +| size | type | meaning | +| ------- | ------ | ------- | +| 4 bytes | String | Magic Number "BJPG" | +| 4 bytes | u32 | *Format type*
0: colour
1: for monochrome | +| 4 bytes | u32 | Length of Jpeg 0 | +| 4 bytes | u32 | Length of Jpeg 1 | +| 4 bytes | u32 | Length of Jpeg 2 | +| 4 bytes | u32 | Length of Jpeg 3 | +| 1552 bytes | | unknown | + +***Monochrome*** +Jpeg0 contains a full resolution grayscale image + +***Colour*** +The bayered image is split across the four Jpeg, one +for each colour location. + +I.E. an image from the ar1335 sensor, color filter bggr, you'd get +- 1 jpeg for the blue channel +- 2 jpeg for each green location +- 1 jpeg for the red channel + +It's not currently known if these are in the order you'd expect. + +***Considerations*** +When the L16 decides to use BayerJPEG, it has to save four copies of each frame. A JPEG is limited to a bit depth of eight, but the sensors output 10-bit data. In order to not loose 75% of the precision, they seemingly divide the image into fours and expect you to sum them later. \ No newline at end of file diff --git a/lri-rs/src/block.rs b/lri-rs/src/block.rs index 2dc921b..4ee0ea1 100644 --- a/lri-rs/src/block.rs +++ b/lri-rs/src/block.rs @@ -1,9 +1,11 @@ +use std::time::Duration; + use lri_proto::{ gps_data::GPSData, lightheader::LightHeader, matrix3x3f::Matrix3x3F, view_preferences::ViewPreferences, Message as PbMessage, }; -use crate::{CameraId, CameraInfo, ColorInfo, RawImage, SensorModel}; +use crate::{CameraId, CameraInfo, ColorInfo, DataFormat, RawData, RawImage, SensorModel}; pub(crate) struct Block<'lri> { pub header: Header, @@ -37,24 +39,27 @@ impl<'lri> Block<'lri> { pub fn extract_meaningful_data( &self, + ext: &mut ExtractedData, images: &mut Vec>, colors: &mut Vec, infos: &mut Vec, - ) -> ExtractedData { - let mut ext = ExtractedData { - reference_camera: None, - }; - + ) { let LightHeader { mut hw_info, - mut module_calibration, - mut modules, - mut image_reference_camera, + module_calibration, + modules, + image_reference_camera, + device_fw_version, + image_focal_length, + af_info, .. } = if let Message::LightHeader(lh) = self.message() { lh + } else if let Message::ViewPreferences(vp) = self.message() { + self.extract_view(vp, ext); + return; } else { - return ext; + return; }; // Form the CameraInfo struct for mapping CameraId to SensorType @@ -117,7 +122,51 @@ impl<'lri> Block<'lri> { let data_length = surface.row_stride() as usize * height; let format = surface.format().into(); - let image_data = &self.data[offset..offset + data_length]; + let image_data = match format { + DataFormat::BayerJpeg => { + let bjpg_header_len = 1576; + let mut wrk = &self.data[offset..]; + + let format = u32::from_le_bytes(wrk[4..8].try_into().unwrap()); + + let jpeg0_len = u32::from_le_bytes(wrk[8..12].try_into().unwrap()) as usize; + let jpeg1_len = u32::from_le_bytes(wrk[12..16].try_into().unwrap()) as usize; + let jpeg2_len = u32::from_le_bytes(wrk[16..20].try_into().unwrap()) as usize; + let jpeg3_len = u32::from_le_bytes(wrk[20..24].try_into().unwrap()) as usize; + + let mut get = |len: usize| -> &[u8] { + let data = &wrk[..len]; + wrk = &wrk[len..]; + data + }; + + let header = get(bjpg_header_len); + let jpeg0 = get(jpeg0_len); + + match format { + 1 => RawData::BayerJpeg { + header, + format, + jpeg0, + jpeg1: &wrk[0..0], + jpeg2: &wrk[0..0], + jpeg3: &wrk[0..0], + }, + 0 => RawData::BayerJpeg { + header, + format, + jpeg0, + jpeg1: get(jpeg1_len), + jpeg2: get(jpeg2_len), + jpeg3: get(jpeg3_len), + }, + _ => unreachable!(), + } + } + DataFormat::Packed10bpp => RawData::Packed10bpp { + data: &self.data[offset..offset + data_length], + }, + }; let sbro = module.sensor_bayer_red_override.clone().unwrap(); @@ -139,7 +188,17 @@ impl<'lri> Block<'lri> { ext.reference_camera = Some(irc.into()); } - ext + if let Some(afd) = af_info.clone().take() { + ext.af_achieved.get_or_insert(afd.focus_achieved()); + } + + if let Some(fwv) = device_fw_version { + ext.fw_version.get_or_insert(fwv); + } + + if let Some(x) = image_focal_length { + ext.focal_length.get_or_insert(x); + } } // It kept making my neat little array very, very tall @@ -151,10 +210,33 @@ impl<'lri> Block<'lri> { mat.x20(), mat.x21(), mat.x22(), ] } + + fn extract_view(&self, vp: ViewPreferences, ext: &mut ExtractedData) { + let ViewPreferences { + image_integration_time_ns, + image_gain, + .. + } = vp; + + if let Some(ns) = image_integration_time_ns { + ext.image_integration_time = Some(Duration::from_nanos(ns)); + } + + if let Some(g) = image_gain { + ext.image_gain.get_or_insert(g); + } + } } +#[derive(Debug, Default)] pub(crate) struct ExtractedData { pub reference_camera: Option, + pub fw_version: Option, + pub focal_length: Option, + + pub image_gain: Option, + pub image_integration_time: Option, + pub af_achieved: Option, } pub enum Message { diff --git a/lri-rs/src/lib.rs b/lri-rs/src/lib.rs index 44b2466..0bc5a11 100644 --- a/lri-rs/src/lib.rs +++ b/lri-rs/src/lib.rs @@ -1,4 +1,4 @@ -use std::fmt; +use std::{fmt, time::Duration}; use block::{Block, ExtractedData, Header}; use lri_proto::{ @@ -13,16 +13,23 @@ pub struct LriFile<'lri> { pub images: Vec>, pub colors: Vec, pub camera_infos: Vec, + + pub focal_length: Option, + pub firmware_version: Option, + pub image_integration_time: Option, + pub af_achieved: Option, + pub image_gain: Option, } impl<'lri> LriFile<'lri> { /// Read pub fn decode(mut data: &'lri [u8]) -> Self { - let mut reference = None; let mut images = vec![]; let mut colors = vec![]; let mut camera_infos = vec![]; + let mut ext = ExtractedData::default(); + // Read data blocks and extract informtion we care about loop { if data.len() == 0 { @@ -40,14 +47,7 @@ impl<'lri> LriFile<'lri> { data: block_data, }; - match block.extract_meaningful_data(&mut images, &mut colors, &mut camera_infos) { - ExtractedData { - reference_camera: Some(irc), - } => { - reference = Some(irc); - } - _ => (), - } + block.extract_meaningful_data(&mut ext, &mut images, &mut colors, &mut camera_infos); } // Further fill in the RawImage's we extracted @@ -66,10 +66,16 @@ impl<'lri> LriFile<'lri> { } LriFile { - image_reference_camera: reference, + image_reference_camera: ext.reference_camera, images, colors, camera_infos, + + firmware_version: ext.fw_version, + focal_length: ext.focal_length, + image_integration_time: ext.image_integration_time, + af_achieved: ext.af_achieved, + image_gain: ext.image_gain, } } @@ -92,6 +98,20 @@ impl<'lri> LriFile<'lri> { } } +pub enum RawData<'img> { + BayerJpeg { + header: &'img [u8], + format: u32, + jpeg0: &'img [u8], + jpeg1: &'img [u8], + jpeg2: &'img [u8], + jpeg3: &'img [u8], + }, + Packed10bpp { + data: &'img [u8], + }, +} + pub struct RawImage<'img> { /// Camera that captured this image pub camera: CameraId, @@ -101,10 +121,9 @@ pub struct RawImage<'img> { pub width: usize, pub height: usize, - /// How the image data is encoded in the file + /// What format the data is in pub format: DataFormat, - /// Image data - pub data: &'img [u8], + pub data: RawData<'img>, /// "sensor bayer red offset" pub sbro: (i32, i32), /// All color information associated with this [CameraId] for different [Whitepoint]s @@ -135,6 +154,9 @@ impl<'img> RawImage<'img> { // The AR1335 seems to be BGGR, which was weird. fn cfa_string_ar1335(&self) -> Option<&'static str> { + //if self.format == DataFormat::BayerJpeg { + // Some("BGGR") + //} else { match self.sbro { (-1, -1) => None, (0, 0) => Some("BGGR"), @@ -143,6 +165,7 @@ impl<'img> RawImage<'img> { (1, 1) => Some("RGGB"), _ => unreachable!(), } + //} } /// Uses the [SensorModel] to determine if the image's [ColorType]. @@ -192,14 +215,15 @@ pub struct CameraInfo { sensor: SensorModel, } -#[derive(Copy, Clone, Debug)] +#[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, - Packed12bpp, - Packed14bpp, + // Never seen + //Packed12bpp, + //Packed14bpp, } impl fmt::Display for DataFormat { @@ -207,8 +231,8 @@ impl fmt::Display for DataFormat { let str = match self { Self::BayerJpeg => "BayerJpeg", Self::Packed10bpp => "Packed10bpp", - Self::Packed12bpp => "Packed12bpp", - Self::Packed14bpp => "Packed14bpp", + //Self::Packed12bpp => "Packed12bpp", + //Self::Packed14bpp => "Packed14bpp", }; write!(f, "{str}") @@ -220,8 +244,8 @@ impl From for DataFormat { match proto { FormatType::RAW_BAYER_JPEG => Self::BayerJpeg, FormatType::RAW_PACKED_10BPP => Self::Packed10bpp, - FormatType::RAW_PACKED_12BPP => Self::Packed12bpp, - FormatType::RAW_PACKED_14BPP => Self::Packed14bpp, + FormatType::RAW_PACKED_12BPP => unreachable!(), + FormatType::RAW_PACKED_14BPP => unreachable!(), FormatType::RAW_RESERVED_0 | FormatType::RAW_RESERVED_1 | FormatType::RAW_RESERVED_2 @@ -232,7 +256,7 @@ impl From for DataFormat { } } -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum CameraId { A1, A2, diff --git a/lri-study/Cargo.toml b/lri-study/Cargo.toml new file mode 100644 index 0000000..f92b7de --- /dev/null +++ b/lri-study/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "lri-study" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +camino = "1.1.6" +lri-rs = { path = "../lri-rs" } +owo-colors = "3.5.0" diff --git a/lri-study/src/main.rs b/lri-study/src/main.rs new file mode 100644 index 0000000..3e3f225 --- /dev/null +++ b/lri-study/src/main.rs @@ -0,0 +1,139 @@ +use std::{ + collections::HashMap, + time::{Duration, Instant}, +}; + +use camino::Utf8PathBuf; +use lri_rs::{DataFormat, LriFile, SensorModel}; +use owo_colors::OwoColorize; + +const DATA: &'static str = "/Users/gen/thanks_lak"; + +fn main() { + match std::env::args().nth(1).as_deref() { + Some("gather") => gather(), + _ => (), + } +} + +fn gather() -> ! { + let data_dir = Utf8PathBuf::from(DATA); + let mut files: HashMap = HashMap::new(); + + for entry in data_dir.read_dir_utf8().unwrap() { + let entry = entry.unwrap(); + let meta = entry.metadata().unwrap(); + let path = entry.path(); + + if meta.is_file() { + let stub = path.file_stem().unwrap().to_owned(); + + match path.extension() { + Some("jpg") => files + .entry(stub.clone()) + .and_modify(|e| e.jpg = Some(path.to_owned())) + .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)), + Some("lris") => files + .entry(stub.clone()) + .and_modify(|e| e.lris = Some(path.to_owned())) + .or_insert(Photo::new_lris(&path)), + None | Some(_) => continue, + }; + } + } + + let start = Instant::now(); + + 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 photo in photos { + let lri_path = match photo.lri { + Some(p) => p, + None => continue, + }; + let data = match std::fs::read(&lri_path) { + Ok(d) => d, + Err(e) => { + println!("{}: {}", lri_path.red(), e); + continue; + } + }; + let lri = LriFile::decode(&data); + + print!("{} - ", lri_path.file_stem().unwrap()); + + if let Some(fwv) = lri.firmware_version.as_ref() { + print!( + "[{}] focal:{:<3} iit:{:>2}ms gain:{:2.0} ", + fwv, + lri.focal_length.unwrap(), + lri.image_integration_time + .unwrap_or(Duration::ZERO) + .as_millis(), + lri.image_gain.unwrap_or_default() + ); + + match lri.af_achieved { + None => print!("{} - ", "af".dimmed()), + Some(false) => print!("{} - ", "af".red()), + Some(true) => print!("{} - ", "af".green()), + } + } + + for img in lri.images() { + let sens = match img.sensor { + SensorModel::Ar1335 => "a13", + SensorModel::Ar1335Mono => "a1m", + SensorModel::Ar835 => "!!!ar8", + SensorModel::Imx386 => "!!!imx", + SensorModel::Imx386Mono => "!!!imm", + SensorModel::Unknown => "???", + }; + + match img.format { + DataFormat::BayerJpeg => print!("{} ", sens.cyan()), + DataFormat::Packed10bpp => print!("{} ", sens.yellow()), + } + } + println!(""); + } + + println!(" ---\nTook {:.2}s", start.elapsed().as_secs_f32()); + + std::process::exit(0) +} + +struct Photo { + jpg: Option, + lri: Option, + lris: Option, +} + +impl Photo { + pub fn new_jpg>(jpg: P) -> Self { + Self { + jpg: Some(jpg.into()), + lri: None, + lris: None, + } + } + pub fn new_lri>(lri: P) -> Self { + Self { + lri: Some(lri.into()), + jpg: None, + lris: None, + } + } + pub fn new_lris>(lris: P) -> Self { + Self { + lris: Some(lris.into()), + lri: None, + jpg: None, + } + } +} diff --git a/prism/src/main.rs b/prism/src/main.rs index 60baa18..e0da42a 100644 --- a/prism/src/main.rs +++ b/prism/src/main.rs @@ -1,9 +1,16 @@ -use lri_rs::{DataFormat, LriFile, RawImage, Whitepoint}; +use std::collections::HashMap; + +use lri_rs::{CameraId, DataFormat, LriFile, RawData, RawImage, SensorModel, Whitepoint}; use nalgebra::{Matrix3, Matrix3x1}; mod rotate; mod unpack; +pub struct Entry { + sensor: SensorModel, + count: usize, +} + fn main() { let file_name = std::env::args().nth(1).unwrap(); let bytes = std::fs::read(file_name).unwrap(); @@ -14,6 +21,21 @@ fn main() { lri.reference_image() .map(|raw| make(raw, String::from("reference.png"))); + let mut set: HashMap = HashMap::new(); + + for img in lri.images() { + set.entry(img.camera) + .and_modify(|e| e.count += 1) + .or_insert(Entry { + sensor: img.sensor, + count: 1, + }); + } + + 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!( @@ -35,7 +57,7 @@ fn main() { //std::process::exit(0); make(img, format!("image_{idx}.png")); - return; + //return; } } @@ -49,7 +71,7 @@ fn make(img: &RawImage, path: String) { width, height, format, - mut data, + data, sbro, color, } = img; @@ -59,99 +81,18 @@ fn make(img: &RawImage, path: String) { sbro.0, sbro.1 ); - let stem = &path[..path.len() - 4]; - - if *format == DataFormat::BayerJpeg { - //FF d9 | 42 4A 50 47 - let bjp_end = &[0xFF, 0xd9, 0x42, 0x4A, 0x50, 0x47]; - let jfif_start = &[0xFF, 0xD8, 0xFF, 0xE0]; - let jfif_end = &[0xFF, 0xD9]; - - for idx in 0..data.len() { - if &data[idx..idx + 6] == bjp_end { - data = &data[..idx + 2]; - break; - } - } - - let mut start = None; - let mut idx = 0; - let mut jfif_count = 0; - loop { - if idx >= data.len() { - break; - } - - match start { - None => { - if &data[idx..idx + 4] == jfif_start { - start = Some(idx); - - if jfif_count == 0 { - let path = format!("{stem}_only.bjp"); - let out = &data[..idx]; - std::fs::write(path, out).unwrap(); - } - - idx += 4; - continue; - } - } - Some(start_idx) => { - if &data[idx..idx + 2] == jfif_end { - let path = format!("{stem}_{jfif_count}.jpg"); - let out = &data[start_idx..idx + 2]; - std::fs::write(path, out).unwrap(); - - start = None; - jfif_count += 1; - idx += 2; - continue; - } - } - } - - idx += 1; - } - - std::fs::write(format!("{}.bjp", &path[..path.len() - 4]), data).unwrap(); - return; - } + let mut bayered = bayer( + data, + *width, + *height, + format!("{}_bjpg", &path[..path.len() - 4]), + ); - // 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 - ten_data.iter_mut().for_each(|p| *p = p.saturating_sub(42)); - - // B G B G B G - // G R G R G R - - // A1 - 1:0 - // A2 - -1:-1 - // A3 - 1:0 - // A4 - 1:0 - // A5 - 0:1 - - // B1 - NO - // B2 - RO - // B3 - RO - // B4 - RO - // B5 - NO - - // C1 - NO - // C2 - RO - // C3 - NO - // C4 - RO - // C5 - RO - // C6 - -1:-1 + bayered.iter_mut().for_each(|p| *p = p.saturating_sub(42)); let (mut rgb, color_format) = match img.cfa_string() { Some(cfa_string) => { - let rawimg: Image = Image::from_raw_parts( + let rawimg: Image = Image::from_raw_parts( 4160, 3120, // We only care about CFA here because all we're doing is debayering @@ -163,21 +104,24 @@ fn make(img: &RawImage, path: String) { cfa: rawloader::CFA::new(cfa_string), cam_to_xyz: nalgebra::Matrix3::zeros(), }, - ten_data, + bayered, ); (rawimg.debayer().data, png::ColorType::Rgb) + //(bayered, png::ColorType::Grayscale) } - None => (ten_data, png::ColorType::Grayscale), + None => (bayered, png::ColorType::Grayscale), }; rotate::rotate_180(rgb.as_mut_slice()); - let mut floats: Vec = rgb.into_iter().map(|p| p as f32 / 1023.0).collect(); + let mut floats: Vec = rgb.into_iter().map(|p| p as f32 / 255.0).collect(); - print!("\t"); - color.iter().for_each(|c| print!("{:?} ", c.whitepoint)); - println!(); + if color.len() > 0 { + print!("\t"); + color.iter().for_each(|c| print!("{:?} ", c.whitepoint)); + println!(); + } match img.color_info(Whitepoint::F11) { Some(c) => { @@ -189,7 +133,7 @@ fn make(img: &RawImage, path: String) { let xyz_d65 = to_xyz * d50_d65; - println!("{color}"); + //println!("{color}"); let white = xyz_d65 * Matrix3x1::new(1.0, 1.0, 1.0); @@ -197,15 +141,15 @@ fn make(img: &RawImage, path: String) { let white_y = white[1] / (white[0] + white[1] + white[2]); let white_z = 1.0 - white_x - white_y; - println!( + /*println!( "\t{:?} ||| white: x = {} y = {} z = {}", c.whitepoint, white_x, white_y, white_z - ); + );*/ let premul = to_xyz * to_srgb; let prenorm = premul.normalize(); - println!("{prenorm}"); + //println!("{prenorm}"); for chnk in floats.chunks_mut(3) { let r = chnk[0] * (1.0 / c.rg); @@ -271,6 +215,73 @@ 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 { + 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 + //ten_data.iter_mut().for_each(|p| *p = p.saturating_sub(42)); + + ten_data.into_iter().map(|p| (p >> 2) as u8).collect() + } + RawData::BayerJpeg { + header: _, + format, + jpeg0, + jpeg1, + jpeg2, + jpeg3, + } => { + let mut bayered = vec![0; width * height]; + + match format { + 0 => { + let mut into = vec![0; (width * height) / 4]; + + let mut channel = |jpeg: &[u8], offset: usize| { + zune_jpeg::JpegDecoder::new(jpeg) + .decode_into(&mut into) + .unwrap(); + + for idx in 0..into.len() { + let ww = width / 2; + let in_x = idx % ww; + let in_y = idx / ww; + + let bayer_x = (in_x * 2) + (offset % 2); + let bayer_y = (in_y * 2) + (offset / 2); + + let bayer_idx = bayer_y * width + bayer_x; + bayered[bayer_idx] = into[idx]; + } + }; + + //BGGR + //RGGB + //GRBG + channel(jpeg0, 0); + channel(jpeg1, 1); + channel(jpeg2, 2); + channel(jpeg3, 3); + } + 1 => { + zune_jpeg::JpegDecoder::new(jpeg0) + .decode_into(&mut bayered) + .unwrap(); + } + _ => unreachable!(), + } + + bayered + } + } +} + fn make_png>( path: P, width: usize, -- cgit 1.4.1-3-g733a5