filter autocorrelation
This commit is contained in:
parent
9f3d56c50a
commit
94097c9d84
2 changed files with 76 additions and 100 deletions
2
justfile
Normal file
2
justfile
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
diehard file:
|
||||
dieharder -a -g 201 -f {{file}}
|
||||
174
src/main.rs
174
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<u8> {
|
||||
fn get_entropy(&self, current: &Vec<u8>, previous: &Vec<u8>, width: usize, minimum_distance: usize) -> Vec<u8> {
|
||||
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<u8>) -> Vec<u8> {
|
||||
// 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<Vec<u8>> = 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<u8> = 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<u8>) -> Vec<u8> {
|
||||
|
|
@ -116,7 +103,7 @@ impl Ocelli {
|
|||
}
|
||||
|
||||
/// Calculates the Shannon entropy of binary data
|
||||
fn shannon(&self, data: &[u8]) -> f64 {
|
||||
fn shannon(&self, data: &Vec<u8>) -> 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<u8>, 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<String> = env::args().collect();
|
||||
if args.len() < 4 {
|
||||
eprintln!("Usage: {} <entropy length in bytes> <resolution width> <resolution height>", args[0]);
|
||||
if args.len() < 3 {
|
||||
eprintln!("Usage: {} <camera index> <entropy length in bytes> [-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<u8> = 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::<String>();
|
||||
// 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(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue