function/src/face_detection.rs (28 lines of code) (raw):

// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. extern crate base64; extern crate image; #[cfg(feature = "mesalock_sgx")] extern crate rustface; use std::convert::TryFrom; use teaclave_types::{FunctionArguments, FunctionRuntime}; #[derive(Default)] pub struct FaceDetection; #[derive(serde::Deserialize)] struct FaceDetectionArguments { image: Vec<u8>, /// Set the size of the sliding window. /// /// The minimum size is constrained as no smaller than 20. /// /// # Panics /// /// Panics if `wnd_size` is less than 20. window_size: Option<u32>, /// Set the sliding window step in horizontal and vertical directions. /// /// The steps should take positive values. /// Usually a step of 4 is a reasonable choice. /// /// # Panics /// /// Panics if `step_x` or `step_y` is less than or equal to 0. slide_window_step_x: Option<u32>, slide_window_step_y: Option<u32>, /// Set the minimum size of faces to detect. /// /// The minimum size is constrained as no smaller than 20. /// /// # Panics /// /// Panics if `min_face_size` is less than 20. min_face_size: Option<u32>, /// Set the maximum size of faces to detect. /// /// The maximum face size actually used is computed as the minimum among: /// user specified size, image width, image height. max_face_size: Option<u32>, /// Set the factor between adjacent scales of image pyramid. /// /// The value of the factor lies in (0.1, 0.99). For example, when it is set as 0.5, /// an input image of size w x h will be resized to 0.5w x 0.5h, 0.25w x 0.25h, 0.125w x 0.125h, etc. /// /// # Panics /// /// Panics if `scale_factor` is less than 0.01 or greater than 0.99 pyramid_scale_factor: Option<f32>, /// Set the score threshold of detected faces. /// /// Detections with scores smaller than the threshold will not be returned. /// Typical threshold values include 0.95, 2.8, 4.5. One can adjust the /// threshold based on his or her own test set. /// /// Smaller values result in more detections (possibly increasing the number of false positives), /// larger values result in fewer detections (possibly increasing the number of false negatives). /// /// # Panics /// /// Panics if `thresh` is less than or equal to 0. score_thresh: Option<f64>, } impl TryFrom<FunctionArguments> for FaceDetectionArguments { type Error = anyhow::Error; fn try_from(arguments: FunctionArguments) -> Result<Self, Self::Error> { use anyhow::Context; serde_json::from_str(&arguments.into_string()).context("Cannot deserialize arguments") } } impl FaceDetection { pub const NAME: &'static str = "builtin-face-detection"; pub fn new() -> Self { Default::default() } pub fn run( &self, arguments: FunctionArguments, _runtime: FunctionRuntime, ) -> anyhow::Result<String> { let arguments = FaceDetectionArguments::try_from(arguments)?; let image = arguments.image; let img = image::load_from_memory(&image)?; let mut detector = rustface::create_default_detector()?; if let Some(window_size) = arguments.window_size { detector.set_window_size(window_size); } if let (Some(step_x), Some(step_y)) = (arguments.slide_window_step_x, arguments.slide_window_step_y) { detector.set_slide_window_step(step_x, step_y); } if let Some(min_face_size) = arguments.min_face_size { detector.set_min_face_size(min_face_size); } if let Some(max_face_size) = arguments.max_face_size { detector.set_max_face_size(max_face_size); } if let Some(pyramid_scale_factor) = arguments.pyramid_scale_factor { detector.set_pyramid_scale_factor(pyramid_scale_factor); } if let Some(score_thresh) = arguments.score_thresh { detector.set_score_thresh(score_thresh); } let faces = rustface::detect_faces(&mut *detector, img); let result = serde_json::to_string(&faces)?; Ok(result) } } #[cfg(feature = "enclave_unit_test")] pub mod tests { use super::*; use serde_json::json; use std::untrusted::fs; use teaclave_runtime::*; use teaclave_test_utils::*; use teaclave_types::*; pub fn run_tests() -> bool { run_tests!(test_face_detection) } fn test_face_detection() { let input = "fixtures/functions/face_detection/input.jpg"; let image = fs::read(input).unwrap(); let arguments = FunctionArguments::from_json(json!({ "image": &image, "min_face_size": 20, "score_thresh": 2.0, "pyramid_scale_factor": 0.8, "slide_window_step_x": 4, "slide_window_step_y": 4 })) .unwrap(); let input_files = StagedFiles::new(hashmap!()); let output_files = StagedFiles::new(hashmap!()); let runtime = Box::new(RawIoRuntime::new(input_files, output_files)); let result = FaceDetection::new().run(arguments, runtime).unwrap(); let json_result: serde_json::Value = serde_json::from_str(&result).unwrap(); assert_eq!(json_result.as_array().unwrap().len(), 29); } }