From: Max Value Date: Tue, 7 Oct 2025 17:22:48 +0000 (+0100) Subject: Fully built the LUT system X-Git-Url: https://git.ozva.co.uk/?a=commitdiff_plain;h=e1a6109777897b9f4a214deec4b334ea49245537;p=rust_fft Fully built the LUT system implemented into the main program via correct.rs but not yet tested --- diff --git a/.gitignore b/.gitignore index 9680458..370aa2a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ -/target +target/ camera.a +cube.npy +__pycache__/ +cargo.lock diff --git a/Cargo.lock b/Cargo.lock index de3f340..b7efc4e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "ab_glyph" @@ -147,7 +147,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -230,7 +230,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -278,6 +278,15 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "block-sys" version = "0.1.0-beta.1" @@ -343,7 +352,7 @@ dependencies = [ "log", "nix 0.25.1", "slotmap", - "thiserror", + "thiserror 1.0.63", "vec_map", ] @@ -521,6 +530,15 @@ dependencies = [ "windows 0.54.0", ] +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.4.2" @@ -561,6 +579,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "d3d12" version = "0.7.0" @@ -578,6 +606,16 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "dispatch" version = "0.2.0" @@ -678,7 +716,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -770,6 +808,16 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -778,7 +826,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.7+wasi-0.2.4", ] [[package]] @@ -848,7 +908,7 @@ checksum = "ce95f9e2e11c2c6fadfce42b5af60005db06576f231f5c92550fdded43c423e8" dependencies = [ "backtrace", "log", - "thiserror", + "thiserror 1.0.63", "winapi", "windows 0.44.0", ] @@ -909,7 +969,7 @@ dependencies = [ "com-rs", "libc", "libloading 0.7.4", - "thiserror", + "thiserror 1.0.63", "widestring", "winapi", ] @@ -1011,7 +1071,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -1043,7 +1103,7 @@ dependencies = [ "combine", "jni-sys", "log", - "thiserror", + "thiserror 1.0.63", "walkdir", "windows-sys 0.45.0", ] @@ -1273,7 +1333,7 @@ checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.48.0", ] @@ -1294,7 +1354,7 @@ dependencies = [ "rustc-hash", "spirv", "termcolor", - "thiserror", + "thiserror 1.0.63", "unicode-xid", ] @@ -1309,7 +1369,7 @@ dependencies = [ "ndk-sys 0.4.1+23.1.7779620", "num_enum 0.5.11", "raw-window-handle", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -1323,7 +1383,7 @@ dependencies = [ "log", "ndk-sys 0.5.0+25.2.9519653", "num_enum 0.7.3", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -1397,6 +1457,17 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" +[[package]] +name = "npyz" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f0e759e014e630f90af745101b614f761306ddc541681e546649068e25ec1b9" +dependencies = [ + "byteorder", + "num-bigint", + "py_literal", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -1424,7 +1495,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -1504,7 +1575,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -1516,7 +1587,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -1655,6 +1726,50 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pest" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21e0a3a33733faeaf8651dfee72dd0f388f0c8e5ad496a3478fa5a922f49cfa8" +dependencies = [ + "memchr", + "thiserror 2.0.16", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc58706f770acb1dbd0973e6530a3cff4746fb721207feb3a8a6064cd0b6c663" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d4f36811dfe07f7b8573462465d5cb8965fffc2e71ae377a33aecf14c2c9a2f" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "pest_meta" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42919b05089acbd0a5dcd5405fb304d17d1053847b81163d09c4ad18ce8e8420" +dependencies = [ + "pest", + "sha2", +] + [[package]] name = "petgraph" version = "0.6.5" @@ -1749,7 +1864,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" dependencies = [ "quote", - "syn 2.0.77", + "syn 2.0.87", +] + +[[package]] +name = "py_literal" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "102df7a3d46db9d3891f178dcc826dc270a6746277a9ae6436f8d29fd490a8e1" +dependencies = [ + "num-bigint", + "num-complex", + "num-traits", + "pest", + "pest_derive", ] [[package]] @@ -1776,6 +1904,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "rand" version = "0.8.5" @@ -1783,8 +1917,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", ] [[package]] @@ -1794,7 +1938,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", ] [[package]] @@ -1803,7 +1957,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", ] [[package]] @@ -1838,11 +2001,11 @@ dependencies = [ "once_cell", "paste", "profiling", - "rand", - "rand_chacha", + "rand 0.8.5", + "rand_chacha 0.3.1", "simd_helpers", "system-deps", - "thiserror", + "thiserror 1.0.63", "v_frame", "wasm-bindgen", ] @@ -1973,6 +2136,8 @@ dependencies = [ "hound", "image", "libc", + "npyz", + "rand 0.9.2", "rscam", "rustfft", "show-image", @@ -2071,7 +2236,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] @@ -2083,6 +2248,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "shlex" version = "1.3.0" @@ -2215,9 +2391,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -2258,7 +2434,16 @@ version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.63", +] + +[[package]] +name = "thiserror" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +dependencies = [ + "thiserror-impl 2.0.16", ] [[package]] @@ -2269,7 +2454,18 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", ] [[package]] @@ -2369,6 +2565,18 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5be21190ff5d38e8b4a2d3b6a3ae57f612cc39c96e83cedeaf7abc338a8bac4a" +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + [[package]] name = "unicode-ident" version = "1.0.13" @@ -2432,6 +2640,24 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.14.7+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" +dependencies = [ + "wasip2", +] + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + [[package]] name = "wasm-bindgen" version = "0.2.93" @@ -2454,7 +2680,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", "wasm-bindgen-shared", ] @@ -2488,7 +2714,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2629,7 +2855,7 @@ dependencies = [ "raw-window-handle", "rustc-hash", "smallvec", - "thiserror", + "thiserror 1.0.63", "web-sys", "wgpu-hal", "wgpu-types", @@ -2669,7 +2895,7 @@ dependencies = [ "renderdoc-sys", "rustc-hash", "smallvec", - "thiserror", + "thiserror 1.0.63", "wasm-bindgen", "web-sys", "wgpu-types", @@ -3020,6 +3246,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + [[package]] name = "x11-dl" version = "2.21.0" @@ -3061,7 +3293,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.87", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index ecee24f..c7bbe3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,8 @@ show-image = {version = "0.14.0", features = ["image"]} hound = "3.5.1" cpal = "0.15.3" rscam = "0.5.5" +npyz = "0.8.4" +rand = "0.9.2" [build-dependencies] cc = "1.0" diff --git a/src/correct.rs b/src/correct.rs new file mode 100644 index 0000000..2fc909b --- /dev/null +++ b/src/correct.rs @@ -0,0 +1,89 @@ +use npyz::NpyFile; +use std::io::BufReader; +use std::fs::File; + +pub struct Correcter { + lut: Vec, + strides: [u64;4], + scaler: f64 +} + +impl Correcter { + pub fn new(path: &str) -> Self { + let file = BufReader::new(File::open(path).unwrap()); + let npy = NpyFile::new(file).unwrap(); + + let strides = npy.strides().try_into().expect("Wrong number of dimentions!"); + let scaler = npy.shape()[0] as f64 / 255.; + + let data: Vec = npy.into_vec().unwrap(); + + Self { + lut: data, + strides, + scaler + } + } + + pub fn correct(&self, r: u8, g:u8, b:u8) -> [u8; 3] { + let r_scale = r as f64 * self.scaler * self.strides[0] as f64; + let g_scale = g as f64 * self.scaler * self.strides[1] as f64; + let b_scale = b as f64 * self.scaler * self.strides[2] as f64; + + println!("scaler {}", self.scaler); + println!("strides {:?}", self.strides); + println!("r scale {}", r_scale); + println!("g scale {}", g_scale); + println!("b scale {}", b_scale); + + let r_upper = r_scale.ceil(); + let g_upper = g_scale.ceil(); + let b_upper = b_scale.ceil(); + let r_lower = r_scale.floor(); + let g_lower = g_scale.floor(); + let b_lower = b_scale.floor(); + + println!("r upper {}", r_upper); + println!("g upper {}", g_upper); + println!("b upper {}", b_upper); + + println!("r lower {}", r_lower); + println!("g lower {}", g_lower); + println!("b lower {}", b_lower); + + let r_remainder = r_scale.fract(); + let g_remainder = g_scale.fract(); + let b_remainder = b_scale.fract(); + + println!("r remainder {}", r_remainder); + println!("g remainder {}", g_remainder); + println!("b remainder {}", b_remainder); + + let i_upper = (r_upper + g_upper + b_upper) as usize; + let i_lower = (r_lower + g_lower + b_lower) as usize; + + println!("i upper {}", i_upper); + println!("i lower {}", i_lower); + + let r_upper = self.lut[i_upper] as f64; + let g_upper = self.lut[i_upper+1] as f64; + let b_upper = self.lut[i_upper+2] as f64; + let r_lower = self.lut[i_lower] as f64; + let g_lower = self.lut[i_lower+1] as f64; + let b_lower = self.lut[i_lower+2] as f64; + + println!("r upper real {}", r_upper); + println!("g upper real {}", g_upper); + println!("b upper real {}", b_upper); + + println!("r lower real {}", r_lower); + println!("g lower real {}", g_lower); + println!("b lower real {}", b_lower); + + [ + (((r_upper - r_lower) * r_remainder) + r_lower) as u8, + (((g_upper - g_lower) * g_remainder) + g_lower) as u8, + (((b_upper - b_lower) * b_remainder) + b_lower) as u8 + ] + } +} diff --git a/src/cube/camera.py b/src/cube/camera.py new file mode 100644 index 0000000..bed2a29 --- /dev/null +++ b/src/cube/camera.py @@ -0,0 +1,59 @@ +import numpy as np +import cv2 as cv + +CAP_WAIT = 1 + +class Camera(): + def __init__(self, device): + cv.namedWindow("LUT Calibration", cv.WINDOW_GUI_NORMAL) + + self.camera = cv.VideoCapture(device) + self.homography = None + + #self.calibrate() + + def get(self, image): + import main + + cv.imshow("LUT Calibration", image) + cv.waitKey(CAP_WAIT) + + #_, capture = self.camera.read() + #capture = cv.warpPerspective(capture, self.homography, (main.IMAGE_WIDTH, main.IMAGE_HEIGHT)) + + return image + return capture + + def calibrate(self): + calibration_image = cv.imread("../calibration.jpg") + + # remove toolbar from named calibration window + cv.imshow("LUT Calibration", calibration_image) + cv.waitKey(0) + + _, capture = self.camera.read() + + sift = cv.SIFT_create() + kp1, des1 = sift.detectAndCompute(calibration_image, None) + kp2, des2 = sift.detectAndCompute(capture, None) + + # get good matches between calibration image and the captured image + flann = cv.FlannBasedMatcher( + {"algorithm": 1, "trees": 5}, + {"checks": 50} + ) + matches = flann.knnMatch(des1, des2, k=2) + + #get good matches via ratio test + good = [] + for m,n in matches: + if m.distance < 0.7 * n.distance: + good.append(m) + + if len(good) > 10: + src_pts = np.float32([ kp1[m.queryIdx].pt for m in good ]).reshape(-1,1,2) + dst_pts = np.float32([ kp2[m.trainIdx].pt for m in good ]).reshape(-1,1,2) + self.homography, _ = cv.findHomography(dst_pts, src_pts, cv.RANSAC, 5.0) + + else: + raise Exception("Calibration failed") diff --git a/src/cube/cube.py b/src/cube/cube.py old mode 100644 new mode 100755 index ad3062c..ba6cf6f --- a/src/cube/cube.py +++ b/src/cube/cube.py @@ -1,4 +1,89 @@ #!.venv/bin/python +from tqdm import tqdm +import numpy as np import cv2 as cv +import time + +WAIT_TIME = 0.1 +WAIT_TICKS = 100 + +class Cube(): + def __init__(self, camera, size): + self.camera = camera + self.size = size + self.lut = np.zeros((size, size, size, 3), dtype=np.uint8) + self.s = 255 / (self.size-1) + + print(f""" +creating LUT... + +size:\t{self.size} +scaler:\t{self.s} +colors:\t{self.size**3} + """) + + def process(self): + seq = [i * self.s for i in range(0,self.size)] + for r in tqdm(seq): + for g in seq: + for b in seq: + self.check(color=[r, g, b]) + + for _ in range(WAIT_TICKS): + pass#self.check() + #time.sleep(WAIT_TIME) + + self.invert() + + def invert(self): + + print("inverting LUT...") + + def iter_channels(a): + r = self.lut[:,:,:,0] + g = self.lut[:,:,:,1] + b = self.lut[:,:,:,2] + + r = resample_channel(np.rot90(r, 1, (0,2))) + g = resample_channel(np.rot90(g, 1, (1,2))) + + r = np.rot90(r, -1, (0,2)) + g = np.rot90(g, -1, (1,2)) + + b = resample_channel(b) + + return np.stack((r, g, b), axis=-1) + + def resample_channel(c): + c = np.reshape(c, (self.size * self.size, self.size)) + + for i in range(self.size * self.size): + seq = np.linspace(0, 255, self.size) + + c[i] = np.interp(seq, c[i], seq) + + c = np.reshape(c, (self.size, self.size, self.size)) + return c + + self.lut = iter_channels(self.lut) + + def check(self, color=None): + from frame import blank, generate, result + + if color is None: + image = blank() + else: + image = generate(color) + + capture = self.camera.get(image) + + data, new = result(capture) + + if data is not None: + data = np.divide(data, self.s).round().astype(np.uint8) + + self.lut[data[0], data[1], data[2]] = new + + diff --git a/src/cube/frame.py b/src/cube/frame.py new file mode 100644 index 0000000..d331c2e --- /dev/null +++ b/src/cube/frame.py @@ -0,0 +1,73 @@ +#!.venv/bin/python + +from pyzbar.pyzbar import decode +import numpy as np +import cv2 as cv +import random +import qrcode +import os + +def cast(a): + a = np.array(a, dtype=np.float64) + a = ((a / 255.) ** 0.4) * 255. + a = np.clip(a.astype(np.uint8), 0, 255) + + return a + +def generate(color): + import main + + # make qr code + qr = qrcode.QRCode( + version=1, + error_correction=qrcode.constants.ERROR_CORRECT_L, + border=4, + ) + qr.add_data(" ".join(["{:03d}".format(int(x)) for x in color])) + qr.make(fit=True) + + # transform qr into array with correct shape + qr_image = np.array(qr.get_matrix()) + qr_image = np.where(qr_image, 0, 255).astype(np.uint8) + qr_image = np.repeat(qr_image[:, :, np.newaxis], 3, axis=2) + qr_image = cv.resize(qr_image, (main.QR_SIZE,main.QR_SIZE), interpolation=cv.INTER_NEAREST) + + color = cast(color) + + # create color image of correct shape + c_image = np.array([[color[::-1]]], dtype=np.uint8) + c_image = cv.resize(c_image, (main.IMAGE_WIDTH, main.IMAGE_HEIGHT)) + + # put qr codes in the corners + tl = np.s_[:main.QR_SIZE,:main.QR_SIZE] + tr = np.s_[:main.QR_SIZE,-main.QR_SIZE:] + bl = np.s_[-main.QR_SIZE:,:main.QR_SIZE] + br = np.s_[-main.QR_SIZE:,-main.QR_SIZE:] + + c_image[tl] = c_image[tr] = c_image[bl] = c_image[br] = qr_image + + return c_image + +def blank(): + import main + + image = np.zeros((main.IMAGE_HEIGHT,main.IMAGE_WIDTH,3), dtype=np.uint8) + return image + +def result(capture): + import main + + l = main.QR_SIZE + main.QR_PADDING + + new = np.mean(capture[l:-l,l:-l], axis=(0,1)).astype(np.uint8) + + codes = decode(capture) + + if codes == []: return None, None + + codes.sort(key=lambda x:x.quality) + data = codes[0].data + data = [int(x) for x in data.split()] + data = np.array(data) + + return data, new[::-1] diff --git a/src/cube/graph.py b/src/cube/graph.py new file mode 100755 index 0000000..79139f0 --- /dev/null +++ b/src/cube/graph.py @@ -0,0 +1,49 @@ +#!.venv/bin/python + +import matplotlib.pyplot as plt +import numpy as np + +def show(a): + size = a.shape[0] + fig = plt.figure(figsize=plt.figaspect(1.)) + + ax = fig.add_subplot(2, 1, 1) + + b = a.astype(np.float64) + + ax.plot(b[1,1,...,2]+50, c="blue") + ax.plot(b[1,...,1,1]+25, c="green") + ax.plot(b[...,1,1,0], c="red") + + ax = fig.add_subplot(2, 1, 2, projection='3d') + + xs = [] + ys = [] + zs = [] + cs = [] + + # look im going to do this in a lazy way please forgive me + for x in range(size): + for y in range(size): + for z in range(size): + xs.append(x) + ys.append(y) + zs.append(z) + + r, g, b = a[x][y][z] + cs.append("#{0:02x}{1:02x}{2:02x}".format(r, g, b)) + + ax.scatter(xs, ys, zs, c=cs) + ax.set_xlabel("r") + ax.set_ylabel("g") + ax.set_zlabel("b") + plt.show() + +def compare(a, b): + plt.hist(a.flat, bins=range(100), fc='k', ec='k', color="red") + plt.hist(b.flat, bins=range(100), fc='k', ec='k', color="blue") + plt.show() + +if __name__ == "__main__": + a = np.load("../../cube.npy") + show(a) diff --git a/src/cube/main.py b/src/cube/main.py new file mode 100755 index 0000000..02dccbd --- /dev/null +++ b/src/cube/main.py @@ -0,0 +1,32 @@ +#!.venv/bin/python + +import cube +from camera import Camera +from numpy import save +from graph import show +from test import validate + +import matplotlib.pyplot as plt + +LUT_SIZE = 12 + +IMAGE_WIDTH = 640#1920 +IMAGE_HEIGHT = 360#1080 + +QR_SIZE = 100 +QR_PADDING = 10 + +if __name__ == "__main__": + eye = Camera(0) + + times = [] + means = [] + + lut = cube.Cube(eye, LUT_SIZE) + lut.process() + show(lut.lut) + validate(lut) + + save("../../cube.npy", lut.lut) + + diff --git a/src/cube/test.py b/src/cube/test.py new file mode 100755 index 0000000..f5d0834 --- /dev/null +++ b/src/cube/test.py @@ -0,0 +1,68 @@ +from frame import cast +import cv2 as cv +import numpy as np +from graph import compare +import time + +def validate(cube): + print("testing LUT...") + + image = cv.imread("../calibration.jpg") + height, width, _ = image.shape + a = cast(np.flip(image, axis=-1)).astype(np.uint8) + casted = cast(np.flip(image, axis=-1)).astype(np.uint8) + + start = time.time() + + a = np.divide(a, cube.s) + + c1 = np.floor(a).astype(np.uint8) + c2 = np.ceil(a).astype(np.uint8) + rem = np.remainder(a, 1) + + def index_lut(a, i): + for ih in range(height): + for iw in range(width): + pos = i[ih,iw] + pos = np.clip(pos, 0, a.shape[0]) + i[ih,iw] = a[pos[0], pos[1], pos[2]] + + return i + + c1 = index_lut(cube.lut, c1) + c2 = index_lut(cube.lut, c2) + + a = c1 + np.array((c2 - c1) * rem, dtype=np.uint8) + a = np.flip(a, axis=-1) + + dur = time.time() - start + + casted = np.flip(casted, axis=-1) + + # do the diff + + diff = np.abs(image.sum(axis=-1, dtype=np.int16) - a.sum(axis=-1, dtype=np.int16)) + diff = np.clip(diff, 0, 255).astype(np.uint8) + diff = np.stack((diff, diff, diff), axis=-1) + + print(f""" +cast mean:\t\t{np.mean(np.abs(casted - a))} + +max error:\t\t{diff.max()} +mean error:\t\t{np.mean(diff)} +standard deviation:\t{np.std(diff)} + +time taken:\t\t{dur}s + """) + + # make the composite image + + left = np.vstack((image, a), dtype=np.uint8) + right = np.vstack((casted, diff), dtype=np.uint8) + + composite = np.hstack((left, right)) + + composite = cv.resize(composite, (640,360)) + cv.imshow("LUT Calibration", composite) + + cv.waitKey(0) diff --git a/src/main.rs b/src/main.rs index 832355d..dc73dc2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,6 +11,12 @@ use std::sync::mpsc::Sender; use rscam::{Camera, Config}; use image::ImageReader; +use rand::Rng; + +mod correct; + +use crate::correct::Correcter; + const WINDOW_SIZE: usize = 128; const CHUNK_SIZE: usize = 72; const SPECTOGRAM_AREA: usize = WINDOW_SIZE * CHUNK_SIZE; @@ -48,6 +54,7 @@ extern "C" { struct ImageArray { data: Vec, homography: [f64; 9], + corrector: Correcter, camera_buffer: Vec, camera: Camera, chunks: usize @@ -58,8 +65,9 @@ impl ImageArray { let mut array = Self { data: vec![0u8; SPECTOGRAM_AREA * 3], homography, - camera_buffer: vec![0u8; IMAGE_AREA], + corrector: Correcter::new("./cube.npy"), + camera_buffer: vec![0u8; IMAGE_AREA], camera: Camera::new("/dev/video2").unwrap(), chunks: SPECTOGRAM_AREA @@ -78,6 +86,18 @@ impl ImageArray { self.camera_buffer = self.camera.capture().unwrap()[..].try_into().expect("Image is wrong size"); unsafe{ApplyHomography (self.camera_buffer.as_ptr() as usize, self.data.as_ptr() as usize, self.homography.as_ptr() as usize);} + + for i in 0..self.camera_buffer.len()/3 { + let mut r = self.camera_buffer[i*3]; + let mut g = self.camera_buffer[(i*3)+1]; + let mut b = self.camera_buffer[(i*3)+3]; + + [r, g, b] = self.corrector.correct(r, g, b); + + self.camera_buffer[i*3] = r; + self.camera_buffer[(i*3)+1] = g; + self.camera_buffer[(i*3)+3] = b; + } } fn calibrate (&mut self) { @@ -213,6 +233,28 @@ impl SampleBuffer { #[show_image::main] fn main () -> Result<(), Box> { + + + let mut rng = rand::rng(); + + let cor = Correcter::new("./cube.npy"); + + let r = rng.gen_range(0..255) as u8; + let g = rng.gen_range(0..255) as u8; + let b = rng.gen_range(0..255) as u8; + + println!("{}", r); + println!("{}", g); + println!("{}", b); + + let c = cor.correct(r, g, b); + + println!("{:?}", c); + + std::process::exit(1); + + + // pregenerate the fft transformers let forward_transform = Radix4::::new(WINDOW_SIZE, FftDirection::Forward); let inverse_transform = Radix4::::new(WINDOW_SIZE, FftDirection::Inverse);