Skip to content

Commit a8c709a

Browse files
authored
Automatic Video Frame Resizing in edgeimpulsevideoinfer (#29)
This PR enhances the `edgeimpulsevideoinfer` GStreamer element to automatically handle video frame resizing internally, eliminating the need for users to manually resize frames before inference. Previously, users were required to ensure video frames matched the ML model's expected input dimensions (e.g., 224x224). This forced complex GStreamer pipelines with external `videoscale` elements and manual resolution management: ```bash # Old approach - complex pipeline with manual resizing gst-launch-1.0 qtiqmmfsrc camera=0 ! video/x-raw,format=NV12,width=1920,height=1080,framerate=30/1 ! \ videoconvert ! videoscale ! video/x-raw,format=RGB,width=224,height=224 ! \ edgeimpulsevideoinfer threshold="3.minScore=0.5" ! edgeimpulseoverlay ! \ videoconvert ! videoscale ! video/x-raw,format=RGB,width=1920,height=1080 ! \ waylandsink display="wayland-1" sync=false fullscreen=true ``` ```bash # New approach - clean, simple pipeline gst-launch-1.0 qtiqmmfsrc camera=0 ! video/x-raw,format=NV12,width=1920,height=1080,framerate=30/1 ! \ videoconvert ! edgeimpulsevideoinfer threshold="3.minScore=0.5" ! \ edgeimpulseoverlay ! waylandsink display="wayland-1" sync=false fullscreen=true ```
1 parent eab604a commit a8c709a

File tree

9 files changed

+653
-181
lines changed

9 files changed

+653
-181
lines changed

README.md

Lines changed: 234 additions & 39 deletions
Large diffs are not rendered by default.

build.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
11
fn main() {
2+
// Set PKG_CONFIG_PATH to include all necessary directories for macOS
3+
if cfg!(target_os = "macos") {
4+
let homebrew_prefix = "/opt/homebrew";
5+
let pkg_config_paths = vec![
6+
format!("{}/opt/libxml2/lib/pkgconfig", homebrew_prefix),
7+
format!("{}/lib/pkgconfig", homebrew_prefix),
8+
format!("{}/share/pkgconfig", homebrew_prefix),
9+
];
10+
11+
let existing_path = std::env::var("PKG_CONFIG_PATH").unwrap_or_default();
12+
let new_path = if existing_path.is_empty() {
13+
pkg_config_paths.join(":")
14+
} else {
15+
format!("{}:{}", pkg_config_paths.join(":"), existing_path)
16+
};
17+
18+
println!("cargo:warning=Setting PKG_CONFIG_PATH to: {}", new_path);
19+
std::env::set_var("PKG_CONFIG_PATH", &new_path);
20+
println!("cargo:rerun-if-env-changed=PKG_CONFIG_PATH");
21+
}
22+
223
gst_plugin_version_helper::info()
324
}

examples/audio_inference.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
//! # FFI mode (default, no model path needed):
99
//! cargo run --example audio_inference [OPTIONS]
1010
//!
11-
//! # EIM mode (legacy, requires model path):
11+
//! # EIM mode (requires model path):
1212
//! cargo run --example audio_inference -- --model <path_to_model> [OPTIONS]
1313
//!
1414
//! Optional arguments:
@@ -37,7 +37,7 @@ use std::path::Path;
3737
/// Command line parameters for the real-time audio classification example
3838
#[derive(Parser, Debug)]
3939
struct AudioClassifyParams {
40-
/// Path to the Edge Impulse model file (.eim) - EIM mode only (legacy)
40+
/// Path to the Edge Impulse model file (.eim) - EIM mode only
4141
#[clap(short, long)]
4242
model: Option<String>,
4343

examples/image_inference.rs

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,17 @@
55
//! This example demonstrates how to use the Edge Impulse GStreamer plugin to perform
66
//! image classification using a trained model on a single image file.
77
//!
8+
//! The edgeimpulsevideoinfer element automatically handles frame resizing to match model
9+
//! input requirements and scales detection results back to the original resolution.
10+
//!
811
//! Usage:
912
//! # EIM mode (requires model path):
1013
//! cargo run --example image_inference -- --model <path_to_model> --image <path_to_image>
1114
//!
1215
//! # FFI mode (no model path needed):
1316
//! cargo run --example image_inference -- --image <path_to_image>
1417
//!
18+
//!
1519
//! Environment setup:
1620
//! export GST_PLUGIN_PATH="target/debug:$GST_PLUGIN_PATH"
1721
@@ -42,14 +46,6 @@ struct ImageClassifyParams {
4246
#[arg(short, long, default_value = "RGB")]
4347
format: String,
4448

45-
/// Input width
46-
#[arg(short = 'W', long, default_value = "96")]
47-
width: i32,
48-
49-
/// Input height
50-
#[arg(short = 'H', long, default_value = "96")]
51-
height: i32,
52-
5349
/// Enable debug output
5450
#[arg(short, long)]
5551
debug: bool,
@@ -103,8 +99,6 @@ fn create_pipeline(args: &ImageClassifyParams) -> Result<gst::Pipeline, Box<dyn
10399
"caps",
104100
&gst::Caps::builder("video/x-raw")
105101
.field("format", &args.format)
106-
.field("width", &args.width)
107-
.field("height", &args.height)
108102
.build(),
109103
)
110104
.build()?;
@@ -222,7 +216,6 @@ fn example_main() -> Result<(), Box<dyn Error>> {
222216
if let Some(output) = &args.output {
223217
println!("💾 Output image: {}", output);
224218
}
225-
println!("📐 Image dimensions: {}x{}", args.width, args.height);
226219
println!("🎨 Format: {}", args.format);
227220
println!("🔧 Debug mode: {}", args.debug);
228221

examples/image_slideshow.rs

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use image::io::Reader as ImageReader;
1313
use image::ImageFormat;
1414

1515
/// A GStreamer-based image slideshow that runs inference on images from a folder.
16+
/// The edgeimpulsevideoinfer element automatically handles frame resizing to match model requirements.
1617
#[derive(Parser, Debug)]
1718
#[command(author, version, about, long_about = None)]
1819
struct Args {
@@ -28,14 +29,6 @@ struct Args {
2829
#[arg(short = 'n', long, default_value = "100")]
2930
max_images: usize,
3031

31-
/// Input width
32-
#[arg(short = 'W', long)]
33-
width: i32,
34-
35-
/// Input height
36-
#[arg(short = 'H', long)]
37-
height: i32,
38-
3932
/// Slideshow framerate (images per second, default: 1)
4033
#[arg(long, default_value = "1")]
4134
framerate: i32,
@@ -187,7 +180,7 @@ fn example_main() -> Result<()> {
187180
.property("max-size-buffers", 1u32)
188181
.property("max-size-time", 30000000000u64)
189182
.build()?;
190-
let videoscale = ElementFactory::make("videoscale").build()?;
183+
// videoscale removed - edgeimpulsevideoinfer now handles resizing
191184
let videorate = ElementFactory::make("videorate")
192185
.property("max-rate", args.framerate)
193186
.build()?;
@@ -203,18 +196,15 @@ fn example_main() -> Result<()> {
203196
.build()?;
204197

205198
// Set caps for GRAY8 before inference, including framerate to control slideshow speed
199+
// The edgeimpulsevideoinfer element will automatically resize to match model requirements
206200
let caps_gray = gstreamer::Caps::builder("video/x-raw")
207201
.field("format", "GRAY8")
208-
.field("width", args.width)
209-
.field("height", args.height)
210202
.field("framerate", &gstreamer::Fraction::new(args.framerate, 1))
211203
.build();
212204
capsfilter_gray.set_property("caps", &caps_gray);
213205

214206
let caps_rgb = gstreamer::Caps::builder("video/x-raw")
215207
.field("format", "RGB")
216-
.field("width", args.width)
217-
.field("height", args.height)
218208
.build();
219209
capsfilter_rgb.set_property("caps", &caps_rgb);
220210

@@ -223,7 +213,6 @@ fn example_main() -> Result<()> {
223213
&decodebin,
224214
&videoconvert1,
225215
&queue,
226-
&videoscale,
227216
&videorate,
228217
&capsfilter_gray,
229218
&edgeimpulse,
@@ -259,7 +248,6 @@ fn example_main() -> Result<()> {
259248
Element::link_many([
260249
&videoconvert1,
261250
&queue,
262-
&videoscale,
263251
&videorate,
264252
&capsfilter_gray,
265253
&edgeimpulse,

0 commit comments

Comments
 (0)