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
+1 -1
View File
@@ -15,7 +15,7 @@ fn main() {
env_logger::init();
let cfg = Cfg::parse();
let desc = DCT::new().with_quality(50);
let desc = DCT::new().with_quality(95);
let img = image::open(cfg.path)
.expect("Unable to open file");
+2 -2
View File
@@ -18,7 +18,7 @@ struct Cfg {
fn main() {
env_logger::init();
let desc = DCT::new().with_quality(50);
let desc = DCT::new().with_quality(95);
let cfg = Cfg::parse();
@@ -32,7 +32,7 @@ fn main() {
// viuer::print(&img, &conf).expect("Image printing failed.");
let phash: u64 = desc.describe(&img);
let store =
DescriptorStore::new(desc)
DescriptorStore::new(Box::new(desc))
.with_file("dct50.messagepack");
info!("Phash integer:\n{phash}");
info!("Phash binary:\n{phash:064b}");
+22 -13
View File
@@ -112,22 +112,26 @@ impl Descriptor for DCT {
debug!("DCT-coefficients:\n {}", print_matrix(dct_values));
// Quantization:
//debug!("Using quantization matrix:\n{}", print_matrix(self.quantization_matrix));
for i in 0..64 {
dct_values[i] = (dct_values[i] / self.quantization_matrix[i] as f64).round();
}
debug!("DCT-coefficients, quantized:\n {}", print_matrix(dct_values));
// debug!("Using quantization matrix:\n{}", print_matrix(self.quantization_matrix));
// for i in 0..64 {
// dct_values[i] = (dct_values[i] / self.quantization_matrix[i] as f64).round();
// if dct_values[i] == -0.0 && i % 2 != 0 {
// dct_values[i] = 0.0;
// }
// }
// debug!("DCT-coefficients, quantized:\n {}", print_matrix(dct_values));
if log_enabled!(Level::Debug) {
resized.save("resize.png").expect("Error saving file");
// De-quantization:
let mut dequant = dct_values.clone();
for i in 0..64 {
dct_values[i] = dct_values[i] * self.quantization_matrix[i] as f64;
dequant[i] = dct_values[i] * self.quantization_matrix[i] as f64;
}
//debug!("DCT-coefficients, de-quantized:\n {}", print_matrix(dct_values));
// Reconstruction original pixel values:
let reconstructed = self.idct(dct_values);
let reconstructed = self.idct(dequant);
save_buffer(
"reconstructed.png",
@@ -158,24 +162,27 @@ impl Descriptor for DCT {
// If first horizontal AC coefficient is negative
// This might account for horizontal flips when applied to all horizontal coefficients.
let sign_mult = dct_values[1].signum();
//debug!("Sign multiplier of first horizontal component: {}", sign_mult);
// Mask that indicates if a coefficient is bigger or smaller than previous in order
let mut pearson_mask: u64 = 0;
let mut prev = dct_values[0];
let mut prev = dct_values[49].abs();
for i in zigzag {
let cur = dct_values[i];
let cur = dct_values[i].abs();
if cur > prev {
pearson_mask += 1;
}
prev = cur;
let signum = dct_values[i].signum();
let mut signum = dct_values[i].signum();
if i % 2 != 0 {
signum *= sign_mult;
}
//debug!("[{}]: {} has signum {}, and i%2 is {}", i, cur, signum, i%2);
// Only multiply sign if dct-coefficient contains a horizontal component.
if
i % 8 != 0 && sign_mult * signum < 0.0
||
signum < 0.0
{
sign_mask += 1;
@@ -184,9 +191,11 @@ impl Descriptor for DCT {
// Shift masks
sign_mask = sign_mask << 1;
pearson_mask = pearson_mask << 1;
}
debug!("Sign mask: {:028b}", sign_mask);
debug!("Pearson mask: {:028b}", pearson_mask);
}
// debug!("Sign mask: {:028b}", sign_mask);
// debug!("Pearson mask: {:028b}", pearson_mask);
let mut mask = sign_mask;
debug!("Mask: {:064b}", mask);
+7 -2
View File
@@ -26,7 +26,7 @@ fn main() {
}
for node in fs::read_dir(cfg.path).unwrap() {
println!("{:?}", node);
//println!("{:?}", node);
let file = node.expect("Error walking directory");
let name = match file.file_name().into_string() {
Ok(v) => v,
@@ -56,8 +56,13 @@ fn main() {
}
}
}
for store in &stores {
println!("{}", store);
}
for store in &stores {
println!("Store for {}: ", store.descriptor.info());
store.print_stats();
//println!("{}", store);
}
}
+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 {