This PR makes it possible to use `rav1e` and `dav1d` to pass av1 encoded frame very easily making it possible to reduce the size by ~100x compared to raw and ~10x compared to jpeg. This makes it possible to send image in unstable network like <10MB/s instead of ~100MB/s, which is the case for 5G cellular network as well as most public wifi. I'm also adding support for depth frame encoding in 12bit AV1 making it possible to stream pointcloud over the network which is very hard to do in other languages than Rust,tags/v0.3.11
| @@ -63,11 +63,11 @@ jobs: | |||
| cache-directories: ${{ env.CARGO_TARGET_DIR }} | |||
| - name: "Check" | |||
| run: cargo check --all | |||
| run: cargo check --all --exclude dora-dav1d --exclude dora-rav1e | |||
| - name: "Build (Without Python dep as it is build with maturin)" | |||
| run: cargo build --all --exclude dora-node-api-python --exclude dora-operator-api-python --exclude dora-ros2-bridge-python | |||
| run: cargo build --all --exclude dora-dav1d --exclude dora-rav1e --exclude dora-node-api-python --exclude dora-operator-api-python --exclude dora-ros2-bridge-python | |||
| - name: "Test" | |||
| run: cargo test --all --exclude dora-node-api-python --exclude dora-operator-api-python --exclude dora-ros2-bridge-python | |||
| run: cargo test --all --exclude dora-dav1d --exclude dora-rav1e --exclude dora-node-api-python --exclude dora-operator-api-python --exclude dora-ros2-bridge-python | |||
| # Run examples as separate job because otherwise we will exhaust the disk | |||
| # space of the GitHub action runners. | |||
| @@ -307,7 +307,7 @@ jobs: | |||
| # Test Rust template Project | |||
| dora new test_rust_project --internal-create-with-path-dependencies | |||
| cd test_rust_project | |||
| cargo build --all | |||
| cargo build --all --exclude dora-dav1d --exclude dora-rav1e | |||
| dora up | |||
| dora list | |||
| dora start dataflow.yml --name ci-rust-test --detach | |||
| @@ -447,12 +447,12 @@ jobs: | |||
| - run: cargo --version --verbose | |||
| - name: "Clippy" | |||
| run: cargo clippy --all | |||
| run: cargo clippy --all --exclude dora-dav1d --exclude dora-rav1e | |||
| - name: "Clippy (tracing feature)" | |||
| run: cargo clippy --all --features tracing | |||
| run: cargo clippy --all --exclude dora-dav1d --exclude dora-rav1e --features tracing | |||
| if: false # only the dora-runtime has this feature, but it is currently commented out | |||
| - name: "Clippy (metrics feature)" | |||
| run: cargo clippy --all --features metrics | |||
| run: cargo clippy --all --exclude dora-dav1d --exclude dora-rav1e --features metrics | |||
| if: false # only the dora-runtime has this feature, but it is currently commented out | |||
| rustfmt: | |||
| @@ -532,4 +532,4 @@ jobs: | |||
| with: | |||
| use-cross: true | |||
| command: check | |||
| args: --target ${{ matrix.platform.target }} --all --exclude dora-node-api-python --exclude dora-operator-api-python --exclude dora-ros2-bridge-python | |||
| args: --target ${{ matrix.platform.target }} --all --exclude dora-dav1d --exclude dora-rav1e --exclude dora-node-api-python --exclude dora-operator-api-python --exclude dora-ros2-bridge-python | |||
| @@ -34,7 +34,7 @@ jobs: | |||
| strategy: | |||
| fail-fast: ${{ github.event_name != 'workflow_dispatch' && !(github.event_name == 'release' && startsWith(github.ref, 'refs/tags/')) }} | |||
| matrix: | |||
| platform: [ubuntu-22.04, macos-14] | |||
| platform: [ubuntu-24.04, macos-14] | |||
| folder: ${{ fromJson(needs.find-jobs.outputs.folders )}} | |||
| steps: | |||
| - name: Checkout repository | |||
| @@ -52,7 +52,9 @@ jobs: | |||
| - name: Install system-level dependencies | |||
| if: runner.os == 'Linux' | |||
| run: | | |||
| sudo apt update | |||
| sudo apt-get install portaudio19-dev | |||
| sudo apt-get install libdav1d-dev nasm | |||
| # Install mingw-w64 cross-compilers | |||
| sudo apt install g++-mingw-w64-x86-64 gcc-mingw-w64-x86-64 | |||
| @@ -60,6 +62,7 @@ jobs: | |||
| if: runner.os == 'MacOS' && (github.event_name == 'workflow_dispatch' || (github.event_name == 'release' && startsWith(github.ref, 'refs/tags/'))) | |||
| run: | | |||
| brew install portaudio | |||
| brew install dav1d nasm | |||
| - name: Set up Python | |||
| if: runner.os == 'Linux' || github.event_name == 'workflow_dispatch' || (github.event_name == 'release' && startsWith(github.ref, 'refs/tags/')) | |||
| @@ -273,6 +273,15 @@ dependencies = [ | |||
| "rgb", | |||
| ] | |||
| [[package]] | |||
| name = "ansi_term" | |||
| version = "0.12.1" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" | |||
| dependencies = [ | |||
| "winapi 0.3.9", | |||
| ] | |||
| [[package]] | |||
| name = "anstream" | |||
| version = "0.6.18" | |||
| @@ -374,6 +383,17 @@ version = "1.7.1" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" | |||
| [[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.100", | |||
| ] | |||
| [[package]] | |||
| name = "array-init" | |||
| version = "2.1.0" | |||
| @@ -397,6 +417,9 @@ name = "arrayvec" | |||
| version = "0.7.6" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" | |||
| dependencies = [ | |||
| "serde", | |||
| ] | |||
| [[package]] | |||
| name = "arrow" | |||
| @@ -1297,6 +1320,36 @@ dependencies = [ | |||
| "num-traits", | |||
| ] | |||
| [[package]] | |||
| name = "av-metrics" | |||
| version = "0.9.1" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "996ce95bbdb0203e5b91d4a0c9b81c0d67d11c80f884482a0c1ea19e732e3530" | |||
| dependencies = [ | |||
| "crossbeam", | |||
| "itertools 0.10.5", | |||
| "lab", | |||
| "num-traits", | |||
| "rayon", | |||
| "thiserror 1.0.69", | |||
| "v_frame", | |||
| ] | |||
| [[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 7.1.3", | |||
| "num-rational", | |||
| "serde", | |||
| "v_frame", | |||
| ] | |||
| [[package]] | |||
| name = "axum" | |||
| version = "0.7.9" | |||
| @@ -1516,6 +1569,12 @@ dependencies = [ | |||
| "serde", | |||
| ] | |||
| [[package]] | |||
| name = "bitstream-io" | |||
| version = "2.6.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" | |||
| [[package]] | |||
| name = "block" | |||
| version = "0.1.6" | |||
| @@ -1605,6 +1664,15 @@ dependencies = [ | |||
| "sysinfo 0.33.1", | |||
| ] | |||
| [[package]] | |||
| name = "built" | |||
| version = "0.7.3" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "c6a6c0b39c38fd754ac338b00a88066436389c0f029da5d37d1e01091d9b7c17" | |||
| dependencies = [ | |||
| "git2", | |||
| ] | |||
| [[package]] | |||
| name = "bumpalo" | |||
| version = "3.17.0" | |||
| @@ -1905,6 +1973,16 @@ dependencies = [ | |||
| "uuid 1.16.0", | |||
| ] | |||
| [[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 = "0.1.10" | |||
| @@ -1986,6 +2064,21 @@ dependencies = [ | |||
| "thiserror 1.0.69", | |||
| ] | |||
| [[package]] | |||
| name = "clap" | |||
| version = "2.34.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" | |||
| dependencies = [ | |||
| "ansi_term", | |||
| "atty", | |||
| "bitflags 1.3.2", | |||
| "strsim 0.8.0", | |||
| "textwrap 0.11.0", | |||
| "unicode-width 0.1.14", | |||
| "vec_map", | |||
| ] | |||
| [[package]] | |||
| name = "clap" | |||
| version = "3.2.25" | |||
| @@ -2000,7 +2093,7 @@ dependencies = [ | |||
| "once_cell", | |||
| "strsim 0.10.0", | |||
| "termcolor", | |||
| "textwrap", | |||
| "textwrap 0.16.2", | |||
| ] | |||
| [[package]] | |||
| @@ -2026,6 +2119,15 @@ dependencies = [ | |||
| "terminal_size", | |||
| ] | |||
| [[package]] | |||
| name = "clap_complete" | |||
| version = "4.5.47" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "c06f5378ea264ad4f82bbc826628b5aad714a75abf6ece087e923010eb937fb6" | |||
| dependencies = [ | |||
| "clap 4.5.32", | |||
| ] | |||
| [[package]] | |||
| name = "clap_derive" | |||
| version = "3.2.25" | |||
| @@ -2675,6 +2777,28 @@ version = "0.3.1" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" | |||
| [[package]] | |||
| name = "dav1d" | |||
| version = "0.10.4" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "80c3f80814db85397819d464bb553268992c393b4b3b5554b89c1655996d5926" | |||
| dependencies = [ | |||
| "av-data", | |||
| "bitflags 2.9.0", | |||
| "dav1d-sys", | |||
| "static_assertions", | |||
| ] | |||
| [[package]] | |||
| name = "dav1d-sys" | |||
| version = "0.8.2" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "6ecb1c5e8f4dc438eedc1b534a54672fb0e0a56035dae6b50162787bd2c50e95" | |||
| dependencies = [ | |||
| "libc", | |||
| "system-deps", | |||
| ] | |||
| [[package]] | |||
| name = "defmac" | |||
| version = "0.1.3" | |||
| @@ -3049,6 +3173,19 @@ dependencies = [ | |||
| "zenoh 1.3.0", | |||
| ] | |||
| [[package]] | |||
| name = "dora-dav1d" | |||
| version = "0.0.0" | |||
| dependencies = [ | |||
| "bitstream-io", | |||
| "bytemuck", | |||
| "dav1d", | |||
| "dora-node-api", | |||
| "eyre", | |||
| "log", | |||
| "structopt", | |||
| ] | |||
| [[package]] | |||
| name = "dora-download" | |||
| version = "0.3.10" | |||
| @@ -3296,6 +3433,17 @@ dependencies = [ | |||
| "safer-ffi", | |||
| ] | |||
| [[package]] | |||
| name = "dora-rav1e" | |||
| version = "0.3.10" | |||
| dependencies = [ | |||
| "bytemuck", | |||
| "dora-node-api", | |||
| "eyre", | |||
| "log", | |||
| "rav1e", | |||
| ] | |||
| [[package]] | |||
| name = "dora-record" | |||
| version = "0.3.10" | |||
| @@ -4086,6 +4234,15 @@ dependencies = [ | |||
| "simd-adler32", | |||
| ] | |||
| [[package]] | |||
| name = "fern" | |||
| version = "0.6.2" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "d9f0c14694cbd524c8720dd69b0e3179344f04ebb5f90f2e4a440c6ea3b2f1ee" | |||
| dependencies = [ | |||
| "log", | |||
| ] | |||
| [[package]] | |||
| name = "ffmpeg-sidecar" | |||
| version = "2.0.5" | |||
| @@ -5716,6 +5873,17 @@ version = "3.0.4" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" | |||
| [[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.100", | |||
| ] | |||
| [[package]] | |||
| name = "interprocess" | |||
| version = "2.2.3" | |||
| @@ -5842,6 +6010,15 @@ dependencies = [ | |||
| "either", | |||
| ] | |||
| [[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" | |||
| @@ -5866,6 +6043,15 @@ version = "1.0.15" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" | |||
| [[package]] | |||
| name = "ivf" | |||
| version = "0.1.3" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "552f657140ee72c552b728601179c10abea14cd7d815de2d75d75dea42485eca" | |||
| dependencies = [ | |||
| "bitstream-io", | |||
| ] | |||
| [[package]] | |||
| name = "jiff" | |||
| version = "0.2.4" | |||
| @@ -6044,6 +6230,12 @@ dependencies = [ | |||
| "log", | |||
| ] | |||
| [[package]] | |||
| name = "lab" | |||
| version = "0.11.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "bf36173d4167ed999940f804952e6b08197cae5ad5d572eb4db150ce8ad5d58f" | |||
| [[package]] | |||
| name = "lazy_static" | |||
| version = "1.5.0" | |||
| @@ -6135,6 +6327,16 @@ version = "0.2.171" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" | |||
| [[package]] | |||
| name = "libfuzzer-sys" | |||
| version = "0.4.9" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "cf78f52d400cf2d84a3a973a78a592b4adc535739e0a5597a0da6f0c357adc75" | |||
| dependencies = [ | |||
| "arbitrary", | |||
| "cc", | |||
| ] | |||
| [[package]] | |||
| name = "libgit2-sys" | |||
| version = "0.16.2+1.7.2" | |||
| @@ -6407,6 +6609,16 @@ dependencies = [ | |||
| "rawpointer", | |||
| ] | |||
| [[package]] | |||
| name = "maybe-rayon" | |||
| version = "0.1.1" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" | |||
| dependencies = [ | |||
| "cfg-if 1.0.0", | |||
| "rayon", | |||
| ] | |||
| [[package]] | |||
| name = "md5" | |||
| version = "0.7.0" | |||
| @@ -6918,6 +7130,15 @@ dependencies = [ | |||
| "getrandom 0.2.15", | |||
| ] | |||
| [[package]] | |||
| name = "nasm-rs" | |||
| version = "0.2.5" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "fe4d98d0065f4b1daf164b3eafb11974c94662e5e2396cf03f32d0bb5c17da51" | |||
| dependencies = [ | |||
| "rayon", | |||
| ] | |||
| [[package]] | |||
| name = "nasm-rs" | |||
| version = "0.3.0" | |||
| @@ -7042,6 +7263,12 @@ version = "0.1.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "c96aba5aa877601bb3f6dd6a63a969e1f82e60646e81e71b14496995e9853c91" | |||
| [[package]] | |||
| name = "new_debug_unreachable" | |||
| version = "1.0.6" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" | |||
| [[package]] | |||
| name = "newline-converter" | |||
| version = "0.2.2" | |||
| @@ -7130,6 +7357,12 @@ dependencies = [ | |||
| "memchr", | |||
| ] | |||
| [[package]] | |||
| name = "noop_proc_macro" | |||
| version = "0.3.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" | |||
| [[package]] | |||
| name = "notify" | |||
| version = "5.2.0" | |||
| @@ -9092,6 +9325,55 @@ dependencies = [ | |||
| "rand_core 0.9.3", | |||
| ] | |||
| [[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", | |||
| "av-metrics", | |||
| "av1-grain", | |||
| "bitstream-io", | |||
| "built", | |||
| "cc", | |||
| "cfg-if 1.0.0", | |||
| "clap 4.5.32", | |||
| "clap_complete", | |||
| "console", | |||
| "fern", | |||
| "interpolate_name", | |||
| "itertools 0.12.1", | |||
| "ivf", | |||
| "libc", | |||
| "libfuzzer-sys", | |||
| "log", | |||
| "maybe-rayon", | |||
| "nasm-rs 0.2.5", | |||
| "new_debug_unreachable", | |||
| "nom 7.1.3", | |||
| "noop_proc_macro", | |||
| "num-derive", | |||
| "num-traits", | |||
| "once_cell", | |||
| "paste", | |||
| "profiling", | |||
| "rand 0.8.5", | |||
| "rand_chacha 0.3.1", | |||
| "scan_fmt", | |||
| "serde", | |||
| "serde-big-array", | |||
| "signal-hook", | |||
| "simd_helpers", | |||
| "system-deps", | |||
| "thiserror 1.0.69", | |||
| "toml", | |||
| "v_frame", | |||
| "y4m", | |||
| ] | |||
| [[package]] | |||
| name = "raw-cpuid" | |||
| version = "10.7.0" | |||
| @@ -9829,7 +10111,7 @@ dependencies = [ | |||
| "cc", | |||
| "cfg-if 1.0.0", | |||
| "libc", | |||
| "nasm-rs", | |||
| "nasm-rs 0.3.0", | |||
| "parking_lot", | |||
| "paste", | |||
| "raw-cpuid 11.5.0", | |||
| @@ -11591,6 +11873,12 @@ dependencies = [ | |||
| "winapi-util", | |||
| ] | |||
| [[package]] | |||
| name = "scan_fmt" | |||
| version = "0.2.6" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "0b53b0a5db882a8e2fdaae0a43f7b39e7e9082389e978398bdf223a55b581248" | |||
| [[package]] | |||
| name = "schannel" | |||
| version = "0.1.27" | |||
| @@ -12183,6 +12471,15 @@ version = "0.3.7" | |||
| 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 = "simdutf8" | |||
| version = "0.1.5" | |||
| @@ -12548,6 +12845,12 @@ dependencies = [ | |||
| "float-cmp", | |||
| ] | |||
| [[package]] | |||
| name = "strsim" | |||
| version = "0.8.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" | |||
| [[package]] | |||
| name = "strsim" | |||
| version = "0.10.0" | |||
| @@ -12560,6 +12863,30 @@ version = "0.11.1" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" | |||
| [[package]] | |||
| name = "structopt" | |||
| version = "0.3.26" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" | |||
| dependencies = [ | |||
| "clap 2.34.0", | |||
| "lazy_static", | |||
| "structopt-derive", | |||
| ] | |||
| [[package]] | |||
| name = "structopt-derive" | |||
| version = "0.4.18" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" | |||
| dependencies = [ | |||
| "heck 0.3.3", | |||
| "proc-macro-error", | |||
| "proc-macro2", | |||
| "quote", | |||
| "syn 1.0.109", | |||
| ] | |||
| [[package]] | |||
| name = "strum" | |||
| version = "0.26.3" | |||
| @@ -12781,6 +13108,19 @@ dependencies = [ | |||
| "libc", | |||
| ] | |||
| [[package]] | |||
| name = "system-deps" | |||
| version = "6.2.2" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" | |||
| dependencies = [ | |||
| "cfg-expr", | |||
| "heck 0.5.0", | |||
| "pkg-config", | |||
| "toml", | |||
| "version-compare", | |||
| ] | |||
| [[package]] | |||
| name = "tabwriter" | |||
| version = "1.4.1" | |||
| @@ -12871,6 +13211,15 @@ dependencies = [ | |||
| "libc", | |||
| ] | |||
| [[package]] | |||
| name = "textwrap" | |||
| version = "0.11.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" | |||
| dependencies = [ | |||
| "unicode-width 0.1.14", | |||
| ] | |||
| [[package]] | |||
| name = "textwrap" | |||
| version = "0.16.2" | |||
| @@ -13897,6 +14246,18 @@ dependencies = [ | |||
| "syn 2.0.100", | |||
| ] | |||
| [[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", | |||
| "serde", | |||
| "wasm-bindgen", | |||
| ] | |||
| [[package]] | |||
| name = "validated_struct" | |||
| version = "2.1.1" | |||
| @@ -13968,6 +14329,12 @@ 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" | |||
| @@ -15404,6 +15771,12 @@ version = "0.8.15" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" | |||
| [[package]] | |||
| name = "y4m" | |||
| version = "0.8.0" | |||
| source = "registry+https://github.com/rust-lang/crates.io-index" | |||
| checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448" | |||
| [[package]] | |||
| name = "yaml-rust" | |||
| version = "0.4.5" | |||
| @@ -37,6 +37,8 @@ members = [ | |||
| "node-hub/dora-kit-car", | |||
| "node-hub/dora-object-to-pose", | |||
| "node-hub/dora-mistral-rs", | |||
| "node-hub/dora-rav1e", | |||
| "node-hub/dora-dav1d", | |||
| "libraries/extensions/ros2-bridge", | |||
| "libraries/extensions/ros2-bridge/msg-gen", | |||
| "libraries/extensions/ros2-bridge/python", | |||
| @@ -251,21 +251,99 @@ impl Daemon { | |||
| None => None, | |||
| }; | |||
| let zenoh_config = match std::env::var(zenoh::Config::DEFAULT_CONFIG_PATH_ENV) { | |||
| Ok(path) => zenoh::Config::from_file(&path) | |||
| .map_err(|e| eyre!(e)) | |||
| .wrap_err_with(|| format!("failed to read zenoh config from {path}"))?, | |||
| Err(std::env::VarError::NotPresent) => zenoh::Config::default(), | |||
| let zenoh_session = match std::env::var(zenoh::Config::DEFAULT_CONFIG_PATH_ENV) { | |||
| Ok(path) => { | |||
| let zenoh_config = zenoh::Config::from_file(&path) | |||
| .map_err(|e| eyre!(e)) | |||
| .wrap_err_with(|| format!("failed to read zenoh config from {path}"))?; | |||
| zenoh::open(zenoh_config) | |||
| .await | |||
| .map_err(|e| eyre!(e)) | |||
| .context("failed to open zenoh session")? | |||
| } | |||
| Err(std::env::VarError::NotPresent) => { | |||
| let mut zenoh_config = zenoh::Config::default(); | |||
| if let Some(addr) = coordinator_addr { | |||
| // Linkstate make it possible to connect two daemons on different network through a public daemon | |||
| // TODO: There is currently a CI/CD Error in windows linkstate. | |||
| if cfg!(not(target_os = "windows")) { | |||
| zenoh_config | |||
| .insert_json5("routing/peer", r#"{ mode: "linkstate" }"#) | |||
| .unwrap(); | |||
| } | |||
| zenoh_config | |||
| .insert_json5( | |||
| "connect/endpoints", | |||
| &format!( | |||
| r#"{{ router: ["tcp/[::]:7447"], peer: ["tcp/{}:5456"] }}"#, | |||
| addr.ip() | |||
| ), | |||
| ) | |||
| .unwrap(); | |||
| zenoh_config | |||
| .insert_json5( | |||
| "listen/endpoints", | |||
| r#"{ router: ["tcp/[::]:7447"], peer: ["tcp/[::]:5456"] }"#, | |||
| ) | |||
| .unwrap(); | |||
| if cfg!(target_os = "macos") { | |||
| warn!("disabling multicast on macos systems. Enable it with the ZENOH_CONFIG env variable or file"); | |||
| zenoh_config | |||
| .insert_json5("scouting/multicast", r#"{ enabled: false }"#) | |||
| .unwrap(); | |||
| } | |||
| }; | |||
| if let Ok(zenoh_session) = zenoh::open(zenoh_config).await { | |||
| zenoh_session | |||
| } else { | |||
| warn!( | |||
| "failed to open zenoh session, retrying with default config + coordinator" | |||
| ); | |||
| let mut zenoh_config = zenoh::Config::default(); | |||
| // Linkstate make it possible to connect two daemons on different network through a public daemon | |||
| // TODO: There is currently a CI/CD Error in windows linkstate. | |||
| if cfg!(not(target_os = "windows")) { | |||
| zenoh_config | |||
| .insert_json5("routing/peer", r#"{ mode: "linkstate" }"#) | |||
| .unwrap(); | |||
| } | |||
| if let Some(addr) = coordinator_addr { | |||
| zenoh_config | |||
| .insert_json5( | |||
| "connect/endpoints", | |||
| &format!( | |||
| r#"{{ router: ["tcp/[::]:7447"], peer: ["tcp/{}:5456"] }}"#, | |||
| addr.ip() | |||
| ), | |||
| ) | |||
| .unwrap(); | |||
| if cfg!(target_os = "macos") { | |||
| warn!("disabling multicast on macos systems. Enable it with the ZENOH_CONFIG env variable or file"); | |||
| zenoh_config | |||
| .insert_json5("scouting/multicast", r#"{ enabled: false }"#) | |||
| .unwrap(); | |||
| } | |||
| } | |||
| if let Ok(zenoh_session) = zenoh::open(zenoh_config).await { | |||
| zenoh_session | |||
| } else { | |||
| warn!("failed to open zenoh session, retrying with default config"); | |||
| let zenoh_config = zenoh::Config::default(); | |||
| zenoh::open(zenoh_config) | |||
| .await | |||
| .map_err(|e| eyre!(e)) | |||
| .context("failed to open zenoh session")? | |||
| } | |||
| } | |||
| } | |||
| Err(std::env::VarError::NotUnicode(_)) => eyre::bail!( | |||
| "{} env variable is not valid unicode", | |||
| zenoh::Config::DEFAULT_CONFIG_PATH_ENV | |||
| ), | |||
| }; | |||
| let zenoh_session = zenoh::open(zenoh_config) | |||
| .await | |||
| .map_err(|e| eyre!(e)) | |||
| .context("failed to open zenoh session")?; | |||
| let (dora_events_tx, dora_events_rx) = mpsc::channel(5); | |||
| let daemon = Self { | |||
| logger: Logger { | |||
| @@ -0,0 +1,69 @@ | |||
| nodes: | |||
| - id: camera | |||
| build: pip install ../../node-hub/opencv-video-capture | |||
| path: opencv-video-capture | |||
| _unstable_deploy: | |||
| machine: encoder | |||
| inputs: | |||
| tick: dora/timer/millis/50 | |||
| outputs: | |||
| - image | |||
| env: | |||
| CAPTURE_PATH: 0 | |||
| IMAGE_WIDTH: 1280 | |||
| IMAGE_HEIGHT: 720 | |||
| - id: rav1e-local | |||
| path: dora-rav1e | |||
| build: cargo build -p dora-rav1e --release | |||
| _unstable_deploy: | |||
| machine: encoder | |||
| inputs: | |||
| image: camera/image | |||
| outputs: | |||
| - image | |||
| - id: dav1d-remote | |||
| path: dora-dav1d | |||
| build: cargo build -p dora-dav1d --release | |||
| _unstable_deploy: | |||
| machine: decoder | |||
| inputs: | |||
| image: rav1e-local/image | |||
| outputs: | |||
| - image | |||
| - id: rav1e-remote | |||
| path: dora-rav1e | |||
| build: cargo build -p dora-rav1e --release | |||
| _unstable_deploy: | |||
| machine: decoder | |||
| inputs: | |||
| image: dav1d-remote/image | |||
| outputs: | |||
| - image | |||
| env: | |||
| IMAGE_WIDTH: 640 | |||
| IMAGE_HEIGHT: 480 | |||
| - id: dav1d-local | |||
| path: dora-dav1d | |||
| build: cargo build -p dora-dav1d --release | |||
| _unstable_deploy: | |||
| machine: encoder | |||
| inputs: | |||
| image: rav1e-remote/image | |||
| outputs: | |||
| - image | |||
| env: | |||
| IMAGE_WIDTH: 640 | |||
| IMAGE_HEIGHT: 480 | |||
| - id: plot | |||
| build: pip install -e ../../node-hub/dora-rerun | |||
| _unstable_deploy: | |||
| machine: encoder | |||
| path: dora-rerun | |||
| inputs: | |||
| image_decode: dav1d-local/image | |||
| image_echo: echo/image | |||
| @@ -0,0 +1,68 @@ | |||
| nodes: | |||
| - id: camera | |||
| path: dora-reachy2-camera | |||
| _unstable_deploy: | |||
| machine: encoder | |||
| inputs: | |||
| tick: dora/timer/millis/50 | |||
| outputs: | |||
| - image_right | |||
| - image_left | |||
| - image_depth | |||
| - depth | |||
| env: | |||
| CAPTURE_PATH: 0 | |||
| IMAGE_WIDTH: 640 | |||
| IMAGE_HEIGHT: 480 | |||
| ROBOT_IP: 127.0.0.1 | |||
| - id: rav1e-local | |||
| path: dora-rav1e | |||
| build: cargo build -p dora-rav1e --release | |||
| _unstable_deploy: | |||
| machine: encoder | |||
| inputs: | |||
| depth: camera/depth | |||
| outputs: | |||
| - depth | |||
| env: | |||
| RAV1E_SPEED: 7 | |||
| - id: rav1e-local-image | |||
| path: dora-rav1e | |||
| build: cargo build -p dora-rav1e --release | |||
| _unstable_deploy: | |||
| machine: encoder | |||
| inputs: | |||
| image_depth: camera/image_depth | |||
| image_left: camera/image_left | |||
| outputs: | |||
| - image_left | |||
| - image_depth | |||
| - depth | |||
| env: | |||
| RAV1E_SPEED: 10 | |||
| - id: dav1d-remote | |||
| path: dora-dav1d | |||
| build: cargo build -p dora-dav1d --release | |||
| _unstable_deploy: | |||
| machine: plot | |||
| inputs: | |||
| image_depth: rav1e-local-image/image_depth | |||
| image_left: rav1e-local-image/image_left | |||
| depth: rav1e-local/depth | |||
| outputs: | |||
| - image_left | |||
| - image_depth | |||
| - depth | |||
| - id: plot | |||
| build: pip install -e ../../node-hub/dora-rerun | |||
| _unstable_deploy: | |||
| machine: plot | |||
| path: dora-rerun | |||
| inputs: | |||
| image: dav1d-remote/image_depth | |||
| depth: dav1d-remote/depth | |||
| image_left: dav1d-remote/image_left | |||
| @@ -0,0 +1,73 @@ | |||
| nodes: | |||
| - id: camera | |||
| build: pip install -e ../../node-hub/dora-ios-lidar | |||
| path: dora-ios-lidar | |||
| _unstable_deploy: | |||
| machine: encoder-ios | |||
| inputs: | |||
| tick: dora/timer/millis/20 | |||
| outputs: | |||
| - image | |||
| - depth | |||
| env: | |||
| IMAGE_WIDTH: 640 | |||
| IMAGE_HEIGHT: 480 | |||
| - id: rav1e-local | |||
| path: dora-rav1e | |||
| build: cargo build -p dora-rav1e --release | |||
| _unstable_deploy: | |||
| machine: encoder-ios | |||
| inputs: | |||
| image: camera/image | |||
| depth: camera/depth | |||
| outputs: | |||
| - image | |||
| - depth | |||
| env: | |||
| RAV1E_SPEED: 4 | |||
| - id: dav1d-remote | |||
| path: dora-dav1d | |||
| build: cargo build -p dora-dav1d --release | |||
| _unstable_deploy: | |||
| machine: decoder | |||
| inputs: | |||
| image: rav1e-local/image | |||
| depth: rav1e-local/depth | |||
| outputs: | |||
| - image | |||
| - depth | |||
| - id: rav1e-remote | |||
| path: dora-rav1e | |||
| build: cargo build -p dora-rav1e --release | |||
| _unstable_deploy: | |||
| machine: decoder | |||
| inputs: | |||
| image: dav1d-remote/image | |||
| depth: dav1d-remote/depth | |||
| outputs: | |||
| - image | |||
| - depth | |||
| - id: dav1d-local | |||
| path: dora-dav1d | |||
| build: cargo build -p dora-dav1d --release | |||
| _unstable_deploy: | |||
| machine: encoder-ios | |||
| inputs: | |||
| image: rav1e-remote/image | |||
| depth: rav1e-remote/depth | |||
| outputs: | |||
| - image | |||
| - depth | |||
| - id: plot | |||
| build: pip install -e ../../node-hub/dora-rerun | |||
| path: dora-rerun | |||
| _unstable_deploy: | |||
| machine: encoder-ios | |||
| inputs: | |||
| image: dav1d-remote/image | |||
| depth: dav1d-remote/depth | |||
| @@ -7,6 +7,9 @@ nodes: | |||
| outputs: | |||
| - image | |||
| - depth | |||
| env: | |||
| IMAGE_WIDTH: 640 | |||
| IMAGE_HEIGHT: 480 | |||
| - id: plot | |||
| build: pip install -e ../../node-hub/dora-rerun | |||
| @@ -0,0 +1,14 @@ | |||
| [package] | |||
| name = "dora-dav1d" | |||
| edition = "2021" | |||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | |||
| [dependencies] | |||
| dav1d = "0.10" | |||
| bitstream-io = "2.0" | |||
| log = "0.4" | |||
| structopt = "0.3" | |||
| dora-node-api = { workspace = true, features = ["tracing"] } | |||
| eyre = "0.6.8" | |||
| bytemuck = "1.7.0" | |||
| @@ -0,0 +1,108 @@ | |||
| use dav1d::Settings; | |||
| use dora_node_api::{arrow::array::UInt8Array, DoraNode, Event, IntoArrow}; | |||
| use eyre::{Context, Result}; | |||
| use log::warn; | |||
| fn yuv420_to_bgr( | |||
| y_plane: &[u8], | |||
| u_plane: &[u8], | |||
| v_plane: &[u8], | |||
| width: u32, | |||
| height: u32, | |||
| ) -> Vec<u8> { | |||
| let width = width as usize; | |||
| let height = height as usize; | |||
| let mut rgb_data = vec![0u8; width * height * 3]; // Output RGB data buffer | |||
| for j in 0..height { | |||
| for i in 0..width { | |||
| let y_idx = j * width + i; // Index in Y plane | |||
| let uv_idx = (j / 2) * (width / 2) + (i / 2); // Index in U/V planes | |||
| let y = y_plane[y_idx] as f32; | |||
| let u = u_plane[uv_idx] as f32 - 128.0; | |||
| let v = v_plane[uv_idx] as f32 - 128.0; | |||
| // Convert YUV to RGB using BT.601 standard formula | |||
| let r = (y + 1.402 * v).clamp(0.0, 255.0) as u8; | |||
| let g = (y - 0.344136 * u - 0.714136 * v).clamp(0.0, 255.0) as u8; | |||
| let b = (y + 1.772 * u).clamp(0.0, 255.0) as u8; | |||
| // Set the RGB values in the output buffer | |||
| let rgb_idx = y_idx * 3; | |||
| rgb_data[rgb_idx] = b; | |||
| rgb_data[rgb_idx + 1] = g; | |||
| rgb_data[rgb_idx + 2] = r; | |||
| } | |||
| } | |||
| rgb_data | |||
| } | |||
| fn main() -> Result<()> { | |||
| let mut settings = Settings::new(); | |||
| // settings.set_n_threads(16); | |||
| settings.set_max_frame_delay(1); | |||
| let mut dec = | |||
| dav1d::Decoder::with_settings(&settings).expect("failed to create decoder instance"); | |||
| let (mut node, mut events) = | |||
| DoraNode::init_from_env().context("Could not initialize dora node")?; | |||
| loop { | |||
| match events.recv() { | |||
| Some(Event::Input { | |||
| id, | |||
| data, | |||
| mut metadata, | |||
| }) => { | |||
| if let Some(data) = data.as_any().downcast_ref::<UInt8Array>() { | |||
| let data = data.values().clone(); | |||
| match dec.send_data(data, None, None, None) { | |||
| Err(e) => { | |||
| warn!("Error sending data to the decoder: {}", e); | |||
| } | |||
| Ok(()) => { | |||
| if let Ok(p) = dec.get_picture() { | |||
| match p.pixel_layout() { | |||
| dav1d::PixelLayout::I420 => { | |||
| let y = p.plane(dav1d::PlanarImageComponent::Y); | |||
| let u = p.plane(dav1d::PlanarImageComponent::U); | |||
| let v = p.plane(dav1d::PlanarImageComponent::V); | |||
| let y = yuv420_to_bgr(&y, &u, &v, p.width(), p.height()); | |||
| let arrow = y.into_arrow(); | |||
| metadata.parameters.insert( | |||
| "encoding".to_string(), | |||
| dora_node_api::Parameter::String("bgr8".to_string()), | |||
| ); | |||
| node.send_output(id, metadata.parameters, arrow).unwrap(); | |||
| } | |||
| dav1d::PixelLayout::I400 => { | |||
| let y = p.plane(dav1d::PlanarImageComponent::Y); | |||
| let vec16: Vec<u16> = bytemuck::cast_slice(&y).to_vec(); | |||
| let arrow = vec16.into_arrow(); | |||
| metadata.parameters.insert( | |||
| "encoding".to_string(), | |||
| dora_node_api::Parameter::String("mono16".to_string()), | |||
| ); | |||
| node.send_output(id, metadata.parameters, arrow).unwrap(); | |||
| } | |||
| _ => { | |||
| warn!("Unsupported pixel layout"); | |||
| continue; | |||
| } | |||
| }; | |||
| } | |||
| } | |||
| } | |||
| } else { | |||
| warn!("Unsupported data type {}", data.data_type()); | |||
| continue; | |||
| } | |||
| } | |||
| None => break, | |||
| Some(_) => break, | |||
| } | |||
| } | |||
| Ok(()) | |||
| } | |||
| @@ -1,5 +1,6 @@ | |||
| """TODO: Add docstring.""" | |||
| import os | |||
| from threading import Event | |||
| import cv2 | |||
| @@ -7,6 +8,10 @@ import numpy as np | |||
| import pyarrow as pa | |||
| from dora import Node | |||
| from record3d import Record3DStream | |||
| from scipy.spatial.transform import Rotation | |||
| image_width = os.getenv("IMAGE_WIDTH") | |||
| image_height = os.getenv("IMAGE_HEIGHT") | |||
| class DemoApp: | |||
| @@ -54,6 +59,21 @@ class DemoApp: | |||
| [[coeffs.fx, 0, coeffs.tx], [0, coeffs.fy, coeffs.ty], [0, 0, 1]], | |||
| ) | |||
| def get_camera_pose(self): | |||
| """Get Camera Pose.""" | |||
| pose = self.session.get_camera_pose() | |||
| rot = Rotation.from_quat([pose.qx, pose.qy, pose.qz, pose.qw]) | |||
| euler = rot.as_euler("xyz", degrees=False) | |||
| return [ | |||
| pose.tx, | |||
| pose.ty, | |||
| pose.tz, | |||
| pose.qx, | |||
| euler[1], | |||
| euler[2], | |||
| ] | |||
| def start_processing_stream(self): | |||
| """TODO: Add docstring.""" | |||
| node = Node() | |||
| @@ -61,47 +81,65 @@ class DemoApp: | |||
| for event in node: | |||
| if self.stop: | |||
| break | |||
| if event["type"] == "INPUT": | |||
| if event["id"] == "TICK": | |||
| self.event.wait() # Wait for new frame to arrive | |||
| # Copy the newly arrived RGBD frame | |||
| depth = self.session.get_depth_frame() | |||
| rgb = self.session.get_rgb_frame() | |||
| intrinsic_mat = self.get_intrinsic_mat_from_coeffs( | |||
| self.session.get_intrinsic_mat(), | |||
| ) | |||
| if depth.shape != rgb.shape: | |||
| rgb = cv2.resize(rgb, (depth.shape[1], depth.shape[0])) | |||
| node.send_output( | |||
| "image", | |||
| pa.array(rgb.ravel()), | |||
| metadata={ | |||
| "encoding": "rgb8", | |||
| "width": rgb.shape[1], | |||
| "height": rgb.shape[0], | |||
| }, | |||
| ) | |||
| node.send_output( | |||
| "depth", | |||
| pa.array(depth.ravel().astype(np.float64())), | |||
| metadata={ | |||
| "width": depth.shape[1], | |||
| "height": depth.shape[0], | |||
| "encoding": "CV_64F", | |||
| "focal": [ | |||
| int(intrinsic_mat[0, 0]), | |||
| int(intrinsic_mat[1, 1]), | |||
| ], | |||
| "resolution": [ | |||
| int(intrinsic_mat[0, 2]), | |||
| int(intrinsic_mat[1, 2]), | |||
| ], | |||
| }, | |||
| ) | |||
| self.event.wait() # Wait for new frame to arrive | |||
| # Copy the newly arrived RGBD frame | |||
| depth = self.session.get_depth_frame() | |||
| rgb = self.session.get_rgb_frame() | |||
| intrinsic_mat = self.get_intrinsic_mat_from_coeffs( | |||
| self.session.get_intrinsic_mat(), | |||
| ) | |||
| # pose = self.get_camera_pose() | |||
| if depth.shape != rgb.shape: | |||
| rgb = cv2.resize(rgb, (depth.shape[1], depth.shape[0])) | |||
| if image_width is not None and image_height is not None: | |||
| f_0 = intrinsic_mat[0, 0] * (int(image_height) / rgb.shape[0]) | |||
| f_1 = intrinsic_mat[1, 1] * (int(image_width) / rgb.shape[1]) | |||
| r_0 = intrinsic_mat[0, 2] * (int(image_height) / rgb.shape[0]) | |||
| r_1 = intrinsic_mat[1, 2] * (int(image_width) / rgb.shape[1]) | |||
| rgb = cv2.resize(rgb, (int(image_width), int(image_height))) | |||
| depth = cv2.resize(depth, (int(image_width), int(image_height))) | |||
| else: | |||
| f_0 = intrinsic_mat[0, 0] | |||
| f_1 = intrinsic_mat[1, 1] | |||
| r_0 = intrinsic_mat[0, 2] | |||
| r_1 = intrinsic_mat[1, 2] | |||
| node.send_output( | |||
| "image", | |||
| pa.array(rgb.ravel()), | |||
| metadata={ | |||
| "encoding": "rgb8", | |||
| "width": rgb.shape[1], | |||
| "height": rgb.shape[0], | |||
| }, | |||
| ) | |||
| depth = (np.array(depth) * 1_000).astype(np.uint16) | |||
| depth = np.clip(depth, 0, 4095) # Clip depth to uint12 | |||
| node.send_output( | |||
| "depth", | |||
| pa.array(depth.ravel()), | |||
| metadata={ | |||
| "width": depth.shape[1], | |||
| "height": depth.shape[0], | |||
| "encoding": "mono16", | |||
| "focal": [ | |||
| int(f_0), | |||
| int(f_1), | |||
| ], | |||
| "resolution": [ | |||
| int(r_0), | |||
| int(r_1), | |||
| ], | |||
| # "roll": pose[3], | |||
| # "pitch": pose[4], # Adding 90 degrees to pitch | |||
| # "yaw": pose[5], | |||
| }, | |||
| ) | |||
| self.event.clear() | |||
| @@ -7,7 +7,7 @@ license = { text = "MIT" } | |||
| readme = "README.md" | |||
| requires-python = ">=3.8" | |||
| dependencies = ["dora-rs >= 0.3.9", "opencv-python>=4.11.0.86", "record3d>=1.4"] | |||
| dependencies = ["dora-rs >= 0.3.9", "opencv-python>=4.11.0.86", "record3d>=1.4", "scipy"] | |||
| [dependency-groups] | |||
| dev = ["pytest >=8.1.1", "ruff >=0.9.1"] | |||
| @@ -15,11 +15,11 @@ requires-python = ">=3.8" | |||
| dependencies = [ | |||
| "dora-rs >= 0.3.9", | |||
| "numpy < 2.0.0", | |||
| "transformers >= 4.45", | |||
| "modelscope >= 1.18.1", | |||
| "sentencepiece >= 0.1.99", | |||
| "torch >= 2.2.0", | |||
| "sacremoses>=0.1.1", | |||
| ] | |||
| [dependency-groups] | |||
| @@ -1,12 +1,11 @@ | |||
| """TODO: Add docstring.""" | |||
| import pytest | |||
| def test_import_main(): | |||
| """TODO: Add docstring.""" | |||
| from dora_opus.main import main | |||
| pass # OPUS is no longer maintained in favor of dora-phi4 | |||
| # from dora_opus.main import main | |||
| # Check that everything is working, and catch dora Runtime Exception as we're not running in a dora dataflow. | |||
| with pytest.raises(RuntimeError): | |||
| main() | |||
| # nwith pytest.raises(RuntimeError): | |||
| # nmain() | |||
| @@ -1,5 +1,7 @@ | |||
| # dora-outtetts | |||
| > dora-outtetts is no longer maintained in favor of dora-kokorotts | |||
| ## Getting started | |||
| - Install it with pip: | |||
| @@ -2,26 +2,16 @@ | |||
| import os | |||
| import pytest | |||
| from dora_outtetts.main import load_interface, main | |||
| CI = os.getenv("CI", "false") in ["True", "true"] | |||
| def test_import_main(): | |||
| """TODO: Add docstring.""" | |||
| with pytest.raises(RuntimeError): | |||
| main([]) | |||
| pass # Outetts is no longer maintained in favor of dora-kokorotts | |||
| # with pytest.raises(RuntimeError): | |||
| # main([]) | |||
| def test_load_interface(): | |||
| """TODO: Add docstring.""" | |||
| try: | |||
| interface = load_interface() | |||
| except RuntimeError: | |||
| # Error raised by MPS out of memory. | |||
| if CI: | |||
| interface = "ok" | |||
| assert interface is not None | |||
| pass | |||
| @@ -0,0 +1,17 @@ | |||
| [package] | |||
| name = "dora-rav1e" | |||
| edition = "2021" | |||
| version.workspace = true | |||
| description.workspace = true | |||
| documentation.workspace = true | |||
| license.workspace = true | |||
| repository.workspace = true | |||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | |||
| [dependencies] | |||
| rav1e = { version = "0.7.1", features = ["serialize"] } | |||
| dora-node-api = { workspace = true, features = ["tracing"] } | |||
| eyre = "0.6.8" | |||
| log = "0.4" | |||
| bytemuck = "1.20" | |||
| @@ -0,0 +1,350 @@ | |||
| // Copyright (c) 2019-2022, The rav1e contributors. All rights reserved | |||
| // | |||
| // This source code is subject to the terms of the BSD 2 Clause License and | |||
| // the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License | |||
| // was not distributed with this source code in the LICENSE file, you can | |||
| // obtain it at www.aomedia.org/license/software. If the Alliance for Open | |||
| // Media Patent License 1.0 was not distributed with this source code in the | |||
| // PATENTS file, you can obtain it at www.aomedia.org/license/patent. | |||
| use std::env::var; | |||
| use dora_node_api::arrow::array::{UInt16Array, UInt8Array}; | |||
| use dora_node_api::{DoraNode, Event, IntoArrow, Parameter}; | |||
| use eyre::{Context as EyreContext, Result}; | |||
| use log::warn; | |||
| // Encode the same tiny blank frame 30 times | |||
| use rav1e::config::SpeedSettings; | |||
| use rav1e::*; | |||
| fn bgr8_to_yuv420(bgr_data: Vec<u8>, width: usize, height: usize) -> (Vec<u8>, Vec<u8>, Vec<u8>) { | |||
| let mut y_plane = vec![0; width * height]; | |||
| let mut u_plane = vec![0; (width / 2) * (height / 2)]; | |||
| let mut v_plane = vec![0; (width / 2) * (height / 2)]; | |||
| for y in 0..height { | |||
| for x in 0..width { | |||
| let bgr_index = (y * width + x) * 3; | |||
| let b = bgr_data[bgr_index] as f32; | |||
| let g = bgr_data[bgr_index + 1] as f32; | |||
| let r = bgr_data[bgr_index + 2] as f32; | |||
| // Corrected YUV conversion formulas | |||
| let y_value = (0.299 * r + 0.587 * g + 0.114 * b).clamp(0.0, 255.0); | |||
| let u_value = (-0.14713 * r - 0.28886 * g + 0.436 * b + 128.0).clamp(0.0, 255.0); | |||
| let v_value = (0.615 * r - 0.51499 * g - 0.10001 * b + 128.0).clamp(0.0, 255.0); | |||
| let y_index = y * width + x; | |||
| y_plane[y_index] = y_value.round() as u8; | |||
| if x % 2 == 0 && y % 2 == 0 { | |||
| let uv_index = (y / 2) * (width / 2) + (x / 2); | |||
| u_plane[uv_index] = u_value.round() as u8; | |||
| v_plane[uv_index] = v_value.round() as u8; | |||
| } | |||
| } | |||
| } | |||
| (y_plane, u_plane, v_plane) | |||
| } | |||
| fn get_yuv_planes(buffer: &[u8], width: usize, height: usize) -> (&[u8], &[u8], &[u8]) { | |||
| // Calculate sizes of Y, U, and V planes for YUV420 format | |||
| let y_size = width * height; // Y has full resolution | |||
| let uv_width = width / 2; // U and V are subsampled by 2 in both dimensions | |||
| let uv_height = height / 2; // U and V are subsampled by 2 in both dimensions | |||
| let uv_size = uv_width * uv_height; // Size for U and V planes | |||
| // Ensure the buffer has the correct size | |||
| // if buffer.len() != y_size + 2 * uv_size { | |||
| // panic!("Invalid buffer size for the given width and height!"); | |||
| // } | |||
| // Extract Y, U, and V planes | |||
| let y_plane = &buffer[0..y_size]; //.to_vec(); | |||
| let u_plane = &buffer[y_size..y_size + uv_size]; //.to_vec(); | |||
| let v_plane = &buffer[y_size + uv_size..]; //.to_vec(); | |||
| (y_plane, u_plane, v_plane) | |||
| } | |||
| fn main() -> Result<()> { | |||
| let mut height = std::env::var("IMAGE_HEIGHT") | |||
| .unwrap_or_else(|_| "480".to_string()) | |||
| .parse() | |||
| .unwrap(); | |||
| let mut width = std::env::var("IMAGE_WIDTH") | |||
| .unwrap_or_else(|_| "640".to_string()) | |||
| .parse() | |||
| .unwrap(); | |||
| let speed = var("RAV1E_SPEED").map(|s| s.parse().unwrap()).unwrap_or(10); | |||
| let mut enc = EncoderConfig { | |||
| width, | |||
| height, | |||
| speed_settings: SpeedSettings::from_preset(speed), | |||
| low_latency: true, | |||
| ..Default::default() | |||
| }; | |||
| let cfg = Config::new().with_encoder_config(enc.clone()); | |||
| cfg.validate()?; | |||
| let (mut node, mut events) = | |||
| DoraNode::init_from_env().context("Could not initialize dora node")?; | |||
| loop { | |||
| match events.recv() { | |||
| Some(Event::Input { | |||
| id, | |||
| data, | |||
| mut metadata, | |||
| }) => { | |||
| if let Some(Parameter::Integer(h)) = metadata.parameters.get("height") { | |||
| height = *h as usize; | |||
| }; | |||
| if let Some(Parameter::Integer(w)) = metadata.parameters.get("width") { | |||
| width = *w as usize; | |||
| }; | |||
| let encoding = if let Some(Parameter::String(encoding)) = | |||
| metadata.parameters.get("encoding") | |||
| { | |||
| encoding | |||
| } else { | |||
| "bgr8" | |||
| }; | |||
| enc = EncoderConfig { | |||
| width, | |||
| height, | |||
| speed_settings: SpeedSettings::from_preset(speed), | |||
| low_latency: true, | |||
| ..Default::default() | |||
| }; | |||
| match encoding { | |||
| "mono16" => { | |||
| enc.bit_depth = 12; | |||
| enc.chroma_sampling = color::ChromaSampling::Cs400; | |||
| } | |||
| _ => {} | |||
| } | |||
| let cfg = Config::new().with_encoder_config(enc.clone()); | |||
| if encoding == "bgr8" { | |||
| let buffer: &UInt8Array = data.as_any().downcast_ref().unwrap(); | |||
| let buffer: Vec<u8> = buffer.values().to_vec(); | |||
| let (y, u, v) = bgr8_to_yuv420(buffer, width, height); | |||
| // Transpose values from BGR to RGB | |||
| // let buffer: Vec<u8> = buffer.chunks(3).flat_map(|x| [x[2], x[1], x[0]]).collect(); | |||
| let mut ctx: Context<u8> = cfg.new_context().unwrap(); | |||
| let mut f = ctx.new_frame(); | |||
| let xdec = f.planes[0].cfg.xdec; | |||
| let stride = (width + xdec) >> xdec; | |||
| f.planes[0].copy_from_raw_u8(&y, stride, 1); | |||
| let xdec = f.planes[1].cfg.xdec; | |||
| let stride = (width + xdec) >> xdec; | |||
| f.planes[1].copy_from_raw_u8(&u, stride, 1); | |||
| let xdec = f.planes[2].cfg.xdec; | |||
| let stride = (width + xdec) >> xdec; | |||
| f.planes[2].copy_from_raw_u8(&v, stride, 1); | |||
| match ctx.send_frame(f) { | |||
| Ok(_) => {} | |||
| Err(e) => match e { | |||
| EncoderStatus::EnoughData => { | |||
| warn!("Unable to send frame "); | |||
| continue; | |||
| } | |||
| _ => { | |||
| warn!("Unable to send frame "); | |||
| continue; | |||
| } | |||
| }, | |||
| } | |||
| metadata | |||
| .parameters | |||
| .insert("encoding".to_string(), Parameter::String("av1".to_string())); | |||
| ctx.flush(); | |||
| match ctx.receive_packet() { | |||
| Ok(pkt) => { | |||
| let data = pkt.data; | |||
| let arrow = data.into_arrow(); | |||
| node.send_output(id, metadata.parameters, arrow) | |||
| .context("could not send output") | |||
| .unwrap(); | |||
| } | |||
| Err(e) => match e { | |||
| EncoderStatus::LimitReached => {} | |||
| EncoderStatus::Encoded => {} | |||
| EncoderStatus::NeedMoreData => {} | |||
| _ => { | |||
| panic!("Unable to receive packet",); | |||
| } | |||
| }, | |||
| } | |||
| } else if encoding == "yuv420" { | |||
| let buffer: &UInt8Array = data.as_any().downcast_ref().unwrap(); | |||
| let buffer = buffer.values(); //.to_vec(); | |||
| let (y, u, v) = get_yuv_planes(buffer, width, height); | |||
| let mut ctx: Context<u8> = cfg.new_context().unwrap(); | |||
| let mut f = ctx.new_frame(); | |||
| let xdec = f.planes[0].cfg.xdec; | |||
| let stride = (width + xdec) >> xdec; | |||
| f.planes[0].copy_from_raw_u8(&y, stride, 1); | |||
| let xdec = f.planes[1].cfg.xdec; | |||
| let stride = (width + xdec) >> xdec; | |||
| f.planes[1].copy_from_raw_u8(&u, stride, 1); | |||
| let xdec = f.planes[2].cfg.xdec; | |||
| let stride = (width + xdec) >> xdec; | |||
| f.planes[2].copy_from_raw_u8(&v, stride, 1); | |||
| match ctx.send_frame(f) { | |||
| Ok(_) => {} | |||
| Err(e) => match e { | |||
| EncoderStatus::EnoughData => { | |||
| warn!("Unable to send frame "); | |||
| } | |||
| _ => { | |||
| warn!("Unable to send frame "); | |||
| } | |||
| }, | |||
| } | |||
| metadata | |||
| .parameters | |||
| .insert("encoding".to_string(), Parameter::String("av1".to_string())); | |||
| ctx.flush(); | |||
| match ctx.receive_packet() { | |||
| Ok(pkt) => { | |||
| let data = pkt.data; | |||
| let arrow = data.into_arrow(); | |||
| node.send_output(id, metadata.parameters, arrow) | |||
| .context("could not send output") | |||
| .unwrap(); | |||
| } | |||
| Err(e) => match e { | |||
| EncoderStatus::LimitReached => {} | |||
| EncoderStatus::Encoded => {} | |||
| EncoderStatus::NeedMoreData => {} | |||
| _ => { | |||
| panic!("Unable to receive packet",); | |||
| } | |||
| }, | |||
| } | |||
| } else if encoding == "mono16" { | |||
| let buffer: &UInt16Array = data.as_any().downcast_ref().unwrap(); | |||
| let buffer: &[u16] = buffer.values(); | |||
| // let buffer = shift_u16_slice_to_upper_12_bits(buffer); | |||
| let bytes: &[u8] = &bytemuck::cast_slice(&buffer); | |||
| let mut ctx: Context<u16> = cfg.new_context().unwrap(); | |||
| let mut f = ctx.new_frame(); | |||
| let xdec = f.planes[0].cfg.xdec; | |||
| let stride = (width + xdec) >> xdec; | |||
| // Multiply by 2 the stride as it is going to be width * 2 as we're converting 16-bit to 2*8-bit. | |||
| f.planes[0].copy_from_raw_u8(bytes, stride * 2, 2); | |||
| match ctx.send_frame(f) { | |||
| Ok(_) => {} | |||
| Err(e) => match e { | |||
| EncoderStatus::EnoughData => { | |||
| warn!("Unable to send frame "); | |||
| } | |||
| _ => { | |||
| warn!("Unable to send frame "); | |||
| } | |||
| }, | |||
| } | |||
| metadata | |||
| .parameters | |||
| .insert("encoding".to_string(), Parameter::String("av1".to_string())); | |||
| ctx.flush(); | |||
| match ctx.receive_packet() { | |||
| Ok(pkt) => { | |||
| let data = pkt.data; | |||
| let arrow = data.into_arrow(); | |||
| node.send_output(id, metadata.parameters, arrow) | |||
| .context("could not send output") | |||
| .unwrap(); | |||
| } | |||
| Err(e) => match e { | |||
| EncoderStatus::LimitReached => {} | |||
| EncoderStatus::Encoded => {} | |||
| EncoderStatus::NeedMoreData => {} | |||
| _ => { | |||
| panic!("Unable to receive packet",); | |||
| } | |||
| }, | |||
| } | |||
| } else if encoding == "rgb8" { | |||
| let buffer: &UInt8Array = data.as_any().downcast_ref().unwrap(); | |||
| let buffer: Vec<u8> = buffer.values().to_vec(); | |||
| let buffer: Vec<u8> = | |||
| buffer.chunks(3).flat_map(|x| [x[2], x[1], x[0]]).collect(); | |||
| let (y, u, v) = bgr8_to_yuv420(buffer, width, height); | |||
| // Transpose values from BGR to RGB | |||
| let mut ctx: Context<u8> = cfg.new_context().unwrap(); | |||
| let mut f = ctx.new_frame(); | |||
| let xdec = f.planes[0].cfg.xdec; | |||
| let stride = (width + xdec) >> xdec; | |||
| f.planes[0].copy_from_raw_u8(&y, stride, 1); | |||
| let xdec = f.planes[1].cfg.xdec; | |||
| let stride = (width + xdec) >> xdec; | |||
| f.planes[1].copy_from_raw_u8(&u, stride, 1); | |||
| let xdec = f.planes[2].cfg.xdec; | |||
| let stride = (width + xdec) >> xdec; | |||
| f.planes[2].copy_from_raw_u8(&v, stride, 1); | |||
| match ctx.send_frame(f) { | |||
| Ok(_) => {} | |||
| Err(e) => match e { | |||
| EncoderStatus::EnoughData => { | |||
| warn!("Unable to send frame "); | |||
| continue; | |||
| } | |||
| _ => { | |||
| warn!("Unable to send frame "); | |||
| continue; | |||
| } | |||
| }, | |||
| } | |||
| metadata | |||
| .parameters | |||
| .insert("encoding".to_string(), Parameter::String("av1".to_string())); | |||
| ctx.flush(); | |||
| match ctx.receive_packet() { | |||
| Ok(pkt) => { | |||
| let data = pkt.data; | |||
| let arrow = data.into_arrow(); | |||
| node.send_output(id, metadata.parameters, arrow) | |||
| .context("could not send output") | |||
| .unwrap(); | |||
| } | |||
| Err(e) => match e { | |||
| EncoderStatus::LimitReached => {} | |||
| EncoderStatus::Encoded => {} | |||
| EncoderStatus::NeedMoreData => {} | |||
| _ => { | |||
| panic!("Unable to receive packet",); | |||
| } | |||
| }, | |||
| } | |||
| } else { | |||
| unimplemented!("We haven't worked on additional encodings."); | |||
| } | |||
| } | |||
| Some(Event::Error(_e)) => { | |||
| continue; | |||
| } | |||
| _ => break, | |||
| }; | |||
| } | |||
| Ok(()) | |||
| } | |||
| @@ -3,10 +3,8 @@ | |||
| use std::{collections::HashMap, env::VarError, path::Path}; | |||
| use dora_node_api::{ | |||
| arrow::{ | |||
| array::{Array, AsArray, Float32Array, Float64Array, StringArray, UInt8Array}, | |||
| datatypes::Float32Type, | |||
| }, | |||
| arrow::array::{Array, Float32Array, Float64Array, StringArray, UInt16Array, UInt8Array}, | |||
| arrow::{array::AsArray, datatypes::Float32Type}, | |||
| dora_core::config::DataId, | |||
| DoraNode, Event, Parameter, | |||
| }; | |||
| @@ -96,6 +94,10 @@ pub fn lib_main() -> Result<()> { | |||
| } | |||
| Err(VarError::NotPresent) => (), | |||
| }; | |||
| let camera_pitch = std::env::var("CAMERA_PITCH") | |||
| .unwrap_or("0.0".to_string()) | |||
| .parse::<f32>() | |||
| .unwrap(); | |||
| while let Some(event) = events.recv() { | |||
| if let Event::Input { id, data, metadata } = event { | |||
| @@ -181,21 +183,77 @@ pub fn lib_main() -> Result<()> { | |||
| } else { | |||
| vec![640, 480] | |||
| }; | |||
| let buffer: &Float64Array = data.as_any().downcast_ref().unwrap(); | |||
| let points_3d = buffer.iter().enumerate().map(|(i, z)| { | |||
| let u = i as f32 % *width as f32; // Calculate x-coordinate (u) | |||
| let v = i as f32 / *width as f32; // Calculate y-coordinate (v) | |||
| let z = z.unwrap_or_default() as f32; | |||
| ( | |||
| (u - resolution[0] as f32) * z / focal_length[0] as f32, | |||
| (v - resolution[1] as f32) * z / focal_length[1] as f32, | |||
| z, | |||
| ) | |||
| }); | |||
| let points_3d = Points3D::new(points_3d); | |||
| let pitch = if let Some(Parameter::Float(pitch)) = metadata.parameters.get("pitch") | |||
| { | |||
| *pitch as f32 | |||
| } else { | |||
| camera_pitch | |||
| }; | |||
| let cos_theta = pitch.cos(); | |||
| let sin_theta = pitch.sin(); | |||
| let points = match data.data_type() { | |||
| dora_node_api::arrow::datatypes::DataType::Float64 => { | |||
| let buffer: &Float64Array = data.as_any().downcast_ref().unwrap(); | |||
| let mut points = vec![]; | |||
| buffer.iter().enumerate().for_each(|(i, z)| { | |||
| let u = i as f32 % *width as f32; // Calculate x-coordinate (u) | |||
| let v = i as f32 / *width as f32; // Calculate y-coordinate (v) | |||
| if let Some(z) = z { | |||
| let z = z as f32; | |||
| // Skip points that have empty depth or is too far away | |||
| if z == 0. || z > 8.0 { | |||
| points.push((0., 0., 0.)); | |||
| return; | |||
| } | |||
| let y = (u - resolution[0] as f32) * z / focal_length[0] as f32; | |||
| let x = (v - resolution[1] as f32) * z / focal_length[1] as f32; | |||
| let new_x = sin_theta * z + cos_theta * x; | |||
| let new_y = -y; | |||
| let new_z = cos_theta * z - sin_theta * x; | |||
| points.push((new_x, new_y, new_z)); | |||
| } else { | |||
| points.push((0., 0., 0.)); | |||
| } | |||
| }); | |||
| Points3D::new(points) | |||
| } | |||
| dora_node_api::arrow::datatypes::DataType::UInt16 => { | |||
| let buffer: &UInt16Array = data.as_any().downcast_ref().unwrap(); | |||
| let mut points = vec![]; | |||
| buffer.iter().enumerate().for_each(|(i, z)| { | |||
| let u = i as f32 % *width as f32; // Calculate x-coordinate (u) | |||
| let v = i as f32 / *width as f32; // Calculate y-coordinate (v) | |||
| if let Some(z) = z { | |||
| let z = z as f32 / 1000.0; // Convert to meters | |||
| // Skip points that have empty depth or is too far away | |||
| if z == 0. || z > 8.0 { | |||
| points.push((0., 0., 0.)); | |||
| return; | |||
| } | |||
| let y = (u - resolution[0] as f32) * z / focal_length[0] as f32; | |||
| let x = (v - resolution[1] as f32) * z / focal_length[1] as f32; | |||
| let new_x = sin_theta * z + cos_theta * x; | |||
| let new_y = -y; | |||
| let new_z = cos_theta * z - sin_theta * x; | |||
| points.push((new_x, new_y, new_z)); | |||
| } else { | |||
| points.push((0., 0., 0.)); | |||
| } | |||
| }); | |||
| Points3D::new(points) | |||
| } | |||
| _ => { | |||
| return Err(eyre!("Unsupported depth data type {}", data.data_type())); | |||
| } | |||
| }; | |||
| if let Some(color_buffer) = image_cache.get(&id.replace("depth", "image")) { | |||
| let colors = if let Some(mask) = mask_cache.get(&id.replace("depth", "mask")) { | |||
| let colors = if let Some(mask) = mask_cache.get(&id.replace("depth", "masks")) { | |||
| let mask_length = color_buffer.len() / 3; | |||
| let number_masks = mask.len() / mask_length; | |||
| color_buffer | |||
| @@ -224,10 +282,7 @@ pub fn lib_main() -> Result<()> { | |||
| .map(|x| rerun::Color::from_rgb(x[0], x[1], x[2])) | |||
| .collect::<Vec<_>>() | |||
| }; | |||
| rec.log(id.as_str(), &points_3d.with_colors(colors)) | |||
| .context("could not log points")?; | |||
| } else { | |||
| rec.log(id.as_str(), &points_3d) | |||
| rec.log(id.as_str(), &points.with_colors(colors)) | |||
| .context("could not log points")?; | |||
| } | |||
| } else if id.as_str().contains("text") { | |||
| @@ -242,7 +297,7 @@ pub fn lib_main() -> Result<()> { | |||
| })?; | |||
| } else if id.as_str().contains("boxes2d") { | |||
| boxes2d::update_boxes2d(&rec, id, data, metadata).context("update boxes 2d")?; | |||
| } else if id.as_str().contains("mask") { | |||
| } else if id.as_str().contains("masks") { | |||
| let masks = if let Some(data) = data.as_primitive_opt::<Float32Type>() { | |||
| let data = data | |||
| .iter() | |||