diff --git a/Cargo.lock b/Cargo.lock index 66a23d4..26d2ac8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "ansi_colours" version = "1.2.2" @@ -138,12 +153,32 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "cc" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.5", +] + [[package]] name = "clap" version = "4.5.4" @@ -208,6 +243,58 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "core-text" +version = "20.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9d2790b5c08465d49f8dc05c8bcae9fea467855947db39b0f8145c091aaced5" +dependencies = [ + "core-foundation", + "core-graphics", + "foreign-types", + "libc", +] + [[package]] name = "crc32fast" version = "1.3.2" @@ -291,6 +378,58 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "cstr" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68523903c8ae5aacfa32a0d9ae60cadeb764e1da14ee0d26b1f3089f13a54636" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading", +] + +[[package]] +name = "dwrote" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" +dependencies = [ + "lazy_static", + "libc", + "winapi", + "wio", +] + [[package]] name = "either" version = "1.8.0" @@ -367,6 +506,12 @@ dependencies = [ "miniz_oxide 0.5.4", ] +[[package]] +name = "float-ord" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d" + [[package]] name = "flume" version = "0.10.14" @@ -380,6 +525,69 @@ dependencies = [ "spin", ] +[[package]] +name = "font-kit" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2845a73bbd781e691ab7c2a028c579727cd254942e8ced57ff73e0eafd60de87" +dependencies = [ + "bitflags 2.5.0", + "byteorder", + "core-foundation", + "core-graphics", + "core-text", + "dirs-next", + "dwrote", + "float-ord", + "freetype-sys", + "lazy_static", + "libc", + "log", + "pathfinder_geometry", + "pathfinder_simd", + "walkdir", + "winapi", + "yeslogic-fontconfig-sys", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.64", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "freetype-sys" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7edc5b9669349acfda99533e9e0bcf26a51862ab43b08ee7745c55d28eb134" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "futures-core" version = "0.3.25" @@ -415,6 +623,16 @@ dependencies = [ "weezl", ] +[[package]] +name = "gif" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "half" version = "2.1.0" @@ -445,6 +663,29 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "image" version = "0.24.4" @@ -455,7 +696,7 @@ dependencies = [ "byteorder", "color_quant", "exr", - "gif", + "gif 0.11.4", "jpeg-decoder", "num-rational", "num-traits", @@ -474,6 +715,7 @@ dependencies = [ "env_logger", "image", "log", + "plotters", "rmp-serde", "serde", "viuer", @@ -496,9 +738,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -521,6 +763,26 @@ version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +[[package]] +name = "libloading" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +dependencies = [ + "cfg-if", + "windows-targets 0.52.5", +] + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.5.0", + "libc", +] + [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -715,6 +977,25 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" +[[package]] +name = "pathfinder_geometry" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3" +dependencies = [ + "log", + "pathfinder_simd", +] + +[[package]] +name = "pathfinder_simd" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebf45976c56919841273f2a0fc684c28437e2f304e264557d9c72be5d5a718be" +dependencies = [ + "rustc_version", +] + [[package]] name = "pin-project" version = "1.0.12" @@ -735,6 +1016,58 @@ dependencies = [ "syn 1.0.103", ] +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "plotters" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" +dependencies = [ + "chrono", + "font-kit", + "image", + "lazy_static", + "num-traits", + "pathfinder_geometry", + "plotters-backend", + "plotters-bitmap", + "plotters-svg", + "ttf-parser", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" + +[[package]] +name = "plotters-bitmap" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7e7f6fb8302456d7c264a94dada86f76d76e1a03e2294ee86ca7da92983b0a6" +dependencies = [ + "gif 0.12.0", + "image", + "plotters-backend", +] + +[[package]] +name = "plotters-svg" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705" +dependencies = [ + "plotters-backend", +] + [[package]] name = "png" version = "0.17.7" @@ -798,6 +1131,17 @@ dependencies = [ "bitflags 2.5.0", ] +[[package]] +name = "redox_users" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "regex" version = "1.10.4" @@ -858,6 +1202,15 @@ dependencies = [ "serde", ] +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.38.34" @@ -871,6 +1224,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scoped_threadpool" version = "0.1.9" @@ -883,6 +1245,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + [[package]] name = "serde" version = "1.0.147" @@ -983,6 +1351,26 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "thiserror" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.64", +] + [[package]] name = "threadpool" version = "1.8.1" @@ -1003,6 +1391,12 @@ dependencies = [ "weezl", ] +[[package]] +name = "ttf-parser" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" + [[package]] name = "unicode-ident" version = "1.0.5" @@ -1031,6 +1425,16 @@ dependencies = [ "termcolor", ] +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1039,9 +1443,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1049,24 +1453,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.103", + "syn 2.0.64", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1074,22 +1478,32 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 1.0.103", + "syn 2.0.64", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] [[package]] name = "weezl" @@ -1128,6 +1542,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.5", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -1266,3 +1689,24 @@ name = "windows_x86_64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "wio" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" +dependencies = [ + "winapi", +] + +[[package]] +name = "yeslogic-fontconfig-sys" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb6b23999a8b1a997bf47c7bb4d19ad4029c3327bb3386ebe0a5ff584b33c7a" +dependencies = [ + "cstr", + "dlib", + "once_cell", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml index 9d7e515..a5ba1b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,3 +15,4 @@ env_logger = "0.11.3" bktree = "1.0.1" viuer = "0.7.1" clap = { version = "4.5.4", features = ["derive"] } +plotters = "0.3.6" diff --git a/src/bin/dctquery.rs b/src/bin/dctquery.rs index 3db6f9d..7915382 100644 --- a/src/bin/dctquery.rs +++ b/src/bin/dctquery.rs @@ -12,7 +12,7 @@ struct Cfg { /// Try to output the images to terminal with viuer #[arg(short, long, default_value_t=false)] show_images: bool, - distance: isize, + distance: usize, } fn main() { diff --git a/src/descriptors/dct.rs b/src/descriptors/dct.rs index bc3169a..50dcd3d 100644 --- a/src/descriptors/dct.rs +++ b/src/descriptors/dct.rs @@ -108,7 +108,7 @@ impl Descriptor for DCT { } fn describe(&self, img: &image::DynamicImage) -> u64 { let resized = self.resize(img); - let mut dct_values = self.dct(&resized); + let dct_values = self.dct(&resized); debug!("DCT-coefficients:\n {}", print_matrix(dct_values)); // Quantization: diff --git a/src/descriptors/mod.rs b/src/descriptors/mod.rs index ee01e49..5e0e747 100644 --- a/src/descriptors/mod.rs +++ b/src/descriptors/mod.rs @@ -76,6 +76,6 @@ impl Descriptor for Median { pub fn get_all_descriptors() -> Vec> { let mut descriptors: Vec> = Vec::with_capacity(2); descriptors.push(Box::new(DCT::new())); - //descriptors.push(Box::new(Median)); + descriptors.push(Box::new(Median)); descriptors } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 33866f8..67cfef0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,11 @@ use image_similarity::store::DescriptorStore; use image_similarity::descriptors::get_all_descriptors; use image_similarity::mutators::get_all_mutators; -use log::{error, info}; +use log::{error}; use std::path::PathBuf; -use std::{fs, thread}; +use std::{fs}; use clap::Parser; +use plotters::prelude::*; #[derive(Parser)] #[command(version, about, long_about = None)] @@ -21,7 +22,8 @@ fn main() { let mut stores: Vec = Vec::new(); for descriptor in get_all_descriptors() { - let store = DescriptorStore::new(descriptor); + let filename = format!("{}.store", descriptor.info()); + let store = DescriptorStore::new(descriptor).with_file(filename); stores.push(store); } @@ -35,34 +37,84 @@ fn main() { continue } }; - let img = match image::open(file.path()) { - Ok(v) => v, - Err(e) => { - error!("Failed to process {}: {}", name, e); - continue - } - }; - //Store the phashes of the base image - for store in &mut stores { - store.store(&img, name.clone()); + let mut get = true; + for store in &stores { + if store.has_value(&name) { + get = false; + break; + } } - for mutator in &mutators { - let mutated = mutator.mutate(&img); - let mutated_name = "mut".to_string() + &mutator.tag() + &name.clone(); + // We assume that if a base image exists in the store, the mutated images also exist + if get { + let img = match image::open(file.path()) { + Ok(v) => v, + Err(e) => { + error!("Failed to process {}: {}", name, e); + continue + } + }; + + //Store the phashes of the base image for store in &mut stores { - store.store(&mutated, mutated_name.clone()); + store.store(&img, name.clone()); + } + + for mutator in &mutators { + let mutated = mutator.mutate(&img); + let mutated_name = "mut".to_string() + &mutator.tag() + &name.clone(); + for store in &mut stores { + store.store(&mutated, mutated_name.clone()); + } } } } for store in &stores { + store.save().expect("Error saving store"); println!("{}", store); } + // Create PR-curve graphs from stores for store in &stores { println!("Store for {}: ", store.descriptor.info()); - store.print_stats(); + //store.print_stats(); + let stats = store.get_stats(16); + let filename = format!("{}-pr.png", store.descriptor.info()); + let root = BitMapBackend::new(&filename, (1024, 768)).into_drawing_area(); + root.fill(&WHITE).unwrap(); + let mut chart = ChartBuilder::on(&root) + // Set the caption of the chart + .caption("PR curves", ("sans-serif", 40).into_font()) + .margin(25) + .set_all_label_area_size(50) + // Finally attach a coordinate on the drawing area and make a chart context + .build_cartesian_2d(0f64..1f64, 0f64..1f64).unwrap(); + chart.configure_mesh() + .x_labels(10) + .y_labels(10) + .disable_mesh() + .x_desc("Recall") + .y_desc("Precision") + .x_label_formatter(&|x| format!("{:.3}", x)) + .y_label_formatter(&|x| format!("{:.3}", x)) + .draw().unwrap(); + let colors = [&RED, &BLUE, &CYAN, &MAGENTA, &BLACK, &GREEN, &YELLOW]; + let n = colors.len(); + let mut i = 0; + for mutator_stats in stats { + // And we can draw something in the drawing area + let pr_curve = mutator_stats.pr_curve(); + let color = colors[i % n]; + chart.draw_series(LineSeries::new( + pr_curve, + color, + )).unwrap() + .label(mutator_stats.name) + .legend(move |(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], color)); + i += 1; + } + chart.configure_series_labels().border_style(BLACK).draw().unwrap(); //println!("{}", store); } } diff --git a/src/store.rs b/src/store.rs index 3db0bb3..8cfd2dc 100644 --- a/src/store.rs +++ b/src/store.rs @@ -18,12 +18,55 @@ pub enum SaveError { } /// Struct for calculating Recall-Precision per mutation -struct PRStats { +pub struct PRStats { true_positives: [u64; 64], false_positives: [u64; 64], false_negatives: [u64; 64], - tag: String, - name: String + pub tag: String, + pub name: String +} + +impl PRStats { + pub fn precision(&self, threshold: usize) -> f64 { + let t = match threshold { + 0..=64 => threshold, + _ => std::cmp::max(0, std::cmp::min(64, threshold)) + }; + let tpos = self.true_positives[t] as f64; + let fpos = self.false_positives[t] as f64; + let p = tpos / (tpos + fpos); + p + } + pub fn recall(&self, threshold: usize) -> f64 { + let t = match threshold { + 0..=64 => threshold, + _ => std::cmp::max(0, std::cmp::min(64, threshold)) + }; + let tpos = self.true_positives[t] as f64; + let fneg = self.false_negatives[t] as f64; + let r = tpos / (tpos + fneg); + r + } + pub fn pr(&self, threshold: usize) -> (f64, f64) { + let t = match threshold { + 0..=64 => threshold, + _ => std::cmp::max(0, std::cmp::min(64, threshold)) + }; + let tpos = self.true_positives[t] as f64; + let fpos = self.false_positives[t] as f64; + let fneg = self.false_negatives[t] as f64; + let p = tpos / (tpos + fpos); + let r = tpos / (tpos + fneg); + (r, p) + } + pub fn pr_curve(&self) -> Vec<(f64, f64)> { + let mut curve = Vec::new(); + for i in 0..64 { + curve.push(self.pr(i)); + } + curve + } + } /// Uses a hashmap to map descriptors to buckets of files. @@ -71,7 +114,9 @@ impl DescriptorStore { let mut seen: HashSet = HashSet::new(); for bucket in self.map.values() { for element in bucket { - seen.insert(element.clone()); + if !element.starts_with("mut.") { + seen.insert(element.clone()); + } } } self.seen = Some(seen); @@ -199,15 +244,15 @@ impl DescriptorStore { } /// Nearest neighbours - pub fn nn(&self, from: u64, max_distance: isize) -> Vec<(&u64, isize)> { - let mut neighbours = self.bktree.find(from, max_distance); + pub fn nn(&self, from: u64, max_distance: usize) -> Vec<(&u64, isize)> { + let mut neighbours = self.bktree.find(from, max_distance.try_into().unwrap()); neighbours.sort_by(|a, b| a.1.cmp(&b.1)); neighbours } /// Returns a flat vector with all the filenames that are within a given distance - pub fn nn_flat_results(&self, from: u64, max_distance: isize) -> Vec { + pub fn nn_flat_results(&self, from: u64, max_distance: usize) -> Vec { let nn = self.nn(from, max_distance); let mut results: Vec = Vec::new(); for (key, _) in nn { @@ -218,7 +263,7 @@ impl DescriptorStore { results } - pub fn print_nn(&self, from: u64, max_distance: isize, show_images: bool) { + pub fn print_nn(&self, from: u64, max_distance: usize, show_images: bool) { //println!("{from:064b}"); let neighbours = self.nn(from, max_distance); let (term_width, _) = viuer::terminal_size(); @@ -280,7 +325,8 @@ impl DescriptorStore { print!("DescriptorStore:\n{}Total: {}", output, self.map.len()) } - pub fn print_stats(&self) { + pub fn get_stats(&self, mut max_threshold: usize) -> Vec { + max_threshold = std::cmp::min(64, max_threshold); let mut mutator_stats = Vec::new(); for mutator in get_all_mutators() { mutator_stats.push( @@ -298,7 +344,7 @@ impl DescriptorStore { for image in set.iter() { let phash = self.get(image).unwrap(); debug!("Checking {}, with phash: {}", image, phash); - for threshold in 0..64 { + for threshold in 0..max_threshold { debug!("Threshold: {}", threshold); //Assume miss, therefore a false negative //Undo the miss when there is a true positive @@ -328,8 +374,13 @@ impl DescriptorStore { } } } + mutator_stats + } + pub fn print_stats(&self) { + let mut mutator_stats = self.get_stats(64); for mutator in &mut mutator_stats { + println!("{}:", mutator.name); for threshold in 0..64 { let tpos = mutator.true_positives[threshold] as f64; let fpos = mutator.false_positives[threshold] as f64; @@ -337,27 +388,11 @@ impl DescriptorStore { let p = tpos / (tpos + fpos); let r = tpos / (tpos + fneg); let f1 = 2.0*tpos / (2.0*tpos + fpos + fneg); - println!("{} / {}:", mutator.name, threshold); - println!("Precision: {}, Recall: {}, F1 score: {}", p, r, f1); - println!("Tp: {}, Fp: {}, Fn: {}", tpos, fpos, fneg); + debug!("Precision: {}, Recall: {}, F1 score: {}", p, r, f1); + debug!("Tp: {}, Fp: {}, Fn: {}", tpos, fpos, fneg); + println!("{}, {}" , p, r) } - // println!("\t{} true positives: {}", mutator.name, mutator.true_positives); - // println!("\t{} false positives: {}", mutator.name, mutator.false_positives); - // println!( - // "\t{} Precision: {:.3}", mutator.name, - // mutator.true_positives as f64 / - // (mutator.true_positives + mutator.false_positives) as f64 - // ); - // println!( - // "\t{} Recall: {:.3}", mutator.name, - // mutator.true_positives as f64 / - // //(mutator.true_positives + mutator.false_negatives) as f64 - // set.len() as f64 - // ); } - //println!("Images processed: {}", set.len()) - // println!("{} true positives", true_positives); - // println!("{} false positives", false_positives); } }