about summary refs log tree commit diff
path: root/prism
diff options
context:
space:
mode:
authorgennyble <gen@nyble.dev>2023-09-11 00:02:34 -0500
committergennyble <gen@nyble.dev>2023-09-11 00:02:34 -0500
commit22b07838e7d83998bc9d46e219410d1431527687 (patch)
treea555d319221b9f6c2ab17808d2ea4b6f0244765a /prism
parent33fcfd870447724f17f868da082a74ce9fcec7cd (diff)
downloadlri-rs-22b07838e7d83998bc9d46e219410d1431527687.tar.gz
lri-rs-22b07838e7d83998bc9d46e219410d1431527687.zip
ahh
Diffstat (limited to 'prism')
-rw-r--r--prism/Cargo.toml3
-rw-r--r--prism/src/main.rs198
-rw-r--r--prism/src/unpack.rs9
3 files changed, 158 insertions, 52 deletions
diff --git a/prism/Cargo.toml b/prism/Cargo.toml
index 7301109..4601f43 100644
--- a/prism/Cargo.toml
+++ b/prism/Cargo.toml
@@ -8,3 +8,6 @@ edition = "2021"
 [dependencies]
 lri-rs = { path = "../lri-rs" }
 png = "0.17.10"
+rawproc = { git = "https://github.com/eclecticnybles/gaze" }
+rawloader = "0.37.1"
+nalgebra = "0.31.4"
diff --git a/prism/src/main.rs b/prism/src/main.rs
index 52d8860..37b2ff6 100644
--- a/prism/src/main.rs
+++ b/prism/src/main.rs
@@ -1,77 +1,181 @@
-use lri_rs::LriFile;
+use lri_rs::{LriFile, RawImage, Whitepoint};
+use nalgebra::{Matrix3, Matrix3x1};
+
+mod unpack;
 
 fn main() {
 	let file_name = std::env::args().nth(1).unwrap();
 	let bytes = std::fs::read(file_name).unwrap();
-	let lri = LriFile::decode(bytes);
+	let lri = LriFile::decode(&bytes);
 
-	println!("{} blocks", lri.blocks.len());
 	println!("{} images", lri.image_count());
+
+	for (idx, img) in lri.images().enumerate() {
+		make(img, format!("image_{idx}.png"));
+	}
 }
 
