blob: 7046b7c9111fcfc3ef6b439498d2c882ccd6ef39 [file] [log] [blame]
// 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);
}
}