about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Cargo.lock406
-rw-r--r--lri-rs/src/block.rs209
-rw-r--r--lri-rs/src/lib.rs436
-rw-r--r--prism/src/main.rs70
-rw-r--r--prism/src/rotate.rs11
6 files changed, 873 insertions, 262 deletions
diff --git a/.gitignore b/.gitignore
index 54466f5..0036efe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
 /target
-
+*.png
+lri-study
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
index 253ba71..c91bb1d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -24,6 +24,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
 
 [[package]]
+name = "approx"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
 name = "autocfg"
 version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -42,6 +51,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
 
 [[package]]
+name = "bytemuck"
+version = "1.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6"
+
+[[package]]
+name = "byteorder"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+
+[[package]]
 name = "bytes"
 version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -72,12 +93,66 @@ dependencies = [
 ]
 
 [[package]]
+name = "crossbeam-channel"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
+dependencies = [
+ "cfg-if",
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
+dependencies = [
+ "autocfg",
+ "cfg-if",
+ "crossbeam-utils",
+ "memoffset",
+ "scopeguard",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
 name = "either"
 version = "1.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
 
 [[package]]
+name = "enumn"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2ad8cef1d801a4686bfd8919f0b30eac4c8e48968c437a6405ded4fb5272d2b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.31",
+]
+
+[[package]]
 name = "errno"
 version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -124,12 +199,35 @@ dependencies = [
 ]
 
 [[package]]
+name = "getrandom"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "glob"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+
+[[package]]
 name = "hashbrown"
 version = "0.12.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
 
 [[package]]
+name = "hermit-abi"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
+
+[[package]]
 name = "home"
 version = "0.5.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -149,6 +247,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
 name = "libc"
 version = "0.2.147"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -182,12 +286,31 @@ dependencies = [
 ]
 
 [[package]]
+name = "matrixmultiply"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "090126dc04f95dc0d1c1c91f61bdd474b3930ca064c1edc8a849da2c6cbe1e77"
+dependencies = [
+ "autocfg",
+ "rawpointer",
+]
+
+[[package]]
 name = "memchr"
 version = "2.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c"
 
 [[package]]
+name = "memoffset"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
 name = "miniz_oxide"
 version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -198,12 +321,94 @@ dependencies = [
 ]
 
 [[package]]
+name = "nalgebra"
+version = "0.31.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20bd243ab3dbb395b39ee730402d2e5405e448c75133ec49cc977762c4cba3d1"
+dependencies = [
+ "approx",
+ "matrixmultiply",
+ "nalgebra-macros",
+ "num-complex",
+ "num-rational",
+ "num-traits",
+ "simba",
+ "typenum",
+]
+
+[[package]]
+name = "nalgebra-macros"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01fcc0b8149b4632adc89ac3b7b31a12fb6099a0317a4eb2ebff574ef7de7218"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "num-complex"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
+dependencies = [
+ "autocfg",
+ "num-traits",
+]
+
+[[package]]
+name = "num-rational"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
 name = "once_cell"
 version = "1.18.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
 
 [[package]]
+name = "paste"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
+
+[[package]]
 name = "png"
 version = "0.17.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -217,11 +422,20 @@ dependencies = [
 ]
 
 [[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
 name = "prism"
 version = "0.1.0"
 dependencies = [
  "lri-rs",
+ "nalgebra",
  "png",
+ "rawloader",
+ "rawproc",
 ]
 
 [[package]]
@@ -295,6 +509,91 @@ dependencies = [
 ]
 
 [[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rawloader"
+version = "0.37.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d8c6f168c492ffd326537b3aa5a8d5fe07f0d8a3970c5957f286bcd13f888aa"
+dependencies = [
+ "byteorder",
+ "enumn",
+ "glob",
+ "lazy_static",
+ "rayon",
+ "rustc_version",
+ "toml",
+]
+
+[[package]]
+name = "rawpointer"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
+
+[[package]]
+name = "rawproc"
+version = "0.1.0"
+source = "git+https://github.com/eclecticnybles/gaze#47dbf3f29edd932bb8e363146e7a5d1505d90c22"
+dependencies = [
+ "nalgebra",
+ "num-traits",
+ "rand",
+ "rawloader",
+ "thiserror",
+]
+
+[[package]]
+name = "rayon"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
+dependencies = [
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "crossbeam-utils",
+ "num_cpus",
+]
+
+[[package]]
 name = "redox_syscall"
 version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -333,6 +632,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
 
 [[package]]
+name = "rustc_version"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+dependencies = [
+ "semver",
+]
+
+[[package]]
 name = "rustix"
 version = "0.38.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -346,6 +654,60 @@ dependencies = [
 ]
 
 [[package]]
+name = "safe_arch"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f398075ce1e6a179b46f51bd88d0598b92b00d3551f1a2d4ac49e771b56ac354"
+dependencies = [
+ "bytemuck",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "semver"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
+
+[[package]]
+name = "serde"
+version = "1.0.188"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.188"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.31",
+]
+
+[[package]]
+name = "simba"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f3fd720c48c53cace224ae62bef1bbff363a70c68c4802a78b5cc6159618176"
+dependencies = [
+ "approx",
+ "num-complex",
+ "num-traits",
+ "paste",
+ "wide",
+]
+
+[[package]]
 name = "simd-adler32"
 version = "0.3.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -353,6 +715,17 @@ checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
 
 [[package]]
 name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
 version = "2.0.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398"
@@ -392,16 +765,37 @@ checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.31",
 ]
 
 [[package]]
+name = "toml"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "typenum"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
+
+[[package]]
 name = "unicode-ident"
 version = "1.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
 
 [[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
 name = "which"
 version = "4.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -414,6 +808,16 @@ dependencies = [
 ]
 
 [[package]]
+name = "wide"
+version = "0.7.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa469ffa65ef7e0ba0f164183697b89b854253fd31aeb92358b7b6155177d62f"
+dependencies = [
+ "bytemuck",
+ "safe_arch",
+]
+
+[[package]]
 name = "windows-sys"
 version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/lri-rs/src/block.rs b/lri-rs/src/block.rs
new file mode 100644
index 0000000..2dc921b
--- /dev/null
+++ b/lri-rs/src/block.rs
@@ -0,0 +1,209 @@
+use lri_proto::{
+	gps_data::GPSData, lightheader::LightHeader, matrix3x3f::Matrix3x3F,
+	view_preferences::ViewPreferences, Message as PbMessage,
+};
+
+use crate::{CameraId, CameraInfo, ColorInfo, RawImage, SensorModel};
+
+pub(crate) struct Block<'lri> {
+	pub header: Header,
+	/// This includes the 32 bytes that make up the header.
+	pub data: &'lri [u8],
+}
+
+impl<'lri> Block<'lri> {
+	pub fn body(&self) -> &[u8] {
+		&self.data[32..]
+	}
+
+	pub fn message_data(&self) -> &[u8] {
+		let end = self.header.message_offset + self.header.message_length;
+		&self.data[self.header.message_offset..end]
+	}
+
+	pub fn message(&self) -> Message {
+		match self.header.kind {
+			BlockType::LightHeader => {
+				Message::LightHeader(LightHeader::parse_from_bytes(self.message_data()).unwrap())
+			}
+			BlockType::ViewPreferences => Message::ViewPreferences(
+				ViewPreferences::parse_from_bytes(self.message_data()).unwrap(),
+			),
+			BlockType::GPSData => {
+				Message::Gps(GPSData::parse_from_bytes(self.message_data()).unwrap())
+			}
+		}
+	}
+
+	pub fn extract_meaningful_data(
+		&self,
+		images: &mut Vec<RawImage<'lri>>,
+		colors: &mut Vec<ColorInfo>,
+		infos: &mut Vec<CameraInfo>,
+	) -> ExtractedData {
+		let mut ext = ExtractedData {
+			reference_camera: None,
+		};
+
+		let LightHeader {
+			mut hw_info,
+			mut module_calibration,
+			mut modules,
+			mut image_reference_camera,
+			..
+		} = if let Message::LightHeader(lh) = self.message() {
+			lh
+		} else {
+			return ext;
+		};
+
+		// Form the CameraInfo struct for mapping CameraId to SensorType
+		if let Some(hw_info) = hw_info.take() {
+			for info in hw_info.camera {
+				let info = CameraInfo {
+					camera: info.id().into(),
+					sensor: info.sensor().into(),
+				};
+
+				infos.push(info);
+			}
+		}
+
+		// Color information for the Camera moduels.
+		for mcal in module_calibration {
+			let camera = mcal.camera_id().into();
+
+			for mut color in mcal.color {
+				let whitepoint = color.type_().into();
+				let forward_matrix = match color.forward_matrix.take() {
+					Some(fw) => Self::deconstruct_matrix3x3(fw),
+					// The forward matrix is like, what we want! If we don't get it, don't bother
+					// with the struct
+					None => continue,
+				};
+				let color_matrix = match color.color_matrix.take() {
+					None => [0.0; 9],
+					Some(cm) => Self::deconstruct_matrix3x3(cm),
+				};
+
+				let rg = color.rg_ratio();
+				let bg = color.bg_ratio();
+
+				colors.push(ColorInfo {
+					camera,
+					whitepoint,
+					forward_matrix,
+					color_matrix,
+					rg,
+					bg,
+				})
+			}
+		}
+
+		// The images themselves
+		for mut module in modules {
+			let camera = module.id().into();
+			let mut surface = match module.sensor_data_surface.take() {
+				Some(sur) => sur,
+				// The surface is what we're after here. Don't bother with anything lacking it
+				None => continue,
+			};
+
+			let size = surface.size.take().unwrap();
+			let width = size.x() as usize;
+			let height = size.y() as usize;
+
+			let offset = surface.data_offset() as usize;
+			let data_length = surface.row_stride() as usize * height;
+
+			let format = surface.format().into();
+			let image_data = &self.data[offset..offset + data_length];
+
+			let sbro = module.sensor_bayer_red_override.clone().unwrap();
+
+			images.push(RawImage {
+				camera,
+				// Populated after all the blocks are processed
+				sensor: SensorModel::Unknown,
+				width,
+				height,
+				format,
+				data: image_data,
+				sbro: (sbro.x(), sbro.y()),
+				// Populated after all the blocks are processed
+				color: vec![],
+			});
+		}
+
+		if let Some(Ok(irc)) = image_reference_camera.map(|ev| ev.enum_value()) {
+			ext.reference_camera = Some(irc.into());
+		}
+
+		ext
+	}
+
+	// It kept making my neat little array very, very tall
+	#[rustfmt::skip]
+	fn deconstruct_matrix3x3(mat: Matrix3x3F) -> [f32; 9] {
+		[
+			mat.x00(), mat.x01(), mat.x02(),
+			mat.x10(), mat.x11(), mat.x12(),
+			mat.x20(), mat.x21(), mat.x22(),
+		]
+	}
+}
+
+pub(crate) struct ExtractedData {
+	pub reference_camera: Option<CameraId>,
+}
+
+pub enum Message {
+	LightHeader(LightHeader),
+	ViewPreferences(ViewPreferences),
+	Gps(GPSData),
+}
+
+pub struct Header {
+	/// The length of this header plus the data after it.
+	pub block_length: usize,
+	/// An offset from the start of the header to the block's protobuf message
+	pub message_offset: usize,
+	/// block's protobuf message length
+	pub message_length: usize,
+	/// The kind of protobuf message in the block
+	pub kind: BlockType,
+}
+
+impl Header {
+	pub fn ingest(data: &[u8]) -> Self {
+		let magic = b"LELR";
+
+		if &data[0..4] != magic {
+			panic!("Magic nubmer is wrong");
+		}
+
+		let combined_length = u64::from_le_bytes(data[4..12].try_into().unwrap()) as usize;
+		let message_offset = u64::from_le_bytes(data[12..20].try_into().unwrap()) as usize;
+		let message_length = u32::from_le_bytes(data[20..24].try_into().unwrap()) as usize;
+
+		let kind = match data[24] {
+			0 => BlockType::LightHeader,
+			1 => BlockType::ViewPreferences,
+			2 => BlockType::GPSData,
+			t => panic!("block type {t} is unknown"),
+		};
+
+		Header {
+			block_length: combined_length,
+			message_offset,
+			message_length,
+			kind,
+		}
+	}
+}
+
+pub enum BlockType {
+	LightHeader,
+	ViewPreferences,
+	GPSData,
+}
diff --git a/lri-rs/src/lib.rs b/lri-rs/src/lib.rs
index 2b5d8fb..44b2466 100644
--- a/lri-rs/src/lib.rs
+++ b/lri-rs/src/lib.rs
@@ -1,287 +1,200 @@
-use std::{fmt, vec::IntoIter};
+use std::fmt;
 
-use lri_proto::Message as PbMessage;
+use block::{Block, ExtractedData, Header};
 use lri_proto::{
-	camera_id::CameraID,
-	camera_module::{camera_module::surface::FormatType, CameraModule},
+	camera_id::CameraID as PbCameraID, camera_module::camera_module::surface::FormatType,
 	color_calibration::color_calibration::IlluminantType,
-	gps_data::GPSData,
-	lightheader::LightHeader,
-	view_preferences::ViewPreferences,
 };
 
-pub struct LriFile {
-	pub blocks: Vec<Block>,
-	pub models: Vec<SensorModel>,
+mod block;
+
+pub struct LriFile<'lri> {
+	pub image_reference_camera: Option<CameraId>,
+	pub images: Vec<RawImage<'lri>>,
+	pub colors: Vec<ColorInfo>,
+	pub camera_infos: Vec<CameraInfo>,
 }
 
-impl LriFile {
+impl<'lri> LriFile<'lri> {
 	/// Read
-	pub fn decode(mut data: Vec<u8>) -> Self {
-		let mut blocks = vec![];
+	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![];
 
+		// Read data blocks and extract informtion we care about
 		loop {
-			let header = Header::ingest(&data[..]);
-			let end = header.combined_length as usize;
-
-			if end == data.len() {
-				blocks.push(Block { header, data });
+			if data.len() == 0 {
 				break;
-			} else {
-				let remain = data.split_off(end);
-				blocks.push(Block { header, data });
-				data = remain;
 			}
-		}
 
-		let models = Self::grab_sensor_models(&blocks);
+			let header = Header::ingest(&data[..]);
+			let end = header.block_length as usize;
 
-		Self { blocks, models }
-	}
+			let block_data = &data[..end];
+			data = &data[end..];
 
-	fn grab_sensor_models(blocks: &[Block]) -> Vec<SensorModel> {
-		let mut models = vec![];
-
-		for blk in blocks {
-			match blk.message() {
-				Message::LightHeader(LightHeader {
-					module_calibration, ..
-				}) => {
-					for mcal in module_calibration {
-						let id = mcal.camera_id().into();
-						let color = match mcal.color.first() {
-							None => continue,
-							Some(c) => c,
-						};
-						let whitepoint = color.type_().into();
-						let forward = color.forward_matrix.clone().unwrap();
-						let our_forward = [
-							forward.x00(),
-							forward.x01(),
-							forward.x02(),
-							forward.x10(),
-							forward.x11(),
-							forward.x12(),
-							forward.x20(),
-							forward.x21(),
-							forward.x22(),
-						];
-
-						let forward = color.color_matrix.clone().unwrap();
-						let our_color = [
-							forward.x00(),
-							forward.x01(),
-							forward.x02(),
-							forward.x10(),
-							forward.x11(),
-							forward.x12(),
-							forward.x20(),
-							forward.x21(),
-							forward.x22(),
-						];
-
-						let rg = color.rg_ratio();
-						let bg = color.bg_ratio();
-
-						let model = SensorModel {
-							id,
-							whitepoint,
-							forward_matrix: our_forward,
-							color_matrix: our_color,
-							rg,
-							bg,
-						};
-						models.push(model);
-					}
+			let block = Block {
+				header,
+				data: block_data,
+			};
+
+			match block.extract_meaningful_data(&mut images, &mut colors, &mut camera_infos) {
+				ExtractedData {
+					reference_camera: Some(irc),
+				} => {
+					reference = Some(irc);
 				}
 				_ => (),
 			}
 		}
 
-		models
-	}
-
-	pub fn image_count(&self) -> usize {
-		let mut count = 0;
-
-		for block in &self.blocks {
-			match block.message() {
-				Message::LightHeader(LightHeader { modules, .. }) => {
-					for cam in modules {
-						count += 1;
-					}
-				}
-				_ => (),
+		// Further fill in the RawImage's we extracted
+		for img in images.iter_mut() {
+			if let Some(info) = camera_infos.iter().find(|i| i.camera == img.camera) {
+				img.sensor = info.sensor;
 			}
+
+			let profiles = colors
+				.iter()
+				.filter(|c| c.camera == img.camera)
+				.map(<_>::clone)
+				.collect();
+
+			img.color = profiles;
 		}
 
-		count
+		LriFile {
+			image_reference_camera: reference,
+			images,
+			colors,
+			camera_infos,
+		}
 	}
 
-	pub fn images(&self) -> ImageIterator {
-		ImageIterator {
-			blocks: &self.blocks,
-			modules: None,
-		}
+	/// Number of images present in the file
+	pub fn image_count(&self) -> usize {
+		self.images.len()
 	}
 
-	pub fn color_models(&self, cameraid: SensorId) -> Vec<&SensorModel> {
-		self.models.iter().filter(|sm| sm.id == cameraid).collect()
+	/// Iterator over the images
+	pub fn images(&self) -> std::slice::Iter<'_, RawImage> {
+		self.images.iter()
 	}
-}
 
-pub struct ImageIterator<'lri> {
-	blocks: &'lri [Block],
-	modules: Option<(&'lri Block, IntoIter<CameraModule>)>,
+	/// Get the image the camera showed in the viewfinder, if it's been
+	/// recorded in the file.
+	pub fn reference_image(&self) -> Option<&RawImage> {
+		self.image_reference_camera
+			.map(|irc| self.images().find(|ri| ri.camera == irc))
+			.flatten()
+	}
 }
 
-impl<'lri> Iterator for ImageIterator<'lri> {
-	type Item = RawImage<'lri>;
+pub struct RawImage<'img> {
+	/// Camera that captured this image
+	pub camera: CameraId,
+	/// The model of the sensor of the camera
+	pub sensor: SensorModel,
 
-	fn next(&mut self) -> Option<Self::Item> {
-		loop {
-			match self.modules.as_mut() {
-				None => match self.blocks.first() {
-					None => return None,
-					Some(block) => {
-						self.blocks = &self.blocks[1..];
-
-						if let Message::LightHeader(lh) = block.message() {
-							let mod_iter = lh.modules.into_iter();
-							self.modules = Some((block, mod_iter));
-						} else {
-							continue;
-						}
-					}
-				},
-				Some((block, mods)) => {
-					for module in mods {
-						let sensor_id = module.id().into();
-						let mut surface = module.sensor_data_surface.unwrap();
-						let size = surface.size.take().unwrap();
-						let offset = surface.data_offset() as usize;
-						let data_length = surface.row_stride() as usize * size.y() as usize;
-
-						let data = &block.data[offset..offset + data_length];
-
-						return Some(RawImage {
-							sensor_id,
-							width: size.x() as usize,
-							height: size.y() as usize,
-							format: surface.format().into(),
-							data,
-						});
-					}
-
-					self.modules = None;
-				}
-			}
-		}
-	}
-}
+	pub width: usize,
+	pub height: usize,
 
-pub struct Block {
-	pub header: Header,
-	/// This includes the 32 bytes that make up the header.
-	pub data: Vec<u8>,
+	/// How the image data is encoded in the file
+	pub format: DataFormat,
+	/// Image data
+	pub data: &'img [u8],
+	/// "sensor bayer red offset"
+	pub sbro: (i32, i32),
+	/// All color information associated with this [CameraId] for different [Whitepoint]s
+	pub color: Vec<ColorInfo>,
 }
 
-impl Block {
-	pub fn body(&self) -> &[u8] {
-		&self.data[32..]
+impl<'img> RawImage<'img> {
+	/// Get the color profile for noon daylight. First looks for F7 and, if it can't find that, D65
+	pub fn daylight(&self) -> Option<&ColorInfo> {
+		self.color
+			.iter()
+			.find(|c| c.whitepoint == Whitepoint::F7)
+			.or_else(|| self.color.iter().find(|c| c.whitepoint == Whitepoint::D65))
 	}
 
-	pub fn message_data(&self) -> &[u8] {
-		let end = self.header.message_offset + self.header.message_length;
-		&self.data[self.header.message_offset..end]
+	/// Get a color profile matching the provided Whitepoint
+	pub fn color_info(&self, whitepoint: Whitepoint) -> Option<&ColorInfo> {
+		self.color.iter().find(|c| c.whitepoint == whitepoint)
 	}
 
-	pub fn message(&self) -> Message {
-		match self.header.kind {
-			BlockType::LightHeader => {
-				Message::LightHeader(LightHeader::parse_from_bytes(self.message_data()).unwrap())
-			}
-			BlockType::ViewPreferences => Message::ViewPreferences(
-				ViewPreferences::parse_from_bytes(self.message_data()).unwrap(),
-			),
-			BlockType::GPSData => {
-				Message::Gps(GPSData::parse_from_bytes(self.message_data()).unwrap())
-			}
+	pub fn cfa_string(&self) -> Option<&'static str> {
+		match self.sensor {
+			SensorModel::Ar1335Mono => None,
+			SensorModel::Ar1335 => self.cfa_string_ar1335(),
+			_ => unimplemented!(),
 		}
 	}
-}
-
-pub enum Message {
-	LightHeader(LightHeader),
-	ViewPreferences(ViewPreferences),
-	Gps(GPSData),
-}
 
-pub struct Header {
-	/// The length of this header and it's associated block
-	pub combined_length: usize,
-	/// An offset from the start of the header to the block's protobuf message
-	pub message_offset: usize,
-	/// block's protobuf message length
-	pub message_length: usize,
-	/// The kind of protobuf message in the block
-	pub kind: BlockType,
-}
-
-impl Header {
-	pub fn ingest(data: &[u8]) -> Self {
-		let magic = b"LELR";
-
-		if &data[0..4] != magic {
-			panic!("Magic nubmer is wrong");
+	// The AR1335 seems to be BGGR, which was weird.
+	fn cfa_string_ar1335(&self) -> Option<&'static str> {
+		match self.sbro {
+			(-1, -1) => None,
+			(0, 0) => Some("BGGR"),
+			(1, 0) => Some("GRBG"),
+			(0, 1) => Some("GBRG"),
+			(1, 1) => Some("RGGB"),
+			_ => unreachable!(),
 		}
+	}
 
-		let combined_length = u64::from_le_bytes(data[4..12].try_into().unwrap()) as usize;
-		let message_offset = u64::from_le_bytes(data[12..20].try_into().unwrap()) as usize;
-		let message_length = u32::from_le_bytes(data[20..24].try_into().unwrap()) as usize;
-
-		let kind = match data[24] {
-			0 => BlockType::LightHeader,
-			1 => BlockType::ViewPreferences,
-			2 => BlockType::GPSData,
-			t => panic!("block type {t} is unknown"),
-		};
-
-		Header {
-			combined_length,
-			message_offset,
-			message_length,
-			kind,
+	/// Uses the [SensorModel] to determine if the image's [ColorType].
+	/// If the sensor model is unknown, [SensorModel::Unknown], then [ColorType::Grayscale] is returned
+	pub fn color_type(&self) -> ColorType {
+		match self.sensor {
+			SensorModel::Ar1335 | SensorModel::Ar835 | SensorModel::Imx386 => ColorType::Rgb,
+			SensorModel::Ar1335Mono | SensorModel::Imx386Mono | SensorModel::Unknown => {
+				ColorType::Grayscale
+			}
 		}
 	}
 }
 
-pub enum BlockType {
-	LightHeader,
-	ViewPreferences,
-	GPSData,
+pub enum ColorType {
+	Rgb,
+	Grayscale,
 }
 
-pub struct RawImage<'img> {
-	pub sensor_id: SensorId,
-	pub width: usize,
-	pub height: usize,
-	pub format: ImageFormat,
-	pub data: &'img [u8],
-}
+#[derive(Copy, Clone, Debug)]
+/// Colour information about the camera. Used to correct the image
+pub struct ColorInfo {
+	/// Which specific colour this image was taken by
+	pub camera: CameraId,
 
-pub struct SensorModel {
-	pub id: SensorId,
+	/// The whitepoint that the forward matrix corresponds to.
 	pub whitepoint: Whitepoint,
-	/// From the camera debayered data to XYZ in the given whitepoint
+
+	/// Camera RGB -> XYZ conversion matrix.
 	pub forward_matrix: [f32; 9],
-	/// ??? is this cam -> sRGB?
+
+	/// A 3x3 Matrix with unclear usage.
+	///
+	/// If you know what this is or think you have information, PRs are accepted.
+	/// Or emails, if you'd rather. gen@nyble.dev
 	pub color_matrix: [f32; 9],
+
+	/// Red-green ratio.
 	pub rg: f32,
+	/// Blue-green ratio.
 	pub bg: f32,
 }
 
-pub enum ImageFormat {
+#[derive(Copy, Clone, Debug)]
+pub struct CameraInfo {
+	camera: CameraId,
+	sensor: SensorModel,
+}
+
+#[derive(Copy, Clone, Debug)]
+/// 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,
@@ -289,7 +202,7 @@ pub enum ImageFormat {
 	Packed14bpp,
 }
 
-impl fmt::Display for ImageFormat {
+impl fmt::Display for DataFormat {
 	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 		let str = match self {
 			Self::BayerJpeg => "BayerJpeg",
@@ -302,7 +215,7 @@ impl fmt::Display for ImageFormat {
 	}
 }
 
-impl From<FormatType> for ImageFormat {
+impl From<FormatType> for DataFormat {
 	fn from(proto: FormatType) -> Self {
 		match proto {
 			FormatType::RAW_BAYER_JPEG => Self::BayerJpeg,
@@ -320,7 +233,7 @@ impl From<FormatType> for ImageFormat {
 }
 
 #[derive(Copy, Clone, Debug, PartialEq)]
-pub enum SensorId {
+pub enum CameraId {
 	A1,
 	A2,
 	A3,
@@ -339,37 +252,37 @@ pub enum SensorId {
 	C6,
 }
 
-impl From<CameraID> for SensorId {
-	fn from(pbid: CameraID) -> Self {
+impl From<PbCameraID> for CameraId {
+	fn from(pbid: PbCameraID) -> Self {
 		match pbid {
-			CameraID::A1 => Self::A1,
-			CameraID::A2 => Self::A2,
-			CameraID::A3 => Self::A3,
-			CameraID::A4 => Self::A4,
-			CameraID::A5 => Self::A5,
-			CameraID::B1 => Self::B1,
-			CameraID::B2 => Self::B2,
-			CameraID::B3 => Self::B3,
-			CameraID::B4 => Self::B4,
-			CameraID::B5 => Self::B5,
-			CameraID::C1 => Self::C1,
-			CameraID::C2 => Self::C2,
-			CameraID::C3 => Self::C3,
-			CameraID::C4 => Self::C4,
-			CameraID::C5 => Self::C5,
-			CameraID::C6 => Self::C6,
+			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 SensorId {
+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(Debug)]
+#[derive(Copy, Clone, Debug, PartialEq)]
 pub enum Whitepoint {
 	A,
 	D50,
@@ -396,3 +309,28 @@ impl From<IlluminantType> for Whitepoint {
 		}
 	}
 }
+
+#[derive(Copy, Clone, Debug)]
+pub enum SensorModel {
+	Unknown,
+	Ar835,
+	Ar1335,
+	Ar1335Mono,
+	Imx386,
+	Imx386Mono,
+}
+
+impl From<lri_proto::sensor_type::SensorType> 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,
+		}
+	}
+}
diff --git a/prism/src/main.rs b/prism/src/main.rs
index 37b2ff6..5f4af1c 100644
--- a/prism/src/main.rs
+++ b/prism/src/main.rs
@@ -1,6 +1,7 @@
 use lri_rs::{LriFile, RawImage, Whitepoint};
 use nalgebra::{Matrix3, Matrix3x1};
 
+mod rotate;
 mod unpack;
 
 fn main() {
@@ -10,17 +11,33 @@ fn main() {
 
 	println!("{} images", lri.image_count());
 
+	lri.reference_image()
+		.map(|raw| make(raw, String::from("reference.png")));
+
 	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"));
 	}
 }
 
-// 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};
@@ -72,7 +89,7 @@ fn make(img: &RawImage, path: String) {
 	// C5 - RO
 	// C6 - -1:-1
 
-	let (rgb, color_format) = match img.cfa_string() {
+	let (mut rgb, color_format) = match img.cfa_string() {
 		Some(cfa_string) => {
 			let rawimg: Image<u16, BayerRgb> = Image::from_raw_parts(
 				4160,
@@ -94,21 +111,42 @@ fn make(img: &RawImage, path: String) {
 		None => (ten_data, png::ColorType::Grayscale),
 	};
 
+	rotate::rotate_180(rgb.as_mut_slice());
+
 	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() {
+	match img.color_info(Whitepoint::F11) {
 		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 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
+			);
 
 			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 g = chnk[1];
@@ -117,7 +155,10 @@ fn make(img: &RawImage, path: String) {
 				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;
 
 				chnk[0] = srgb_gamma(rgb[0]) * 255.0;
@@ -147,9 +188,16 @@ const BRUCE_XYZ_RGB_D50: [f32; 9] = [
 
 #[rustfmt::skip]
 const BRUCE_XYZ_RGB_D65: [f32; 9] = [
-	3.2404542,  -1.5371385, -0.4985314,
+	 3.2404542, -1.5371385, -0.4985314,
 	-0.9692660,  1.8760108,  0.0415560,
- 	0.0556434,  -0.2040259,  1.0572252
+ 	 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]
diff --git a/prism/src/rotate.rs b/prism/src/rotate.rs
new file mode 100644
index 0000000..82f29d1
--- /dev/null
+++ b/prism/src/rotate.rs
@@ -0,0 +1,11 @@
+pub fn rotate_180<T: Copy>(data: &mut [T]) {
+	let mut rat = vec![data[0]; data.len()];
+
+	for (idx, px) in data.chunks(3).rev().enumerate() {
+		rat[idx * 3] = px[0];
+		rat[idx * 3 + 1] = px[1];
+		rat[idx * 3 + 2] = px[2];
+	}
+
+	data.copy_from_slice(&rat);
+}