-/*fn good(models: &[&SensorModel], img: RawImage, img_id: usize) {
+// R G R G
+// G B G B
+// R G R G
+
+const CFAS: &[&'static str] = &["RGGB", "GRBG", "GBRG", "BGGR"];
+
+fn make(img: &RawImage, path: String) {
+	use rawproc::image::RawMetadata;
+	use rawproc::{colorspace::BayerRgb, image::Image};
+
 	let RawImage {
-		sensor_id,
+		camera,
+		sensor,
 		width,
 		height,
 		format,
 		data,
+		sbro,
+		color,
 	} = img;
 
 	println!(
-		"{sensor_id} {width}x{height} {format} - {} kB",
-		data.len() / 1024
+		"{camera} {sensor:?} [{}:{}] {width}x{height} {format}",
+		sbro.0, sbro.1
 	);
-	return;
 
-	for model in models {
-		println!("{:?}", model.whitepoint);
+	// 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
+
+	let (rgb, color_format) = match img.cfa_string() {
+		Some(cfa_string) => {
+			let rawimg: Image<u16, BayerRgb> = Image::from_raw_parts(
+				4160,
+				3120,
+				// We only care about CFA here because all we're doing is debayering
+				RawMetadata {
+					whitebalance: [1.0; 3],
+					whitelevels: [1024; 3],
+					crop: None,
+					// ugh CFA isn't exposed, so we pulled in rawloader for now
+					cfa: rawloader::CFA::new(cfa_string),
+					cam_to_xyz: nalgebra::Matrix3::zeros(),
+				},
+				ten_data,
+			);
+
+			(rawimg.debayer().data, png::ColorType::Rgb)
+		}
+		None => (ten_data, png::ColorType::Grayscale),
+	};
+
+	let mut floats: Vec<f32> = rgb.into_iter().map(|p| p as f32 / 1023.0).collect();
+
+	print!("\t");
+	color.iter().for_each(|c| print!("{:?} ", c.whitepoint));
+	println!();
+
+	match img.daylight() {
+		Some(c) => {
+			//println!("\tApplying color profile: {:?}", c.color_matrix);
+			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 premul = to_xyz * to_srgb;
+
+			for chnk in floats.chunks_mut(3) {
+				let r = chnk[0] * (1.0 / c.rg);
+				let g = chnk[1];
+				let b = chnk[2] * (1.0 / c.bg);
+
+				let px = Matrix3x1::new(r, g, b);
+
+				//let rgb = premul * px;
+				let xyz = to_xyz * px;
+				let rgb = to_srgb * xyz;
+
+				chnk[0] = srgb_gamma(rgb[0]) * 255.0;
+				chnk[1] = srgb_gamma(rgb[1]) * 255.0;
+				chnk[2] = srgb_gamma(rgb[2]) * 255.0;
+			}
+		}
+		None => {
+			println!("\tno color profile found");
+			floats.iter_mut().for_each(|f| *f = srgb_gamma(*f) * 255.0);
+		}
 	}
 
-	for color in models {
-		let size = width * height;
-		let mut ten_data = vec![0; size];
-		crate::unpack::tenbit(data, width * height, ten_data.as_mut_slice());
-
-		let mut rawimg: Image<u16, BayerRgb> = Image::from_raw_parts(
-			4160,
-			3120,
-			RawMetadata {
-				whitebalance: [1.0 / color.rg, 1.0, 1.0 / color.bg],
-				whitelevels: [1024, 1024, 1024],
-				crop: None,
-				cfa: CFA::new("BGGR"),
-				cam_to_xyz: Matrix3::from_row_slice(&color.forward_matrix),
-			},
-			ten_data,
-		);
-
-		/*rawimg
-		.data
-		.iter_mut()
-		.for_each(|p| *p = p.saturating_sub(42));*/
-
-		rawimg.whitebalance();
-		let img = rawimg.debayer();
-		let srgb = img.to_xyz().to_linsrgb().gamma();
-		let bytes = srgb.floats().bytes();
-
-		make_png(
-			format!("tenbit_{img_id}_{:?}.png", color.whitepoint),
-			width,
-			height,
-			&bytes.data,
-		);
+	let bytes: Vec<u8> = floats.into_iter().map(|f| f as u8).collect();
+
+	println!("Writing {}", &path);
+	make_png(path, *width, *height, &bytes, color_format)
+}
+
+#[rustfmt::skip]
+#[allow(dead_code)]
+const BRUCE_XYZ_RGB_D50: [f32; 9] = [
+	3.1338561,  -1.6168667, -0.4906146,
+	-0.9787684,  1.9161415,  0.0334540,
+	0.0719453,  -0.2289914,  1.4052427
+];
+
+#[rustfmt::skip]
+const BRUCE_XYZ_RGB_D65: [f32; 9] = [
+	3.2404542,  -1.5371385, -0.4985314,
+	-0.9692660,  1.8760108,  0.0415560,
+ 	0.0556434,  -0.2040259,  1.0572252
+];
+
+#[inline]
+pub fn srgb_gamma(mut float: f32) -> f32 {
+	if float <= 0.0031308 {
+		float *= 12.92;
+	} else {
+		float = float.powf(1.0 / 2.4) * 1.055 - 0.055;
 	}
-}*/
 
-fn make_png<P: AsRef<std::path::Path>>(path: P, width: usize, height: usize, data: &[u8]) {
+	float.clamp(0.0, 1.0)
+}
+
+fn make_png<P: AsRef<std::path::Path>>(
+	path: P,
+	width: usize,
+	height: usize,
+	data: &[u8],
+	color_format: png::ColorType,
+) {
 	//return;
 	use std::fs::File;
 
 	let file = File::create(path).unwrap();
 	let mut enc = png::Encoder::new(file, width as u32, height as u32);
-	enc.set_color(png::ColorType::Rgb);
+	enc.set_color(color_format);
 	enc.set_depth(png::BitDepth::Eight);
 	let mut writer = enc.write_header().unwrap();
 	writer.write_image_data(data).unwrap();
diff --git a/prism/src/unpack.rs b/prism/src/unpack.rs
index 03e0018..bc761f3 100644
--- a/prism/src/unpack.rs
+++ b/prism/src/unpack.rs
@@ -3,11 +3,6 @@ const TEN_MASK: u64 = 1023; // ten bits
 pub fn tenbit(packd: &[u8], count: usize, upack: &mut [u16]) {
 	let required_len_packd = (count as f32 * (10.0 / 8.0)).ceil() as usize;
 
-	println!(
-		"requires {required_len_packd} bytes | {} groups of 5",
-		count / 4
-	);
-
 	if count > upack.len() {
 		panic!(
 			"expected output buffer to be {count} bytes, got {} bytes",
@@ -60,3 +55,7 @@ pub fn tenbit(packd: &[u8], count: usize, upack: &mut [u16]) {
 		}
 	}
 }
+
+//pub fn twelvebit(packed: &[u8]) {
+// 3 bytes per 2 12-bits
+//}