diff --git a/Cargo.lock b/Cargo.lock index 62ab7dd8..e1e66694 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -70,6 +70,54 @@ version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "argmin" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5698c8cd3510117a4e6b96749a8061ba7dce1a19578ce4ecdb12dd36d94a7f8d" +dependencies = [ + "anyhow", + "argmin-math", + "bincode", + "instant", + "num-traits", + "paste", + "rand 0.8.5", + "rand_xoshiro", + "serde", + "serde_json", + "slog", + "slog-async", + "slog-json", + "slog-term", + "thiserror", +] + +[[package]] +name = "argmin-math" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f2b0dada81340718682df780c9a696b090b6ef7e83c3dcc770af6de9302995" +dependencies = [ + "anyhow", + "cfg-if", + "nalgebra 0.31.4", + "num-complex", + "num-integer", + "num-traits", + "rand 0.8.5", + "thiserror", +] + [[package]] name = "arrayvec" version = "0.7.2" @@ -153,6 +201,20 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "autoquant" +version = "0.1.0" +source = "git+https://github.com/truedoctor/autoquant#425fe5bd743c502922bfdcda808b8a0b3d1332d2" +dependencies = [ + "anyhow", + "argmin", + "argmin-math", + "log", + "nalgebra 0.30.1", + "num", + "varpro", +] + [[package]] name = "axum" version = "0.6.1" @@ -231,6 +293,15 @@ dependencies = [ "wasm-bindgen-test", ] +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -374,9 +445,9 @@ dependencies = [ [[package]] name = "cargo_toml" -version = "0.13.0" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa0e3586af56b3bfa51fca452bd56e8dbbbd5d8d81cbf0b7e4e35b695b537eb8" +checksum = "497049e9477329f8f6a559972ee42e117487d01d1e8c2cc9f836ea6fa23a9e1a" dependencies = [ "serde", "toml", @@ -894,6 +965,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + [[package]] name = "dtoa" version = "0.4.8" @@ -1561,6 +1638,7 @@ dependencies = [ name = "graphene-std" version = "0.1.0" dependencies = [ + "autoquant", "bezier-rs", "borrow_stack", "bytemuck", @@ -1755,9 +1833,9 @@ dependencies = [ [[package]] name = "half" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad6a9459c9c30b177b925162351f97e7d967c7ea8bab3b8352805327daf45554" +checksum = "6c467d36af040b7b2681f5fddd27427f6da8d3d072f575a265e181d2f8e8d157" dependencies = [ "crunchy", ] @@ -2193,10 +2271,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] -name = "libc" -version = "0.2.138" +name = "levenberg-marquardt" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" +checksum = "758849cc08a08003567842a85887e6a528017114a893d5402b6f08744a5f51aa" +dependencies = [ + "cfg-if", + "nalgebra 0.30.1", + "num-traits", + "rustc_version 0.4.0", +] + +[[package]] +name = "libc" +version = "0.2.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "libdbus-sys" @@ -2338,6 +2428,15 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40" +[[package]] +name = "matrixmultiply" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add85d4dd35074e6fedc608f8c8f513a3548619a9024b751949ef0e8e45a4d84" +dependencies = [ + "rawpointer", +] + [[package]] name = "memchr" version = "2.5.0" @@ -2424,6 +2523,50 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "nalgebra" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb2d0de08694bed883320212c18ee3008576bfe8c306f4c3c4a58b4876998be" +dependencies = [ + "approx", + "matrixmultiply", + "nalgebra-macros", + "num-complex", + "num-rational", + "num-traits", + "simba", + "typenum", +] + +[[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", + "serde", + "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", +] + [[package]] name = "nanorand" version = "0.7.0" @@ -2520,6 +2663,41 @@ dependencies = [ "winapi", ] +[[package]] +name = "num" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19" +dependencies = [ + "num-traits", + "serde", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -2530,6 +2708,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-rational" version = "0.4.1" @@ -2537,6 +2726,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" dependencies = [ "autocfg", + "num-bigint", "num-integer", "num-traits", ] @@ -2582,6 +2772,15 @@ dependencies = [ "syn", ] +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + [[package]] name = "objc" version = "0.2.7" @@ -2623,9 +2822,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" [[package]] name = "open" @@ -2777,9 +2976,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.5.1" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc8bed3549e0f9b0a2a78bf7c0018237a2cdf085eecbbc048e52612438e4e9d0" +checksum = "0f6e86fb9e7026527a0d46bc308b841d73170ef8f443e1807f6ef88526a816d4" dependencies = [ "thiserror", "ucd-trie", @@ -3066,6 +3265,7 @@ dependencies = [ "libc", "rand_chacha 0.3.1", "rand_core 0.6.4", + "serde", ] [[package]] @@ -3104,6 +3304,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom 0.2.8", + "serde", ] [[package]] @@ -3124,6 +3325,16 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "rand_xoshiro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +dependencies = [ + "rand_core 0.6.4", + "serde", +] + [[package]] name = "range-alloc" version = "0.1.2" @@ -3139,6 +3350,12 @@ dependencies = [ "cty", ] +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + [[package]] name = "rayon" version = "1.6.1" @@ -3398,6 +3615,15 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" +[[package]] +name = "safe_arch" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "794821e4ccb0d9f979512f9c1973480123f9bd62a90d74ab0f9426fcf8f4a529" +dependencies = [ + "bytemuck", +] + [[package]] name = "safemem" version = "0.3.3" @@ -3529,9 +3755,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.151" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fed41fc1a24994d044e6db6935e69511a1153b52c15eb42493b26fa87feba0" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" dependencies = [ "serde_derive", ] @@ -3549,9 +3775,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.151" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "255abe9a125a985c05190d687b320c12f9b1f0b99445e608c21ba0782c719ad8" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", "quote", @@ -3694,6 +3920,19 @@ dependencies = [ "libc", ] +[[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 = "siphasher" version = "0.3.10" @@ -3709,6 +3948,49 @@ dependencies = [ "autocfg", ] +[[package]] +name = "slog" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8347046d4ebd943127157b94d63abb990fcf729dc4e9978927fdf4ac3c998d06" + +[[package]] +name = "slog-async" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "766c59b252e62a34651412870ff55d8c4e6d04df19b43eecb2703e417b097ffe" +dependencies = [ + "crossbeam-channel", + "slog", + "take_mut", + "thread_local", +] + +[[package]] +name = "slog-json" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e1e53f61af1e3c8b852eef0a9dee29008f55d6dd63794f3f12cef786cf0f219" +dependencies = [ + "serde", + "serde_json", + "slog", + "time 0.3.17", +] + +[[package]] +name = "slog-term" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87d29185c55b7b258b4f120eab00f48557d4d9bc814f41713f449d35b0f8977c" +dependencies = [ + "atty", + "slog", + "term", + "thread_local", + "time 0.3.17", +] + [[package]] name = "slotmap" version = "1.0.6" @@ -3724,6 +4006,28 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +[[package]] +name = "snafu" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0656e7e3ffb70f6c39b3c2a86332bb74aa3c679da781642590f3c1118c5045" +dependencies = [ + "doc-comment", + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "475b3bbe5245c26f2d8a6f62d67c1f30eb9fffeccee721c45d162c3ebbdf81b2" +dependencies = [ + "heck 0.4.0", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "socket2" version = "0.4.7" @@ -3789,8 +4093,8 @@ dependencies = [ [[package]] name = "spirv-std" -version = "0.4.0-alpha.17" -source = "git+https://github.com/EmbarkStudios/rust-gpu#d2d6ee2f7513a51c193a65b0fd99a7d40be74ec8" +version = "0.4.0" +source = "git+https://github.com/EmbarkStudios/rust-gpu#8fcb61e82a3bd74df67204c9b4fb1f6f71d20c73" dependencies = [ "bitflags", "glam", @@ -3801,8 +4105,8 @@ dependencies = [ [[package]] name = "spirv-std-macros" -version = "0.4.0-alpha.17" -source = "git+https://github.com/EmbarkStudios/rust-gpu#d2d6ee2f7513a51c193a65b0fd99a7d40be74ec8" +version = "0.4.0" +source = "git+https://github.com/EmbarkStudios/rust-gpu#8fcb61e82a3bd74df67204c9b4fb1f6f71d20c73" dependencies = [ "proc-macro2", "quote", @@ -3812,8 +4116,8 @@ dependencies = [ [[package]] name = "spirv-std-types" -version = "0.4.0-alpha.17" -source = "git+https://github.com/EmbarkStudios/rust-gpu#d2d6ee2f7513a51c193a65b0fd99a7d40be74ec8" +version = "0.4.0" +source = "git+https://github.com/EmbarkStudios/rust-gpu#8fcb61e82a3bd74df67204c9b4fb1f6f71d20c73" [[package]] name = "stable_deref_trait" @@ -3932,6 +4236,12 @@ dependencies = [ "version-compare 0.1.1", ] +[[package]] +name = "take_mut" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" + [[package]] name = "tao" version = "0.15.8" @@ -3991,9 +4301,9 @@ dependencies = [ [[package]] name = "tauri" -version = "1.2.2" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8ea1d785ab2164373703817bff144c4610a69ad3f659becaca0e1ea004b98d8" +checksum = "5b48820ee3bb6a5031a83b2b6e11f8630bdc5a2f68cb841ab8ebc7a15a916679" dependencies = [ "anyhow", "attohttpc", @@ -4202,6 +4512,17 @@ dependencies = [ "utf-8", ] +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + [[package]] name = "termcolor" version = "1.1.3" @@ -4306,6 +4627,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" dependencies = [ "itoa 1.0.5", + "libc", + "num_threads", "serde", "time-core", "time-macros", @@ -4667,6 +4990,18 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "varpro" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f63a5740646db392ee87330118c898c257232366a27bf328e2aa0d0af37a28e" +dependencies = [ + "levenberg-marquardt", + "nalgebra 0.30.1", + "num-traits", + "snafu", +] + [[package]] name = "vcpkg" version = "0.2.15" @@ -5122,6 +5457,16 @@ dependencies = [ "bitflags", ] +[[package]] +name = "wide" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae41ecad2489a1655c8ef8489444b0b113c0a0c795944a3572a0931cf7d2525c" +dependencies = [ + "bytemuck", + "safe_arch", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/editor/Cargo.toml b/editor/Cargo.toml index 88cd627d..6f60fbd1 100644 --- a/editor/Cargo.toml +++ b/editor/Cargo.toml @@ -12,6 +12,7 @@ license = "Apache-2.0" [features] gpu = ["interpreted-executor/gpu", "graphene-std/gpu", "graphene-core/gpu"] +quantization = ["graphene-std/quantization", "interpreted-executor/quantization"] [dependencies] log = "0.4" diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs index 82b979a9..d1109e2d 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs @@ -105,12 +105,37 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[ DocumentInputType { name: "Path", data_type: FrontendGraphDataType::Text, - default: NodeInput::value(TaggedValue::String(String::new()), true), + default: NodeInput::value(TaggedValue::String(String::new()), false), }, ], outputs: &[FrontendGraphDataType::Raster], properties: node_properties::gpu_map_properties, }, + #[cfg(feature = "quantization")] + DocumentNodeType { + name: "QuantizeImage", + category: "Image Adjustments", + identifier: NodeIdentifier::new("graphene_std::quantization::GenerateQuantizationNode", &[concrete!("&TypeErasedNode")]), + inputs: &[ + DocumentInputType { + name: "Image", + data_type: FrontendGraphDataType::Raster, + default: NodeInput::value(TaggedValue::Image(Image::empty()), true), + }, + DocumentInputType { + name: "samples", + data_type: FrontendGraphDataType::Number, + default: NodeInput::value(TaggedValue::U32(100), false), + }, + DocumentInputType { + name: "Fn index", + data_type: FrontendGraphDataType::Number, + default: NodeInput::value(TaggedValue::U32(0), false), + }, + ], + outputs: &[FrontendGraphDataType::Raster], + properties: node_properties::quantize_properties, + }, DocumentNodeType { name: "Invert RGB", category: "Image Adjustments", diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/node_properties.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/node_properties.rs index 83addf11..fb7651de 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/node_properties.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/node_properties.rs @@ -141,6 +141,20 @@ fn number_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, na })), ]) } + if let NodeInput::Value { + tagged_value: TaggedValue::U32(x), + exposed: false, + } = document_node.inputs[index] + { + widgets.extend_from_slice(&[ + WidgetHolder::unrelated_separator(), + WidgetHolder::new(Widget::NumberInput(NumberInput { + value: Some(x as f64), + on_update: update_value(|x: &NumberInput| TaggedValue::U32(x.value.unwrap() as u32), node_id, index), + ..NumberInput::default() + })), + ]) + } widgets } @@ -199,6 +213,12 @@ pub fn posterize_properties(document_node: &DocumentNode, node_id: NodeId, _cont vec![LayoutGroup::Row { widgets: value }] } +pub fn quantize_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec { + let value = number_widget(document_node, node_id, 1, "Levels", NumberInput::new().min(1.).max(1000.).int(), true); + let index = number_widget(document_node, node_id, 1, "Fit Fn Index", NumberInput::new().min(0.).max(2.).int(), true); + + vec![LayoutGroup::Row { widgets: value }, LayoutGroup::Row { widgets: index }] +} pub fn exposure_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec { let value = number_widget(document_node, node_id, 1, "Value", NumberInput::new().min(-3.).max(3.), true); diff --git a/node-graph/gcore/src/raster.rs b/node-graph/gcore/src/raster.rs index 027b151e..fe2ea09a 100644 --- a/node-graph/gcore/src/raster.rs +++ b/node-graph/gcore/src/raster.rs @@ -3,7 +3,7 @@ use crate::Node; pub mod color; pub use self::color::Color; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Default)] pub struct GrayscaleColorNode; impl Node for GrayscaleColorNode { @@ -53,6 +53,32 @@ impl + Copy> BrightenColorNode { } } +#[derive(Debug, Clone, Copy)] +pub struct GammaColorNode>(N); + +impl> Node for GammaColorNode { + type Output = Color; + fn eval(self, color: Color) -> Color { + let gamma = self.0.eval(()); + let per_channel = |col: f32| col.powf(gamma); + Color::from_rgbaf32_unchecked(per_channel(color.r()), per_channel(color.g()), per_channel(color.b()), color.a()) + } +} +impl + Copy> Node for &GammaColorNode { + type Output = Color; + fn eval(self, color: Color) -> Color { + let gamma = self.0.eval(()); + let per_channel = |col: f32| col.powf(gamma); + Color::from_rgbaf32_unchecked(per_channel(color.r()), per_channel(color.g()), per_channel(color.b()), color.a()) + } +} + +impl + Copy> GammaColorNode { + pub fn new(node: N) -> Self { + Self(node) + } +} + #[derive(Debug, Clone, Copy)] #[cfg(not(target_arch = "spirv"))] pub struct HueShiftColorNode>(N); diff --git a/node-graph/gstd/Cargo.toml b/node-graph/gstd/Cargo.toml index 7c11dafd..7ef93201 100644 --- a/node-graph/gstd/Cargo.toml +++ b/node-graph/gstd/Cargo.toml @@ -14,9 +14,11 @@ default = ["memoization"] gpu = ["graphene-core/gpu", "gpu-compiler-bin-wrapper", "compilation-client"] vulkan = ["gpu", "vulkan-executor"] wgpu = ["gpu", "wgpu-executor"] +quantization = ["autoquant"] [dependencies] +autoquant = { git = "https://github.com/truedoctor/autoquant", optional = true, features = ["fitting"] } graphene-core = {path = "../gcore", features = ["async", "std" ], default-features = false} borrow_stack = {path = "../borrow_stack"} dyn-any = {path = "../../libraries/dyn-any", features = ["derive"]} diff --git a/node-graph/gstd/src/lib.rs b/node-graph/gstd/src/lib.rs index 60c7bb05..1f3121a4 100644 --- a/node-graph/gstd/src/lib.rs +++ b/node-graph/gstd/src/lib.rs @@ -15,4 +15,7 @@ pub mod any; #[cfg(feature = "gpu")] pub mod executor; +#[cfg(feature = "quantization")] +pub mod quantization; + pub use graphene_core::*; diff --git a/node-graph/gstd/src/quantization.rs b/node-graph/gstd/src/quantization.rs new file mode 100644 index 00000000..28bea9b3 --- /dev/null +++ b/node-graph/gstd/src/quantization.rs @@ -0,0 +1,54 @@ +use graphene_core::raster::{Color, Image}; +use graphene_core::Node; + +/// The `GenerateQuantizationNode` encodes the brightness of each channel of the image as an integer number +/// sepified by the samples parameter. This node is used to asses the loss of visual information when +/// quantizing the image using different fit functions. +pub struct GenerateQuantizationNode, M: Node<(), Output = u32>> { + samples: N, + function: M, +} + +#[node_macro::node_fn(GenerateQuantizationNode)] +fn generate_quantization_fn(image: Image, samples: u32, function: u32) -> Image { + // Scale the input image, this can be removed by adding an extra parameter to the fit function. + let max_energy = 16380.; + let data: Vec = image.data.iter().flat_map(|x| vec![x.r() as f64, x.g() as f64, x.b() as f64]).collect(); + let data: Vec = data.iter().map(|x| x * max_energy).collect(); + let mut dist = autoquant::integrate_distribution(data); + autoquant::drop_duplicates(&mut dist); + let dist = autoquant::normalize_distribution(dist.as_slice()); + let max = dist.last().unwrap().0; + let linear = Box::new(autoquant::SimpleFitFn { + function: move |x| x / max, + inverse: move |x| x * max, + name: "identity", + }); + let best = match function { + 0 => linear as Box, + 1 => linear as Box, + 2 => Box::new(autoquant::models::OptimizedLog::new(dist, 20)) as Box, + _ => linear as Box, + }; + + let roundtrip = |sample: f32| -> f32 { + let encoded = autoquant::encode(sample as f64 * max_energy, best.as_ref(), samples); + let decoded = autoquant::decode(encoded, best.as_ref(), samples) / max_energy; + log::trace!("{} enc: {} dec: {}", sample, encoded, decoded); + decoded as f32 + }; + + let new_data = image + .data + .iter() + .map(|c| { + let r = roundtrip(c.r()); + let g = roundtrip(c.g()); + let b = roundtrip(c.b()); + let a = c.a(); + + Color::from_rgbaf32_unchecked(r, g, b, a) + }) + .collect(); + Image { data: new_data, ..image } +} diff --git a/node-graph/interpreted-executor/Cargo.toml b/node-graph/interpreted-executor/Cargo.toml index ea5052d6..3cedb42d 100644 --- a/node-graph/interpreted-executor/Cargo.toml +++ b/node-graph/interpreted-executor/Cargo.toml @@ -8,6 +8,7 @@ license = "MIT OR Apache-2.0" default = [] serde = ["dep:serde", "graphene-std/serde", "glam/serde"] gpu = ["graphene-std/gpu", "graphene-core/gpu", "graphene-std/wgpu"] +quantization = ["graphene-std/quantization"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/node-graph/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index 2c91c534..342581ac 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -268,6 +268,32 @@ static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[ } }, ), + #[cfg(feature = "quantization")] + ( + NodeIdentifier::new("graphene_std::quantization::GenerateQuantizationNode", &[concrete!("&TypeErasedNode")]), + |proto_node, stack| { + if let ConstructionArgs::Nodes(operation_node_id) = proto_node.construction_args { + stack.push_fn(move |nodes| { + info!("Quantization Depending upon id {:?}", operation_node_id); + let samples_node = nodes.get(operation_node_id[0] as usize).unwrap(); + let index_node = nodes.get(operation_node_id[1] as usize).unwrap(); + let samples_node: DowncastBothNode<_, (), u32> = DowncastBothNode::new(samples_node); + let index_node: DowncastBothNode<_, (), u32> = DowncastBothNode::new(index_node); + let map_node = graphene_std::quantization::GenerateQuantizationNode::new(samples_node, index_node); + let map_node = DynAnyNode::new(map_node); + + if let ProtoNodeInput::Node(node_id) = proto_node.input { + let pre_node = nodes.get(node_id as usize).unwrap(); + (pre_node).then(map_node).into_type_erased() + } else { + map_node.into_type_erased() + } + }) + } else { + unimplemented!() + } + }, + ), (NodeIdentifier::new("graphene_std::raster::MapImageNode", &[]), |proto_node, stack| { if let ConstructionArgs::Nodes(operation_node_id) = proto_node.construction_args { stack.push_fn(move |nodes| {