filter autocorrelation

This commit is contained in:
randogoth 2024-12-29 22:05:54 +02:00
parent 9f3d56c50a
commit 94097c9d84
2 changed files with 76 additions and 100 deletions

2
justfile Normal file
View file

@ -0,0 +1,2 @@
diehard file:
dieharder -a -g 201 -f {{file}}

View file

@ -13,71 +13,58 @@ 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
}
// Calculate the height of the frame
let height = current.len() / width;
bit_count += 1;
// Skip the first and last 100 rows (width * 100 pixels)
let start_row = 100;
let end_row = height - 100;
// Push the byte once we have 8 bits
if bit_count == 8 {
entropy.push(current_byte);
current_byte = 0;
bit_count = 0;
if start_row >= end_row || width <= 200 {
panic!("Resolution is too small to apply the grid selection with the given offset.");
}
// 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;
// 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;
}
}
}
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);
}
// Determine the length of each fold
let fold_len = data.len() / 4;
// 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
}
// 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
}
/// Apply Van Neumann whitening to a vector of entropy bits
fn whiten(&self, entropy: &Vec<u8>) -> Vec<u8> {
let mut whitened_entropy = Vec::new();
@ -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(&current_frame_data, &previous_frame_data);
previous_frame_data = current_frame_data;
}
entropy = ocelli.get_entropy(&current_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();