"memchr",
]
+[[package]]
+name = "aligned-vec"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1"
+
[[package]]
name = "allocator-api2"
version = "0.2.18"
"libc",
]
+[[package]]
+name = "anyhow"
+version = "1.0.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74f37166d7d48a0284b99dd824694c26119c700b53bf0d1540cdb147dbdaaf13"
+
+[[package]]
+name = "arbitrary"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
+
+[[package]]
+name = "arg_enum_proc_macro"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
[[package]]
name = "arrayref"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+[[package]]
+name = "av1-grain"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf"
+dependencies = [
+ "anyhow",
+ "arrayvec",
+ "log",
+ "nom",
+ "num-rational",
+ "v_frame",
+]
+
+[[package]]
+name = "avif-serialize"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e335041290c43101ca215eed6f43ec437eb5a42125573f600fc3fa42b9bddd62"
+dependencies = [
+ "arrayvec",
+]
+
[[package]]
name = "backtrace"
version = "0.3.74"
"bitflags 2.6.0",
"cexpr",
"clang-sys",
- "itertools",
+ "itertools 0.13.0",
"proc-macro2",
"quote",
"regex",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
+[[package]]
+name = "bit_field"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
+
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+[[package]]
+name = "bitstream-io"
+version = "2.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b81e1519b0d82120d2fd469d5bfb2919a9361c48b02d82d04befc1cdd2002452"
+
[[package]]
name = "block"
version = "0.1.6"
"objc2-encode",
]
+[[package]]
+name = "built"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c360505aed52b7ec96a3636c3f039d99103c37d1d9b4f7a8c743d3ea9ffcd03b"
+
[[package]]
name = "bumpalo"
version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae"
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "byteorder-lite"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
+
[[package]]
name = "bytes"
version = "1.7.2"
"nom",
]
+[[package]]
+name = "cfg-expr"
+version = "0.15.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02"
+dependencies = [
+ "smallvec",
+ "target-lexicon",
+]
+
[[package]]
name = "cfg-if"
version = "1.0.0"
"unicode-width",
]
+[[package]]
+name = "color_quant"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
+
[[package]]
name = "com-rs"
version = "0.2.1"
"cfg-if",
]
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
+
+[[package]]
+name = "crunchy"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
+
[[package]]
name = "d3d12"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+[[package]]
+name = "exr"
+version = "1.73.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0"
+dependencies = [
+ "bit_field",
+ "half",
+ "lebe",
+ "miniz_oxide 0.8.0",
+ "rayon-core",
+ "smallvec",
+ "zune-inflate",
+]
+
[[package]]
name = "fdeflate"
version = "0.3.4"
"slab",
]
+[[package]]
+name = "getrandom"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "gif"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2"
+dependencies = [
+ "color_quant",
+ "weezl",
+]
+
[[package]]
name = "gimli"
version = "0.31.0"
"bitflags 2.6.0",
]
+[[package]]
+name = "half"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888"
+dependencies = [
+ "cfg-if",
+ "crunchy",
+]
+
[[package]]
name = "hashbrown"
version = "0.12.3"
"winapi",
]
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
[[package]]
name = "hexf-parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62adaabb884c94955b19907d60019f4e145d091c75345379e70d1ee696f7854f"
+[[package]]
+name = "image"
+version = "0.25.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc144d44a31d753b02ce64093d532f55ff8dc4ebf2ffb8a63c0dda691385acae"
+dependencies = [
+ "bytemuck",
+ "byteorder-lite",
+ "color_quant",
+ "exr",
+ "gif",
+ "image-webp",
+ "num-traits",
+ "png",
+ "qoi",
+ "ravif",
+ "rayon",
+ "rgb",
+ "tiff",
+ "zune-core",
+ "zune-jpeg",
+]
+
+[[package]]
+name = "image-webp"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e031e8e3d94711a9ccb5d6ea357439ef3dcbed361798bd4071dc4d9793fbe22f"
+dependencies = [
+ "byteorder-lite",
+ "quick-error",
+]
+
+[[package]]
+name = "imgref"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408"
+
[[package]]
name = "indexmap"
version = "1.9.3"
"web-sys",
]
+[[package]]
+name = "interpolate_name"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "itertools"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
+dependencies = [
+ "either",
+]
+
[[package]]
name = "itertools"
version = "0.13.0"
"libc",
]
+[[package]]
+name = "jpeg-decoder"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0"
+
[[package]]
name = "js-sys"
version = "0.3.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+[[package]]
+name = "lebe"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
+
[[package]]
name = "libc"
version = "0.2.161"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
+[[package]]
+name = "libfuzzer-sys"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7"
+dependencies = [
+ "arbitrary",
+ "cc",
+ "once_cell",
+]
+
[[package]]
name = "libloading"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+[[package]]
+name = "loop9"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062"
+dependencies = [
+ "imgref",
+]
+
[[package]]
name = "mach2"
version = "0.4.2"
"libc",
]
+[[package]]
+name = "maybe-rayon"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519"
+dependencies = [
+ "cfg-if",
+]
+
[[package]]
name = "memchr"
version = "2.7.4"
"jni-sys",
]
+[[package]]
+name = "new_debug_unreachable"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
+
[[package]]
name = "nix"
version = "0.24.3"
"minimal-lexical",
]
+[[package]]
+name = "noop_proc_macro"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
+
+[[package]]
+name = "num-bigint"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
+dependencies = [
+ "num-integer",
+ "num-traits",
+]
+
[[package]]
name = "num-complex"
version = "0.4.6"
"num-traits",
]
+[[package]]
+name = "num-rational"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
+dependencies = [
+ "num-bigint",
+ "num-integer",
+ "num-traits",
+]
+
[[package]]
name = "num-traits"
version = "0.2.19"
"miniz_oxide 0.7.4",
]
+[[package]]
+name = "ppv-lite86"
+version = "0.2.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
+dependencies = [
+ "zerocopy",
+]
+
[[package]]
name = "primal-check"
version = "0.3.4"
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
dependencies = [
"once_cell",
- "toml_edit",
+ "toml_edit 0.19.15",
]
[[package]]
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58"
+dependencies = [
+ "profiling-procmacros",
+]
+
+[[package]]
+name = "profiling-procmacros"
+version = "1.0.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30"
+dependencies = [
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "qoi"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
+dependencies = [
+ "bytemuck",
+]
+
+[[package]]
+name = "quick-error"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]]
name = "quote"
"proc-macro2",
]
+[[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 = "range-alloc"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab"
+[[package]]
+name = "rav1e"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9"
+dependencies = [
+ "arbitrary",
+ "arg_enum_proc_macro",
+ "arrayvec",
+ "av1-grain",
+ "bitstream-io",
+ "built",
+ "cfg-if",
+ "interpolate_name",
+ "itertools 0.12.1",
+ "libc",
+ "libfuzzer-sys",
+ "log",
+ "maybe-rayon",
+ "new_debug_unreachable",
+ "noop_proc_macro",
+ "num-derive",
+ "num-traits",
+ "once_cell",
+ "paste",
+ "profiling",
+ "rand",
+ "rand_chacha",
+ "simd_helpers",
+ "system-deps",
+ "thiserror",
+ "v_frame",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "ravif"
+version = "0.11.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2413fd96bd0ea5cdeeb37eaf446a22e6ed7b981d792828721e74ded1980a45c6"
+dependencies = [
+ "avif-serialize",
+ "imgref",
+ "loop9",
+ "quick-error",
+ "rav1e",
+ "rgb",
+]
+
[[package]]
name = "raw-window-handle"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9"
+[[package]]
+name = "rayon"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
[[package]]
name = "redox_syscall"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
+[[package]]
+name = "rgb"
+version = "0.8.50"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a"
+
[[package]]
name = "rscam"
version = "0.5.5"
"cc",
"cpal",
"hound",
+ "image",
"libc",
"rscam",
"rustfft",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
+[[package]]
+name = "serde"
+version = "1.0.210"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.210"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.77",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "0.6.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
+dependencies = [
+ "serde",
+]
+
[[package]]
name = "shlex"
version = "1.3.0"
dependencies = [
"futures",
"glam",
+ "image",
"indexmap 2.5.0",
"rustc_version",
"show-image-macros",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
+[[package]]
+name = "simd_helpers"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6"
+dependencies = [
+ "quote",
+]
+
[[package]]
name = "slab"
version = "0.4.9"
"unicode-ident",
]
+[[package]]
+name = "system-deps"
+version = "6.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349"
+dependencies = [
+ "cfg-expr",
+ "heck",
+ "pkg-config",
+ "toml",
+ "version-compare",
+]
+
+[[package]]
+name = "target-lexicon"
+version = "0.12.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
+
[[package]]
name = "termcolor"
version = "1.4.1"
"syn 2.0.77",
]
+[[package]]
+name = "tiff"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e"
+dependencies = [
+ "flate2",
+ "jpeg-decoder",
+ "weezl",
+]
+
[[package]]
name = "tiny-skia"
version = "0.8.4"
"strict-num",
]
+[[package]]
+name = "toml"
+version = "0.8.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit 0.22.22",
+]
+
[[package]]
name = "toml_datetime"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
+dependencies = [
+ "serde",
+]
[[package]]
name = "toml_edit"
dependencies = [
"indexmap 2.5.0",
"toml_datetime",
- "winnow",
+ "winnow 0.5.40",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.22.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
+dependencies = [
+ "indexmap 2.5.0",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "winnow 0.6.20",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a"
+[[package]]
+name = "v_frame"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b"
+dependencies = [
+ "aligned-vec",
+ "num-traits",
+ "wasm-bindgen",
+]
+
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
+[[package]]
+name = "version-compare"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b"
+
[[package]]
name = "version_check"
version = "0.9.5"
"wasm-bindgen",
]
+[[package]]
+name = "weezl"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
+
[[package]]
name = "wgpu"
version = "0.17.2"
"memchr",
]
+[[package]]
+name = "winnow"
+version = "0.6.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
+dependencies = [
+ "memchr",
+]
+
[[package]]
name = "x11-dl"
version = "2.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
+ "byteorder",
"zerocopy-derive",
]
"quote",
"syn 2.0.77",
]
+
+[[package]]
+name = "zune-core"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
+
+[[package]]
+name = "zune-inflate"
+version = "0.2.54"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
+dependencies = [
+ "simd-adler32",
+]
+
+[[package]]
+name = "zune-jpeg"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16099418600b4d8f028622f73ff6e3deaabdff330fb9a2a131dea781ee8b0768"
+dependencies = [
+ "zune-core",
+]
[dependencies]
libc = "0.2.161"
rustfft = "6.2.0"
-show-image = "0.14.0"
+image = "0.25.4"
+show-image = {version = "0.14.0", features = ["image"]}
hound = "3.5.1"
cpal = "0.15.3"
rscam = "0.5.5"
use rustfft::algorithm::Radix4;
use rustfft::{Fft, FftDirection};
use rustfft::num_complex::Complex;
-use show_image::{ImageView, ImageInfo, create_window};
+use show_image::{ImageView, ImageInfo, create_window, event};
use hound;
use cpal::{StreamConfig, BufferSize, SampleRate};
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use std::sync::mpsc;
use std::sync::mpsc::Sender;
use rscam::{Camera, Config};
+use image::ImageReader;
const WINDOW_SIZE: usize = 128;
const CHUNK_SIZE: usize = 72;
const SPECTOGRAM_AREA: usize = WINDOW_SIZE * CHUNK_SIZE;
+// configuration of the camera used
const IMAGE_WIDTH: usize = 1920;
const IMAGE_HEIGHT: usize = 1080;
const IMAGE_AREA: usize = IMAGE_WIDTH * IMAGE_HEIGHT * 3;
const FPS: usize = 30;
+// maximum and minimum pixel values of angle and amplitude. could be confined to
+// improve performance for quiet sounds.
const AMPLITUDE_MAX: f32 = 255.0;
const AMPLITUDE_MIN: f32 = 0.0;
const AMPLITUDE_REL: f32 = AMPLITUDE_MAX - AMPLITUDE_MIN;
const ANGLE_MIN: f32 = 0.0;
const ANGLE_REL: f32 = ANGLE_MAX - ANGLE_MIN;
-const VOLUME_MAX: f32 = 100.0; // 60 - 65
+// because of the opposingly logarithmic properties of both noise error and volume
+// this maximum value is important. -40 to 100 may not work under real conditions,
+// and -40 to 65 or even lower may produce better results.
+const VOLUME_MAX: f32 = 65.0; // 60 - 65
const VOLUME_MIN: f32 = -40.0;
const VOLUME_REL: f32 = VOLUME_MAX - VOLUME_MIN;
+const DEBUG_MODE: bool = true;
+
extern "C" {
fn GetHomography(camera_ptr: usize, homography_ptr: usize);
fn ApplyHomography(camera_ptr: usize, buffer_ptr: usize, homography_ptr: usize);
}
fn calibrate (&mut self) {
- println!("Getting homography... unsafe!");
+ // rscam gives and empty image if its not prompted a couple times
+ for _i in 0..10 {
+ self.camera_buffer = self.camera.capture().unwrap()[..].try_into().expect("Image is wrong size");
+ }
+
+ // enter unsafe and get the homography array
unsafe {
GetHomography(self.camera_buffer.as_ptr() as usize, self.homography.as_ptr() as usize);
}
- println!("Homography: {:?}", self.homography)
}
fn from_buffer (&mut self, buffer: &Vec<Complex<f32>>) -> () {
#[show_image::main]
fn main () -> Result<(), Box<dyn std::error::Error>> {
-
+ // pregenerate the fft transformers
let forward_transform = Radix4::<f32>::new(WINDOW_SIZE, FftDirection::Forward);
let inverse_transform = Radix4::<f32>::new(WINDOW_SIZE, FftDirection::Inverse);
let scratch_size = forward_transform.get_inplace_scratch_len();
+ // preallocate complex space for the ffts
let mut buffer = vec![Complex{re: 0f32, im: 0f32}; SPECTOGRAM_AREA];
let mut scratch = vec![Complex{re: 0f32, im: 0f32}; scratch_size];
+ // setup communication between the main thread and the audio thread
let (tx, rx) = mpsc::channel();
let sample_buffer = Arc::new(Mutex::new([0i16; 2*SPECTOGRAM_AREA]));
let mut samples = SampleBuffer::new(sample_buffer.clone(), tx);
- let window = create_window("image", Default::default())?;
-
- let homography = [0f64; 9];
+ // setup homography space and get ready for the calibration
+ let homography = [0f64; 9]; // homography is a 3x3 matrix of 64-bit floats
let mut image_array = ImageArray::new(homography);
+
+ // create the debug window
+ let debug_window = create_window("Debug", Default::default())?;
+
+ // create window for displaying images and display calibration image
+ let display_window = create_window("Display", Default::default())?;
+ let calibration_image = ImageReader::open("calibration.jpg")?.decode()?;
+ display_window.set_image("Display", calibration_image)?;
+
+ // wait for the user to press a key before continuing
+ for event in display_window.event_channel()? {
+ if let event::WindowEvent::KeyboardInput(event) = event {
+ if event.input.key_code == Some(event::VirtualKeyCode::Return) && event.input.state.is_pressed() {
+ break;
+ }
+ }
+ }
+
+ // calibrate camera
image_array.calibrate();
+ // open audio file
let mut reader = hound::WavReader::open("/home/will/Downloads/Adducci - Around the Horn.wav").unwrap();
let file_rate = reader.spec().sample_rate;
+ // setup audio output and build output stream
let host = cpal::default_host();
let device = host.default_output_device().expect("No output device available");
},
None
).unwrap();
- stream.play().expect("Stream pay failed");
+ stream.play().expect("Stream play failed");
+ // begin looping though the samples until the buffer is full of complex audio with an imag element of 0
let mut i = 0;
for sample in reader.samples::<i16>() {
if i == SPECTOGRAM_AREA {
+ // transform and do power scaling
forward_transform.process_with_scratch(&mut buffer, &mut scratch);
for x in buffer.iter_mut() {
*x *= 1f32 / WINDOW_SIZE as f32;
}
image_array.from_buffer(&mut buffer);
+ // show the image on screen
let image = ImageView::new(ImageInfo::rgb8(WINDOW_SIZE as u32, CHUNK_SIZE as u32), &image_array.data);
- window.set_image ("image", image)?;
+ display_window.set_image ("image", image)?;
+ // capture and transform camera view to image
image_array.from_camera();
- image_array.to_buffer(&mut buffer);
+ // make and display debug image
+ if DEBUG_MODE {
+ let debug_image = ImageView::new(ImageInfo::rgb8(WINDOW_SIZE as u32, CHUNK_SIZE as u32), &image_array.data);
+ debug_window.set_image("Debug", debug_image)?;
+ }
+ // convert image to audio
+ image_array.to_buffer(&mut buffer);
inverse_transform.process_with_scratch(&mut *buffer, &mut scratch);
+ // when a "true" is receved by rx, get lock for sample buffer and fill the last half with the audio
if rx.recv().unwrap() {
let mut write_buffer = sample_buffer.lock().unwrap();
for (i, x) in write_buffer[SPECTOGRAM_AREA..].iter_mut().enumerate() {
Ok(t) => t,
Err(_) => 0i16
};
+ // if buffer is not full convert value and add to buffer
buffer[i] = Complex{re: value as f32, im: 0f32};
i += 1;
}
// g++ ./perspective.cpp -I/usr/share/include/opencv4/ -lopencv_core -lopencv_calib3d -lopencv_highgui -lopencv_xfeatures2d -lopencv_features2d -lopencv_imgproc -lopencv_videoio -lopencv_imgcodecs -lopencv_features2d -o perspective.a
//
-#include "/usr/local/include/opencv4/opencv2/core.hpp"
-#include "/usr/local/include/opencv4/opencv2/highgui.hpp"
-#include "/usr/local/include/opencv4/opencv2/xfeatures2d.hpp"
-#include "/usr/local/include/opencv4/opencv2/calib3d.hpp"
-#include "/usr/local/include/opencv4/opencv2/imgproc.hpp"
-#include <cstring>
-#include <opencv4/opencv2/core/hal/interface.h>
+#include "opencv4/opencv2/core.hpp"
+#include "opencv4/opencv2/highgui.hpp"
+#include "opencv4/opencv2/xfeatures2d.hpp"
+#include "opencv4/opencv2/calib3d.hpp"
+#include "opencv4/opencv2/imgproc.hpp"
using namespace cv;
using namespace cv::xfeatures2d;
const size_t IMAGE_WIDTH = 1920;
const size_t IMAGE_HEIGHT = 1080;
-// Mat get_frame()
-// {
-// VideoCapture cap;
-// Mat frame;
-//
-// int device = 0;
-// int api = CAP_V4L;
-// cap.open(device, api);
-//
-// cap.read(frame);
-// resize(frame, frame, DSIZE_AREA, INTER_LINEAR);
-// return frame;
-// }
-
-// int main()
-// {
-// Mat img1 = imread( samples::findFile("box.jpg"), IMREAD_GRAYSCALE );
-// Mat img2 = imread( samples::findFile("box_in_scene.jpg"), IMREAD_GRAYSCALE );
-//
-// // detect keypoints and compute descriptors
-// int minHessian = 400;
-// Ptr<SURF> detector = SURF::create( minHessian );
-//
-// std::vector<KeyPoint> keypoints1, keypoints2;
-// Mat descriptors1, descriptors2;
-// detector->detectAndCompute( img1, noArray(), keypoints1, descriptors1 );
-// detector->detectAndCompute( img2, noArray(), keypoints2, descriptors2 );
-//
-// // match descriptors
-// Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create(DescriptorMatcher::FLANNBASED);
-// std::vector< std::vector<DMatch> > knn_matches;
-//
-// matcher->knnMatch( descriptors1, descriptors2, knn_matches, 2 );
-//
-// // filter matches by the ratio test
-// const float ratio_thresh = 0.7f;
-// std::vector<DMatch> good_matches;
-// for (size_t i = 0; i < knn_matches.size(); i++)
-// {
-// if (knn_matches[i][0].distance < ratio_thresh * knn_matches[i][1].distance)
-// {
-// good_matches.push_back(knn_matches[i][0]);
-// }
-// }
-//
-// // get the source and destination points
-// std::vector<Point2f> source_points, dst_points;
-// for (size_t i = 0; i < good_matches.size(); i++)
-// {
-// Point2f s_point = keypoints2[good_matches[i].trainIdx].pt;
-// Point2f d_point = keypoints1[good_matches[i].queryIdx].pt;
-// source_points.push_back(s_point);
-// dst_points.push_back(d_point);
-// }
-//
-// // perform homography
-// double ransac_thresh = 5.0f;
-// Mat homography = findHomography(source_points, dst_points, RANSAC, ransac_thresh);
-//
-// const double* h_ptr = homography.ptr<double>(0);
-// for(int i = 0; i < 9; i++)
-// {
-// std::cout << std::max(h_ptr[i], 0.) << "\n";
-// }
-// }
-
extern "C"
{
void GetHomography(uint8_t *camera_ptr, double *homography_ptr)
{
- Mat capture(IMAGE_HEIGHT, IMAGE_WIDTH, CV_8UC3, camera_ptr), img2;
+ try
+ {
+ Mat img1 = imread( samples::findFile("calibration.jpg")/*, IMREAD_GRAYSCALE */);
+ Mat img2(IMAGE_HEIGHT, IMAGE_WIDTH, CV_8UC3, camera_ptr);
- Mat img1 = imread( samples::findFile("calibration.jpg"), IMREAD_GRAYSCALE );
- cv::cvtColor(capture, img2, COLOR_RGB2GRAY);;
+ // detect keypoints and compute descriptors
+ int minHessian = 400;
+ Ptr<SURF> detector = SURF::create( minHessian );
- // detect keypoints and compute descriptors
- int minHessian = 400;
- Ptr<SURF> detector = SURF::create( minHessian );
+ std::vector<KeyPoint> keypoints1, keypoints2;
+ Mat descriptors1, descriptors2;
+ detector->detectAndCompute( img1, noArray(), keypoints1, descriptors1 );
+ detector->detectAndCompute( img2, noArray(), keypoints2, descriptors2 );
- std::vector<KeyPoint> keypoints1, keypoints2;
- Mat descriptors1, descriptors2;
- detector->detectAndCompute( img1, noArray(), keypoints1, descriptors1 );
- detector->detectAndCompute( img2, noArray(), keypoints2, descriptors2 );
+ // match descriptors
+ Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create(DescriptorMatcher::FLANNBASED);
+ std::vector< std::vector<DMatch> > knn_matches;
- // match descriptors
- Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create(DescriptorMatcher::FLANNBASED);
- std::vector< std::vector<DMatch> > knn_matches;
+ matcher->knnMatch( descriptors1, descriptors2, knn_matches, 2 );
- matcher->knnMatch( descriptors1, descriptors2, knn_matches, 2 );
+ // filter matches by the ratio test
+ const float ratio_thresh = 0.7f;
+ std::vector<DMatch> good_matches;
+ for (size_t i = 0; i < knn_matches.size(); i++)
+ {
+ if (knn_matches[i][0].distance < ratio_thresh * knn_matches[i][1].distance)
+ {
+ good_matches.push_back(knn_matches[i][0]);
+ }
+ }
- // filter matches by the ratio test
- const float ratio_thresh = 0.7f;
- std::vector<DMatch> good_matches;
- for (size_t i = 0; i < knn_matches.size(); i++)
- {
- if (knn_matches[i][0].distance < ratio_thresh * knn_matches[i][1].distance)
+ // get the source and destination points
+ std::vector<Point2f> source_points, dst_points;
+ for (size_t i = 0; i < good_matches.size(); i++)
{
- good_matches.push_back(knn_matches[i][0]);
+ Point2f s_point = keypoints2[good_matches[i].trainIdx].pt;
+ Point2f d_point = keypoints1[good_matches[i].queryIdx].pt;
+ source_points.push_back(s_point);
+ dst_points.push_back(d_point);
}
- }
- // get the source and destination points
- std::vector<Point2f> source_points, dst_points;
- for (size_t i = 0; i < good_matches.size(); i++)
+ // perform homography
+ double ransac_thresh = 5.0f;
+ Mat homography = findHomography(source_points, dst_points, RANSAC, ransac_thresh);
+
+ // copy the result to the homography location
+ const double* result_ptr = homography.ptr<double>(0);
+ std::memcpy(homography_ptr, result_ptr, 72); // size of [f64; 9]
+ }
+ catch (const std::exception &e) // handle exceptions for rust
{
- Point2f s_point = keypoints2[good_matches[i].trainIdx].pt;
- Point2f d_point = keypoints1[good_matches[i].queryIdx].pt;
- source_points.push_back(s_point);
- dst_points.push_back(d_point);
+ std::cout << "Exception " << e.what() << std::endl;
}
-
- // perform homography
- double ransac_thresh = 5.0f;
- Mat homography = findHomography(source_points, dst_points, RANSAC, ransac_thresh);
-
- // copy the result to the homography location
- // it should be possible to just point the result of findHomography at the pointer location
- const double* result_ptr = homography.ptr<double>(0);
- std::memcpy(homography_ptr, result_ptr, 72); // size of [f64; 9]
}
- void ApplyHomography(uint8_t *camera_ptr, uint8_t *buffer_ptr, double *homography_ptr)
- {
- Mat capture(IMAGE_HEIGHT, IMAGE_WIDTH, CV_8UC3, camera_ptr);
- Mat buffer(CHUNK_SIZE, WINDOW_SIZE, CV_8UC3, buffer_ptr);
- Mat homography(3, 3, CV_64F, homography_ptr);
+ void ApplyHomography(uint8_t *camera_ptr, uint8_t *buffer_ptr, double *homography_ptr)
+ {
+ Mat capture(IMAGE_HEIGHT, IMAGE_WIDTH, CV_8UC3, camera_ptr);
+ Mat buffer(CHUNK_SIZE, WINDOW_SIZE, CV_8UC3, buffer_ptr);
+ Mat homography(3, 3, CV_64F, homography_ptr);
- warpPerspective(capture, buffer, homography, buffer.size());
- }
+ warpPerspective(capture, capture, homography, capture.size());
+ resize(capture, buffer, buffer.size());
+ }
}