diff --git a/README.md b/README.md index d0b1429..3aab15f 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Client/server face detection from your webcam with [`tokio`][tokio], ![Concurrent inference](./resources/Concurrent_Inference_Example.png) -In the example image above, we use two laptops. Both run a `cam_sender` client +In the example image above, we use two laptops. Both run a `socket_sender` client and send image streams to the second laptop, which runs the inference for face detection on both of those streams concurrently with the `infer_server`. We access both streams from the first laptop. This show-cases a few features: @@ -17,6 +17,18 @@ access both streams from the first laptop. This show-cases a few features: streams while a performant server does the inference) - Access to the streams over the network +Run with: + +```sh +# server +RUST_LOG=debug cargo run --release --bin infer_server + +# client +RUST_LOG=debug cargo run --release --bin socket_sender +``` + +Then check e.g. http://127.0.0.1:3000/face_stream?name=simon + ## Overview This is my second implementation of this project. Changes to the first version: @@ -31,11 +43,8 @@ This is my second implementation of this project. Changes to the first version: streams and serves endpoints of both the raw streams and streams with faces infered. The previous version used [`actix-web`][actix-web] as web framework, switching to [`axum`][axum] was mostly curiosity. -- `socket_sender` establishes a TCP connection to the `infer_sender` and streams +- `socket_sender` establishes a TCP connection to the `infer_server` and streams frames to it which can be shown raw or infered in the browser. -- `multipart_sender` allows us to send a stream as multipart form data via HTTP - to the `infer_server`. It does not yield a good performance in practice and is - only left in here for reference purposes. - In the first version, opening a tab to either the raw or infered stream endpoint triggered an independent run of the capture function. So opening four tabs meant having four streams capture independently. In the refactored diff --git a/cam_sender/Cargo.toml b/cam_sender/Cargo.toml index 3c9ec39..852dd1b 100644 --- a/cam_sender/Cargo.toml +++ b/cam_sender/Cargo.toml @@ -3,8 +3,6 @@ name = "cam_sender" version = "0.3.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] anyhow = "1.0.75" bincode = "1.3.3" diff --git a/cam_sender/src/sensors.rs b/cam_sender/src/sensors.rs index fd4677d..66871c3 100644 --- a/cam_sender/src/sensors.rs +++ b/cam_sender/src/sensors.rs @@ -64,7 +64,6 @@ pub fn get_max_res_mjpg_capture_fn() -> Result> { ..Default::default() })?; - // Ok(Box::new(move || cam.capture().ok())) Ok(CameraWrapper { inner: cam }) } diff --git a/common/Cargo.toml b/common/Cargo.toml index 28a2e56..72e7af6 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -3,8 +3,6 @@ name = "common" version = "0.3.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] bincode = "1.3.3" serde = { version = "1.0.145", features = ["derive"] } diff --git a/infer_server/Cargo.toml b/infer_server/Cargo.toml index c9b839e..ee456c2 100644 --- a/infer_server/Cargo.toml +++ b/infer_server/Cargo.toml @@ -3,11 +3,6 @@ name = "infer_server" version = "0.3.2" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[lib] -name = "infer_server" -path = "src/lib.rs" - [dependencies] anyhow = "1.0.75" async-stream = "0.3.3" @@ -37,6 +32,3 @@ tokio-stream = { version = "0.1.14", features = ["sync"] } tokio-util = { version = "0.7.4", features = ["net", "codec"] } tract-onnx = "0.19.2" turbojpeg = { version = "0.5.2", features = ["image"] } - -[profile.release] -debug = true diff --git a/infer_server/src/bin/infer_server.rs b/infer_server/src/bin/infer_server.rs index 47ff4fc..d6631c5 100644 --- a/infer_server/src/bin/infer_server.rs +++ b/infer_server/src/bin/infer_server.rs @@ -18,11 +18,11 @@ use infer_server::{ #[derive(Parser, Debug)] #[clap(author, version)] struct Args { - /// Address of the infer server to connect to + /// Address of the HTTP server #[clap(long, default_value = "127.0.0.1:3000")] server_address: String, - /// Address of the infer server to connect to + /// Address of the data socket #[clap(long, default_value = "127.0.0.1:3001")] socket_address: String, } diff --git a/infer_server/src/inferer.rs b/infer_server/src/inferer.rs index a1ed400..cbf20bc 100644 --- a/infer_server/src/inferer.rs +++ b/infer_server/src/inferer.rs @@ -32,7 +32,7 @@ impl Inferer { let width = recv_ref.0; let height = recv_ref.1; - let image: RgbImage = turbojpeg::decompress_image(&recv_ref.2.as_slice()) + let image: RgbImage = turbojpeg::decompress_image(recv_ref.2.as_slice()) .expect("failed to decompress"); if let Ok(bboxes_with_confidences) = self.infer_faces(&image) { let frame = draw_bboxes_on_image(image, bboxes_with_confidences, width, height); diff --git a/infer_server/src/lib.rs b/infer_server/src/lib.rs index 5870745..a9ef6f8 100644 --- a/infer_server/src/lib.rs +++ b/infer_server/src/lib.rs @@ -49,7 +49,7 @@ fn as_jpeg_stream_item(data: &[u8]) -> Bytes { Bytes::copy_from_slice( &[ "--frame\r\nContent-Type: image/jpeg\r\n\r\n".as_bytes(), - &data[..], + data, "\r\n\r\n".as_bytes(), ] .concat(), diff --git a/infer_server/src/meter.rs b/infer_server/src/meter.rs index c3fbb30..3c5ed3e 100644 --- a/infer_server/src/meter.rs +++ b/infer_server/src/meter.rs @@ -7,6 +7,7 @@ use tokio::{task::JoinHandle, time::interval}; pub static METER: Meter = Meter::new(); +#[derive(Default)] pub struct Meter { raw_frames: AtomicU64, infered_frames: AtomicU64, diff --git a/infer_server/tests/integration_tests.rs b/infer_server/tests/integration_tests.rs index fd792c2..0b18137 100644 --- a/infer_server/tests/integration_tests.rs +++ b/infer_server/tests/integration_tests.rs @@ -2,37 +2,37 @@ use std::path::Path; use infer_server::nn::{InferModel, UltrafaceModel}; -// #[tokio::test] -// async fn test_ultraface_640() -> Result<(), Box> { -// let current_workdir = std::env::current_dir()?; -// println!("Running with workdir {}", current_workdir.display()); -// let model = UltrafaceModel::new(infer_server::nn::UltrafaceVariant::W640H480, 0.5, 0.5).await?; +#[tokio::test] +async fn test_ultraface_640() -> Result<(), Box> { + let current_workdir = std::env::current_dir()?; + println!("Running with workdir {}", current_workdir.display()); + let model = UltrafaceModel::new(infer_server::nn::UltrafaceVariant::W640H480, 0.5, 0.5).await?; -// // `cargo test` and debugging the test via IDE have differing work dirs -// let test_pic_dir = { -// let base_dir = "resources/test_pics"; -// match Path::new(base_dir).is_dir() { -// true => base_dir.to_owned(), -// false => format!("../{}", base_dir), -// } -// }; + // `cargo test` and debugging the test via IDE have differing work dirs + let test_pic_dir = { + let base_dir = "resources/test_pics"; + match Path::new(base_dir).is_dir() { + true => base_dir.to_owned(), + false => format!("../{}", base_dir), + } + }; -// let images_with_num_faces = vec![ -// ("bruce-mars-ZXq7xoo98b0-unsplash.jpg", 3), -// ("clarke-sanders-ybPJ47PMT_M-unsplash.jpg", 6), -// ("helena-lopes-e3OUQGT9bWU-unsplash.jpg", 4), -// ("kaleidico-d6rTXEtOclk-unsplash.jpg", 3), -// ("michael-dam-mEZ3PoFGs_k-unsplash.jpg", 1), -// ("mika-W0i1N6FdCWA-unsplash.jpg", 1), -// ("omar-lopez-T6zu4jFhVwg-unsplash.jpg", 10), -// ("ken-cheung-KonWFWUaAuk-unsplash.jpg", 0), -// ]; -// for (filename, expected_num_faces) in images_with_num_faces { -// let image = image::open(Path::new(&test_pic_dir).join(Path::new(filename)))?.to_rgb8(); -// let bboxes_with_confidences = model.run(image)?; + let images_with_num_faces = vec![ + ("bruce-mars-ZXq7xoo98b0-unsplash.jpg", 3), + ("clarke-sanders-ybPJ47PMT_M-unsplash.jpg", 6), + ("helena-lopes-e3OUQGT9bWU-unsplash.jpg", 4), + ("kaleidico-d6rTXEtOclk-unsplash.jpg", 3), + ("michael-dam-mEZ3PoFGs_k-unsplash.jpg", 1), + ("mika-W0i1N6FdCWA-unsplash.jpg", 1), + ("omar-lopez-T6zu4jFhVwg-unsplash.jpg", 10), + ("ken-cheung-KonWFWUaAuk-unsplash.jpg", 0), + ]; + for (filename, expected_num_faces) in images_with_num_faces { + let image = image::open(Path::new(&test_pic_dir).join(Path::new(filename)))?.to_rgb8(); + let bboxes_with_confidences = model.run(&image)?; -// assert_eq!(bboxes_with_confidences.len(), expected_num_faces); -// } + assert_eq!(bboxes_with_confidences.len(), expected_num_faces); + } -// Ok(()) -// } + Ok(()) +}