Skip to main content

oximedia_codec/motion/
mod.rs

1//! Motion estimation module for video encoders.
2//!
3//! This module provides comprehensive motion estimation functionality for
4//! inter-frame prediction in video encoding. Motion estimation finds the
5//! best matching block in a reference frame for each block in the current
6//! frame, enabling efficient temporal compression.
7//!
8//! # Architecture
9//!
10//! The module is organized into several submodules:
11//!
12//! - [`types`] - Core types: `MotionVector`, `BlockMatch`, `SearchRange`, etc.
13//! - [`search`] - Motion search algorithms: full search, diamond, hexagon, UMH
14//! - [`diamond`] - Diamond search patterns (SDSP, LDSP)
15//! - [`hierarchical`] - Multi-resolution pyramid-based search
16//! - [`subpel`] - Sub-pixel refinement and SATD computation
17//! - [`predictor`] - MV prediction from spatial/temporal neighbors
18//! - [`partition`] - Block partitioning decisions
19//! - [`cache`] - MV caching for improved performance
20//!
21//! # Usage Example
22//!
23//! ```ignore
24//! use oximedia_codec::motion::{
25//!     MotionVector, SearchConfig, SearchRange, DiamondSearch, MotionSearch,
26//! };
27//!
28//! // Configure search
29//! let config = SearchConfig::default()
30//!     .range(SearchRange::symmetric(32))
31//!     .early_termination(true);
32//!
33//! // Create search algorithm
34//! let searcher = DiamondSearch::new();
35//!
36//! // Perform search
37//! let result = searcher.search(&context, &config);
38//! println!("Best MV: ({}, {}), SAD: {}", result.mv.dx, result.mv.dy, result.sad);
39//! ```
40//!
41//! # Search Algorithms
42//!
43//! The module provides several motion search algorithms with different
44//! speed/quality tradeoffs:
45//!
46//! | Algorithm | Speed | Quality | Use Case |
47//! |-----------|-------|---------|----------|
48//! | `FullSearch` | Slow | Best | Reference, small ranges |
49//! | `DiamondSearch` | Fast | Good | General purpose |
50//! | `HexagonSearch` | Fast | Good | Alternative to diamond |
51//! | `UmhSearch` | Medium | Very Good | High quality encoding |
52//! | `HierarchicalSearch` | Medium | Good | Large motion, HD content |
53//!
54//! # Sub-pixel Precision
55//!
56//! Motion vectors support multiple precision levels:
57//!
58//! - **Full-pel** - Integer pixel precision
59//! - **Half-pel** - 1/2 pixel precision
60//! - **Quarter-pel** - 1/4 pixel precision (common in H.264/AV1)
61//! - **Eighth-pel** - 1/8 pixel precision (VP9)
62//!
63//! Sub-pixel positions are interpolated using filters defined in [`subpel`].
64
65#![forbid(unsafe_code)]
66#![allow(dead_code)]
67#![allow(clippy::doc_markdown)]
68
69pub mod cache;
70pub mod diamond;
71pub mod hierarchical;
72pub mod partition;
73pub mod predictor;
74pub mod search;
75pub mod subpel;
76pub mod types;
77
78// Re-export primary types for convenient access
79pub use cache::{CacheManager, CoLocatedMvLookup, MvCache, MvCacheEntry, RefFrameMvs};
80pub use diamond::{
81    AdaptiveDiamond, CrossDiamond, ExtendedDiamond, HexagonalSearch, LargeDiamond,
82    PredictorDiamond, SmallDiamond, UMHexSearch,
83};
84pub use hierarchical::{
85    CoarseToFineRefiner, HierarchicalConfig, HierarchicalSearch, ImagePyramid, PyramidLevel,
86};
87pub use partition::{
88    InterMode, MergeCandidate, MergeCandidateList, PartitionContext, PartitionDecider,
89    PartitionDecision, PartitionType, SkipDetector, SplitDecision,
90};
91pub use predictor::{
92    MvCandidate, MvCostCalculator, MvPredContext, MvPredictor, MvPredictorList, MvpMode,
93    NeighborInfo, NeighborPosition, SpatialPredictor, TemporalPredictor,
94};
95pub use search::{
96    AdaptiveSearch, DiamondSearch, FullSearch, HexagonSearch, MotionSearch, SearchConfig,
97    SearchContext, ThreeStepSearch, UmhSearch,
98};
99pub use subpel::{
100    HadamardTransform, HalfPelFilter, HalfPelInterpolator, QuarterPelFilter,
101    QuarterPelInterpolator, SatdCalculator, SubpelConfig, SubpelPatterns, SubpelRefiner,
102};
103pub use types::{BlockMatch, BlockSize, MotionVector, MvCost, MvPrecision, SearchRange};
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108
109    #[test]
110    fn test_module_imports() {
111        // Verify that primary types can be used
112        let mv = MotionVector::new(10, 20);
113        assert_eq!(mv.dx, 10);
114        assert_eq!(mv.dy, 20);
115
116        let range = SearchRange::symmetric(32);
117        assert_eq!(range.horizontal, 32);
118
119        let block_match = BlockMatch::zero_mv(100);
120        assert_eq!(block_match.sad, 100);
121    }
122
123    #[test]
124    fn test_search_algorithm_creation() {
125        let _full = FullSearch::new();
126        let _diamond = DiamondSearch::new();
127        let _hexagon = HexagonSearch::new();
128        let _umh = UmhSearch::new();
129        let _adaptive = AdaptiveSearch::new();
130        let _three_step = ThreeStepSearch::new();
131    }
132
133    #[test]
134    fn test_diamond_patterns() {
135        let small = SmallDiamond::new();
136        let large = LargeDiamond::new();
137
138        assert_eq!(small.size(), 4);
139        assert_eq!(large.size(), 8);
140    }
141
142    #[test]
143    fn test_predictor_creation() {
144        let predictor = MvPredictor::new();
145        let mvp = predictor.best_mvp();
146        assert!(mvp.is_zero());
147    }
148
149    #[test]
150    fn test_cache_creation() {
151        let cache = MvCache::new();
152        assert_eq!(cache.mi_cols(), 0);
153        assert_eq!(cache.mi_rows(), 0);
154    }
155
156    #[test]
157    fn test_partition_types() {
158        assert_eq!(PartitionType::None.num_parts(), 1);
159        assert_eq!(PartitionType::Split.num_parts(), 4);
160    }
161
162    #[test]
163    fn test_subpel_components() {
164        let _satd = SatdCalculator::new();
165        let identical = vec![128u8; 16];
166        let result = SatdCalculator::satd_4x4(&identical, 4, &identical, 4);
167        assert_eq!(result, 0);
168    }
169
170    #[test]
171    fn test_hierarchical_components() {
172        let pyramid = ImagePyramid::new();
173        assert_eq!(pyramid.num_levels(), 0);
174
175        let config = HierarchicalConfig::new(3);
176        assert_eq!(config.levels, 3);
177    }
178
179    #[test]
180    fn test_mv_precision() {
181        assert_eq!(MvPrecision::FullPel.fractional_bits(), 0);
182        assert_eq!(MvPrecision::HalfPel.fractional_bits(), 1);
183        assert_eq!(MvPrecision::QuarterPel.fractional_bits(), 2);
184        assert_eq!(MvPrecision::EighthPel.fractional_bits(), 3);
185    }
186
187    #[test]
188    fn test_block_sizes() {
189        assert_eq!(BlockSize::Block4x4.width(), 4);
190        assert_eq!(BlockSize::Block8x8.width(), 8);
191        assert_eq!(BlockSize::Block16x16.width(), 16);
192        assert_eq!(BlockSize::Block64x64.width(), 64);
193        assert_eq!(BlockSize::Block128x128.width(), 128);
194    }
195
196    #[test]
197    fn test_integration_search_workflow() {
198        // Create test data
199        let src = vec![100u8; 64]; // 8x8 block
200        let mut reference = vec![50u8; 256]; // 16x16 frame
201
202        // Place matching block at (4, 4)
203        for row in 0..8 {
204            for col in 0..8 {
205                reference[(row + 4) * 16 + col + 4] = 100;
206            }
207        }
208
209        // Setup context
210        let ctx = SearchContext::new(&src, 8, &reference, 16, BlockSize::Block8x8, 0, 0, 16, 16);
211
212        // Configure search
213        let config = SearchConfig::default().range(SearchRange::symmetric(8));
214
215        // Search with different algorithms
216        let full_result = FullSearch::new().search(&ctx, &config);
217        let diamond_result = DiamondSearch::new().search(&ctx, &config);
218        let hex_result = HexagonSearch::new().search(&ctx, &config);
219
220        // All should find reasonably good matches
221        assert!(full_result.sad < 1000);
222        assert!(diamond_result.sad < 1000);
223        assert!(hex_result.sad < 1000);
224
225        // Full search should find optimal
226        assert_eq!(full_result.mv.full_pel_x(), 4);
227        assert_eq!(full_result.mv.full_pel_y(), 4);
228    }
229}