about summary refs log tree commit diff
path: root/prism/src
diff options
context:
space:
mode:
Diffstat (limited to 'prism/src')
-rw-r--r--prism/src/main.rs220
-rw-r--r--prism/src/output.rs240
-rw-r--r--prism/src/unpack.rs61
3 files changed, 243 insertions, 278 deletions
diff --git a/prism/src/main.rs b/prism/src/main.rs
index 43278a6..8a52e0f 100644
--- a/prism/src/main.rs
+++ b/prism/src/main.rs
@@ -4,8 +4,8 @@ use camino::Utf8PathBuf;
 use lri_rs::{AwbGain, CameraId, LriFile, RawData, RawImage, SensorModel, Whitepoint};
 use nalgebra::{Matrix3, Matrix3x1};
 
+mod output;
 mod rotate;
-mod unpack;
 
 pub struct Entry {
 	sensor: SensorModel,
@@ -34,7 +34,7 @@ fn main() {
 	println!("{} images", lri.image_count());
 
 	if let Some(refimg) = lri.reference_image() {
-		make(refimg, directory.join("reference.png"), gain);
+		output::make_png(refimg, directory.join("reference.png"), gain);
 	}
 
 	let mut set: HashMap<CameraId, Entry> = HashMap::new();
@@ -53,220 +53,6 @@ fn main() {
 	});*/
 
 	for (idx, img) in lri.images().enumerate() {
-		make(img, directory.join(format!("image_{idx}.png")), gain);
+		output::make_png(img, directory.join(format!("image_{idx}.png")), gain);
 	}
 }
