// This file is part of the open-source port of SeetaFace engine, which originally includes three modules: // SeetaFace Detection, SeetaFace Alignment, and SeetaFace Identification. // // This file is part of the SeetaFace Detection module, containing codes implementing the face detection method described in the following paper: // // Funnel-structured cascade for multi-view face detection with alignment awareness, // Shuzhe Wu, Meina Kan, Zhenliang He, Shiguang Shan, Xilin Chen. // In Neurocomputing (under review) // // Copyright (C) 2016, Visual Information Processing and Learning (VIPL) group, // Institute of Computing Technology, Chinese Academy of Sciences, Beijing, China. // // As an open-source face recognition engine: you can redistribute SeetaFace source codes // and/or modify it under the terms of the BSD 2-Clause License. // // You should have received a copy of the BSD 2-Clause License along with the software. // If not, see < https://opensource.org/licenses/BSD-2-Clause>. use std::env::Args; use std::time::{Duration, Instant}; use image::{DynamicImage, GrayImage, Rgb}; use imageproc::drawing::draw_hollow_rect_mut; use imageproc::rect::Rect; use rustface::{Detector, FaceInfo, ImageData}; const OUTPUT_FILE: &str = "test.png"; fn main() { let options = match Options::parse(std::env::args()) { Ok(options) => options, Err(message) => { println!("Failed to parse program arguments: {}", message); std::process::exit(1) } }; let mut detector = match rustface::create_detector(options.model_path()) { Ok(detector) => detector, Err(error) => { println!("Failed to create detector: {}", error.to_string()); std::process::exit(1) } }; detector.set_min_face_size(20); detector.set_score_thresh(2.0); detector.set_pyramid_scale_factor(0.8); detector.set_slide_window_step(4, 4); let image: DynamicImage = match image::open(options.image_path()) { Ok(image) => image, Err(message) => { println!("Failed to read image: {}", message); std::process::exit(1) } }; let mut rgb = image.to_rgb8(); let faces = detect_faces(&mut *detector, &image.to_luma8()); for face in faces { let bbox = face.bbox(); let rect = Rect::at(bbox.x(), bbox.y()).of_size(bbox.width(), bbox.height()); draw_hollow_rect_mut(&mut rgb, rect, Rgb([255, 0, 0])); } match rgb.save(OUTPUT_FILE) { Ok(_) => println!("Saved result to {}", OUTPUT_FILE), Err(message) => println!("Failed to save result to a file. Reason: {}", message), } } fn detect_faces(detector: &mut dyn Detector, gray: &GrayImage) -> Vec { let (width, height) = gray.dimensions(); let mut image = ImageData::new(gray, width, height); let now = Instant::now(); let faces = detector.detect(&mut image); println!( "Found {} faces in {} ms", faces.len(), get_millis(now.elapsed()) ); faces } fn get_millis(duration: Duration) -> u64 { duration.as_secs() * 1000u64 + u64::from(duration.subsec_nanos() / 1_000_000) } struct Options { image_path: String, model_path: String, } impl Options { fn parse(args: Args) -> Result { let args: Vec = args.into_iter().collect(); if args.len() != 3 { return Err(format!("Usage: {} ", args[0])); } let model_path = args[1].clone(); let image_path = args[2].clone(); Ok(Options { image_path, model_path, }) } fn image_path(&self) -> &str { &self.image_path[..] } fn model_path(&self) -> &str { &self.model_path[..] } }