@@ -76,6 +76,6 @@ impl Descriptor for Median {
|
|||||||
pub fn get_all_descriptors() -> Vec<Box<dyn Descriptor>> {
|
pub fn get_all_descriptors() -> Vec<Box<dyn Descriptor>> {
|
||||||
let mut descriptors: Vec<Box<dyn Descriptor>> = Vec::with_capacity(2);
|
let mut descriptors: Vec<Box<dyn Descriptor>> = Vec::with_capacity(2);
|
||||||
descriptors.push(Box::new(DCT::new()));
|
descriptors.push(Box::new(DCT::new()));
|
||||||
descriptors.push(Box::new(Median));
|
//descriptors.push(Box::new(Median));
|
||||||
descriptors
|
descriptors
|
||||||
}
|
}
|
||||||
+72
-39
@@ -19,9 +19,9 @@ pub enum SaveError {
|
|||||||
|
|
||||||
/// Struct for calculating Recall-Precision per mutation
|
/// Struct for calculating Recall-Precision per mutation
|
||||||
struct PRStats {
|
struct PRStats {
|
||||||
true_positives: u64,
|
true_positives: [u64; 64],
|
||||||
false_positives: u64,
|
false_positives: [u64; 64],
|
||||||
false_negatives: u64,
|
false_negatives: [u64; 64],
|
||||||
tag: String,
|
tag: String,
|
||||||
name: String
|
name: String
|
||||||
}
|
}
|
||||||
@@ -131,6 +131,15 @@ impl DescriptorStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, value: &String) -> Option<u64> {
|
||||||
|
for (key, bucket) in self.map.iter() {
|
||||||
|
if bucket.contains(&value) {
|
||||||
|
return Some(*key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
/// Inserts a single value into the store
|
/// Inserts a single value into the store
|
||||||
pub fn insert(&mut self, key: &u64, value: String) {
|
pub fn insert(&mut self, key: &u64, value: String) {
|
||||||
let bucket = match self.map.get(&key) {
|
let bucket = match self.map.get(&key) {
|
||||||
@@ -196,6 +205,19 @@ impl DescriptorStore {
|
|||||||
neighbours
|
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<String> {
|
||||||
|
let nn = self.nn(from, max_distance);
|
||||||
|
let mut results: Vec<String> = Vec::new();
|
||||||
|
for (key, _) in nn {
|
||||||
|
// We know the key exists, so getting the result should never be none
|
||||||
|
let mut bucket = self.map.get(key).unwrap().clone();
|
||||||
|
results.append(&mut bucket);
|
||||||
|
}
|
||||||
|
results
|
||||||
|
}
|
||||||
|
|
||||||
pub fn print_nn(&self, from: u64, max_distance: isize, show_images: bool) {
|
pub fn print_nn(&self, from: u64, max_distance: isize, show_images: bool) {
|
||||||
//println!("{from:064b}");
|
//println!("{from:064b}");
|
||||||
let neighbours = self.nn(from, max_distance);
|
let neighbours = self.nn(from, max_distance);
|
||||||
@@ -263,9 +285,9 @@ impl DescriptorStore {
|
|||||||
for mutator in get_all_mutators() {
|
for mutator in get_all_mutators() {
|
||||||
mutator_stats.push(
|
mutator_stats.push(
|
||||||
PRStats {
|
PRStats {
|
||||||
true_positives: 0,
|
true_positives: [0; 64],
|
||||||
false_positives: 0,
|
false_positives: [0; 64],
|
||||||
false_negatives: 0,
|
false_negatives: [0; 64],
|
||||||
tag: "mut".to_string() + &mutator.tag(),
|
tag: "mut".to_string() + &mutator.tag(),
|
||||||
name: mutator.info(),
|
name: mutator.info(),
|
||||||
}
|
}
|
||||||
@@ -273,54 +295,65 @@ impl DescriptorStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let set = self.seen.clone().unwrap();
|
let set = self.seen.clone().unwrap();
|
||||||
|
|
||||||
for image in set.iter() {
|
for image in set.iter() {
|
||||||
debug!("Checking {}", image);
|
let phash = self.get(image).unwrap();
|
||||||
|
debug!("Checking {}, with phash: {}", image, phash);
|
||||||
|
for threshold in 0..64 {
|
||||||
|
debug!("Threshold: {}", threshold);
|
||||||
|
//Assume miss, therefore a false negative
|
||||||
|
//Undo the miss when there is a true positive
|
||||||
|
for mutator in &mut mutator_stats {
|
||||||
|
mutator.false_negatives[threshold as usize] += 1;
|
||||||
|
}
|
||||||
|
|
||||||
// find the bucket this image is in
|
for found in self.nn_flat_results(phash, threshold) {
|
||||||
for (key, bucket) in self.map.iter() {
|
if found.eq(image) || found.ends_with(&format!(".{}", image)) { // True positive
|
||||||
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 {
|
for mutator in &mut mutator_stats {
|
||||||
mutator.false_negatives += 1;
|
if found.starts_with(&mutator.tag) {
|
||||||
if value.starts_with(&mutator.tag) {
|
debug!("{} is hit for {}", found, mutator.name);
|
||||||
mutator.true_positives += 1;
|
mutator.true_positives[threshold as usize] += 1;
|
||||||
mutator.false_negatives -= 1;
|
mutator.false_negatives[threshold as usize] -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
debug!("We matched {} to {}!", value, image);
|
} else { // False positive!
|
||||||
} else if value != image {
|
// Mutated misses only count for the mutator
|
||||||
// Other image in same bucket
|
// Unmutated misses count for everyone
|
||||||
//false_positives += 1;
|
|
||||||
for mutator in &mut mutator_stats {
|
for mutator in &mut mutator_stats {
|
||||||
if value.starts_with(&mutator.tag) {
|
if found.starts_with(&mutator.tag) || !found.starts_with("mut") {
|
||||||
mutator.false_positives += 1;
|
debug!("{} is false positive for {}", found, mutator.name);
|
||||||
|
mutator.false_positives[threshold as usize] += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
//
|
|
||||||
for mutator in &mut mutator_stats {
|
for mutator in &mut mutator_stats {
|
||||||
|
for threshold in 0..64 {
|
||||||
|
let tpos = mutator.true_positives[threshold] as f64;
|
||||||
|
let fpos = mutator.false_positives[threshold] as f64;
|
||||||
|
let fneg = mutator.false_negatives[threshold] as f64;
|
||||||
|
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);
|
||||||
|
}
|
||||||
// println!("\t{} true positives: {}", mutator.name, mutator.true_positives);
|
// println!("\t{} true positives: {}", mutator.name, mutator.true_positives);
|
||||||
// println!("\t{} false positives: {}", mutator.name, mutator.false_positives);
|
// println!("\t{} false positives: {}", mutator.name, mutator.false_positives);
|
||||||
println!(
|
// println!(
|
||||||
"\t{} Precision: {:.3}", mutator.name,
|
// "\t{} Precision: {:.3}", mutator.name,
|
||||||
mutator.true_positives as f64 /
|
// mutator.true_positives as f64 /
|
||||||
(mutator.true_positives + mutator.false_positives) as f64
|
// (mutator.true_positives + mutator.false_positives) as f64
|
||||||
);
|
// );
|
||||||
println!(
|
// println!(
|
||||||
"\t{} Recall: {:.3}", mutator.name,
|
// "\t{} Recall: {:.3}", mutator.name,
|
||||||
mutator.true_positives as f64 /
|
// mutator.true_positives as f64 /
|
||||||
//(mutator.true_positives + mutator.false_negatives) as f64
|
// //(mutator.true_positives + mutator.false_negatives) as f64
|
||||||
set.len() as f64
|
// set.len() as f64
|
||||||
);
|
// );
|
||||||
}
|
}
|
||||||
//println!("Images processed: {}", set.len())
|
//println!("Images processed: {}", set.len())
|
||||||
// println!("{} true positives", true_positives);
|
// println!("{} true positives", true_positives);
|
||||||
|
|||||||
Reference in New Issue
Block a user