Skip to main content

oximedia_scene/
lib.rs

1//! Scene understanding and AI-powered video analysis for `OxiMedia`.
2//!
3//! `oximedia-scene` provides comprehensive scene understanding and intelligent video
4//! analysis capabilities for the `OxiMedia` multimedia framework. This includes:
5//!
6//! - **Scene Classification**: Classify scenes (indoor/outdoor, day/night, landscape, portrait)
7//! - **Object Detection**: Lightweight patent-free object detection
8//! - **Activity Recognition**: Recognize activities (walking, running, sports)
9//! - **Shot Composition**: Analyze framing (rule of thirds, symmetry, leading lines)
10//! - **Semantic Segmentation**: Segment image into semantic regions (sky, ground, people)
11//! - **Saliency Detection**: Identify visually important regions
12//! - **Aesthetic Scoring**: Rate aesthetic quality of frames
13//! - **Event Detection**: Detect events in sports and live content
14//! - **Face Detection**: Lightweight face detection (Haar cascades)
15//! - **Logo Detection**: Detect brand logos and graphics
16//!
17//! # Patent-Free Algorithms
18//!
19//! All algorithms are carefully selected to be patent-free:
20//!
21//! - **HOG (Histogram of Oriented Gradients)**: Object detection
22//! - **Haar Cascades**: Face detection
23//! - **Color Histograms**: Scene classification
24//! - **Motion Histograms**: Activity recognition
25//! - **Spectral Saliency**: Attention prediction
26//! - **Graph-based Segmentation**: Semantic regions
27//! - **Rule-based Composition**: Framing analysis
28//!
29//! # Modules
30//!
31//! - [`classify`]: Scene, content, and quality classification
32//! - [`detect`]: Object, face, logo, and text detection
33//! - [`activity`]: Activity and sports recognition
34//! - [`composition`]: Composition rules, balance, and depth analysis
35//! - [`segment`]: Semantic and foreground/background segmentation
36//! - [`saliency`]: Saliency detection and attention prediction
37//! - [`aesthetic`]: Aesthetic quality scoring and feature extraction
38//! - [`event`]: Event detection for sports and live content
39//! - [`features`]: Feature extraction and descriptors
40//!
41//! # Example
42//!
43//! ```
44//! use oximedia_scene::classify::scene::SceneClassifier;
45//! use oximedia_scene::detect::face::FaceDetector;
46//! use oximedia_scene::composition::rules::CompositionAnalyzer;
47//!
48//! // Example usage
49//! let classifier = SceneClassifier::new();
50//! let face_detector = FaceDetector::new();
51//! let composition = CompositionAnalyzer::new();
52//! ```
53
54#![warn(missing_docs)]
55#![allow(clippy::module_name_repetitions)]
56#![allow(clippy::similar_names)]
57#![allow(clippy::cast_possible_truncation)]
58#![allow(clippy::cast_sign_loss)]
59#![allow(clippy::cast_precision_loss)]
60#![allow(clippy::cast_lossless)]
61#![allow(clippy::cast_possible_wrap)]
62#![allow(clippy::many_single_char_names)]
63#![allow(clippy::too_many_lines)]
64#![allow(clippy::needless_range_loop)]
65#![allow(clippy::missing_panics_doc)]
66#![allow(clippy::unused_self)]
67#![allow(clippy::items_after_statements)]
68#![allow(clippy::manual_memcpy)]
69#![allow(clippy::unnecessary_wraps)]
70#![allow(clippy::trivially_copy_pass_by_ref)]
71#![allow(clippy::single_match_else)]
72#![allow(clippy::must_use_candidate)]
73#![allow(clippy::missing_errors_doc)]
74#![allow(clippy::manual_swap)]
75#![allow(clippy::doc_markdown)]
76#![allow(dead_code)]
77
78pub mod action_beat;
79pub mod activity;
80pub mod adaptive_scene;
81pub mod aesthetic;
82pub mod audio_visual_correlation;
83#[path = "camera_motion/mod.rs"]
84pub mod camera_motion;
85pub mod classification;
86pub mod classify;
87pub mod color_temperature;
88pub mod complexity_detector;
89pub mod composition;
90pub mod content_moderation;
91pub mod continuity_check;
92pub mod crowd_density;
93pub mod depth_of_field;
94pub mod detect;
95pub mod emotion_recognition;
96pub mod error;
97pub mod event;
98pub mod face_landmark;
99pub mod features;
100pub mod lighting_analysis;
101pub mod location;
102#[cfg(feature = "onnx")]
103pub mod ml;
104pub mod mood;
105pub mod motion_energy;
106pub mod object_tracker;
107pub mod pacing;
108pub mod saliency;
109pub mod scene_boundary;
110pub mod scene_captioning;
111pub mod scene_graph;
112pub mod scene_metadata;
113pub mod scene_score;
114pub mod scene_stats;
115pub mod scene_tags;
116pub mod segment;
117pub mod segmentation;
118pub mod shot_type;
119pub mod storyboard;
120pub mod summarization;
121pub mod temporal_graph;
122pub mod text_detect;
123pub mod thumbnail_selector;
124pub mod transition;
125pub mod visual_quality_map;
126pub mod visual_rhythm;
127
128// Re-export commonly used items at crate root
129pub use error::{SceneError, SceneResult};
130
131#[cfg(feature = "onnx")]
132pub use ml::MlSceneEnricher;
133
134/// Common types and utilities used across modules.
135pub mod common {
136    use serde::{Deserialize, Serialize};
137
138    /// A 2D point in image space.
139    #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
140    pub struct Point {
141        /// X coordinate.
142        pub x: f32,
143        /// Y coordinate.
144        pub y: f32,
145    }
146
147    impl Point {
148        /// Create a new point.
149        #[must_use]
150        pub const fn new(x: f32, y: f32) -> Self {
151            Self { x, y }
152        }
153
154        /// Calculate distance to another point.
155        #[must_use]
156        pub fn distance(&self, other: &Self) -> f32 {
157            let dx = self.x - other.x;
158            let dy = self.y - other.y;
159            (dx * dx + dy * dy).sqrt()
160        }
161    }
162
163    /// A rectangular region in image space.
164    #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
165    pub struct Rect {
166        /// X coordinate of top-left corner.
167        pub x: f32,
168        /// Y coordinate of top-left corner.
169        pub y: f32,
170        /// Width of rectangle.
171        pub width: f32,
172        /// Height of rectangle.
173        pub height: f32,
174    }
175
176    impl Rect {
177        /// Create a new rectangle.
178        #[must_use]
179        pub const fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
180            Self {
181                x,
182                y,
183                width,
184                height,
185            }
186        }
187
188        /// Calculate area of rectangle.
189        #[must_use]
190        pub const fn area(&self) -> f32 {
191            self.width * self.height
192        }
193
194        /// Get center point of rectangle.
195        #[must_use]
196        pub const fn center(&self) -> Point {
197            Point {
198                x: self.x + self.width / 2.0,
199                y: self.y + self.height / 2.0,
200            }
201        }
202
203        /// Check if this rectangle contains a point.
204        #[must_use]
205        pub const fn contains(&self, point: &Point) -> bool {
206            point.x >= self.x
207                && point.x <= self.x + self.width
208                && point.y >= self.y
209                && point.y <= self.y + self.height
210        }
211
212        /// Calculate intersection over union with another rectangle.
213        #[must_use]
214        pub fn iou(&self, other: &Self) -> f32 {
215            let x1 = self.x.max(other.x);
216            let y1 = self.y.max(other.y);
217            let x2 = (self.x + self.width).min(other.x + other.width);
218            let y2 = (self.y + self.height).min(other.y + other.height);
219
220            if x2 <= x1 || y2 <= y1 {
221                return 0.0;
222            }
223
224            let intersection = (x2 - x1) * (y2 - y1);
225            let union = self.area() + other.area() - intersection;
226
227            if union == 0.0 {
228                0.0
229            } else {
230                intersection / union
231            }
232        }
233    }
234
235    /// Confidence score (0.0 to 1.0).
236    #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Serialize, Deserialize)]
237    pub struct Confidence(f32);
238
239    impl Confidence {
240        /// Create a new confidence score (clamped to 0.0-1.0).
241        #[must_use]
242        pub fn new(value: f32) -> Self {
243            Self(value.clamp(0.0, 1.0))
244        }
245
246        /// Get the confidence value.
247        #[must_use]
248        pub const fn value(&self) -> f32 {
249            self.0
250        }
251
252        /// Check if confidence meets threshold.
253        #[must_use]
254        pub const fn meets_threshold(&self, threshold: f32) -> bool {
255            self.0 >= threshold
256        }
257    }
258
259    impl From<f32> for Confidence {
260        fn from(value: f32) -> Self {
261            Self::new(value)
262        }
263    }
264
265    impl From<Confidence> for f32 {
266        fn from(conf: Confidence) -> Self {
267            conf.0
268        }
269    }
270}
271
272#[cfg(test)]
273mod tests {
274    use super::common::*;
275
276    #[test]
277    fn test_point_distance() {
278        let p1 = Point::new(0.0, 0.0);
279        let p2 = Point::new(3.0, 4.0);
280        assert!((p1.distance(&p2) - 5.0).abs() < f32::EPSILON);
281    }
282
283    #[test]
284    fn test_rect_area() {
285        let rect = Rect::new(0.0, 0.0, 10.0, 20.0);
286        assert!((rect.area() - 200.0).abs() < f32::EPSILON);
287    }
288
289    #[test]
290    fn test_rect_center() {
291        let rect = Rect::new(0.0, 0.0, 10.0, 20.0);
292        let center = rect.center();
293        assert!((center.x - 5.0).abs() < f32::EPSILON);
294        assert!((center.y - 10.0).abs() < f32::EPSILON);
295    }
296
297    #[test]
298    fn test_rect_contains() {
299        let rect = Rect::new(0.0, 0.0, 10.0, 20.0);
300        assert!(rect.contains(&Point::new(5.0, 10.0)));
301        assert!(!rect.contains(&Point::new(15.0, 10.0)));
302    }
303
304    #[test]
305    fn test_rect_iou() {
306        let rect1 = Rect::new(0.0, 0.0, 10.0, 10.0);
307        let rect2 = Rect::new(5.0, 5.0, 10.0, 10.0);
308        let iou = rect1.iou(&rect2);
309        // Intersection: 5x5 = 25
310        // Union: 100 + 100 - 25 = 175
311        // IoU: 25/175 ≈ 0.1429
312        assert!((iou - 0.1428571).abs() < 0.001);
313    }
314
315    #[test]
316    fn test_confidence() {
317        let conf = Confidence::new(0.75);
318        assert!((conf.value() - 0.75).abs() < f32::EPSILON);
319        assert!(conf.meets_threshold(0.5));
320        assert!(!conf.meets_threshold(0.8));
321
322        // Test clamping
323        let conf_high = Confidence::new(1.5);
324        assert!((conf_high.value() - 1.0).abs() < f32::EPSILON);
325
326        let conf_low = Confidence::new(-0.5);
327        assert!((conf_low.value() - 0.0).abs() < f32::EPSILON);
328    }
329}