diff --git a/src/pipeline/steps/alignment.rs b/src/pipeline/steps/alignment.rs index 85674be..b80b041 100644 --- a/src/pipeline/steps/alignment.rs +++ b/src/pipeline/steps/alignment.rs @@ -280,21 +280,15 @@ fn apply_affine_transform( let r = (p00[0] as f32 * (1.0 - dx) * (1.0 - dy) + p10[0] as f32 * dx * (1.0 - dy) + p01[0] as f32 * (1.0 - dx) * dy - + p11[0] as f32 * dx * dy) - .round() - .clamp(0.0, 255.0) as u8; + + p11[0] as f32 * dx * dy) as u8; let g = (p00[1] as f32 * (1.0 - dx) * (1.0 - dy) + p10[1] as f32 * dx * (1.0 - dy) + p01[1] as f32 * (1.0 - dx) * dy - + p11[1] as f32 * dx * dy) - .round() - .clamp(0.0, 255.0) as u8; + + p11[1] as f32 * dx * dy) as u8; let b = (p00[2] as f32 * (1.0 - dx) * (1.0 - dy) + p10[2] as f32 * dx * (1.0 - dy) + p01[2] as f32 * (1.0 - dx) * dy - + p11[2] as f32 * dx * dy) - .round() - .clamp(0.0, 255.0) as u8; + + p11[2] as f32 * dx * dy) as u8; Rgb([r, g, b]) }); diff --git a/src/pipeline/steps/brightness.rs b/src/pipeline/steps/brightness.rs index 3e6443a..9d3765a 100644 --- a/src/pipeline/steps/brightness.rs +++ b/src/pipeline/steps/brightness.rs @@ -10,7 +10,7 @@ use crate::pipeline::{ ProcessingStep, StepOutcome, }; use async_trait::async_trait; -use image::{DynamicImage, Rgb}; +use image::{DynamicImage, GenericImageView, Rgb}; /// Validates image brightness and skips images outside acceptable range. /// @@ -35,8 +35,7 @@ impl BrightnessStep { x2: u32, y2: u32, ) -> f32 { - let luma = image.to_luma8(); - let (img_width, img_height) = luma.dimensions(); + let (img_width, img_height) = image.dimensions(); // Clamp coordinates to image bounds let x1 = x1.min(img_width.saturating_sub(1)); @@ -44,26 +43,25 @@ impl BrightnessStep { let x2 = x2.min(img_width); let y2 = y2.min(img_height); - // Ensure valid region if x2 <= x1 || y2 <= y1 { return 0.0; } - let width = x2 - x1; - let height = y2 - y1; - let pixel_count = width as u64 * height as u64; - + let pixel_count = (x2 - x1) as u64 * (y2 - y1) as u64; if pixel_count == 0 { return 0.0; } - // Sum luminance values from the pre-computed luma image - // (to_luma8 uses the standard Y = 0.299*R + 0.587*G + 0.114*B conversion) + // Convert only the face region to RGB (avoids converting the full image). + // to_rgb8() on an already-RGB8 image is a cheap clone. + let rgb = image.crop_imm(x1, y1, x2 - x1, y2 - y1).to_rgb8(); + + // Sum luminance using standard Y = 0.299*R + 0.587*G + 0.114*B. + // Iterate over raw bytes directly to avoid per-pixel get_pixel overhead. let mut total_luminance: u64 = 0; - for y in y1..y2 { - for x in x1..x2 { - total_luminance += luma.get_pixel(x, y)[0] as u64; - } + for chunk in rgb.as_raw().chunks_exact(3) { + total_luminance += + (299 * chunk[0] as u64 + 587 * chunk[1] as u64 + 114 * chunk[2] as u64) / 1000; } (total_luminance as f32 / pixel_count as f32) / 255.0 diff --git a/src/pipeline/steps/head_pose.rs b/src/pipeline/steps/head_pose.rs index 62a57b4..271bf36 100644 --- a/src/pipeline/steps/head_pose.rs +++ b/src/pipeline/steps/head_pose.rs @@ -235,19 +235,13 @@ impl ProcessingStep for HeadPoseStep { image.clone() }; - // Run inference on the face crop (blocking CPU work offloaded from async runtime) - let pose = match tokio::task::spawn_blocking(move || model.estimate(&face_image)).await { - Ok(Ok(p)) => p, - Ok(Err(e)) => { - return StepOutcome::Error { - ctx, - error: format!("Head pose estimation failed: {}", e), - }; - } + // Run inference on the face crop + let pose = match model.estimate(&face_image) { + Ok(p) => p, Err(e) => { return StepOutcome::Error { ctx, - error: format!("Head pose inference task panicked: {}", e), + error: format!("Head pose estimation failed: {}", e), }; } };