diff --git a/examples/detect_faces/Cargo.toml b/examples/detect_faces/Cargo.toml new file mode 100644 index 0000000..da3ebfd --- /dev/null +++ b/examples/detect_faces/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "detect_faces" +version = "0.1.0" +edition = "2021" + +[dependencies] +az_analyze_image = "0.1.1" +image = "0.25.5" +imageproc = "0.25.0" +reqwest = "0.12.9" +tokio = { version = "1.41.1", features = ["full"] } diff --git a/examples/detect_faces/README.md b/examples/detect_faces/README.md new file mode 100644 index 0000000..f593b12 --- /dev/null +++ b/examples/detect_faces/README.md @@ -0,0 +1,22 @@ +# Detect Faces + +> [!NOTE] +> API Version 3.2 + +## About + +In this example, the Analyze Image API is used to detect faces in an image. + +For each detected face, the API returns a **bounding box** (face rectangle), +which defines a rectangle in the image. + +This example draws the bounding boxes on the image for all detected faces and +saves it to `out.jpg`: + +

+ +

+ +## Image Source + +https://images.pexels.com/photos/1367269/pexels-photo-1367269.jpeg diff --git a/examples/detect_faces/out.jpg b/examples/detect_faces/out.jpg new file mode 100644 index 0000000..4c3c060 Binary files /dev/null and b/examples/detect_faces/out.jpg differ diff --git a/examples/detect_faces/src/main.rs b/examples/detect_faces/src/main.rs new file mode 100644 index 0000000..d1f76f0 --- /dev/null +++ b/examples/detect_faces/src/main.rs @@ -0,0 +1,101 @@ +/* +Copyright 2024 Owain Davies +SPDX-License-Identifier: Apache-2.0 OR MIT +*/ + +use az_analyze_image::v32::client::{AnalyzeImageOptions, Client}; +use az_analyze_image::v32::{FaceDescription, VisualFeatureTypes}; + +use image::{load_from_memory_with_format, ImageFormat, Rgb, RgbImage}; +use imageproc::drawing::draw_hollow_rect_mut; +use imageproc::rect::Rect; + +use reqwest::Client as ReqwestClient; + +use std::env::var; +use std::error::Error; + +const IMAGE_URL: &str = + "https://images.pexels.com/photos/1367269/pexels-photo-1367269.jpeg"; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client: Client = get_client(); + + // Call the Analyze Image API with the face detection feature enabled + let faces: Vec = detect_faces(&client, IMAGE_URL).await?; + + // Download the image bytes + let img_bytes: Vec = download_image(IMAGE_URL).await?; + + // Load the image + let mut img: RgbImage = + load_from_memory_with_format(&img_bytes, ImageFormat::Jpeg)?.to_rgb8(); + + // Draw bounding boxes around all detected faces + draw_face_bounding_boxes(&mut img, &faces); + + // Save the new image + img.save("out.jpg")?; + + Ok(()) +} + +fn get_client() -> Client { + let key = var("CV_KEY").expect("no CV_KEY"); + let endpoint = var("CV_ENDPOINT").expect("no CV_ENDPOINT"); + + Client::new(key, &endpoint).expect("failed to create client") +} + +async fn detect_faces( + client: &Client, + url: &str, +) -> Result, Box> { + let features = vec![VisualFeatureTypes::Faces]; + let options = AnalyzeImageOptions { + visual_features: Some(&features), + ..Default::default() + }; + + let analysis = client.analyze_image_url(url, options).await?; + + Ok(analysis.faces.unwrap()) +} + +async fn download_image(url: &str) -> Result, Box> { + let client = ReqwestClient::new(); + let response = client.get(url).send().await?.bytes().await?; + Ok(response.to_vec()) +} + +fn draw_face_bounding_boxes(img: &mut RgbImage, faces: &[FaceDescription]) { + let thickness = 16; + let colors = [Rgb([1, 255, 79]), Rgb([0, 237, 245]), Rgb([118, 232, 181])]; + + for (i, face) in faces.iter().enumerate() { + let bounding_box = Rect::at( + face.face_rectangle.left as i32, + face.face_rectangle.top as i32, + ) + .of_size(face.face_rectangle.width, face.face_rectangle.height); + + let color = colors[i % colors.len()]; + + draw_thick_rect(img, bounding_box, color, thickness); + } +} + +fn draw_thick_rect( + img: &mut RgbImage, + rect: Rect, + color: Rgb, + thickness: i32, +) { + for i in 0..thickness { + let new_rect = Rect::at(rect.left() - i, rect.top() - i) + .of_size(rect.width() + i as u32 * 2, rect.height() + i as u32 * 2); + + draw_hollow_rect_mut(img, new_rect, color); + } +}