|
|
@ -4,7 +4,7 @@ use std::time::Duration; |
|
|
|
use anyhow::Context; |
|
|
|
use anyhow::Context; |
|
|
|
|
|
|
|
|
|
|
|
use chrono::{NaiveDateTime, Utc}; |
|
|
|
use chrono::{NaiveDateTime, Utc}; |
|
|
|
use image::{DynamicImage, GenericImageView, Pixel}; |
|
|
|
use image::{GenericImageView, Pixel}; |
|
|
|
use log::{debug, error, info}; |
|
|
|
use log::{debug, error, info}; |
|
|
|
|
|
|
|
|
|
|
|
/// Folder the camera images are uploaded to
|
|
|
|
/// Folder the camera images are uploaded to
|
|
|
@ -39,7 +39,7 @@ const THUMB_H: usize = 8; |
|
|
|
|
|
|
|
|
|
|
|
/// Threshold for bird detection.
|
|
|
|
/// Threshold for bird detection.
|
|
|
|
/// Bird is detected if the sum of deviations from a median value in a picture exceeds this threshold.
|
|
|
|
/// Bird is detected if the sum of deviations from a median value in a picture exceeds this threshold.
|
|
|
|
const THRESHOLD_DEVIATION: u32 = 500; |
|
|
|
const THRESHOLD_DEVIATION: u32 = 600; |
|
|
|
|
|
|
|
|
|
|
|
fn main() { |
|
|
|
fn main() { |
|
|
|
env_logger::init(); |
|
|
|
env_logger::init(); |
|
|
@ -77,8 +77,7 @@ fn process_camera_pics() -> anyhow::Result<()> { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct Pic { |
|
|
|
struct Pic { |
|
|
|
crop: DynamicImage, |
|
|
|
full_path: PathBuf, |
|
|
|
// the actual thumb is sized to fit here, it can be a bit smaller
|
|
|
|
|
|
|
|
thumb: [u8; THUMB_H * THUMB_W], |
|
|
|
thumb: [u8; THUMB_H * THUMB_W], |
|
|
|
timestamp: i64, |
|
|
|
timestamp: i64, |
|
|
|
filename: String, |
|
|
|
filename: String, |
|
|
@ -108,7 +107,7 @@ fn process_camera_pics() -> anyhow::Result<()> { |
|
|
|
let thumb = crop.thumbnail(THUMB_W as u32, THUMB_H as u32); |
|
|
|
let thumb = crop.thumbnail(THUMB_W as u32, THUMB_H as u32); |
|
|
|
|
|
|
|
|
|
|
|
let mut pic = Pic { |
|
|
|
let mut pic = Pic { |
|
|
|
crop, |
|
|
|
full_path: dir_entry.path(), |
|
|
|
thumb: [0u8; THUMB_H * THUMB_W], |
|
|
|
thumb: [0u8; THUMB_H * THUMB_W], |
|
|
|
timestamp: meta.mtime(), |
|
|
|
timestamp: meta.mtime(), |
|
|
|
filename: dir_entry.path().file_name().unwrap_or_default() |
|
|
|
filename: dir_entry.path().file_name().unwrap_or_default() |
|
|
@ -156,17 +155,17 @@ fn process_camera_pics() -> anyhow::Result<()> { |
|
|
|
|
|
|
|
|
|
|
|
let datetime_str = parse_unix_ts(pic.timestamp).format("%Y-%m-%d_%H-%M-%S"); |
|
|
|
let datetime_str = parse_unix_ts(pic.timestamp).format("%Y-%m-%d_%H-%M-%S"); |
|
|
|
let path = format!("{}/{}.jpg", FOLDER_WITH_BIRD_IMAGES, datetime_str); |
|
|
|
let path = format!("{}/{}.jpg", FOLDER_WITH_BIRD_IMAGES, datetime_str); |
|
|
|
let mut pb = PathBuf::from(path); |
|
|
|
let mut target_path = PathBuf::from(path); |
|
|
|
|
|
|
|
|
|
|
|
// Ensure we do not overwrite a file if there are multiple in the same second!
|
|
|
|
// Ensure we do not overwrite a file if there are multiple in the same second!
|
|
|
|
// This adds -2, -3 etc. to the file name
|
|
|
|
// This adds -2, -3 etc. to the file name
|
|
|
|
let mut cnt = 2; |
|
|
|
let mut cnt = 2; |
|
|
|
while pb.exists() { |
|
|
|
while target_path.exists() { |
|
|
|
pb.set_file_name(format!("{}-{cnt}.jpg", datetime_str)); |
|
|
|
target_path.set_file_name(format!("{}-{cnt}.jpg", datetime_str)); |
|
|
|
cnt += 1; |
|
|
|
cnt += 1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if let Err(e) = pic.crop.save(pb) { |
|
|
|
if let Err(e) = std::fs::copy(pic.full_path, target_path) { |
|
|
|
error!("Fail to save pic: {e}"); |
|
|
|
error!("Fail to save pic: {e}"); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|