diff --git a/Cargo.toml b/Cargo.toml index 9601b49..38455b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ path = "src/main.rs" tokio = { version = "1.40", features = ["full"] } redis = { version = "0.27", features = ["tokio-comp", "streams"] } ort = { version = "2.0.0-rc.9", features = ["ndarray", "download-binaries"] } -ndarray = "0.16" +ndarray = "0.17" image = "0.25" serde = { version = "1", features = ["derive"] } serde_json = "1" @@ -25,6 +25,7 @@ base64 = "0.22" sqlx = { version = "0.8", features = ["runtime-tokio", "postgres", "chrono", "uuid"] } uuid = { version = "1", features = ["v4", "serde"] } chrono = { version = "0.4", features = ["serde"] } +dotenvy = "0.15" [profile.release] opt-level = 3 diff --git a/src/inference/detector.rs b/src/inference/detector.rs index cdcaa8f..4fa25fc 100644 --- a/src/inference/detector.rs +++ b/src/inference/detector.rs @@ -31,10 +31,18 @@ pub struct FailureDetector { impl FailureDetector { pub fn load(model_path: &str, input_size: u32, confidence_threshold: f32, intra_threads: usize) -> anyhow::Result> { - let session = Session::builder()? - .with_optimization_level(GraphOptimizationLevel::Level3)? - .with_intra_threads(intra_threads)? - .commit_from_file(model_path)?; + // ort's builder error types carry raw FFI pointers that aren't + // Send/Sync, so they can't flow through `?` into `anyhow::Result` + // directly (anyhow requires Send + Sync + 'static). Stringify at + // the boundary instead. + let session = Session::builder() + .map_err(|e| anyhow::anyhow!("failed to create ONNX Runtime session builder: {e}"))? + .with_optimization_level(GraphOptimizationLevel::Level3) + .map_err(|e| anyhow::anyhow!("failed to set graph optimization level: {e}"))? + .with_intra_threads(intra_threads) + .map_err(|e| anyhow::anyhow!("failed to set intra-op thread count: {e}"))? + .commit_from_file(model_path) + .map_err(|e| anyhow::anyhow!("failed to load ONNX model at {model_path}: {e}"))?; Ok(Arc::new(Self { session: Mutex::new(session), @@ -50,7 +58,7 @@ impl FailureDetector { let input = Value::from_array(tensor)?; let mut session = self.session.lock().await; - let outputs = session.run(ort::inputs!["images" => input]?)?; + let outputs = session.run(ort::inputs!["images" => input])?; // Exported with NMS baked into the graph: output0 is [1, N, 6] // rows of (x1, y1, x2, y2, confidence, class_id) in input-pixel space. diff --git a/src/inference/preprocess.rs b/src/inference/preprocess.rs index 57f974c..124b1fb 100644 --- a/src/inference/preprocess.rs +++ b/src/inference/preprocess.rs @@ -1,4 +1,4 @@ -use image::{DynamicImage, GenericImageView}; +use image::DynamicImage; use ndarray::Array4; /// Resizes an RGB frame to a square `size x size` input tensor in CHW layout, @@ -18,7 +18,3 @@ pub fn to_input_tensor(image: &DynamicImage, size: u32) -> Array4 { tensor } - -pub fn frame_dimensions(image: &DynamicImage) -> (u32, u32) { - image.dimensions() -} diff --git a/src/main.rs b/src/main.rs index 56ec210..71d8896 100644 --- a/src/main.rs +++ b/src/main.rs @@ -132,18 +132,8 @@ async fn process_entry( Ok(()) } +/// Loads `.env` if present; a missing file is fine (env vars may already be +/// set by the process supervisor), so the error is deliberately ignored. fn dotenvy_load() { - if let Ok(contents) = std::fs::read_to_string(".env") { - for line in contents.lines() { - let line = line.trim(); - if line.is_empty() || line.starts_with('#') { - continue; - } - if let Some((key, value)) = line.split_once('=') { - if std::env::var(key).is_err() { - std::env::set_var(key, value); - } - } - } - } + let _ = dotenvy::dotenv(); }