updated method and sequential read
This commit is contained in:
parent
982603e64f
commit
92fd5531e9
2 changed files with 124 additions and 101 deletions
105
src/bin/main.rs
105
src/bin/main.rs
|
|
@ -6,7 +6,7 @@ use v4l::format::FourCC;
|
||||||
use v4l::io::traits::CaptureStream;
|
use v4l::io::traits::CaptureStream;
|
||||||
use image::ImageReader;
|
use image::ImageReader;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{stdin, Write};
|
use std::io::{stdin, Read, Write};
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
|
|
@ -27,15 +27,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the quick flag is set
|
|
||||||
let quick = args.contains(&String::from("-q"));
|
let quick = args.contains(&String::from("-q"));
|
||||||
|
let pp = args.contains(&String::from("-p"));
|
||||||
|
|
||||||
let camera_index: usize = args[1].parse().expect("Failed to parse camera index as a number");
|
let camera_index: usize = 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 length: usize = args[2].parse().expect("Failed to parse entropy length as a number");
|
||||||
|
|
||||||
let dev = Device::new(camera_index).expect("Failed to open camera");
|
let dev = Device::new(camera_index).expect("Failed to open camera");
|
||||||
|
|
||||||
// Set the desired format and resolution
|
|
||||||
let mut format = dev.format().expect("Failed to get camera format");
|
let mut format = dev.format().expect("Failed to get camera format");
|
||||||
format.fourcc = FourCC::new(b"MJPG"); // Use MJPG for higher resolutions
|
format.fourcc = FourCC::new(b"MJPG"); // Use MJPG for higher resolutions
|
||||||
format.width = 1920;
|
format.width = 1920;
|
||||||
|
|
@ -45,8 +44,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
println!("Failed to set resolution to 1920x1080. Falling back to 1280x720.");
|
println!("Failed to set resolution to 1920x1080. Falling back to 1280x720.");
|
||||||
format.width = 1280;
|
format.width = 1280;
|
||||||
format.height = 720;
|
format.height = 720;
|
||||||
dev.set_format(&format)
|
dev.set_format(&format).expect("Failed to set resolution to 1280x720");
|
||||||
.expect("Failed to set resolution to 1280x720");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
|
|
@ -62,69 +60,76 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
let shannon_threshold = 7.9;
|
let shannon_threshold = 7.9;
|
||||||
let mut frame_count = 0;
|
let mut frame_count = 0;
|
||||||
|
let mut last_frame = None;
|
||||||
|
|
||||||
while total_entropy.len() < length {
|
while total_entropy.len() < length {
|
||||||
// Capture first frame
|
let (data, _) = stream.next().expect("Failed to capture frame");
|
||||||
let (data1, _) = stream.next().expect("Failed to capture frame");
|
frame_count += 1;
|
||||||
|
|
||||||
// Skip the first 30 frames
|
|
||||||
if frame_count <= 30 {
|
if frame_count <= 30 {
|
||||||
frame_count += 1;
|
continue; // Skip the first 30 frames
|
||||||
} else {
|
|
||||||
|
|
||||||
let grayscale_data1 = frame_to_grayscale(&data1);
|
|
||||||
|
|
||||||
let mut entropy: Vec<u8> = [0].to_vec();
|
|
||||||
|
|
||||||
if quick {
|
|
||||||
// Quicker capture using Pick and Flip
|
|
||||||
entropy = ocelli.whiten(&ocelli.pick_and_flip(&grayscale_data1, frame_count as usize));
|
|
||||||
} else {
|
|
||||||
if ocelli.is_covered(&grayscale_data1, 50) {
|
|
||||||
// Capture second frame
|
|
||||||
let (data2, _) = stream.next().expect("Failed to capture second frame");
|
|
||||||
let grayscale_data2 = frame_to_grayscale(&data2);
|
|
||||||
|
|
||||||
// Generate entropy using chop_and_tack
|
|
||||||
entropy = ocelli.chop_and_tack(&grayscale_data1, &grayscale_data2, format.width as usize, 30);
|
|
||||||
} else {
|
|
||||||
println!("Camera is not covered. Please cover the camera.");
|
|
||||||
frame_count = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let shannon_entropy = ocelli.shannon(&entropy);
|
|
||||||
|
|
||||||
if shannon_entropy >= shannon_threshold {
|
|
||||||
total_entropy.extend(entropy);
|
|
||||||
println!(
|
|
||||||
"Collected {} of {} bytes of entropy (Shannon entropy: {:.3})",
|
|
||||||
total_entropy.len(),
|
|
||||||
length,
|
|
||||||
shannon_entropy
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
println!("Rejected entropy for frame {} (Shannon entropy: {:.3})", frame_count, shannon_entropy);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let grayscale_data = frame_to_grayscale(&data);
|
||||||
|
|
||||||
|
let entropy: Vec<u8> = if pp {
|
||||||
|
let mut low = 10;
|
||||||
|
let mut high = 245;
|
||||||
|
|
||||||
|
if ocelli.is_covered(&grayscale_data, 50) {
|
||||||
|
low = 3;
|
||||||
|
high = 252;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(last) = last_frame {
|
||||||
|
ocelli.whiten(&ocelli.tune_and_prune(&grayscale_data, &last, low, high))
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
} else if quick {
|
||||||
|
ocelli.whiten(&ocelli.pick_and_flip(&grayscale_data, frame_count as usize))
|
||||||
|
} else {
|
||||||
|
if ocelli.is_covered(&grayscale_data, 50) {
|
||||||
|
if let Some(last) = last_frame {
|
||||||
|
ocelli.chop_and_tack(&last, &grayscale_data, format.width as usize, 30)
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("Camera is not covered. Please cover the camera.");
|
||||||
|
frame_count = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let shannon_entropy = ocelli.shannon(&entropy);
|
||||||
|
|
||||||
|
if shannon_entropy >= shannon_threshold {
|
||||||
|
total_entropy.extend(entropy);
|
||||||
|
println!(
|
||||||
|
"Collected {} of {} bytes of entropy (Shannon entropy: {:.3})",
|
||||||
|
total_entropy.len(),
|
||||||
|
length,
|
||||||
|
shannon_entropy
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
println!("Rejected entropy for frame {} (Shannon entropy: {:.3})", frame_count, shannon_entropy);
|
||||||
|
}
|
||||||
|
|
||||||
|
last_frame = Some(grayscale_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trim total_entropy to the exact length
|
|
||||||
total_entropy.truncate(length);
|
total_entropy.truncate(length);
|
||||||
|
|
||||||
// Print elapsed time
|
|
||||||
let elapsed_time = start_time.elapsed();
|
let elapsed_time = start_time.elapsed();
|
||||||
println!(
|
println!(
|
||||||
"Process completed in {:.3} seconds.",
|
"Process completed in {:.3} seconds.",
|
||||||
elapsed_time.as_secs_f64()
|
elapsed_time.as_secs_f64()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Final Shannon entropy test
|
|
||||||
let final_shannon_entropy = ocelli.shannon(&total_entropy);
|
let final_shannon_entropy = ocelli.shannon(&total_entropy);
|
||||||
println!("Final Shannon entropy: {:.3}", final_shannon_entropy);
|
println!("Final Shannon entropy: {:.3}", final_shannon_entropy);
|
||||||
|
|
||||||
// Ask if the result should be saved
|
|
||||||
println!("Save result to a file or print as hex string? (file/print):");
|
println!("Save result to a file or print as hex string? (file/print):");
|
||||||
let mut input = String::new();
|
let mut input = String::new();
|
||||||
stdin().read_line(&mut input).expect("Failed to read input");
|
stdin().read_line(&mut input).expect("Failed to read input");
|
||||||
|
|
|
||||||
118
src/lib.rs
118
src/lib.rs
|
|
@ -5,19 +5,25 @@ pub struct Ocelli;
|
||||||
|
|
||||||
impl Ocelli {
|
impl Ocelli {
|
||||||
|
|
||||||
|
fn bits_to_bytes(&self, bits: &[u8]) -> Vec<u8> {
|
||||||
|
bits.chunks(8).filter_map(|chunk| {
|
||||||
|
if chunk.len() == 8 {
|
||||||
|
Some(chunk.iter().fold(0, |byte, &bit| (byte << 1) | bit))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}).collect()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn chop_and_tack(&self, current: &Vec<u8>, previous: &Vec<u8>, width: usize, minimum_distance: usize) -> Vec<u8> {
|
pub fn chop_and_tack(&self, current: &Vec<u8>, previous: &Vec<u8>, width: usize, minimum_distance: usize) -> Vec<u8> {
|
||||||
// Extracts entropy from two frames by comparing pixel values in a specific grid pattern.
|
// Extracts entropy from two frames by comparing pixel values in a specific grid pattern.
|
||||||
// The resulting entropy is constructed by appending 1s or 0s based on pixel differences.
|
// The resulting entropy is constructed by appending 1s or 0s based on pixel differences.
|
||||||
// Algorithm ported from NoiseBasedCamRng by Andika Wasisto https://github.com/awasisto/camrng
|
// Algorithm ported from NoiseBasedCamRng by Andika Wasisto https://github.com/awasisto/camrng
|
||||||
|
|
||||||
let mut entropy = Vec::new();
|
let mut entropy = Vec::new();
|
||||||
let mut current_byte = 0u8;
|
|
||||||
let mut bit_count = 0;
|
|
||||||
|
|
||||||
// Calculate the height of the frame
|
|
||||||
let height = current.len() / width;
|
let height = current.len() / width;
|
||||||
|
|
||||||
// Skip 100 pixels at the top and bottom of the frame
|
// Define bounds for the grid
|
||||||
let start_row = 100;
|
let start_row = 100;
|
||||||
let end_row = height - 100;
|
let end_row = height - 100;
|
||||||
|
|
||||||
|
|
@ -25,39 +31,23 @@ impl Ocelli {
|
||||||
panic!("Resolution is too small to apply the grid selection with the given offset.");
|
panic!("Resolution is too small to apply the grid selection with the given offset.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Process the grid within the defined bounds
|
||||||
for row in start_row..end_row {
|
for row in start_row..end_row {
|
||||||
// Skip 100 pixels at the left and right of the frame
|
|
||||||
let row_start = row * width + 100;
|
let row_start = row * width + 100;
|
||||||
let row_end = (row + 1) * width - 100;
|
let row_end = (row + 1) * width - 100;
|
||||||
|
|
||||||
// Select pixels in the row based on the step size
|
entropy.extend((row_start..row_end).step_by(minimum_distance)
|
||||||
for pixel_index in (row_start..row_end).step_by(minimum_distance) {
|
.filter(|&pixel_index| pixel_index < current.len() && pixel_index < previous.len())
|
||||||
if pixel_index >= current.len() || pixel_index >= previous.len() {
|
.map(|pixel_index| {
|
||||||
continue;
|
let c = current[pixel_index];
|
||||||
}
|
let p = previous[pixel_index];
|
||||||
|
(c > p) as u8 - (c < p) as u8 // 1 for c > p, 0 for c < p, skips if equal
|
||||||
let c = current[pixel_index];
|
})
|
||||||
let p = previous[pixel_index];
|
.filter(|&bit| bit <= 1));
|
||||||
|
|
||||||
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
|
self.bits_to_bytes(&entropy)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pick_and_flip(&self, data: &[u8], current_frame_index: usize) -> Vec<u8> {
|
pub fn pick_and_flip(&self, data: &[u8], current_frame_index: usize) -> Vec<u8> {
|
||||||
|
|
@ -67,29 +57,32 @@ impl Ocelli {
|
||||||
// digital camera image noise for varying lighting conditions," doi: 10.1109/SECON.2015.7132901.
|
// digital camera image noise for varying lighting conditions," doi: 10.1109/SECON.2015.7132901.
|
||||||
|
|
||||||
let mut entropy = Vec::new();
|
let mut entropy = Vec::new();
|
||||||
let mut current_byte = 0u8;
|
|
||||||
let mut bit_count = 0;
|
|
||||||
|
|
||||||
for &pixel_brightness in data {
|
for &pixel in data {
|
||||||
if (2..=253).contains(&pixel_brightness) {
|
if (2..=253).contains(&pixel) { // filter bias
|
||||||
let mut lsb = pixel_brightness & 1;
|
let mut lsb = pixel & 1;
|
||||||
|
if current_frame_index % 2 == 0 { // flip bits
|
||||||
if current_frame_index % 2 == 0 {
|
lsb ^= 1;
|
||||||
lsb ^= 1; // Flip the bit
|
|
||||||
}
|
|
||||||
|
|
||||||
current_byte = (current_byte << 1) | lsb;
|
|
||||||
bit_count += 1;
|
|
||||||
|
|
||||||
if bit_count == 8 {
|
|
||||||
entropy.push(current_byte);
|
|
||||||
current_byte = 0;
|
|
||||||
bit_count = 0;
|
|
||||||
}
|
}
|
||||||
|
entropy.push(lsb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
entropy
|
self.bits_to_bytes(&entropy)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tune_and_prune(&self, current: &Vec<u8>, previous: &Vec<u8>, low: u8, high: u8) -> Vec<u8> {
|
||||||
|
// Extracts the least significant bit (LSB) of each pixel brightness while filtering out bias
|
||||||
|
// It's a combination of methods from the other two entropy extraction algorithms
|
||||||
|
let mut entropy = Vec::new();
|
||||||
|
|
||||||
|
for (&c, &p) in current.iter().zip(previous.iter()) {
|
||||||
|
if c != p && (low..=high).contains(&c) {
|
||||||
|
entropy.push(c & 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.bits_to_bytes(&entropy)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn shannon(&self, data: &Vec<u8>) -> f64 {
|
pub fn shannon(&self, data: &Vec<u8>) -> f64 {
|
||||||
|
|
@ -201,6 +194,31 @@ pub extern "C" fn pick_and_flip(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn tune_and_prune(
|
||||||
|
current_ptr: *const u8,
|
||||||
|
current_len: usize,
|
||||||
|
previous_ptr: *const u8,
|
||||||
|
previous_len: usize,
|
||||||
|
low: u8,
|
||||||
|
high: u8,
|
||||||
|
result_ptr: *mut u8,
|
||||||
|
result_len: &mut usize,
|
||||||
|
) {
|
||||||
|
let current = unsafe { slice::from_raw_parts(current_ptr, current_len) };
|
||||||
|
let previous = unsafe { slice::from_raw_parts(previous_ptr, previous_len) };
|
||||||
|
|
||||||
|
let ocelli = Ocelli;
|
||||||
|
let result = ocelli.tune_and_prune(¤t.to_vec(), &previous.to_vec(), low, high);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let result_slice = slice::from_raw_parts_mut(result_ptr, result.len());
|
||||||
|
result_slice.copy_from_slice(&result);
|
||||||
|
*result_len = result.len();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn shannon(data_ptr: *const u8, data_len: usize) -> f64 {
|
pub extern "C" fn shannon(data_ptr: *const u8, data_len: usize) -> f64 {
|
||||||
let data = unsafe { slice::from_raw_parts(data_ptr, data_len) };
|
let data = unsafe { slice::from_raw_parts(data_ptr, data_len) };
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue