From 94097c9d848d67873bdd1e31be2d61b9cd5913be Mon Sep 17 00:00:00 2001 From: randogoth Date: Sun, 29 Dec 2024 22:05:54 +0200 Subject: [PATCH] filter autocorrelation --- justfile | 2 + src/main.rs | 174 ++++++++++++++++++++++------------------------------ 2 files changed, 76 insertions(+), 100 deletions(-) create mode 100644 justfile diff --git a/justfile b/justfile new file mode 100644 index 0000000..987eabc --- /dev/null +++ b/justfile @@ -0,0 +1,2 @@ +diehard file: + dieharder -a -g 201 -f {{file}} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index aeb4393..fde0fdf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,70 +13,57 @@ use chrono::Local; struct Ocelli; impl Ocelli { - /// Calculates the entropy bits based on two arrays of grayscale values - fn get_entropy(&self, current: &[u8], previous: &[u8]) -> Vec { + fn get_entropy(&self, current: &Vec, previous: &Vec, width: usize, minimum_distance: usize) -> Vec { let mut entropy = Vec::new(); let mut current_byte = 0u8; let mut bit_count = 0; - - for (&c, &p) in current.iter().zip(previous.iter()) { - if c > p { - current_byte = (current_byte << 1) | 1; // Append '1' to the byte - } else if c < p { - current_byte = current_byte << 1; // Append '0' to the byte - } else { - continue; // Skip if values are equal - } - - bit_count += 1; - - // Push the byte once we have 8 bits - if bit_count == 8 { - entropy.push(current_byte); - current_byte = 0; - bit_count = 0; - } - } - - entropy - } - - /// Use the chop & stack method on an array of grayscale values - fn chop_and_stack(&self, mut data: Vec) -> Vec { - // Ensure the input length is divisible by 4 by trimming excess bytes - let remainder = data.len() % 4; - if remainder != 0 { - data.truncate(data.len() - remainder); + + // Calculate the height of the frame + let height = current.len() / width; + + // Skip the first and last 100 rows (width * 100 pixels) + let start_row = 100; + let end_row = height - 100; + + if start_row >= end_row || width <= 200 { + panic!("Resolution is too small to apply the grid selection with the given offset."); } - // Determine the length of each fold - let fold_len = data.len() / 4; + // Iterate through rows, skipping the top and bottom 100 rows + for row in start_row..end_row { + // Skip the first 100 pixels in the row + let row_start = row * width + 100; + let row_end = (row + 1) * width - 100; - // Split the data into four folds - let mut folds: Vec> = data - .chunks(fold_len) - .map(|chunk| chunk.to_vec()) - .collect(); - - // Reverse the second and fourth folds - if folds.len() > 1 { - folds[1].reverse(); // Reverse the second row - } - if folds.len() > 3 { - folds[3].reverse(); // Reverse the fourth row + // Select pixels in the row based on the step size + for pixel_index in (row_start..row_end).step_by(minimum_distance) { + if pixel_index >= current.len() || pixel_index >= previous.len() { + continue; + } + + let c = current[pixel_index]; + let p = previous[pixel_index]; + + if c > p { + current_byte = (current_byte << 1) | 1; // Append '1' + } else if c < p { + current_byte = current_byte << 1; // Append '0' + } else { + continue; // Skip if equal + } + + bit_count += 1; + + if bit_count == 8 { + entropy.push(current_byte); + current_byte = 0; + bit_count = 0; + } + } } - // Combine the folds by summing corresponding elements and applying modulo 256 - let mut combined: Vec = vec![0u8; fold_len]; - for i in 0..fold_len { - combined[i] = ((folds[0][i] as u16 - + folds[1][i] as u16 - + folds[2][i] as u16 - + folds[3][i] as u16) % 256) as u8; - } - - combined - } + entropy + } /// Apply Van Neumann whitening to a vector of entropy bits fn whiten(&self, entropy: &Vec) -> Vec { @@ -116,7 +103,7 @@ impl Ocelli { } /// Calculates the Shannon entropy of binary data - fn shannon(&self, data: &[u8]) -> f64 { + fn shannon(&self, data: &Vec) -> f64 { let mut frequency_map = HashMap::new(); let data_len = data.len(); @@ -131,7 +118,7 @@ impl Ocelli { } /// Determines if the camera is covered based on the unique grayscale values - fn is_covered(&self, grayscale: &[u8], threshold: usize) -> bool { + fn is_covered(&self, grayscale: &Vec, threshold: usize) -> bool { let unique_values: HashSet<_> = grayscale.iter().copied().collect(); unique_values.len() < threshold } @@ -139,32 +126,38 @@ impl Ocelli { fn main() -> opencv::Result<()> { let args: Vec = env::args().collect(); - if args.len() < 4 { - eprintln!("Usage: {} ", args[0]); + if args.len() < 3 { + eprintln!("Usage: {} [-w]", args[0]); std::process::exit(1); } + let camera_index: i32 = args[1].parse().expect("Failed to parse camera index as a number"); + let length: usize = args[2].parse().expect("Failed to parse entropy length as a number"); let whiten_flag = args.contains(&String::from("-w")); - let length: usize = args[1].parse().expect("Failed to parse entropy length as a number"); - let width: usize = args[2].parse().expect("Failed to parse resolution width as a number"); - let height: usize = args[3].parse().expect("Failed to parse resolution height as a number"); - - let mut cam = VideoCapture::new(1, CAP_V4L)?; + let mut cam = VideoCapture::new(camera_index, CAP_V4L)?; if !cam.is_opened()? { - panic!("Failed to open the camera"); + panic!("Failed to open the camera with index {}", camera_index); } - cam.set(opencv::videoio::CAP_PROP_FRAME_WIDTH, width as f64)?; - cam.set(opencv::videoio::CAP_PROP_FRAME_HEIGHT, height as f64)?; + // Capture a single frame to determine resolution + let mut frame = core::Mat::default(); + cam.read(&mut frame)?; + if frame.empty() { + panic!("Failed to capture a frame."); + } - println!("Camera resolution set to {}x{}", width, height); + let width = frame.cols() as usize; + let height = frame.rows() as usize; + + println!( + "Camera index: {}\nCamera resolution detected: {}x{}", + camera_index, width, height + ); let ocelli = Ocelli; - // Capture a single frame to check if the camera is covered - let mut frame = core::Mat::default(); - cam.read(&mut frame)?; + // Convert the frame to grayscale and check if the camera is covered let mut gray_frame = core::Mat::default(); imgproc::cvt_color(&frame, &mut gray_frame, imgproc::COLOR_BGR2GRAY, 0)?; let grayscale_data = gray_frame.data_bytes().expect("Failed to get grayscale data").to_vec(); @@ -172,16 +165,16 @@ fn main() -> opencv::Result<()> { let uncovered = !ocelli.is_covered(&grayscale_data, 50); println!( - "Starting entropy generation... Using {}", + "Camera is {}covered.", if uncovered { - "chop_and_stack" + "un" } else { - "get_entropy" + "" } ); let mut total_entropy = Vec::new(); - let shannon_threshold = 4.0; + let shannon_threshold = 4.5; let mut previous_frame_data = grayscale_data.clone(); let start_time = Instant::now(); @@ -193,32 +186,21 @@ fn main() -> opencv::Result<()> { let current_frame_data = gray_frame.data_bytes().expect("Failed to get grayscale data").to_vec(); let mut entropy: Vec = Vec::new(); - let mut shannon_entropy = 0.0; - if uncovered { - // Process with chop_and_stack - entropy = ocelli.chop_and_stack(current_frame_data.clone()); - - } else { - // Process with get_entropy - entropy = ocelli.get_entropy(¤t_frame_data, &previous_frame_data); - - previous_frame_data = current_frame_data; - } + entropy = ocelli.get_entropy(¤t_frame_data, &previous_frame_data, width, 20); + previous_frame_data = current_frame_data; if whiten_flag { entropy = ocelli.whiten(&entropy); } - shannon_entropy = ocelli.shannon(&entropy); + let shannon_entropy = ocelli.shannon(&entropy); if shannon_entropy >= shannon_threshold { - total_entropy.extend(entropy); - } else { println!( - "Rejected stacked entropy array (Shannon entropy: {:.3}). Retrying...", + "Rejected entropy array (Shannon entropy: {:.3}). Retrying...", shannon_entropy ); } @@ -230,14 +212,6 @@ fn main() -> opencv::Result<()> { ); } - // Convert entropy to hex string - // let entropy_hex = total_entropy - // .iter() - // .take(length) - // .map(|byte| format!("{:02x}", byte)) - // .collect::(); - // println!("Generated entropy (hex): {}", entropy_hex); - let total_shannon_entropy = ocelli.shannon(&total_entropy); let elapsed_time = start_time.elapsed(); @@ -259,4 +233,4 @@ fn main() -> opencv::Result<()> { println!("Entropy saved to file: {}", filename); Ok(()) -} \ No newline at end of file +}