Fixes compilation, horizontal flip for DCT. Adds PR calculation.
continuous-integration/drone/push Build is passing

DCT is now somewhat invariant to horizontal flips.
First version of precision recall variable is calculated.
This commit is contained in:
2024-06-02 14:58:14 +02:00
parent 57bb30b970
commit 9fa594045d
5 changed files with 133 additions and 23 deletions
+100 -4
View File
@@ -3,6 +3,7 @@
use std::collections::{HashMap, HashSet};
use crate::descriptors::Descriptor;
use crate::mutators::get_all_mutators;
use std::fs;
use std::path::Path;
use std::fmt;
@@ -16,10 +17,19 @@ pub enum SaveError {
File,
}
/// Struct for calculating Recall-Precision per mutation
struct PRStats {
true_positives: u64,
false_positives: u64,
false_negatives: u64,
tag: String,
name: String
}
/// Uses a hashmap to map descriptors to buckets of files.
/// Also keeps a BK-tree for quick distance ranking
pub struct DescriptorStore {
descriptor: Box<dyn Descriptor>,
pub descriptor: Box<dyn Descriptor>,
/// Main hashmap that maps descriptors to buckets of filenames
map: HashMap<u64, Vec<String>>,
@@ -71,6 +81,22 @@ impl DescriptorStore {
self
}
/// Stores non-mutated images in a hashset for quick membership checks
pub fn see(&mut self, value: String) {
if !value.starts_with("mut.") {
match &mut self.seen {
Some(set) => {
set.insert(value);
},
None => {
let mut seen: HashSet<String> = HashSet::new();
seen.insert(value);
self.seen = Some(seen);
}
}
}
}
pub fn save(&self) -> Result<(), SaveError> {
let serialized: Vec<u8> = match rmp_serde::to_vec(&self.map) {
Ok(value) => value,
@@ -111,13 +137,14 @@ impl DescriptorStore {
Some(b) => {
let mut n = b.clone();
if !n.contains(&value) {
n.push(value);
n.push(value.clone());
}
n
},
None => vec![value],
None => vec![value.clone()],
};
self.map.insert(*key, bucket);
self.see(value);
self.bktree.insert(*key);
}
@@ -152,7 +179,7 @@ impl DescriptorStore {
self.save().expect("error");
}
// We have done our bulk loading, so we can unset our hashset cache:
self.seen = None;
//self.seen = None;
}
pub fn store(&mut self, img: &DynamicImage, name: String) {
@@ -230,6 +257,75 @@ impl DescriptorStore {
}
print!("DescriptorStore:\n{}Total: {}", output, self.map.len())
}
pub fn print_stats(&self) {
let mut mutator_stats = Vec::new();
for mutator in get_all_mutators() {
mutator_stats.push(
PRStats {
true_positives: 0,
false_positives: 0,
false_negatives: 0,
tag: "mut".to_string() + &mutator.tag(),
name: mutator.info(),
}
);
}
let set = self.seen.clone().unwrap();
for image in set.iter() {
debug!("Checking {}", image);
// find the bucket this image is in
for (key, bucket) in self.map.iter() {
if bucket.contains(&image) {
debug!("{} found in bucket {}", image, key);
for value in bucket {
if value != image && value.ends_with(image) {
// Mutated version of image in same bucket
for mutator in &mut mutator_stats {
mutator.false_negatives += 1;
if value.starts_with(&mutator.tag) {
mutator.true_positives += 1;
mutator.false_negatives -= 1;
}
}
debug!("We matched {} to {}!", value, image);
} else if value != image {
// Other image in same bucket
//false_positives += 1;
for mutator in &mut mutator_stats {
if value.starts_with(&mutator.tag) {
mutator.false_positives += 1;
}
}
}
}
break;
}
}
}
//
for mutator in &mut mutator_stats {
// 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);
}
}
impl fmt::Display for DescriptorStore {