about summary refs log tree commit diff
path: root/prism/src
diff options
context:
space:
mode:
authorgennyble <gen@nyble.dev>2023-11-17 07:59:54 -0600
committergennyble <gen@nyble.dev>2023-11-17 07:59:54 -0600
commit7553dce9dccb7e93efc2ac14e004163e5d2862d4 (patch)
tree5717fdc3eeb75d3435e7174c899ced0c01de0420 /prism/src
parent8317df85c6b2b20aa5935ddfd23b0cf2a60e08e6 (diff)
downloadlri-rs-7553dce9dccb7e93efc2ac14e004163e5d2862d4.tar.gz
lri-rs-7553dce9dccb7e93efc2ac14e004163e5d2862d4.zip
prism produces reasonable images
Diffstat (limited to 'prism/src')
-rw-r--r--prism/src/main.rs123
1 files changed, 47 insertions, 76 deletions
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 <lri_file> <output_directory>");
+		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<CameraId, Entry> = 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<f32> = 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<u8> = 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<u8> {
+fn bayer(data: &RawData<'_>, width: usize, height: usize) -> Vec<u8> {
 	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: _,