-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
134 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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"] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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`: | ||
|
||
<p align="center"> | ||
<img src="https://github.com/OTheDev/az_analyze_image/blob/main/examples/detect_faces/out.jpg?raw=true" /> | ||
</p> | ||
|
||
## Image Source | ||
|
||
https://images.pexels.com/photos/1367269/pexels-photo-1367269.jpeg |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<dyn Error>> { | ||
let client: Client = get_client(); | ||
|
||
// Call the Analyze Image API with the face detection feature enabled | ||
let faces: Vec<FaceDescription> = detect_faces(&client, IMAGE_URL).await?; | ||
|
||
// Download the image bytes | ||
let img_bytes: Vec<u8> = 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<Vec<FaceDescription>, Box<dyn Error>> { | ||
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<Vec<u8>, Box<dyn Error>> { | ||
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<u8>, | ||
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); | ||
} | ||
} |