-
-fn make(img: &RawImage, path: Utf8PathBuf, awb_gain: AwbGain) {
-	use rawproc::image::RawMetadata;
-	use rawproc::{colorspace::BayerRgb, image::Image};
-
-	let RawImage {
-		camera,
-		sensor,
-		width,
-		height,
-		format,
-		data,
-		sbro,
-		color,
-	} = img;
-
-	println!(
-		"{camera} {sensor:?} [{}:{}] {width}x{height} {format}",
-		sbro.0, sbro.1
-	);
-
-	let bayered = bayer(data, *width, *height);
-
-	let (mut rgb, color_format) = match img.cfa_string() {
-		Some(cfa_string) => {
-			let rawimg: Image<u8, 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(),
-				},
-				bayered,
-			);
-
-			(rawimg.debayer().data, png::ColorType::Rgb)
-			//(bayered, png::ColorType::Grayscale)
-		}
-		None => (bayered, png::ColorType::Grayscale),
-	};
-
-	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.is_empty() {
-		print!("\tAvailable whitepoints: ");
-		color.iter().for_each(|c| print!("{:?} ", c.whitepoint));
-		println!();
-	}
-
-	match img.color_info(Whitepoint::D65) {
-		Some(c) => {
-			println!("\tUsing D65");
-			let to_xyz = Matrix3::from_row_slice(&c.forward_matrix);
-			// 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;
-
-			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 r = chnk[0] * awb_gain.r;
-				let g = chnk[1];
-				let b = chnk[2] * awb_gain.b;
-
-				let px = Matrix3x1::new(r, g, b);
-				let rgb = premul * px;
-
-				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!("\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!("\tWriting {}", &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
-];
-
-#[rustfmt::skip]
-const BRADFORD_D50_D65: [f32; 9] = [
-	 0.9555766, -0.0230393,  0.0631636,
-	-0.0282895,  1.0099416,  0.0210077,
-	 0.0122982, -0.0204830,  1.3299098,
-];
-
-#[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;
-	}
-
-	float.clamp(0.0, 1.0)
-}
-
-fn bayer(data: &RawData<'_>, width: usize, height: usize) -> Vec<u8> {
-	match data {
-		RawData::Packed10bpp { data } => {
-			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.
-			// 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.saturating_sub(42)) >> 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<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(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/output.rs b/prism/src/output.rs
new file mode 100644
index 0000000..043f23d
--- /dev/null
+++ b/prism/src/output.rs
@@ -0,0 +1,240 @@
+use camino::Utf8PathBuf;
+use lri_rs::{AwbGain, RawData, RawImage, Whitepoint};
+use nalgebra::{Matrix3, Matrix3x1};
+
+use crate::rotate;
+
+pub fn make_png(img: &RawImage, path: Utf8PathBuf, awb_gain: AwbGain) {
+	use rawproc::image::RawMetadata;
+	use rawproc::{colorspace::BayerRgb, image::Image};
+
+	let RawImage {
+		camera,
+		sensor,
+		width,
+		height,
+		format,
+		data: _data,
+		sbro,
+		color,
+	} = img;
+
+	println!(
+		"{camera} {sensor:?} [{}:{}] {width}x{height} {format}",
+		sbro.0, sbro.1
+	);
+
+	let bayered = img.unpack().unwrap();
+
+	let (mut 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(),
+				},
+				bayered,
+			);
+
+			(rawimg.debayer().data, png::ColorType::Rgb)
+			//(bayered, png::ColorType::Grayscale)
+		}
+		None => (bayered, png::ColorType::Grayscale),
+	};
+
+	rotate::rotate_180(rgb.as_mut_slice());
+	let mut floats: Vec<f32> = rgb
+		.into_iter()
+		.map(|p| ((p.saturating_sub(42) as f32) / 1023.0))
+		.collect();
+
+	if !color.is_empty() {
+		print!("\tAvailable whitepoints: ");
+		color.iter().for_each(|c| print!("{:?} ", c.whitepoint));
+		println!();
+	}
+
+	match img.color_info(Whitepoint::D65) {
+		Some(c) => {
+			println!("\tUsing D65");
+			let to_xyz = Matrix3::from_row_slice(&c.forward_matrix);
+			// 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 wb_max = awb_gain.r.max(awb_gain.b);
+
+			let wb_r = awb_gain.r; // / wb_max;
+			let wb_g = 1.0; // / wb_max;
+			let wb_b = awb_gain.b; // / wb_max;
+
+			for (idx, chnk) in floats.chunks_mut(3).enumerate() {
+				/*let r = chnk[0] * (1.0 / c.rg);
+				let g = chnk[1];
+				let b = chnk[2] * (1.0 / c.bg);*/
+				let r = chnk[0] * wb_r;
+				let g = chnk[1] * wb_g;
+				let b = chnk[2] * wb_b;
+
+				if idx == 4 {
+					println!(
+						"R: {:.2},{:.2},{:.2} - W: {:.2},{:.2},{:.2}",
+						chnk[0], chnk[1], chnk[2], r, g, b
+					);
+				}
+
+				let px = Matrix3x1::new(r, g, b);
+				let rgb = premul * px;
+
+				chnk[0] = srgb_gamma(rgb[0]);
+				chnk[1] = srgb_gamma(rgb[1]);
+				chnk[2] = srgb_gamma(rgb[2]);
+
+				if idx == 4 {
+					println!(
+						"S: {:.2},{:.2},{:.2} - G: {:.2},{:.2},{:.2}",
+						rgb[0], rgb[1], rgb[2], chnk[0], chnk[1], chnk[2]
+					);
+				}
+			}
+		}
+		None => {
+			println!("\tColor profile for D65 not found. Doing gamma and nothing else!");
+			floats.iter_mut().for_each(|f| *f = srgb_gamma(*f));
+		}
+	}
+
+	let bytes: Vec<u8> = floats.into_iter().map(|f| (f * 255.0) as u8).collect();
+
+	println!("\tWriting {}", &path);
+	write_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
+];
+
+#[rustfmt::skip]
+const BRADFORD_D50_D65: [f32; 9] = [
+	 0.9555766, -0.0230393,  0.0631636,
+	-0.0282895,  1.0099416,  0.0210077,
+	 0.0122982, -0.0204830,  1.3299098,
+];
+
+#[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;
+	}
+
+	float.clamp(0.0, 1.0)
+}
+
+fn bayer(img: &RawImage, width: usize, height: usize) -> Vec<u8> {
+	match img.data {
+		RawData::Packed10bpp { data } => {
+			let unpacked = img.unpack().unwrap();
+
+			// I've only seen it on one color defintion or
+			// 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));
+
+			unpacked
+				.into_iter()
+				.map(|p| ((p.saturating_sub(42)) >> 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 write_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(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
deleted file mode 100644
index bc761f3..0000000
--- a/prism/src/unpack.rs
+++ /dev/null
@@ -1,61 +0,0 @@
-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;
-
-	if count > upack.len() {
-		panic!(
-			"expected output buffer to be {count} bytes, got {} bytes",
-			upack.len()
-		)
-	}
-
-	if required_len_packd > packd.len() {
-		panic!(
-			"expected input to be at least {required_len_packd} bytes, it was {}",
-			packd.len()
-		)
-	}
-
-	let mut packd = packd[..required_len_packd].to_vec();
-	packd.reverse();
-	let chunker = packd.chunks_exact(5);
-	let remain = chunker.remainder();
-
-	for (idx, chnk) in chunker.enumerate() {
-		let long = u64::from_be_bytes([
-			0x00, 0x00, 0x00, chnk[0], chnk[1], chnk[2], chnk[3], chnk[4],
-		]);
-
-		let b4 = long & TEN_MASK;
-		let b3 = (long >> 10) & TEN_MASK;
-		let b2 = (long >> 20) & TEN_MASK;
-		let b1 = (long >> 30) & TEN_MASK;
-
-		let idx = idx * 4;
-		upack[idx] = b1 as u16;
-		upack[idx + 1] = b2 as u16;
-		upack[idx + 2] = b3 as u16;
-		upack[idx + 3] = b4 as u16;
-	}
-
-	if remain.len() > 0 {
-		let mut long_bytes = [0x00; 8];
-
-		for (idx, byte) in remain.iter().enumerate() {
-			long_bytes[idx] = *byte;
-		}
-
-		let long = u64::from_le_bytes(long_bytes);
-
-		let count_remain = count % 4;
-		let start = count - count_remain;
-		for idx in 0..count_remain {
-			upack[start + idx] = ((long >> (10 * idx)) & TEN_MASK) as u16;
-		}
-	}
-}
-
-//pub fn twelvebit(packed: &[u8]) {
-// 3 bytes per 2 12-bits
-//}