├── .github └── workflows │ └── rust.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── main.rs └── video.rs /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - "*.yml" 7 | - .gitignore 8 | - "*.md" 9 | pull_request: 10 | paths-ignore: 11 | - "*.yml" 12 | - .gitignore 13 | - "*.md" 14 | 15 | env: 16 | CARGO_INCREMENTAL: 0 17 | RUSTFLAGS: "-Dwarnings" 18 | CARGO_TERM_COLOR: always 19 | 20 | jobs: 21 | msrv: 22 | runs-on: ubuntu-22.04 23 | container: shssoichiro/av1an-ci:latest 24 | steps: 25 | - uses: actions/checkout@v4 26 | - name: Install Rust 1.74.1 27 | uses: dtolnay/rust-toolchain@1.74.1 28 | - name: Build 29 | run: cargo check --locked 30 | 31 | build: 32 | runs-on: ubuntu-latest 33 | container: shssoichiro/av1an-ci:latest 34 | steps: 35 | - uses: actions/checkout@v4 36 | - name: Build 37 | run: cargo clippy 38 | - name: Run tests 39 | run: cargo test 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # These are backup files generated by rustfmt 7 | **/*.rs.bk 8 | 9 | # MSVC Windows builds of rustc generate these, which store debugging information 10 | *.pdb 11 | 12 | # Manually added 13 | /.devcontainer/ 14 | /.vscode 15 | /.idea 16 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Version 0.5.2 2 | 3 | - Re-remove harmonic mean. Unfortunately it cannot be used on sets that have negative numbers, which includes 4 | ssimulacra2 scores. Geometric mean was also tested, and in _theory_ it may be computed on sets with negative values, 5 | in practice it cannot be due to computer algorithms using logarithms for the computation. 6 | 7 | ## Version 0.5.1 8 | 9 | - Add harmonic mean output 10 | - Add ttf feature to `plotters` library 11 | - Bump all dependencies 12 | 13 | ## Version 0.5.0 14 | 15 | - Bump minimum MSRV to 1.74.1 16 | - Add rolling average to progress bar 17 | - Add increment parameter for skipping video frames 18 | - Bump all dependencies 19 | 20 | ## Version 0.4.1 21 | 22 | - Add support for piping y4m input from stdin as one of the sources 23 | 24 | ## Version 0.4.0 25 | 26 | - Update to [version 2.1 of the metric](https://github.com/cloudinary/ssimulacra2/compare/v2.0...v2.1) 27 | 28 | ## Version 0.3.5 29 | 30 | - Minor performance improvements 31 | 32 | ## Version 0.3.4 33 | 34 | - Add `--frame-threads` argument to support frame parallel multithreading 35 | - This is set to 1 by default because it does linearly increase memory usage 36 | - Various performance improvements 37 | 38 | ## Version 0.3.3 39 | 40 | - Fix graphing 41 | 42 | ## Version 0.3.2 43 | 44 | - Use crossterm instead of termion for Windows compatibility 45 | 46 | ## Version 0.3.1 47 | 48 | - Add a progress bar 49 | - Bump dependencies for a pretty healthy speed increase of about 20% 50 | 51 | ## Version 0.3.0 52 | 53 | - Implement upstream changes from https://github.com/libjxl/libjxl/pull/1848 54 | 55 | ## Version 0.2.2 56 | 57 | - Fix decoding of AV1 files with overlays when using `ffmpeg_build` feature 58 | - Or at least attempt to. There still seems to be some flakiness with linking libdav1d. 59 | - Add support for YUVJ files 60 | 61 | ## Version 0.2.1 62 | 63 | - Fix compilation with no default features 64 | 65 | ## Version 0.2.0 66 | 67 | - Add support for video comparison 68 | - This change splits ssimulacra2_rs into two subcommands: 69 | - `ssimulacra2_rs image input.png output.png` for comparing still images. All popular input formats are supported. 70 | - `ssimulacra2_rs video input.y4m output.y4m` for comparing videos. All popular input formats are supported. This 71 | feature requires the `ffmpeg` feature be enabled, which is on by default. 72 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "adler" 7 | version = "1.0.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 10 | 11 | [[package]] 12 | name = "adler2" 13 | version = "2.0.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 16 | 17 | [[package]] 18 | name = "ahash" 19 | version = "0.8.11" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" 22 | dependencies = [ 23 | "cfg-if", 24 | "once_cell", 25 | "version_check", 26 | "zerocopy", 27 | ] 28 | 29 | [[package]] 30 | name = "aligned-vec" 31 | version = "0.5.0" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" 34 | 35 | [[package]] 36 | name = "anstream" 37 | version = "0.6.15" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" 40 | dependencies = [ 41 | "anstyle", 42 | "anstyle-parse", 43 | "anstyle-query", 44 | "anstyle-wincon", 45 | "colorchoice", 46 | "is_terminal_polyfill", 47 | "utf8parse", 48 | ] 49 | 50 | [[package]] 51 | name = "anstyle" 52 | version = "1.0.8" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" 55 | 56 | [[package]] 57 | name = "anstyle-parse" 58 | version = "0.2.5" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" 61 | dependencies = [ 62 | "utf8parse", 63 | ] 64 | 65 | [[package]] 66 | name = "anstyle-query" 67 | version = "1.1.1" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" 70 | dependencies = [ 71 | "windows-sys 0.52.0", 72 | ] 73 | 74 | [[package]] 75 | name = "anstyle-wincon" 76 | version = "3.0.4" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" 79 | dependencies = [ 80 | "anstyle", 81 | "windows-sys 0.52.0", 82 | ] 83 | 84 | [[package]] 85 | name = "anyhow" 86 | version = "1.0.89" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" 89 | 90 | [[package]] 91 | name = "approx" 92 | version = "0.5.1" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" 95 | dependencies = [ 96 | "num-traits", 97 | ] 98 | 99 | [[package]] 100 | name = "autocfg" 101 | version = "1.3.0" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" 104 | 105 | [[package]] 106 | name = "av-data" 107 | version = "0.4.2" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "d75b98a3525d00f920df9a2d44cc99b9cc5b7dc70d7fbb612cd755270dbe6552" 110 | dependencies = [ 111 | "byte-slice-cast", 112 | "bytes", 113 | "num-derive", 114 | "num-rational", 115 | "num-traits", 116 | "thiserror", 117 | ] 118 | 119 | [[package]] 120 | name = "av-metrics" 121 | version = "0.9.1" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "996ce95bbdb0203e5b91d4a0c9b81c0d67d11c80f884482a0c1ea19e732e3530" 124 | dependencies = [ 125 | "crossbeam", 126 | "itertools", 127 | "lab", 128 | "num-traits", 129 | "rayon", 130 | "thiserror", 131 | "v_frame", 132 | ] 133 | 134 | [[package]] 135 | name = "av-metrics-decoders" 136 | version = "0.3.1" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "fbaae4589517c7b8b5af8e15971ca31e79c91a377bebd84fce1cdc5e8ae3e4d0" 139 | dependencies = [ 140 | "anyhow", 141 | "av-metrics", 142 | "vapoursynth", 143 | "y4m", 144 | ] 145 | 146 | [[package]] 147 | name = "bit_field" 148 | version = "0.10.2" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" 151 | 152 | [[package]] 153 | name = "bitflags" 154 | version = "1.3.2" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 157 | 158 | [[package]] 159 | name = "bitflags" 160 | version = "2.6.0" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 163 | 164 | [[package]] 165 | name = "bitreader" 166 | version = "0.3.11" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "886559b1e163d56c765bc3a985febb4eee8009f625244511d8ee3c432e08c066" 169 | dependencies = [ 170 | "cfg-if", 171 | ] 172 | 173 | [[package]] 174 | name = "bumpalo" 175 | version = "3.16.0" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 178 | 179 | [[package]] 180 | name = "byte-slice-cast" 181 | version = "1.2.2" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" 184 | 185 | [[package]] 186 | name = "bytemuck" 187 | version = "1.18.0" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" 190 | 191 | [[package]] 192 | name = "byteorder" 193 | version = "1.5.0" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 196 | 197 | [[package]] 198 | name = "byteorder-lite" 199 | version = "0.1.0" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" 202 | 203 | [[package]] 204 | name = "bytes" 205 | version = "1.7.1" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" 208 | 209 | [[package]] 210 | name = "cc" 211 | version = "1.1.18" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476" 214 | dependencies = [ 215 | "shlex", 216 | ] 217 | 218 | [[package]] 219 | name = "cfg-expr" 220 | version = "0.15.8" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" 223 | dependencies = [ 224 | "smallvec", 225 | "target-lexicon", 226 | ] 227 | 228 | [[package]] 229 | name = "cfg-if" 230 | version = "1.0.0" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 233 | 234 | [[package]] 235 | name = "clap" 236 | version = "4.5.17" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" 239 | dependencies = [ 240 | "clap_builder", 241 | "clap_derive", 242 | ] 243 | 244 | [[package]] 245 | name = "clap_builder" 246 | version = "4.5.17" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" 249 | dependencies = [ 250 | "anstream", 251 | "anstyle", 252 | "clap_lex", 253 | "strsim", 254 | ] 255 | 256 | [[package]] 257 | name = "clap_derive" 258 | version = "4.5.13" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" 261 | dependencies = [ 262 | "heck", 263 | "proc-macro2", 264 | "quote", 265 | "syn", 266 | ] 267 | 268 | [[package]] 269 | name = "clap_lex" 270 | version = "0.7.2" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" 273 | 274 | [[package]] 275 | name = "colorchoice" 276 | version = "1.0.2" 277 | source = "registry+https://github.com/rust-lang/crates.io-index" 278 | checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" 279 | 280 | [[package]] 281 | name = "console" 282 | version = "0.15.8" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" 285 | dependencies = [ 286 | "encode_unicode", 287 | "lazy_static", 288 | "libc", 289 | "unicode-width", 290 | "windows-sys 0.52.0", 291 | ] 292 | 293 | [[package]] 294 | name = "core-foundation" 295 | version = "0.9.4" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" 298 | dependencies = [ 299 | "core-foundation-sys", 300 | "libc", 301 | ] 302 | 303 | [[package]] 304 | name = "core-foundation-sys" 305 | version = "0.8.7" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 308 | 309 | [[package]] 310 | name = "core-graphics" 311 | version = "0.23.2" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" 314 | dependencies = [ 315 | "bitflags 1.3.2", 316 | "core-foundation", 317 | "core-graphics-types", 318 | "foreign-types", 319 | "libc", 320 | ] 321 | 322 | [[package]] 323 | name = "core-graphics-types" 324 | version = "0.1.3" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" 327 | dependencies = [ 328 | "bitflags 1.3.2", 329 | "core-foundation", 330 | "libc", 331 | ] 332 | 333 | [[package]] 334 | name = "core-text" 335 | version = "20.1.0" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "c9d2790b5c08465d49f8dc05c8bcae9fea467855947db39b0f8145c091aaced5" 338 | dependencies = [ 339 | "core-foundation", 340 | "core-graphics", 341 | "foreign-types", 342 | "libc", 343 | ] 344 | 345 | [[package]] 346 | name = "crc32fast" 347 | version = "1.4.2" 348 | source = "registry+https://github.com/rust-lang/crates.io-index" 349 | checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" 350 | dependencies = [ 351 | "cfg-if", 352 | ] 353 | 354 | [[package]] 355 | name = "crossbeam" 356 | version = "0.8.4" 357 | source = "registry+https://github.com/rust-lang/crates.io-index" 358 | checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" 359 | dependencies = [ 360 | "crossbeam-channel", 361 | "crossbeam-deque", 362 | "crossbeam-epoch", 363 | "crossbeam-queue", 364 | "crossbeam-utils", 365 | ] 366 | 367 | [[package]] 368 | name = "crossbeam-channel" 369 | version = "0.5.13" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" 372 | dependencies = [ 373 | "crossbeam-utils", 374 | ] 375 | 376 | [[package]] 377 | name = "crossbeam-deque" 378 | version = "0.8.5" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" 381 | dependencies = [ 382 | "crossbeam-epoch", 383 | "crossbeam-utils", 384 | ] 385 | 386 | [[package]] 387 | name = "crossbeam-epoch" 388 | version = "0.9.18" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" 391 | dependencies = [ 392 | "crossbeam-utils", 393 | ] 394 | 395 | [[package]] 396 | name = "crossbeam-queue" 397 | version = "0.3.11" 398 | source = "registry+https://github.com/rust-lang/crates.io-index" 399 | checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" 400 | dependencies = [ 401 | "crossbeam-utils", 402 | ] 403 | 404 | [[package]] 405 | name = "crossbeam-utils" 406 | version = "0.8.20" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" 409 | 410 | [[package]] 411 | name = "crossterm" 412 | version = "0.27.0" 413 | source = "registry+https://github.com/rust-lang/crates.io-index" 414 | checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" 415 | dependencies = [ 416 | "bitflags 2.6.0", 417 | "crossterm_winapi", 418 | "libc", 419 | "mio", 420 | "parking_lot", 421 | "signal-hook", 422 | "signal-hook-mio", 423 | "winapi", 424 | ] 425 | 426 | [[package]] 427 | name = "crossterm_winapi" 428 | version = "0.9.1" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" 431 | dependencies = [ 432 | "winapi", 433 | ] 434 | 435 | [[package]] 436 | name = "crunchy" 437 | version = "0.2.2" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 440 | 441 | [[package]] 442 | name = "cstr" 443 | version = "0.2.12" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "68523903c8ae5aacfa32a0d9ae60cadeb764e1da14ee0d26b1f3089f13a54636" 446 | dependencies = [ 447 | "proc-macro2", 448 | "quote", 449 | ] 450 | 451 | [[package]] 452 | name = "dav1d" 453 | version = "0.10.3" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "0d4b54a40baf633a71c6f0fb49494a7e4ee7bc26f3e727212b6cb915aa1ea1e1" 456 | dependencies = [ 457 | "av-data", 458 | "bitflags 2.6.0", 459 | "dav1d-sys", 460 | "static_assertions", 461 | ] 462 | 463 | [[package]] 464 | name = "dav1d-sys" 465 | version = "0.8.2" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "6ecb1c5e8f4dc438eedc1b534a54672fb0e0a56035dae6b50162787bd2c50e95" 468 | dependencies = [ 469 | "libc", 470 | "system-deps", 471 | ] 472 | 473 | [[package]] 474 | name = "dirs-next" 475 | version = "2.0.0" 476 | source = "registry+https://github.com/rust-lang/crates.io-index" 477 | checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" 478 | dependencies = [ 479 | "cfg-if", 480 | "dirs-sys-next", 481 | ] 482 | 483 | [[package]] 484 | name = "dirs-sys-next" 485 | version = "0.1.2" 486 | source = "registry+https://github.com/rust-lang/crates.io-index" 487 | checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" 488 | dependencies = [ 489 | "libc", 490 | "redox_users", 491 | "winapi", 492 | ] 493 | 494 | [[package]] 495 | name = "dlib" 496 | version = "0.5.2" 497 | source = "registry+https://github.com/rust-lang/crates.io-index" 498 | checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" 499 | dependencies = [ 500 | "libloading", 501 | ] 502 | 503 | [[package]] 504 | name = "dwrote" 505 | version = "0.11.1" 506 | source = "registry+https://github.com/rust-lang/crates.io-index" 507 | checksum = "2da3498378ed373237bdef1eddcc64e7be2d3ba4841f4c22a998e81cadeea83c" 508 | dependencies = [ 509 | "lazy_static", 510 | "libc", 511 | "winapi", 512 | "wio", 513 | ] 514 | 515 | [[package]] 516 | name = "either" 517 | version = "1.13.0" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" 520 | 521 | [[package]] 522 | name = "encode_unicode" 523 | version = "0.3.6" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" 526 | 527 | [[package]] 528 | name = "equivalent" 529 | version = "1.0.1" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 532 | 533 | [[package]] 534 | name = "exr" 535 | version = "1.72.0" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" 538 | dependencies = [ 539 | "bit_field", 540 | "flume", 541 | "half", 542 | "lebe", 543 | "miniz_oxide 0.7.4", 544 | "rayon-core", 545 | "smallvec", 546 | "zune-inflate", 547 | ] 548 | 549 | [[package]] 550 | name = "fallible_collections" 551 | version = "0.4.9" 552 | source = "registry+https://github.com/rust-lang/crates.io-index" 553 | checksum = "a88c69768c0a15262df21899142bc6df9b9b823546d4b4b9a7bc2d6c448ec6fd" 554 | dependencies = [ 555 | "hashbrown 0.13.2", 556 | ] 557 | 558 | [[package]] 559 | name = "fdeflate" 560 | version = "0.3.4" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" 563 | dependencies = [ 564 | "simd-adler32", 565 | ] 566 | 567 | [[package]] 568 | name = "flate2" 569 | version = "1.0.33" 570 | source = "registry+https://github.com/rust-lang/crates.io-index" 571 | checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" 572 | dependencies = [ 573 | "crc32fast", 574 | "miniz_oxide 0.8.0", 575 | ] 576 | 577 | [[package]] 578 | name = "float-ord" 579 | version = "0.3.2" 580 | source = "registry+https://github.com/rust-lang/crates.io-index" 581 | checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d" 582 | 583 | [[package]] 584 | name = "flume" 585 | version = "0.11.0" 586 | source = "registry+https://github.com/rust-lang/crates.io-index" 587 | checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" 588 | dependencies = [ 589 | "spin", 590 | ] 591 | 592 | [[package]] 593 | name = "font-kit" 594 | version = "0.13.2" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "2845a73bbd781e691ab7c2a028c579727cd254942e8ced57ff73e0eafd60de87" 597 | dependencies = [ 598 | "bitflags 2.6.0", 599 | "byteorder", 600 | "core-foundation", 601 | "core-graphics", 602 | "core-text", 603 | "dirs-next", 604 | "dwrote", 605 | "float-ord", 606 | "freetype-sys", 607 | "lazy_static", 608 | "libc", 609 | "log", 610 | "pathfinder_geometry", 611 | "pathfinder_simd", 612 | "walkdir", 613 | "winapi", 614 | "yeslogic-fontconfig-sys", 615 | ] 616 | 617 | [[package]] 618 | name = "foreign-types" 619 | version = "0.5.0" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" 622 | dependencies = [ 623 | "foreign-types-macros", 624 | "foreign-types-shared", 625 | ] 626 | 627 | [[package]] 628 | name = "foreign-types-macros" 629 | version = "0.2.3" 630 | source = "registry+https://github.com/rust-lang/crates.io-index" 631 | checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" 632 | dependencies = [ 633 | "proc-macro2", 634 | "quote", 635 | "syn", 636 | ] 637 | 638 | [[package]] 639 | name = "foreign-types-shared" 640 | version = "0.3.1" 641 | source = "registry+https://github.com/rust-lang/crates.io-index" 642 | checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" 643 | 644 | [[package]] 645 | name = "freetype-sys" 646 | version = "0.20.1" 647 | source = "registry+https://github.com/rust-lang/crates.io-index" 648 | checksum = "0e7edc5b9669349acfda99533e9e0bcf26a51862ab43b08ee7745c55d28eb134" 649 | dependencies = [ 650 | "cc", 651 | "libc", 652 | "pkg-config", 653 | ] 654 | 655 | [[package]] 656 | name = "getrandom" 657 | version = "0.2.15" 658 | source = "registry+https://github.com/rust-lang/crates.io-index" 659 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 660 | dependencies = [ 661 | "cfg-if", 662 | "libc", 663 | "wasi", 664 | ] 665 | 666 | [[package]] 667 | name = "half" 668 | version = "2.4.1" 669 | source = "registry+https://github.com/rust-lang/crates.io-index" 670 | checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" 671 | dependencies = [ 672 | "cfg-if", 673 | "crunchy", 674 | ] 675 | 676 | [[package]] 677 | name = "hashbrown" 678 | version = "0.13.2" 679 | source = "registry+https://github.com/rust-lang/crates.io-index" 680 | checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" 681 | dependencies = [ 682 | "ahash", 683 | ] 684 | 685 | [[package]] 686 | name = "hashbrown" 687 | version = "0.15.1" 688 | source = "registry+https://github.com/rust-lang/crates.io-index" 689 | checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" 690 | 691 | [[package]] 692 | name = "heck" 693 | version = "0.5.0" 694 | source = "registry+https://github.com/rust-lang/crates.io-index" 695 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 696 | 697 | [[package]] 698 | name = "image" 699 | version = "0.25.6" 700 | source = "registry+https://github.com/rust-lang/crates.io-index" 701 | checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" 702 | dependencies = [ 703 | "bytemuck", 704 | "byteorder-lite", 705 | "dav1d", 706 | "exr", 707 | "image-webp", 708 | "mp4parse", 709 | "num-traits", 710 | "png", 711 | "zune-core", 712 | "zune-jpeg", 713 | ] 714 | 715 | [[package]] 716 | name = "image-webp" 717 | version = "0.2.0" 718 | source = "registry+https://github.com/rust-lang/crates.io-index" 719 | checksum = "e031e8e3d94711a9ccb5d6ea357439ef3dcbed361798bd4071dc4d9793fbe22f" 720 | dependencies = [ 721 | "byteorder-lite", 722 | "quick-error", 723 | ] 724 | 725 | [[package]] 726 | name = "indexmap" 727 | version = "2.6.0" 728 | source = "registry+https://github.com/rust-lang/crates.io-index" 729 | checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" 730 | dependencies = [ 731 | "equivalent", 732 | "hashbrown 0.15.1", 733 | ] 734 | 735 | [[package]] 736 | name = "indicatif" 737 | version = "0.17.8" 738 | source = "registry+https://github.com/rust-lang/crates.io-index" 739 | checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" 740 | dependencies = [ 741 | "console", 742 | "instant", 743 | "number_prefix", 744 | "portable-atomic", 745 | "unicode-width", 746 | ] 747 | 748 | [[package]] 749 | name = "instant" 750 | version = "0.1.13" 751 | source = "registry+https://github.com/rust-lang/crates.io-index" 752 | checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" 753 | dependencies = [ 754 | "cfg-if", 755 | ] 756 | 757 | [[package]] 758 | name = "is_terminal_polyfill" 759 | version = "1.70.1" 760 | source = "registry+https://github.com/rust-lang/crates.io-index" 761 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 762 | 763 | [[package]] 764 | name = "itertools" 765 | version = "0.10.5" 766 | source = "registry+https://github.com/rust-lang/crates.io-index" 767 | checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" 768 | dependencies = [ 769 | "either", 770 | ] 771 | 772 | [[package]] 773 | name = "js-sys" 774 | version = "0.3.70" 775 | source = "registry+https://github.com/rust-lang/crates.io-index" 776 | checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" 777 | dependencies = [ 778 | "wasm-bindgen", 779 | ] 780 | 781 | [[package]] 782 | name = "lab" 783 | version = "0.11.0" 784 | source = "registry+https://github.com/rust-lang/crates.io-index" 785 | checksum = "bf36173d4167ed999940f804952e6b08197cae5ad5d572eb4db150ce8ad5d58f" 786 | 787 | [[package]] 788 | name = "lazy_static" 789 | version = "1.5.0" 790 | source = "registry+https://github.com/rust-lang/crates.io-index" 791 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 792 | 793 | [[package]] 794 | name = "lebe" 795 | version = "0.5.2" 796 | source = "registry+https://github.com/rust-lang/crates.io-index" 797 | checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" 798 | 799 | [[package]] 800 | name = "libc" 801 | version = "0.2.158" 802 | source = "registry+https://github.com/rust-lang/crates.io-index" 803 | checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" 804 | 805 | [[package]] 806 | name = "libloading" 807 | version = "0.8.5" 808 | source = "registry+https://github.com/rust-lang/crates.io-index" 809 | checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" 810 | dependencies = [ 811 | "cfg-if", 812 | "windows-targets 0.52.6", 813 | ] 814 | 815 | [[package]] 816 | name = "libm" 817 | version = "0.2.8" 818 | source = "registry+https://github.com/rust-lang/crates.io-index" 819 | checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" 820 | 821 | [[package]] 822 | name = "libredox" 823 | version = "0.1.3" 824 | source = "registry+https://github.com/rust-lang/crates.io-index" 825 | checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" 826 | dependencies = [ 827 | "bitflags 2.6.0", 828 | "libc", 829 | ] 830 | 831 | [[package]] 832 | name = "lock_api" 833 | version = "0.4.12" 834 | source = "registry+https://github.com/rust-lang/crates.io-index" 835 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 836 | dependencies = [ 837 | "autocfg", 838 | "scopeguard", 839 | ] 840 | 841 | [[package]] 842 | name = "log" 843 | version = "0.4.22" 844 | source = "registry+https://github.com/rust-lang/crates.io-index" 845 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 846 | 847 | [[package]] 848 | name = "matrixmultiply" 849 | version = "0.3.9" 850 | source = "registry+https://github.com/rust-lang/crates.io-index" 851 | checksum = "9380b911e3e96d10c1f415da0876389aaf1b56759054eeb0de7df940c456ba1a" 852 | dependencies = [ 853 | "autocfg", 854 | "rawpointer", 855 | ] 856 | 857 | [[package]] 858 | name = "memchr" 859 | version = "2.7.4" 860 | source = "registry+https://github.com/rust-lang/crates.io-index" 861 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 862 | 863 | [[package]] 864 | name = "miniz_oxide" 865 | version = "0.7.4" 866 | source = "registry+https://github.com/rust-lang/crates.io-index" 867 | checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" 868 | dependencies = [ 869 | "adler", 870 | "simd-adler32", 871 | ] 872 | 873 | [[package]] 874 | name = "miniz_oxide" 875 | version = "0.8.0" 876 | source = "registry+https://github.com/rust-lang/crates.io-index" 877 | checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" 878 | dependencies = [ 879 | "adler2", 880 | ] 881 | 882 | [[package]] 883 | name = "mio" 884 | version = "0.8.11" 885 | source = "registry+https://github.com/rust-lang/crates.io-index" 886 | checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" 887 | dependencies = [ 888 | "libc", 889 | "log", 890 | "wasi", 891 | "windows-sys 0.48.0", 892 | ] 893 | 894 | [[package]] 895 | name = "mp4parse" 896 | version = "0.17.0" 897 | source = "registry+https://github.com/rust-lang/crates.io-index" 898 | checksum = "63a35203d3c6ce92d5251c77520acb2e57108c88728695aa883f70023624c570" 899 | dependencies = [ 900 | "bitreader", 901 | "byteorder", 902 | "fallible_collections", 903 | "log", 904 | "num-traits", 905 | "static_assertions", 906 | ] 907 | 908 | [[package]] 909 | name = "nalgebra" 910 | version = "0.32.6" 911 | source = "registry+https://github.com/rust-lang/crates.io-index" 912 | checksum = "7b5c17de023a86f59ed79891b2e5d5a94c705dbe904a5b5c9c952ea6221b03e4" 913 | dependencies = [ 914 | "approx", 915 | "matrixmultiply", 916 | "nalgebra-macros", 917 | "num-complex", 918 | "num-rational", 919 | "num-traits", 920 | "rand", 921 | "rand_distr", 922 | "simba", 923 | "typenum", 924 | ] 925 | 926 | [[package]] 927 | name = "nalgebra-macros" 928 | version = "0.2.2" 929 | source = "registry+https://github.com/rust-lang/crates.io-index" 930 | checksum = "254a5372af8fc138e36684761d3c0cdb758a4410e938babcff1c860ce14ddbfc" 931 | dependencies = [ 932 | "proc-macro2", 933 | "quote", 934 | "syn", 935 | ] 936 | 937 | [[package]] 938 | name = "num-bigint" 939 | version = "0.4.6" 940 | source = "registry+https://github.com/rust-lang/crates.io-index" 941 | checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" 942 | dependencies = [ 943 | "num-integer", 944 | "num-traits", 945 | ] 946 | 947 | [[package]] 948 | name = "num-complex" 949 | version = "0.4.6" 950 | source = "registry+https://github.com/rust-lang/crates.io-index" 951 | checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" 952 | dependencies = [ 953 | "num-traits", 954 | ] 955 | 956 | [[package]] 957 | name = "num-derive" 958 | version = "0.4.2" 959 | source = "registry+https://github.com/rust-lang/crates.io-index" 960 | checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" 961 | dependencies = [ 962 | "proc-macro2", 963 | "quote", 964 | "syn", 965 | ] 966 | 967 | [[package]] 968 | name = "num-integer" 969 | version = "0.1.46" 970 | source = "registry+https://github.com/rust-lang/crates.io-index" 971 | checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" 972 | dependencies = [ 973 | "num-traits", 974 | ] 975 | 976 | [[package]] 977 | name = "num-rational" 978 | version = "0.4.2" 979 | source = "registry+https://github.com/rust-lang/crates.io-index" 980 | checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" 981 | dependencies = [ 982 | "num-bigint", 983 | "num-integer", 984 | "num-traits", 985 | ] 986 | 987 | [[package]] 988 | name = "num-traits" 989 | version = "0.2.19" 990 | source = "registry+https://github.com/rust-lang/crates.io-index" 991 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 992 | dependencies = [ 993 | "autocfg", 994 | "libm", 995 | ] 996 | 997 | [[package]] 998 | name = "number_prefix" 999 | version = "0.4.0" 1000 | source = "registry+https://github.com/rust-lang/crates.io-index" 1001 | checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" 1002 | 1003 | [[package]] 1004 | name = "once_cell" 1005 | version = "1.20.0" 1006 | source = "registry+https://github.com/rust-lang/crates.io-index" 1007 | checksum = "33ea5043e58958ee56f3e15a90aee535795cd7dfd319846288d93c5b57d85cbe" 1008 | 1009 | [[package]] 1010 | name = "parking_lot" 1011 | version = "0.12.3" 1012 | source = "registry+https://github.com/rust-lang/crates.io-index" 1013 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 1014 | dependencies = [ 1015 | "lock_api", 1016 | "parking_lot_core", 1017 | ] 1018 | 1019 | [[package]] 1020 | name = "parking_lot_core" 1021 | version = "0.9.10" 1022 | source = "registry+https://github.com/rust-lang/crates.io-index" 1023 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 1024 | dependencies = [ 1025 | "cfg-if", 1026 | "libc", 1027 | "redox_syscall", 1028 | "smallvec", 1029 | "windows-targets 0.52.6", 1030 | ] 1031 | 1032 | [[package]] 1033 | name = "paste" 1034 | version = "1.0.15" 1035 | source = "registry+https://github.com/rust-lang/crates.io-index" 1036 | checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" 1037 | 1038 | [[package]] 1039 | name = "pathfinder_geometry" 1040 | version = "0.5.1" 1041 | source = "registry+https://github.com/rust-lang/crates.io-index" 1042 | checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3" 1043 | dependencies = [ 1044 | "log", 1045 | "pathfinder_simd", 1046 | ] 1047 | 1048 | [[package]] 1049 | name = "pathfinder_simd" 1050 | version = "0.5.4" 1051 | source = "registry+https://github.com/rust-lang/crates.io-index" 1052 | checksum = "5cf07ef4804cfa9aea3b04a7bbdd5a40031dbb6b4f2cbaf2b011666c80c5b4f2" 1053 | dependencies = [ 1054 | "rustc_version", 1055 | ] 1056 | 1057 | [[package]] 1058 | name = "pkg-config" 1059 | version = "0.3.30" 1060 | source = "registry+https://github.com/rust-lang/crates.io-index" 1061 | checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" 1062 | 1063 | [[package]] 1064 | name = "plotters" 1065 | version = "0.3.6" 1066 | source = "registry+https://github.com/rust-lang/crates.io-index" 1067 | checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" 1068 | dependencies = [ 1069 | "font-kit", 1070 | "lazy_static", 1071 | "num-traits", 1072 | "pathfinder_geometry", 1073 | "plotters-backend", 1074 | "plotters-bitmap", 1075 | "ttf-parser", 1076 | "wasm-bindgen", 1077 | "web-sys", 1078 | ] 1079 | 1080 | [[package]] 1081 | name = "plotters-backend" 1082 | version = "0.3.7" 1083 | source = "registry+https://github.com/rust-lang/crates.io-index" 1084 | checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" 1085 | 1086 | [[package]] 1087 | name = "plotters-bitmap" 1088 | version = "0.3.7" 1089 | source = "registry+https://github.com/rust-lang/crates.io-index" 1090 | checksum = "72ce181e3f6bf82d6c1dc569103ca7b1bd964c60ba03d7e6cdfbb3e3eb7f7405" 1091 | dependencies = [ 1092 | "plotters-backend", 1093 | ] 1094 | 1095 | [[package]] 1096 | name = "png" 1097 | version = "0.17.13" 1098 | source = "registry+https://github.com/rust-lang/crates.io-index" 1099 | checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" 1100 | dependencies = [ 1101 | "bitflags 1.3.2", 1102 | "crc32fast", 1103 | "fdeflate", 1104 | "flate2", 1105 | "miniz_oxide 0.7.4", 1106 | ] 1107 | 1108 | [[package]] 1109 | name = "portable-atomic" 1110 | version = "1.7.0" 1111 | source = "registry+https://github.com/rust-lang/crates.io-index" 1112 | checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" 1113 | 1114 | [[package]] 1115 | name = "ppv-lite86" 1116 | version = "0.2.20" 1117 | source = "registry+https://github.com/rust-lang/crates.io-index" 1118 | checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" 1119 | dependencies = [ 1120 | "zerocopy", 1121 | ] 1122 | 1123 | [[package]] 1124 | name = "proc-macro2" 1125 | version = "1.0.86" 1126 | source = "registry+https://github.com/rust-lang/crates.io-index" 1127 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 1128 | dependencies = [ 1129 | "unicode-ident", 1130 | ] 1131 | 1132 | [[package]] 1133 | name = "quick-error" 1134 | version = "2.0.1" 1135 | source = "registry+https://github.com/rust-lang/crates.io-index" 1136 | checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" 1137 | 1138 | [[package]] 1139 | name = "quote" 1140 | version = "1.0.37" 1141 | source = "registry+https://github.com/rust-lang/crates.io-index" 1142 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 1143 | dependencies = [ 1144 | "proc-macro2", 1145 | ] 1146 | 1147 | [[package]] 1148 | name = "rand" 1149 | version = "0.8.5" 1150 | source = "registry+https://github.com/rust-lang/crates.io-index" 1151 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1152 | dependencies = [ 1153 | "libc", 1154 | "rand_chacha", 1155 | "rand_core", 1156 | ] 1157 | 1158 | [[package]] 1159 | name = "rand_chacha" 1160 | version = "0.3.1" 1161 | source = "registry+https://github.com/rust-lang/crates.io-index" 1162 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1163 | dependencies = [ 1164 | "ppv-lite86", 1165 | "rand_core", 1166 | ] 1167 | 1168 | [[package]] 1169 | name = "rand_core" 1170 | version = "0.6.4" 1171 | source = "registry+https://github.com/rust-lang/crates.io-index" 1172 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 1173 | dependencies = [ 1174 | "getrandom", 1175 | ] 1176 | 1177 | [[package]] 1178 | name = "rand_distr" 1179 | version = "0.4.3" 1180 | source = "registry+https://github.com/rust-lang/crates.io-index" 1181 | checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" 1182 | dependencies = [ 1183 | "num-traits", 1184 | "rand", 1185 | ] 1186 | 1187 | [[package]] 1188 | name = "rawpointer" 1189 | version = "0.2.1" 1190 | source = "registry+https://github.com/rust-lang/crates.io-index" 1191 | checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" 1192 | 1193 | [[package]] 1194 | name = "rayon" 1195 | version = "1.10.0" 1196 | source = "registry+https://github.com/rust-lang/crates.io-index" 1197 | checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" 1198 | dependencies = [ 1199 | "either", 1200 | "rayon-core", 1201 | ] 1202 | 1203 | [[package]] 1204 | name = "rayon-core" 1205 | version = "1.12.1" 1206 | source = "registry+https://github.com/rust-lang/crates.io-index" 1207 | checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" 1208 | dependencies = [ 1209 | "crossbeam-deque", 1210 | "crossbeam-utils", 1211 | ] 1212 | 1213 | [[package]] 1214 | name = "redox_syscall" 1215 | version = "0.5.4" 1216 | source = "registry+https://github.com/rust-lang/crates.io-index" 1217 | checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" 1218 | dependencies = [ 1219 | "bitflags 2.6.0", 1220 | ] 1221 | 1222 | [[package]] 1223 | name = "redox_users" 1224 | version = "0.4.6" 1225 | source = "registry+https://github.com/rust-lang/crates.io-index" 1226 | checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" 1227 | dependencies = [ 1228 | "getrandom", 1229 | "libredox", 1230 | "thiserror", 1231 | ] 1232 | 1233 | [[package]] 1234 | name = "rustc_version" 1235 | version = "0.4.1" 1236 | source = "registry+https://github.com/rust-lang/crates.io-index" 1237 | checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" 1238 | dependencies = [ 1239 | "semver", 1240 | ] 1241 | 1242 | [[package]] 1243 | name = "safe_arch" 1244 | version = "0.7.2" 1245 | source = "registry+https://github.com/rust-lang/crates.io-index" 1246 | checksum = "c3460605018fdc9612bce72735cba0d27efbcd9904780d44c7e3a9948f96148a" 1247 | dependencies = [ 1248 | "bytemuck", 1249 | ] 1250 | 1251 | [[package]] 1252 | name = "same-file" 1253 | version = "1.0.6" 1254 | source = "registry+https://github.com/rust-lang/crates.io-index" 1255 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 1256 | dependencies = [ 1257 | "winapi-util", 1258 | ] 1259 | 1260 | [[package]] 1261 | name = "scopeguard" 1262 | version = "1.2.0" 1263 | source = "registry+https://github.com/rust-lang/crates.io-index" 1264 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1265 | 1266 | [[package]] 1267 | name = "semver" 1268 | version = "1.0.23" 1269 | source = "registry+https://github.com/rust-lang/crates.io-index" 1270 | checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" 1271 | 1272 | [[package]] 1273 | name = "serde" 1274 | version = "1.0.210" 1275 | source = "registry+https://github.com/rust-lang/crates.io-index" 1276 | checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" 1277 | dependencies = [ 1278 | "serde_derive", 1279 | ] 1280 | 1281 | [[package]] 1282 | name = "serde_derive" 1283 | version = "1.0.210" 1284 | source = "registry+https://github.com/rust-lang/crates.io-index" 1285 | checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" 1286 | dependencies = [ 1287 | "proc-macro2", 1288 | "quote", 1289 | "syn", 1290 | ] 1291 | 1292 | [[package]] 1293 | name = "serde_spanned" 1294 | version = "0.6.8" 1295 | source = "registry+https://github.com/rust-lang/crates.io-index" 1296 | checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" 1297 | dependencies = [ 1298 | "serde", 1299 | ] 1300 | 1301 | [[package]] 1302 | name = "shlex" 1303 | version = "1.3.0" 1304 | source = "registry+https://github.com/rust-lang/crates.io-index" 1305 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 1306 | 1307 | [[package]] 1308 | name = "signal-hook" 1309 | version = "0.3.17" 1310 | source = "registry+https://github.com/rust-lang/crates.io-index" 1311 | checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" 1312 | dependencies = [ 1313 | "libc", 1314 | "signal-hook-registry", 1315 | ] 1316 | 1317 | [[package]] 1318 | name = "signal-hook-mio" 1319 | version = "0.2.4" 1320 | source = "registry+https://github.com/rust-lang/crates.io-index" 1321 | checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" 1322 | dependencies = [ 1323 | "libc", 1324 | "mio", 1325 | "signal-hook", 1326 | ] 1327 | 1328 | [[package]] 1329 | name = "signal-hook-registry" 1330 | version = "1.4.2" 1331 | source = "registry+https://github.com/rust-lang/crates.io-index" 1332 | checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" 1333 | dependencies = [ 1334 | "libc", 1335 | ] 1336 | 1337 | [[package]] 1338 | name = "simba" 1339 | version = "0.8.1" 1340 | source = "registry+https://github.com/rust-lang/crates.io-index" 1341 | checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae" 1342 | dependencies = [ 1343 | "approx", 1344 | "num-complex", 1345 | "num-traits", 1346 | "paste", 1347 | "wide", 1348 | ] 1349 | 1350 | [[package]] 1351 | name = "simd-adler32" 1352 | version = "0.3.7" 1353 | source = "registry+https://github.com/rust-lang/crates.io-index" 1354 | checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" 1355 | 1356 | [[package]] 1357 | name = "smallvec" 1358 | version = "1.13.2" 1359 | source = "registry+https://github.com/rust-lang/crates.io-index" 1360 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 1361 | 1362 | [[package]] 1363 | name = "spin" 1364 | version = "0.9.8" 1365 | source = "registry+https://github.com/rust-lang/crates.io-index" 1366 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 1367 | dependencies = [ 1368 | "lock_api", 1369 | ] 1370 | 1371 | [[package]] 1372 | name = "ssimulacra2" 1373 | version = "0.5.0" 1374 | source = "registry+https://github.com/rust-lang/crates.io-index" 1375 | checksum = "f06a69284fda5ad330ccbae2dca49e88ba4816d0edcc8a4c3b549095d7116fa2" 1376 | dependencies = [ 1377 | "nalgebra", 1378 | "num-traits", 1379 | "thiserror", 1380 | "yuvxyb", 1381 | ] 1382 | 1383 | [[package]] 1384 | name = "ssimulacra2_rs" 1385 | version = "0.5.2" 1386 | dependencies = [ 1387 | "anyhow", 1388 | "av-metrics-decoders", 1389 | "clap", 1390 | "crossterm", 1391 | "image", 1392 | "indicatif", 1393 | "num-traits", 1394 | "plotters", 1395 | "ssimulacra2", 1396 | "statrs", 1397 | ] 1398 | 1399 | [[package]] 1400 | name = "static_assertions" 1401 | version = "1.1.0" 1402 | source = "registry+https://github.com/rust-lang/crates.io-index" 1403 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 1404 | 1405 | [[package]] 1406 | name = "statrs" 1407 | version = "0.17.1" 1408 | source = "registry+https://github.com/rust-lang/crates.io-index" 1409 | checksum = "f697a07e4606a0a25c044de247e583a330dbb1731d11bc7350b81f48ad567255" 1410 | dependencies = [ 1411 | "approx", 1412 | "nalgebra", 1413 | "num-traits", 1414 | "rand", 1415 | ] 1416 | 1417 | [[package]] 1418 | name = "strsim" 1419 | version = "0.11.1" 1420 | source = "registry+https://github.com/rust-lang/crates.io-index" 1421 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 1422 | 1423 | [[package]] 1424 | name = "syn" 1425 | version = "2.0.77" 1426 | source = "registry+https://github.com/rust-lang/crates.io-index" 1427 | checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" 1428 | dependencies = [ 1429 | "proc-macro2", 1430 | "quote", 1431 | "unicode-ident", 1432 | ] 1433 | 1434 | [[package]] 1435 | name = "system-deps" 1436 | version = "6.2.2" 1437 | source = "registry+https://github.com/rust-lang/crates.io-index" 1438 | checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" 1439 | dependencies = [ 1440 | "cfg-expr", 1441 | "heck", 1442 | "pkg-config", 1443 | "toml", 1444 | "version-compare", 1445 | ] 1446 | 1447 | [[package]] 1448 | name = "target-lexicon" 1449 | version = "0.12.16" 1450 | source = "registry+https://github.com/rust-lang/crates.io-index" 1451 | checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" 1452 | 1453 | [[package]] 1454 | name = "thiserror" 1455 | version = "1.0.63" 1456 | source = "registry+https://github.com/rust-lang/crates.io-index" 1457 | checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" 1458 | dependencies = [ 1459 | "thiserror-impl", 1460 | ] 1461 | 1462 | [[package]] 1463 | name = "thiserror-impl" 1464 | version = "1.0.63" 1465 | source = "registry+https://github.com/rust-lang/crates.io-index" 1466 | checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" 1467 | dependencies = [ 1468 | "proc-macro2", 1469 | "quote", 1470 | "syn", 1471 | ] 1472 | 1473 | [[package]] 1474 | name = "toml" 1475 | version = "0.8.19" 1476 | source = "registry+https://github.com/rust-lang/crates.io-index" 1477 | checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" 1478 | dependencies = [ 1479 | "serde", 1480 | "serde_spanned", 1481 | "toml_datetime", 1482 | "toml_edit", 1483 | ] 1484 | 1485 | [[package]] 1486 | name = "toml_datetime" 1487 | version = "0.6.8" 1488 | source = "registry+https://github.com/rust-lang/crates.io-index" 1489 | checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" 1490 | dependencies = [ 1491 | "serde", 1492 | ] 1493 | 1494 | [[package]] 1495 | name = "toml_edit" 1496 | version = "0.22.22" 1497 | source = "registry+https://github.com/rust-lang/crates.io-index" 1498 | checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" 1499 | dependencies = [ 1500 | "indexmap", 1501 | "serde", 1502 | "serde_spanned", 1503 | "toml_datetime", 1504 | "winnow", 1505 | ] 1506 | 1507 | [[package]] 1508 | name = "ttf-parser" 1509 | version = "0.20.0" 1510 | source = "registry+https://github.com/rust-lang/crates.io-index" 1511 | checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" 1512 | 1513 | [[package]] 1514 | name = "typenum" 1515 | version = "1.17.0" 1516 | source = "registry+https://github.com/rust-lang/crates.io-index" 1517 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 1518 | 1519 | [[package]] 1520 | name = "unicode-ident" 1521 | version = "1.0.13" 1522 | source = "registry+https://github.com/rust-lang/crates.io-index" 1523 | checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" 1524 | 1525 | [[package]] 1526 | name = "unicode-width" 1527 | version = "0.1.13" 1528 | source = "registry+https://github.com/rust-lang/crates.io-index" 1529 | checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" 1530 | 1531 | [[package]] 1532 | name = "utf8parse" 1533 | version = "0.2.2" 1534 | source = "registry+https://github.com/rust-lang/crates.io-index" 1535 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 1536 | 1537 | [[package]] 1538 | name = "v_frame" 1539 | version = "0.3.8" 1540 | source = "registry+https://github.com/rust-lang/crates.io-index" 1541 | checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" 1542 | dependencies = [ 1543 | "aligned-vec", 1544 | "num-traits", 1545 | "wasm-bindgen", 1546 | ] 1547 | 1548 | [[package]] 1549 | name = "vapoursynth" 1550 | version = "0.4.0" 1551 | source = "registry+https://github.com/rust-lang/crates.io-index" 1552 | checksum = "0c7df702c65dec1cfa3b93f824a1e58d5b0fdb82ac8a722596f43d7214282f56" 1553 | dependencies = [ 1554 | "anyhow", 1555 | "bitflags 1.3.2", 1556 | "lazy_static", 1557 | "thiserror", 1558 | "vapoursynth-sys", 1559 | ] 1560 | 1561 | [[package]] 1562 | name = "vapoursynth-sys" 1563 | version = "0.4.1" 1564 | source = "registry+https://github.com/rust-lang/crates.io-index" 1565 | checksum = "2b35092be61a799005aabfd2e9e95d074125984013142d87a5d3edecc039b9b5" 1566 | dependencies = [ 1567 | "cfg-if", 1568 | ] 1569 | 1570 | [[package]] 1571 | name = "version-compare" 1572 | version = "0.2.0" 1573 | source = "registry+https://github.com/rust-lang/crates.io-index" 1574 | checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" 1575 | 1576 | [[package]] 1577 | name = "version_check" 1578 | version = "0.9.5" 1579 | source = "registry+https://github.com/rust-lang/crates.io-index" 1580 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 1581 | 1582 | [[package]] 1583 | name = "walkdir" 1584 | version = "2.5.0" 1585 | source = "registry+https://github.com/rust-lang/crates.io-index" 1586 | checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 1587 | dependencies = [ 1588 | "same-file", 1589 | "winapi-util", 1590 | ] 1591 | 1592 | [[package]] 1593 | name = "wasi" 1594 | version = "0.11.0+wasi-snapshot-preview1" 1595 | source = "registry+https://github.com/rust-lang/crates.io-index" 1596 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1597 | 1598 | [[package]] 1599 | name = "wasm-bindgen" 1600 | version = "0.2.93" 1601 | source = "registry+https://github.com/rust-lang/crates.io-index" 1602 | checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" 1603 | dependencies = [ 1604 | "cfg-if", 1605 | "once_cell", 1606 | "wasm-bindgen-macro", 1607 | ] 1608 | 1609 | [[package]] 1610 | name = "wasm-bindgen-backend" 1611 | version = "0.2.93" 1612 | source = "registry+https://github.com/rust-lang/crates.io-index" 1613 | checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" 1614 | dependencies = [ 1615 | "bumpalo", 1616 | "log", 1617 | "once_cell", 1618 | "proc-macro2", 1619 | "quote", 1620 | "syn", 1621 | "wasm-bindgen-shared", 1622 | ] 1623 | 1624 | [[package]] 1625 | name = "wasm-bindgen-macro" 1626 | version = "0.2.93" 1627 | source = "registry+https://github.com/rust-lang/crates.io-index" 1628 | checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" 1629 | dependencies = [ 1630 | "quote", 1631 | "wasm-bindgen-macro-support", 1632 | ] 1633 | 1634 | [[package]] 1635 | name = "wasm-bindgen-macro-support" 1636 | version = "0.2.93" 1637 | source = "registry+https://github.com/rust-lang/crates.io-index" 1638 | checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" 1639 | dependencies = [ 1640 | "proc-macro2", 1641 | "quote", 1642 | "syn", 1643 | "wasm-bindgen-backend", 1644 | "wasm-bindgen-shared", 1645 | ] 1646 | 1647 | [[package]] 1648 | name = "wasm-bindgen-shared" 1649 | version = "0.2.93" 1650 | source = "registry+https://github.com/rust-lang/crates.io-index" 1651 | checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" 1652 | 1653 | [[package]] 1654 | name = "web-sys" 1655 | version = "0.3.70" 1656 | source = "registry+https://github.com/rust-lang/crates.io-index" 1657 | checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" 1658 | dependencies = [ 1659 | "js-sys", 1660 | "wasm-bindgen", 1661 | ] 1662 | 1663 | [[package]] 1664 | name = "wide" 1665 | version = "0.7.28" 1666 | source = "registry+https://github.com/rust-lang/crates.io-index" 1667 | checksum = "b828f995bf1e9622031f8009f8481a85406ce1f4d4588ff746d872043e855690" 1668 | dependencies = [ 1669 | "bytemuck", 1670 | "safe_arch", 1671 | ] 1672 | 1673 | [[package]] 1674 | name = "winapi" 1675 | version = "0.3.9" 1676 | source = "registry+https://github.com/rust-lang/crates.io-index" 1677 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1678 | dependencies = [ 1679 | "winapi-i686-pc-windows-gnu", 1680 | "winapi-x86_64-pc-windows-gnu", 1681 | ] 1682 | 1683 | [[package]] 1684 | name = "winapi-i686-pc-windows-gnu" 1685 | version = "0.4.0" 1686 | source = "registry+https://github.com/rust-lang/crates.io-index" 1687 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1688 | 1689 | [[package]] 1690 | name = "winapi-util" 1691 | version = "0.1.9" 1692 | source = "registry+https://github.com/rust-lang/crates.io-index" 1693 | checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 1694 | dependencies = [ 1695 | "windows-sys 0.52.0", 1696 | ] 1697 | 1698 | [[package]] 1699 | name = "winapi-x86_64-pc-windows-gnu" 1700 | version = "0.4.0" 1701 | source = "registry+https://github.com/rust-lang/crates.io-index" 1702 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1703 | 1704 | [[package]] 1705 | name = "windows-sys" 1706 | version = "0.48.0" 1707 | source = "registry+https://github.com/rust-lang/crates.io-index" 1708 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 1709 | dependencies = [ 1710 | "windows-targets 0.48.5", 1711 | ] 1712 | 1713 | [[package]] 1714 | name = "windows-sys" 1715 | version = "0.52.0" 1716 | source = "registry+https://github.com/rust-lang/crates.io-index" 1717 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 1718 | dependencies = [ 1719 | "windows-targets 0.52.6", 1720 | ] 1721 | 1722 | [[package]] 1723 | name = "windows-targets" 1724 | version = "0.48.5" 1725 | source = "registry+https://github.com/rust-lang/crates.io-index" 1726 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 1727 | dependencies = [ 1728 | "windows_aarch64_gnullvm 0.48.5", 1729 | "windows_aarch64_msvc 0.48.5", 1730 | "windows_i686_gnu 0.48.5", 1731 | "windows_i686_msvc 0.48.5", 1732 | "windows_x86_64_gnu 0.48.5", 1733 | "windows_x86_64_gnullvm 0.48.5", 1734 | "windows_x86_64_msvc 0.48.5", 1735 | ] 1736 | 1737 | [[package]] 1738 | name = "windows-targets" 1739 | version = "0.52.6" 1740 | source = "registry+https://github.com/rust-lang/crates.io-index" 1741 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 1742 | dependencies = [ 1743 | "windows_aarch64_gnullvm 0.52.6", 1744 | "windows_aarch64_msvc 0.52.6", 1745 | "windows_i686_gnu 0.52.6", 1746 | "windows_i686_gnullvm", 1747 | "windows_i686_msvc 0.52.6", 1748 | "windows_x86_64_gnu 0.52.6", 1749 | "windows_x86_64_gnullvm 0.52.6", 1750 | "windows_x86_64_msvc 0.52.6", 1751 | ] 1752 | 1753 | [[package]] 1754 | name = "windows_aarch64_gnullvm" 1755 | version = "0.48.5" 1756 | source = "registry+https://github.com/rust-lang/crates.io-index" 1757 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 1758 | 1759 | [[package]] 1760 | name = "windows_aarch64_gnullvm" 1761 | version = "0.52.6" 1762 | source = "registry+https://github.com/rust-lang/crates.io-index" 1763 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 1764 | 1765 | [[package]] 1766 | name = "windows_aarch64_msvc" 1767 | version = "0.48.5" 1768 | source = "registry+https://github.com/rust-lang/crates.io-index" 1769 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 1770 | 1771 | [[package]] 1772 | name = "windows_aarch64_msvc" 1773 | version = "0.52.6" 1774 | source = "registry+https://github.com/rust-lang/crates.io-index" 1775 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 1776 | 1777 | [[package]] 1778 | name = "windows_i686_gnu" 1779 | version = "0.48.5" 1780 | source = "registry+https://github.com/rust-lang/crates.io-index" 1781 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 1782 | 1783 | [[package]] 1784 | name = "windows_i686_gnu" 1785 | version = "0.52.6" 1786 | source = "registry+https://github.com/rust-lang/crates.io-index" 1787 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 1788 | 1789 | [[package]] 1790 | name = "windows_i686_gnullvm" 1791 | version = "0.52.6" 1792 | source = "registry+https://github.com/rust-lang/crates.io-index" 1793 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 1794 | 1795 | [[package]] 1796 | name = "windows_i686_msvc" 1797 | version = "0.48.5" 1798 | source = "registry+https://github.com/rust-lang/crates.io-index" 1799 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 1800 | 1801 | [[package]] 1802 | name = "windows_i686_msvc" 1803 | version = "0.52.6" 1804 | source = "registry+https://github.com/rust-lang/crates.io-index" 1805 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 1806 | 1807 | [[package]] 1808 | name = "windows_x86_64_gnu" 1809 | version = "0.48.5" 1810 | source = "registry+https://github.com/rust-lang/crates.io-index" 1811 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 1812 | 1813 | [[package]] 1814 | name = "windows_x86_64_gnu" 1815 | version = "0.52.6" 1816 | source = "registry+https://github.com/rust-lang/crates.io-index" 1817 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 1818 | 1819 | [[package]] 1820 | name = "windows_x86_64_gnullvm" 1821 | version = "0.48.5" 1822 | source = "registry+https://github.com/rust-lang/crates.io-index" 1823 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 1824 | 1825 | [[package]] 1826 | name = "windows_x86_64_gnullvm" 1827 | version = "0.52.6" 1828 | source = "registry+https://github.com/rust-lang/crates.io-index" 1829 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 1830 | 1831 | [[package]] 1832 | name = "windows_x86_64_msvc" 1833 | version = "0.48.5" 1834 | source = "registry+https://github.com/rust-lang/crates.io-index" 1835 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 1836 | 1837 | [[package]] 1838 | name = "windows_x86_64_msvc" 1839 | version = "0.52.6" 1840 | source = "registry+https://github.com/rust-lang/crates.io-index" 1841 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 1842 | 1843 | [[package]] 1844 | name = "winnow" 1845 | version = "0.6.20" 1846 | source = "registry+https://github.com/rust-lang/crates.io-index" 1847 | checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" 1848 | dependencies = [ 1849 | "memchr", 1850 | ] 1851 | 1852 | [[package]] 1853 | name = "wio" 1854 | version = "0.2.2" 1855 | source = "registry+https://github.com/rust-lang/crates.io-index" 1856 | checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" 1857 | dependencies = [ 1858 | "winapi", 1859 | ] 1860 | 1861 | [[package]] 1862 | name = "y4m" 1863 | version = "0.8.0" 1864 | source = "registry+https://github.com/rust-lang/crates.io-index" 1865 | checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448" 1866 | 1867 | [[package]] 1868 | name = "yeslogic-fontconfig-sys" 1869 | version = "5.0.0" 1870 | source = "registry+https://github.com/rust-lang/crates.io-index" 1871 | checksum = "ffb6b23999a8b1a997bf47c7bb4d19ad4029c3327bb3386ebe0a5ff584b33c7a" 1872 | dependencies = [ 1873 | "cstr", 1874 | "dlib", 1875 | "once_cell", 1876 | "pkg-config", 1877 | ] 1878 | 1879 | [[package]] 1880 | name = "yuvxyb" 1881 | version = "0.4.0" 1882 | source = "registry+https://github.com/rust-lang/crates.io-index" 1883 | checksum = "838b59f140accc874683473ab67aae5a5896465287652e929521c180896af0e4" 1884 | dependencies = [ 1885 | "av-data", 1886 | "log", 1887 | "nalgebra", 1888 | "num-traits", 1889 | "paste", 1890 | "thiserror", 1891 | "v_frame", 1892 | ] 1893 | 1894 | [[package]] 1895 | name = "zerocopy" 1896 | version = "0.7.35" 1897 | source = "registry+https://github.com/rust-lang/crates.io-index" 1898 | checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 1899 | dependencies = [ 1900 | "byteorder", 1901 | "zerocopy-derive", 1902 | ] 1903 | 1904 | [[package]] 1905 | name = "zerocopy-derive" 1906 | version = "0.7.35" 1907 | source = "registry+https://github.com/rust-lang/crates.io-index" 1908 | checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 1909 | dependencies = [ 1910 | "proc-macro2", 1911 | "quote", 1912 | "syn", 1913 | ] 1914 | 1915 | [[package]] 1916 | name = "zune-core" 1917 | version = "0.4.12" 1918 | source = "registry+https://github.com/rust-lang/crates.io-index" 1919 | checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" 1920 | 1921 | [[package]] 1922 | name = "zune-inflate" 1923 | version = "0.2.54" 1924 | source = "registry+https://github.com/rust-lang/crates.io-index" 1925 | checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" 1926 | dependencies = [ 1927 | "simd-adler32", 1928 | ] 1929 | 1930 | [[package]] 1931 | name = "zune-jpeg" 1932 | version = "0.4.13" 1933 | source = "registry+https://github.com/rust-lang/crates.io-index" 1934 | checksum = "16099418600b4d8f028622f73ff6e3deaabdff330fb9a2a131dea781ee8b0768" 1935 | dependencies = [ 1936 | "zune-core", 1937 | ] 1938 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ssimulacra2_rs" 3 | version = "0.5.2" 4 | edition = "2021" 5 | description = "Binary exposing the ssimulacra2 rust implementation" 6 | repository = "https://github.com/rust-av/ssimulacra2_bin" 7 | homepage = "https://github.com/rust-av/ssimulacra2_bin" 8 | license = "BSD-2-Clause" 9 | 10 | # When changing MSRV: Also update .github/workflows/rust.yml 11 | rust-version = "1.74" 12 | 13 | [dependencies] 14 | anyhow = "1.0.55" 15 | av-metrics-decoders = { version = "0.3.1", features = [ 16 | "vapoursynth", 17 | "y4m", 18 | ], optional = true } 19 | clap = { version = "4.0.18", features = ["derive"] } 20 | crossterm = "0.27.0" 21 | indicatif = "0.17.1" 22 | num-traits = { version = "0.2.15", optional = true } 23 | ssimulacra2 = { version = "0.5.0", default-features = false } 24 | statrs = { version = "0.17.0", optional = true } 25 | 26 | [dependencies.image] 27 | version = "0.25.6" 28 | default-features = false 29 | features = ["png", "jpeg", "webp", "hdr", "exr"] 30 | 31 | [dependencies.plotters] 32 | version = "0.3.4" 33 | default-features = false 34 | features = ["bitmap_backend", "area_series", "ttf"] 35 | optional = true 36 | 37 | [profile.release] 38 | lto = "thin" 39 | codegen-units = 1 40 | 41 | [features] 42 | default = ["video", "avif"] 43 | video = ["av-metrics-decoders", "plotters", "statrs", "num-traits"] 44 | avif = ["image/avif-native"] 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2022-2022, the rav1e contributors 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | - Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | - Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ssimulacra2_rs 2 | 3 | [![Crates.io](https://img.shields.io/crates/v/ssimulacra2_rs?style=for-the-badge)](https://crates.io/crates/ssimulacra2_rs) 4 | [![LICENSE](https://img.shields.io/crates/l/ssimulacra2_rs?style=for-the-badge)](https://github.com/rust-av/ssimulacra2_bin/blob/main/LICENSE) 5 | 6 | Binary interface to the Rust implementation of the SSIMULACRA2 metric: https://github.com/rust-av/ssimulacra2 7 | 8 | ## Quality Guidelines 9 | 10 | The following is a rough estimate of how ssimulacra2 scores correspond to visual quality. 11 | 12 | - 30 = low quality. This corresponds to the p10 worst output of mozjpeg -quality 30. 13 | - 50 = medium quality. This corresponds to the average output of cjxl -q 40 or mozjpeg -quality 40, or the p10 output of cjxl -q 50 or mozjpeg -quality 60. 14 | - 70 = high quality. This corresponds to the average output of cjxl -q 65 or mozjpeg -quality 70, p10 output of cjxl -q 75 or mozjpeg -quality 80. 15 | - 90 = very high quality. Likely impossible to distinguish from the original when viewed at 1:1 from a normal viewing distance. This corresponds to the average output of mozjpeg -quality 95 or the p10 output of cjxl -q 16 | 17 | ## Required packages for video support: 18 | 19 | ### Arch 20 | 21 | ```bash 22 | sudo pacman -S vapoursynth vapoursynth-plugin-lsmashsource gcc make cmake pkg-config ttf-bitstream-vera # Keep install dependencies 23 | ``` 24 | 25 | ### Other Linux 26 | 27 | See http://www.vapoursynth.com/doc/installation.html#linux-installation 28 | 29 | Install l-smash from https://github.com/l-smash/l-smash 30 | Install LSMASHSource VapourSynth plugin from https://github.com/AkarinVS/L-SMASH-Works 31 | 32 | ### Windows 33 | 34 | Do not install or download any pre-release. 35 | 1) Follow Vapoursynth's installation step http://www.vapoursynth.com/doc/installation.html#windows-installation (Not the portable installation) 36 | - If you intend to install Vapoursynth system-wide instead of local, Python will also need to be system-wide. 37 | 3) Get latest version of `VapourSynth-x64-R##.exe` 38 | 4) Run the .exe file to install VapourSynth, don't modify or change any setting if you are not familar with it 39 | 5) After Vapoursynth is installed, find its path 40 | 1. Local - `C:\Users\\AppData\Local\Programs\VapourSynth\` 41 | 2. System-wide - `C:\VapourSynth\` or `C:\Program Files\VapourSynth` 42 | 7) Then download the latest release-x86_64-cachedir-cwd.zip from https://github.com/AkarinVS/L-SMASH-Works/releases/tag/latest 43 | 8) Decompress the release-x86_64-cachedir-cwd.zip, copy and paste the libvslsmashsource.dll to `C:\Path\to\VapourSynth\plugins\` 44 | 9) Install Rust from https://www.rust-lang.org/tools/install 45 | 10) Open Powershell and run `rustc --version` to check if it has been installed 46 | 11) Copy the full path `C:\Path\to\VapourSynth\sdk\lib64` 47 | 12) Enter the command on Powershell with the copied path: `$env:LIB="C:\Path\to\VapourSynth\sdk\lib64;$env:LIB"` 48 | 13) Then enter `cargo install ssimulacra2_rs` 49 | 14) If it fails because it requires Visual Studio or Visual Studio Tools, you can download either of them. 50 | 1. Download from https://visualstudio.microsoft.com/downloads/ 51 | 2. Find the "Tools for Visual Studio" bar and download the "Remote Tools for Visual Studio 2022". 52 | 3. Make sure Desktop Development with C++ is checked, leave the optional check installation alone and download it. 53 | 4. Retry step 10 again after you reboot your PC. 54 | 15) Run `ssimulacra2_rs -h` to check if it's running. 55 | 16) You're done! 56 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "video")] 2 | mod video; 3 | 4 | #[cfg(feature = "video")] 5 | use self::video::*; 6 | use clap::{Parser, Subcommand}; 7 | #[cfg(feature = "video")] 8 | use ssimulacra2::MatrixCoefficients; 9 | use ssimulacra2::{compute_frame_ssimulacra2, ColorPrimaries, Rgb, TransferCharacteristic}; 10 | use std::path::{Path, PathBuf}; 11 | 12 | #[derive(Parser, Debug)] 13 | #[command(author, version, about, long_about = None)] 14 | #[command(propagate_version = true)] 15 | struct Cli { 16 | #[command(subcommand)] 17 | pub command: Commands, 18 | } 19 | 20 | #[derive(Subcommand, Debug)] 21 | #[allow(clippy::large_enum_variant)] 22 | enum Commands { 23 | /// Compare two still images. Resolutions must be identical. 24 | Image { 25 | /// Source image 26 | #[arg(help = "Original unmodified image", value_hint = clap::ValueHint::FilePath)] 27 | source: PathBuf, 28 | 29 | /// Distorted image 30 | #[arg(help = "Distorted image", value_hint = clap::ValueHint::FilePath)] 31 | distorted: PathBuf, 32 | }, 33 | /// Compare two videos. Resolutions and frame counts must be identical. 34 | #[cfg(feature = "video")] 35 | Video { 36 | /// Source video 37 | #[arg(help = "Original unmodified video", value_hint = clap::ValueHint::FilePath)] 38 | source: String, 39 | 40 | /// Distorted video 41 | #[arg(help = "Distorted video", value_hint = clap::ValueHint::FilePath)] 42 | distorted: String, 43 | 44 | /// How many worker threads to use for decoding & calculating scores. 45 | /// Note: Memory usage increases linearly with the number of workers. 46 | #[arg(long, short, verbatim_doc_comment)] 47 | frame_threads: Option, 48 | 49 | /// The amount of frames to skip. 50 | #[arg(long, default_value_t = 0)] 51 | skip_frames: usize, 52 | 53 | /// Limit the amount of frames to compare. 54 | #[arg(long)] 55 | frames: Option, 56 | 57 | /// How to increment current frame count; e.g. 10 will read every 10th frame. 58 | #[arg(long, short)] 59 | increment: Option, 60 | 61 | /// Whether to output a frame-by-frame graph of scores. 62 | #[arg(long, short)] 63 | graph: bool, 64 | 65 | /// Will output scores for every frame followed by the average at the end. 66 | #[arg(long, short)] 67 | verbose: bool, 68 | 69 | /// Source color matrix 70 | #[arg(long)] 71 | src_matrix: Option, 72 | 73 | /// Source transfer characteristics 74 | #[arg(long)] 75 | src_transfer: Option, 76 | 77 | /// Source color primaries 78 | #[arg(long)] 79 | src_primaries: Option, 80 | 81 | /// The source is using full-range data 82 | #[arg(long)] 83 | src_full_range: bool, 84 | 85 | /// Distorted color matrix 86 | #[arg(long)] 87 | dst_matrix: Option, 88 | 89 | /// Distorted transfer characteristics 90 | #[arg(long)] 91 | dst_transfer: Option, 92 | 93 | /// Distorted color primaries 94 | #[arg(long)] 95 | dst_primaries: Option, 96 | 97 | /// The distorted video is using full-range data 98 | #[arg(long)] 99 | dst_full_range: bool, 100 | }, 101 | } 102 | 103 | fn main() { 104 | match Cli::parse().command { 105 | Commands::Image { source, distorted } => compare_images(&source, &distorted), 106 | #[cfg(feature = "video")] 107 | Commands::Video { 108 | source, 109 | distorted, 110 | frame_threads, 111 | skip_frames, 112 | frames, 113 | increment, 114 | graph, 115 | verbose, 116 | src_matrix, 117 | src_transfer, 118 | src_primaries, 119 | src_full_range, 120 | dst_matrix, 121 | dst_transfer, 122 | dst_primaries, 123 | dst_full_range, 124 | } => { 125 | let frame_threads = frame_threads.unwrap_or(1).max(1); 126 | let inc = increment.unwrap_or(1).max(1); 127 | let src_matrix = src_matrix 128 | .map(|i| parse_matrix(&i)) 129 | .unwrap_or(MatrixCoefficients::Unspecified); 130 | let src_transfer = src_transfer 131 | .map(|i| parse_transfer(&i)) 132 | .unwrap_or(TransferCharacteristic::Unspecified); 133 | let src_primaries = src_primaries 134 | .map(|i| parse_primaries(&i)) 135 | .unwrap_or(ColorPrimaries::Unspecified); 136 | let dst_matrix = dst_matrix 137 | .map(|i| parse_matrix(&i)) 138 | .unwrap_or(MatrixCoefficients::Unspecified); 139 | let dst_transfer = dst_transfer 140 | .map(|i| parse_transfer(&i)) 141 | .unwrap_or(TransferCharacteristic::Unspecified); 142 | let dst_primaries = dst_primaries 143 | .map(|i| parse_primaries(&i)) 144 | .unwrap_or(ColorPrimaries::Unspecified); 145 | compare_videos( 146 | &source, 147 | &distorted, 148 | frame_threads, 149 | skip_frames, 150 | frames, 151 | inc, 152 | graph, 153 | verbose, 154 | src_matrix, 155 | src_transfer, 156 | src_primaries, 157 | src_full_range, 158 | dst_matrix, 159 | dst_transfer, 160 | dst_primaries, 161 | dst_full_range, 162 | ) 163 | } 164 | } 165 | } 166 | 167 | fn compare_images(source: &Path, distorted: &Path) { 168 | // For now just assumes the input is sRGB. Trying to keep this as simple as possible for now. 169 | let source = image::open(source).expect("Failed to open source file"); 170 | let distorted = image::open(distorted).expect("Failed to open distorted file"); 171 | 172 | let source_data = source 173 | .to_rgb32f() 174 | .chunks_exact(3) 175 | .map(|chunk| [chunk[0], chunk[1], chunk[2]]) 176 | .collect::>(); 177 | 178 | let source_data = Rgb::new( 179 | source_data, 180 | source.width() as usize, 181 | source.height() as usize, 182 | TransferCharacteristic::SRGB, 183 | ColorPrimaries::BT709, 184 | ) 185 | .expect("Failed to process source_data into RGB"); 186 | 187 | let distorted_data = distorted 188 | .to_rgb32f() 189 | .chunks_exact(3) 190 | .map(|chunk| [chunk[0], chunk[1], chunk[2]]) 191 | .collect::>(); 192 | 193 | let distorted_data = Rgb::new( 194 | distorted_data, 195 | distorted.width() as usize, 196 | distorted.height() as usize, 197 | TransferCharacteristic::SRGB, 198 | ColorPrimaries::BT709, 199 | ) 200 | .expect("Failed to process distorted_data into RGB"); 201 | 202 | let result = compute_frame_ssimulacra2(source_data, distorted_data) 203 | .expect("Failed to calculate ssimulacra2"); 204 | 205 | println!("Score: {result:.8}"); 206 | } 207 | -------------------------------------------------------------------------------- /src/video.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | use std::io::stderr; 3 | use std::sync::{mpsc, Arc, Mutex}; 4 | use std::time::Duration; 5 | use std::{ 6 | path::{Path, PathBuf}, 7 | time::{SystemTime, UNIX_EPOCH}, 8 | }; 9 | 10 | use av_metrics_decoders::{y4m::new_decoder_from_stdin, Decoder, VapoursynthDecoder}; 11 | use crossterm::tty::IsTty; 12 | use image::ColorType; 13 | use indicatif::{HumanDuration, ProgressBar, ProgressDrawTarget, ProgressState, ProgressStyle}; 14 | use num_traits::FromPrimitive; 15 | use ssimulacra2::{ 16 | compute_frame_ssimulacra2, ColorPrimaries, MatrixCoefficients, Pixel, TransferCharacteristic, 17 | Yuv, YuvConfig, 18 | }; 19 | use statrs::statistics::{Data, Distribution, Median, OrderStatistics}; 20 | 21 | const PROGRESS_CHARS: &str = "█▉▊▋▌▍▎▏ "; 22 | const INDICATIF_PROGRESS_TEMPLATE: &str = if cfg!(windows) { 23 | // Do not use a spinner on Windows since the default console cannot display 24 | // the characters used for the spinner 25 | "{elapsed_precise:.bold} ▕{wide_bar:.blue/white.dim}▏ {percent:.bold} {pos} ({fps:.bold}, eta {fixed_eta}{msg})" 26 | } else { 27 | "{spinner:.green.bold} {elapsed_precise:.bold} ▕{wide_bar:.blue/white.dim}▏ {percent:.bold} {pos} ({fps:.bold}, eta {fixed_eta}{msg})" 28 | }; 29 | 30 | const INDICATIF_SPINNER_TEMPLATE: &str = if cfg!(windows) { 31 | // Do not use a spinner on Windows since the default console cannot display 32 | // the characters used for the spinner 33 | "{elapsed_precise:.bold} {pos} ({fps:.bold}{msg})" 34 | } else { 35 | "{spinner:.green.bold} {elapsed_precise:.bold} {pos} ({fps:.bold}{msg})" 36 | }; 37 | 38 | fn pretty_progress_style() -> ProgressStyle { 39 | ProgressStyle::default_bar() 40 | .template(INDICATIF_PROGRESS_TEMPLATE) 41 | .unwrap() 42 | .with_key( 43 | "fps", 44 | |state: &ProgressState, w: &mut dyn std::fmt::Write| { 45 | if state.pos() == 0 || state.elapsed().as_secs_f32() < f32::EPSILON { 46 | write!(w, "0 fps").unwrap(); 47 | } else { 48 | let fps = state.pos() as f32 / state.elapsed().as_secs_f32(); 49 | if fps < 1.0 { 50 | write!(w, "{:.2} s/fr", 1.0 / fps).unwrap(); 51 | } else { 52 | write!(w, "{:.2} fps", fps).unwrap(); 53 | } 54 | } 55 | }, 56 | ) 57 | .with_key( 58 | "fixed_eta", 59 | |state: &ProgressState, w: &mut dyn std::fmt::Write| { 60 | if state.pos() == 0 || state.elapsed().as_secs_f32() < f32::EPSILON { 61 | write!(w, "unknown").unwrap(); 62 | } else { 63 | let spf = state.elapsed().as_secs_f32() / state.pos() as f32; 64 | let remaining = state.len().unwrap_or(0) - state.pos(); 65 | write!( 66 | w, 67 | "{:#}", 68 | HumanDuration(Duration::from_secs_f32(spf * remaining as f32)) 69 | ) 70 | .unwrap(); 71 | } 72 | }, 73 | ) 74 | .with_key( 75 | "pos", 76 | |state: &ProgressState, w: &mut dyn std::fmt::Write| { 77 | write!(w, "{}/{}", state.pos(), state.len().unwrap_or(0)).unwrap(); 78 | }, 79 | ) 80 | .with_key( 81 | "percent", 82 | |state: &ProgressState, w: &mut dyn std::fmt::Write| { 83 | write!(w, "{:>3.0}%", state.fraction() * 100_f32).unwrap(); 84 | }, 85 | ) 86 | .progress_chars(PROGRESS_CHARS) 87 | } 88 | 89 | fn pretty_spinner_style() -> ProgressStyle { 90 | ProgressStyle::default_bar() 91 | .template(INDICATIF_SPINNER_TEMPLATE) 92 | .unwrap() 93 | .with_key( 94 | "fps", 95 | |state: &ProgressState, w: &mut dyn std::fmt::Write| { 96 | if state.pos() == 0 || state.elapsed().as_secs_f32() < f32::EPSILON { 97 | write!(w, "0 fps").unwrap(); 98 | } else { 99 | let fps = state.pos() as f32 / state.elapsed().as_secs_f32(); 100 | if fps < 1.0 { 101 | write!(w, "{:.2} s/fr", 1.0 / fps).unwrap(); 102 | } else { 103 | write!(w, "{:.2} fps", fps).unwrap(); 104 | } 105 | } 106 | }, 107 | ) 108 | .with_key( 109 | "pos", 110 | |state: &ProgressState, w: &mut dyn std::fmt::Write| { 111 | write!(w, "{}", state.pos()).unwrap(); 112 | }, 113 | ) 114 | .progress_chars(PROGRESS_CHARS) 115 | } 116 | 117 | type VideoCompareMutex = Arc>>; 118 | 119 | struct VideoCompare { 120 | current_frame: usize, 121 | next_frame: usize, 122 | source: E, 123 | distorted: F, 124 | } 125 | 126 | fn calc_score( 127 | mtx: &VideoCompareMutex, 128 | src_yuvcfg: &YuvConfig, 129 | dst_yuvcfg: &YuvConfig, 130 | inc: usize, 131 | end_frame: Option, 132 | verbose: bool, 133 | ) -> Option<(usize, f64)> { 134 | let (frame_idx, (src_frame, dst_frame)) = { 135 | let mut guard = mtx.lock().unwrap(); 136 | 137 | let distance_to_next = guard.next_frame - guard.current_frame; 138 | 139 | if let Some(end_frame) = end_frame { 140 | if guard.next_frame >= end_frame { 141 | return None; 142 | } 143 | } 144 | 145 | for ii in 1..distance_to_next { 146 | let _src_frame = guard.source.read_video_frame::(); 147 | let _dst_frame = guard.distorted.read_video_frame::(); 148 | if _src_frame.is_none() || _dst_frame.is_none() { 149 | break; 150 | } 151 | if verbose { 152 | println!("Frame {}: skip", guard.current_frame + ii); 153 | } 154 | } 155 | 156 | let src_frame = guard.source.read_video_frame::(); 157 | let dst_frame = guard.distorted.read_video_frame::(); 158 | 159 | let curr_frame = guard.next_frame; 160 | guard.current_frame = guard.next_frame; 161 | guard.next_frame += inc; 162 | 163 | if let (Some(sf), Some(df)) = (src_frame, dst_frame) { 164 | (curr_frame, (sf, df)) 165 | } else { 166 | return None; 167 | } 168 | }; 169 | 170 | let src_yuv = Yuv::new(src_frame, *src_yuvcfg).unwrap(); 171 | let dst_yuv = Yuv::new(dst_frame, *dst_yuvcfg).unwrap(); 172 | 173 | Some(( 174 | frame_idx, 175 | compute_frame_ssimulacra2(src_yuv, dst_yuv).expect("Failed to calculate ssimulacra2"), 176 | )) 177 | } 178 | 179 | #[allow(clippy::too_many_arguments)] 180 | pub fn compare_videos( 181 | source: &str, 182 | distorted: &str, 183 | frame_threads: usize, 184 | skip_frames: usize, 185 | frames_to_compare: Option, 186 | inc: usize, 187 | graph: bool, 188 | verbose: bool, 189 | src_matrix: MatrixCoefficients, 190 | src_transfer: TransferCharacteristic, 191 | src_primaries: ColorPrimaries, 192 | src_full_range: bool, 193 | dst_matrix: MatrixCoefficients, 194 | dst_transfer: TransferCharacteristic, 195 | dst_primaries: ColorPrimaries, 196 | dst_full_range: bool, 197 | ) { 198 | if source == "-" || source == "/dev/stdin" { 199 | assert!( 200 | !(distorted == "-" || distorted == "/dev/stdin"), 201 | "Source and distorted inputs cannot both be from piped input" 202 | ); 203 | let distorted = if Path::new(distorted) 204 | .extension() 205 | .map(|ext| ext.to_ascii_lowercase().to_string_lossy() == "vpy") 206 | .unwrap_or(false) 207 | { 208 | VapoursynthDecoder::new_from_script(Path::new(distorted)).unwrap() 209 | } else { 210 | VapoursynthDecoder::new_from_video(Path::new(distorted)).unwrap() 211 | }; 212 | let distorted_frame_count = distorted.get_frame_count().ok(); 213 | return compare_videos_inner( 214 | new_decoder_from_stdin().unwrap(), 215 | distorted, 216 | None, 217 | distorted_frame_count, 218 | frame_threads, 219 | skip_frames, 220 | frames_to_compare, 221 | inc, 222 | graph, 223 | verbose, 224 | src_matrix, 225 | src_transfer, 226 | src_primaries, 227 | src_full_range, 228 | dst_matrix, 229 | dst_transfer, 230 | dst_primaries, 231 | dst_full_range, 232 | ); 233 | } 234 | 235 | if distorted == "-" || distorted == "/dev/stdin" { 236 | let source = if Path::new(source) 237 | .extension() 238 | .map(|ext| ext.to_ascii_lowercase().to_string_lossy() == "vpy") 239 | .unwrap_or(false) 240 | { 241 | VapoursynthDecoder::new_from_script(Path::new(source)).unwrap() 242 | } else { 243 | VapoursynthDecoder::new_from_video(Path::new(source)).unwrap() 244 | }; 245 | let source_frame_count = source.get_frame_count().ok(); 246 | return compare_videos_inner( 247 | source, 248 | new_decoder_from_stdin().unwrap(), 249 | source_frame_count, 250 | None, 251 | frame_threads, 252 | skip_frames, 253 | frames_to_compare, 254 | inc, 255 | graph, 256 | verbose, 257 | src_matrix, 258 | src_transfer, 259 | src_primaries, 260 | src_full_range, 261 | dst_matrix, 262 | dst_transfer, 263 | dst_primaries, 264 | dst_full_range, 265 | ); 266 | } 267 | 268 | let source = if Path::new(source) 269 | .extension() 270 | .map(|ext| ext.to_ascii_lowercase().to_string_lossy() == "vpy") 271 | .unwrap_or(false) 272 | { 273 | VapoursynthDecoder::new_from_script(Path::new(source)).unwrap() 274 | } else { 275 | VapoursynthDecoder::new_from_video(Path::new(source)).unwrap() 276 | }; 277 | let distorted = if Path::new(distorted) 278 | .extension() 279 | .map(|ext| ext.to_ascii_lowercase().to_string_lossy() == "vpy") 280 | .unwrap_or(false) 281 | { 282 | VapoursynthDecoder::new_from_script(Path::new(distorted)).unwrap() 283 | } else { 284 | VapoursynthDecoder::new_from_video(Path::new(distorted)).unwrap() 285 | }; 286 | let source_frame_count = source.get_frame_count().ok(); 287 | let distorted_frame_count = distorted.get_frame_count().ok(); 288 | compare_videos_inner( 289 | source, 290 | distorted, 291 | source_frame_count, 292 | distorted_frame_count, 293 | frame_threads, 294 | skip_frames, 295 | frames_to_compare, 296 | inc, 297 | graph, 298 | verbose, 299 | src_matrix, 300 | src_transfer, 301 | src_primaries, 302 | src_full_range, 303 | dst_matrix, 304 | dst_transfer, 305 | dst_primaries, 306 | dst_full_range, 307 | ) 308 | } 309 | 310 | #[allow(clippy::too_many_arguments)] 311 | fn compare_videos_inner( 312 | source: D, 313 | distorted: E, 314 | source_frame_count: Option, 315 | distorted_frame_count: Option, 316 | frame_threads: usize, 317 | skip_frames: usize, 318 | frames_to_compare: Option, 319 | inc: usize, 320 | graph: bool, 321 | verbose: bool, 322 | mut src_matrix: MatrixCoefficients, 323 | mut src_transfer: TransferCharacteristic, 324 | mut src_primaries: ColorPrimaries, 325 | src_full_range: bool, 326 | mut dst_matrix: MatrixCoefficients, 327 | mut dst_transfer: TransferCharacteristic, 328 | mut dst_primaries: ColorPrimaries, 329 | dst_full_range: bool, 330 | ) { 331 | if let Some(source_frame_count) = source_frame_count { 332 | if let Some(distorted_frame_count) = distorted_frame_count { 333 | if source_frame_count != distorted_frame_count { 334 | eprintln!("WARNING: Frame count mismatch detected, scores may be inaccurate"); 335 | } 336 | } 337 | } 338 | 339 | let source_info = source.get_video_details(); 340 | let distorted_info = distorted.get_video_details(); 341 | if src_matrix == MatrixCoefficients::Unspecified { 342 | src_matrix = guess_matrix_coefficients(source_info.width, source_info.height); 343 | } 344 | if dst_matrix == MatrixCoefficients::Unspecified { 345 | dst_matrix = guess_matrix_coefficients(distorted_info.width, distorted_info.height); 346 | } 347 | if src_transfer == TransferCharacteristic::Unspecified { 348 | src_transfer = TransferCharacteristic::BT1886; 349 | } 350 | if dst_transfer == TransferCharacteristic::Unspecified { 351 | dst_transfer = TransferCharacteristic::BT1886; 352 | } 353 | if src_primaries == ColorPrimaries::Unspecified { 354 | src_primaries = guess_color_primaries(src_matrix, source_info.width, source_info.height); 355 | } 356 | if dst_primaries == ColorPrimaries::Unspecified { 357 | dst_primaries = 358 | guess_color_primaries(dst_matrix, distorted_info.width, distorted_info.height); 359 | } 360 | 361 | let src_ss = source_info 362 | .chroma_sampling 363 | .get_decimation() 364 | .unwrap_or((0, 0)); 365 | let dist_ss = distorted_info 366 | .chroma_sampling 367 | .get_decimation() 368 | .unwrap_or((0, 0)); 369 | let src_config = YuvConfig { 370 | bit_depth: source_info.bit_depth as u8, 371 | subsampling_x: src_ss.0 as u8, 372 | subsampling_y: src_ss.1 as u8, 373 | full_range: src_full_range, 374 | matrix_coefficients: src_matrix, 375 | transfer_characteristics: src_transfer, 376 | color_primaries: src_primaries, 377 | }; 378 | let dst_config = YuvConfig { 379 | bit_depth: distorted_info.bit_depth as u8, 380 | subsampling_x: dist_ss.0 as u8, 381 | subsampling_y: dist_ss.1 as u8, 382 | full_range: dst_full_range, 383 | matrix_coefficients: dst_matrix, 384 | transfer_characteristics: dst_transfer, 385 | color_primaries: dst_primaries, 386 | }; 387 | 388 | let (result_tx, result_rx) = mpsc::channel(); 389 | let src_bd = src_config.bit_depth; 390 | let dst_bd = dst_config.bit_depth; 391 | 392 | let current_frame = 0usize; 393 | let end_frame = frames_to_compare 394 | .map(|frames_to_compare| skip_frames + (frames_to_compare * inc)); 395 | 396 | let video_compare = Arc::new( 397 | Mutex::new( 398 | VideoCompare { 399 | current_frame, 400 | next_frame: skip_frames, 401 | source, 402 | distorted, 403 | } 404 | ) 405 | ); 406 | 407 | for _ in 0..frame_threads { 408 | let video_compare = Arc::clone(&video_compare); 409 | let result_tx = result_tx.clone(); 410 | 411 | std::thread::spawn(move || { 412 | loop { 413 | let score = match (src_bd, dst_bd) { 414 | (8, 8) => calc_score::( 415 | &video_compare, 416 | &src_config, 417 | &dst_config, 418 | inc, 419 | end_frame, 420 | verbose, 421 | ), 422 | (8, _) => calc_score::( 423 | &video_compare, 424 | &src_config, 425 | &dst_config, 426 | inc, 427 | end_frame, 428 | verbose, 429 | ), 430 | (_, 8) => calc_score::( 431 | &video_compare, 432 | &src_config, 433 | &dst_config, 434 | inc, 435 | end_frame, 436 | verbose, 437 | ), 438 | (_, _) => calc_score::( 439 | &video_compare, 440 | &src_config, 441 | &dst_config, 442 | inc, 443 | end_frame, 444 | verbose, 445 | ), 446 | }; 447 | 448 | if let Some(result) = score { 449 | result_tx.send(result).unwrap(); 450 | } else { 451 | // no score = no more frames to read 452 | break; 453 | } 454 | } 455 | }); 456 | } 457 | 458 | // Needs to be dropped or the main thread never stops waiting for scores 459 | drop(result_tx); 460 | 461 | let progress = if stderr().is_tty() && !verbose { 462 | let frame_count = source_frame_count.or(distorted_frame_count); 463 | let pb = if let Some(frame_count) = frame_count { 464 | let fc = frames_to_compare.unwrap_or(frame_count - skip_frames) 465 | .min(((frame_count - skip_frames) as f64 / inc as f64).ceil() as usize); 466 | 467 | ProgressBar::new(fc as u64) 468 | .with_style(pretty_progress_style()) 469 | .with_message(", avg: N/A") 470 | } else { 471 | ProgressBar::new_spinner().with_style(pretty_spinner_style()) 472 | }; 473 | pb.set_draw_target(ProgressDrawTarget::stderr()); 474 | pb.enable_steady_tick(Duration::from_millis(100)); 475 | pb.reset(); 476 | pb.reset_eta(); 477 | pb.reset_elapsed(); 478 | pb.set_position(0); 479 | pb 480 | } else { 481 | ProgressBar::hidden() 482 | }; 483 | 484 | let mut results = BTreeMap::new(); 485 | let mut rolling_mean = 0f64; 486 | for score in result_rx { 487 | if verbose { 488 | println!("Frame {}: {:.8}", score.0, score.1); 489 | } 490 | 491 | results.insert(score.0, score.1); 492 | rolling_mean = rolling_mean + (score.1 - rolling_mean) / (results.len() as f64); 493 | progress.set_message(format!(", mean: {rolling_mean:.2}")); 494 | progress.inc(1); 495 | } 496 | 497 | progress.finish(); 498 | 499 | let results: Vec = results.into_values().collect(); 500 | let frames = results.len(); 501 | let mut data = Data::new(results.clone()); 502 | println!("Video Score for {} frames", frames); 503 | println!("Mean: {:.8}", data.mean().unwrap()); 504 | println!("Median: {:.8}", data.median()); 505 | println!("Std Dev: {:.8}", data.std_dev().unwrap()); 506 | println!("5th Percentile: {:.8}", data.percentile(5)); 507 | println!("95th Percentile: {:.8}", data.percentile(95)); 508 | 509 | if graph { 510 | use plotters::prelude::*; 511 | 512 | let out_path = PathBuf::from(format!( 513 | "ssimulacra2-video-{}.png", 514 | SystemTime::now() 515 | .duration_since(UNIX_EPOCH) 516 | .unwrap() 517 | .as_secs() 518 | )); 519 | let width = 1500u32; 520 | let height = 1000u32; 521 | let mut image_buffer = vec![0; (width * height * 3) as usize].into_boxed_slice(); 522 | 523 | { 524 | let root = 525 | BitMapBackend::with_buffer(&mut image_buffer, (width, height)).into_drawing_area(); 526 | root.fill(&BLACK).unwrap(); 527 | let mut chart = ChartBuilder::on(&root) 528 | .set_label_area_size(LabelAreaPosition::Left, 60) 529 | .set_label_area_size(LabelAreaPosition::Bottom, 60) 530 | .caption("SSIMULACRA2", ("sans-serif", 50.0)) 531 | .build_cartesian_2d(0..frames, 0f32..100f32) 532 | .unwrap(); 533 | chart 534 | .configure_mesh() 535 | .disable_x_mesh() 536 | .bold_line_style(WHITE.mix(0.3)) 537 | .y_desc("Score") 538 | .y_label_style(("sans-serif", 16, &WHITE)) 539 | .x_desc("Frame") 540 | .x_label_style(("sans-serif", 16, &WHITE)) 541 | .axis_desc_style(("sans-serif", 18, &WHITE)) 542 | .draw() 543 | .unwrap(); 544 | chart 545 | .draw_series( 546 | AreaSeries::new( 547 | results.into_iter().enumerate().map(|(i, v)| (i, v as f32)), 548 | 0.0, 549 | CYAN.mix(0.5), 550 | ) 551 | .border_style(CYAN.filled()), 552 | ) 553 | .unwrap(); 554 | root.present().expect("Unable to generate image"); 555 | } 556 | 557 | image::save_buffer(&out_path, &image_buffer, width, height, ColorType::Rgb8) 558 | .expect("Unable to save graph image"); 559 | 560 | println!(); 561 | println!("Graph written to {}", out_path.to_string_lossy()); 562 | } 563 | } 564 | 565 | pub fn parse_matrix(input: &str) -> MatrixCoefficients { 566 | if let Ok(intval) = input.parse::() { 567 | if intval <= MatrixCoefficients::ICtCp as u8 { 568 | return MatrixCoefficients::from_u8(intval).expect("Invalid matrix coefficient value"); 569 | } 570 | } 571 | 572 | match input.to_ascii_lowercase().as_str() { 573 | "identity" | "rgb" | "srgb" | "smpte428" | "xyz" => MatrixCoefficients::Identity, 574 | "709" | "bt709" => MatrixCoefficients::BT709, 575 | "unspecified" => MatrixCoefficients::Unspecified, 576 | "bt470m" | "470m" => MatrixCoefficients::BT470M, 577 | "bt470bg" | "470bg" | "601-625" | "bt601-625" | "pal" => MatrixCoefficients::BT470BG, 578 | "smpte170m" | "170m" | "601-525" | "bt601-525" | "bt601" | "601" | "ntsc" => { 579 | MatrixCoefficients::ST170M 580 | } 581 | "240m" | "smpte240m" => MatrixCoefficients::ST240M, 582 | "ycgco" => MatrixCoefficients::YCgCo, 583 | "2020" | "2020ncl" | "2020-ncl" | "bt2020" | "bt2020ncl" | "bt2020-ncl" => { 584 | MatrixCoefficients::BT2020NonConstantLuminance 585 | } 586 | "2020cl" | "2020-cl" | "bt2020cl" | "bt2020-cl" => { 587 | MatrixCoefficients::BT2020ConstantLuminance 588 | } 589 | "2085" | "smpte2085" => MatrixCoefficients::ST2085, 590 | "cd-ncl" => MatrixCoefficients::ChromaticityDerivedNonConstantLuminance, 591 | "cd-cl" => MatrixCoefficients::ChromaticityDerivedConstantLuminance, 592 | "2100" | "bt2100" | "ictcp" => MatrixCoefficients::ICtCp, 593 | _ => panic!("Unrecognized matrix coefficient string"), 594 | } 595 | } 596 | 597 | pub fn parse_transfer(input: &str) -> TransferCharacteristic { 598 | if let Ok(intval) = input.parse::() { 599 | if intval <= TransferCharacteristic::HybridLogGamma as u8 { 600 | return TransferCharacteristic::from_u8(intval) 601 | .expect("Invalid transfer characteristics value"); 602 | } 603 | } 604 | 605 | match input.to_ascii_lowercase().as_str() { 606 | "709" | "bt709" | "1886" | "bt1886" | "1361" | "bt1361" => TransferCharacteristic::BT1886, 607 | "unspecified" => TransferCharacteristic::Unspecified, 608 | "470m" | "bt470m" | "pal" => TransferCharacteristic::BT470M, 609 | "470bg" | "bt470bg" => TransferCharacteristic::BT470BG, 610 | "601" | "bt601" | "ntsc" | "smpte170m" | "170m" | "1358" | "bt1358" | "1700" | "bt1700" => { 611 | TransferCharacteristic::ST170M 612 | } 613 | "240m" | "smpte240m" => TransferCharacteristic::ST240M, 614 | "linear" => TransferCharacteristic::Linear, 615 | "log100" => TransferCharacteristic::Logarithmic100, 616 | "log316" => TransferCharacteristic::Logarithmic316, 617 | "xvycc" => TransferCharacteristic::XVYCC, 618 | "1361e" | "bt1361e" => TransferCharacteristic::BT1361E, 619 | "srgb" => TransferCharacteristic::SRGB, 620 | "2020" | "bt2020" | "2020-10" | "bt2020-10" => TransferCharacteristic::BT2020Ten, 621 | "2020-12" | "bt2020-12" => TransferCharacteristic::BT2020Twelve, 622 | "pq" | "2084" | "smpte2084" | "2100" | "bt2100" => { 623 | TransferCharacteristic::PerceptualQuantizer 624 | } 625 | "428" | "smpte428" => TransferCharacteristic::ST428, 626 | "hlg" | "b67" | "arib-b67" => TransferCharacteristic::HybridLogGamma, 627 | _ => panic!("Unrecognized transfer characteristics string"), 628 | } 629 | } 630 | 631 | pub fn parse_primaries(input: &str) -> ColorPrimaries { 632 | if let Ok(intval) = input.parse::() { 633 | if intval <= ColorPrimaries::Tech3213 as u8 { 634 | return ColorPrimaries::from_u8(intval).expect("Invalid color primaries value"); 635 | } 636 | } 637 | 638 | match input.to_ascii_lowercase().as_str() { 639 | "709" | "bt709" | "1361" | "bt1361" | "srgb" => ColorPrimaries::BT709, 640 | "unspecified" => ColorPrimaries::Unspecified, 641 | "470m" | "bt470m" => ColorPrimaries::BT470M, 642 | "470bg" | "bt470bg" | "601-625" | "bt601-625" | "pal" => ColorPrimaries::BT470BG, 643 | "smpte170m" | "170m" | "601-525" | "bt601-525" | "bt601" | "601" | "ntsc" => { 644 | ColorPrimaries::ST170M 645 | } 646 | "240m" | "smpte240m" => ColorPrimaries::ST240M, 647 | "film" | "c" => ColorPrimaries::Film, 648 | "2020" | "bt2020" | "2100" | "bt2100" => ColorPrimaries::BT2020, 649 | "428" | "smpte428" | "xyz" => ColorPrimaries::ST428, 650 | "p3" | "p3dci" | "p3-dci" | "431" | "smpte431" => ColorPrimaries::P3DCI, 651 | "p3display" | "p3-display" | "432" | "smpte432" => ColorPrimaries::P3Display, 652 | "3213" | "tech3213" => ColorPrimaries::Tech3213, 653 | _ => panic!("Unrecognized color primaries string"), 654 | } 655 | } 656 | 657 | pub const fn guess_matrix_coefficients(width: usize, height: usize) -> MatrixCoefficients { 658 | if width >= 1280 || height > 576 { 659 | MatrixCoefficients::BT709 660 | } else if height == 576 { 661 | MatrixCoefficients::BT470BG 662 | } else { 663 | MatrixCoefficients::ST170M 664 | } 665 | } 666 | 667 | // Heuristic taken from mpv 668 | pub fn guess_color_primaries( 669 | matrix: MatrixCoefficients, 670 | width: usize, 671 | height: usize, 672 | ) -> ColorPrimaries { 673 | if matrix == MatrixCoefficients::BT2020NonConstantLuminance 674 | || matrix == MatrixCoefficients::BT2020ConstantLuminance 675 | { 676 | ColorPrimaries::BT2020 677 | } else if matrix == MatrixCoefficients::BT709 || width >= 1280 || height > 576 { 678 | ColorPrimaries::BT709 679 | } else if height == 576 { 680 | ColorPrimaries::BT470BG 681 | } else if height == 480 || height == 488 { 682 | ColorPrimaries::ST170M 683 | } else { 684 | ColorPrimaries::BT709 685 | } 686 | } 687 | --------------------------------------------------------------------------------