VP7 encoder
[nihav.git] / nihav-duck / src / codecs / vpenc / motion_est.rs
CommitLineData
19cfcd2f
KS
1use nihav_codec_support::codecs::{MV, ZERO_MV};
2
3use std::str::FromStr;
4
5#[derive(Debug,Clone,Copy,PartialEq)]
c5d5793c 6#[allow(dead_code)]
19cfcd2f
KS
7pub enum MVSearchMode {
8 Full,
c5d5793c 9 SEA,
19cfcd2f
KS
10 Diamond,
11 Hexagon,
c5d5793c 12 EPZS,
19cfcd2f
KS
13}
14
15impl Default for MVSearchMode {
16 fn default() -> Self { MVSearchMode::Hexagon }
17}
18
19pub struct ParseError{}
20
21impl FromStr for MVSearchMode {
22 type Err = ParseError;
23
24 #[allow(clippy::single_match)]
25 fn from_str(s: &str) -> Result<Self, Self::Err> {
26 match s {
27 "full" => Ok(MVSearchMode::Full),
c5d5793c 28 "sea" => Ok(MVSearchMode::SEA),
19cfcd2f
KS
29 "dia" => Ok(MVSearchMode::Diamond),
30 "hex" => Ok(MVSearchMode::Hexagon),
c5d5793c 31 "epzs" => Ok(MVSearchMode::EPZS),
19cfcd2f
KS
32 _ => Err(ParseError{}),
33 }
34 }
35}
36
37impl std::fmt::Display for MVSearchMode {
38 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
39 match *self {
40 MVSearchMode::Full => write!(f, "full"),
c5d5793c 41 MVSearchMode::SEA => write!(f, "sea"),
19cfcd2f
KS
42 MVSearchMode::Diamond => write!(f, "dia"),
43 MVSearchMode::Hexagon => write!(f, "hex"),
c5d5793c 44 MVSearchMode::EPZS => write!(f, "epzs"),
19cfcd2f
KS
45 }
46 }
47}
48
49trait FromPixels {
50 fn from_pixels(self) -> Self;
51}
52
53impl FromPixels for MV {
54 fn from_pixels(self) -> MV {
55 MV { x: self.x * 4, y: self.y * 4 }
56 }
57}
58
59pub const DIA_PATTERN: [MV; 9] = [
60 ZERO_MV,
61 MV {x: -2, y: 0},
62 MV {x: -1, y: 1},
63 MV {x: 0, y: 2},
64 MV {x: 1, y: 1},
65 MV {x: 2, y: 0},
66 MV {x: 1, y: -1},
67 MV {x: 0, y: -2},
68 MV {x: -1, y: -1}
69];
70
71pub const HEX_PATTERN: [MV; 7] = [
72 ZERO_MV,
73 MV {x: -2, y: 0},
74 MV {x: -1, y: 2},
75 MV {x: 1, y: 2},
76 MV {x: 2, y: 0},
77 MV {x: 1, y: -2},
78 MV {x: -1, y: -2}
79];
80
81pub const REFINEMENT: [MV; 4] = [
82 MV {x: -1, y: 0},
83 MV {x: 0, y: 1},
84 MV {x: 1, y: 0},
85 MV {x: 0, y: -1}
86];
87
88#[macro_export]
89macro_rules! search_template {
c5d5793c
KS
90 ($self: expr, $mv_est: expr, $cur_blk: expr, $mb_x: expr, $mb_y: expr, $sad_func: ident, $threshold: expr) => ({
91 search_template!($self, $mv_est, $cur_blk, $mb_x, $mb_y, $sad_func, $threshold, ZERO_MV, MAX_DIST, true)
92 });
93 ($self: expr, $mv_est: expr, $cur_blk: expr, $mb_x: expr, $mb_y: expr, $sad_func: ident, $threshold: expr, $start_mv: expr, $best_dist: expr, $fullpel_stage: expr) => ({
94 let mut best_dist = $best_dist;
95 let mut best_mv = $start_mv;
19cfcd2f
KS
96
97 let mut min_dist;
98 let mut min_idx;
99
c5d5793c
KS
100 if $fullpel_stage {
101 $self.reset();
102 loop {
103 let mut cur_best_dist = best_dist;
104 for (dist, &point) in $self.dist.iter_mut().zip($self.point.iter()) {
105 if *dist == MAX_DIST {
106 *dist = $mv_est.$sad_func($cur_blk, $mb_x, $mb_y, point.from_pixels(), cur_best_dist);
107 cur_best_dist = cur_best_dist.min(*dist);
108 if *dist <= $threshold {
109 break;
110 }
19cfcd2f
KS
111 }
112 }
c5d5793c
KS
113 min_dist = $self.dist[0];
114 min_idx = 0;
115 for (i, &dist) in $self.dist.iter().enumerate().skip(1) {
116 if dist < min_dist {
117 min_dist = dist;
118 min_idx = i;
119 if dist <= $threshold {
120 break;
121 }
19cfcd2f
KS
122 }
123 }
c5d5793c
KS
124 if min_dist <= $threshold || min_idx == 0 || best_dist == min_dist || $self.point[min_idx].x.abs() >= $mv_est.mv_range || $self.point[min_idx].y.abs() >= $mv_est.mv_range {
125 break;
126 }
127 best_dist = min_dist;
128 $self.update($self.steps[min_idx]);
19cfcd2f
KS
129 }
130 best_dist = min_dist;
c5d5793c
KS
131 best_mv = $self.point[min_idx];
132 if best_dist <= $threshold {
133 return (best_mv.from_pixels(), best_dist);
134 }
135 for &step in REFINEMENT.iter() {
136 let mv = best_mv + step;
137 let dist = $mv_est.$sad_func($cur_blk, $mb_x, $mb_y, mv.from_pixels(), MAX_DIST);
138 if best_dist > dist {
139 best_dist = dist;
140 best_mv = mv;
141 }
142 }
143 best_mv = best_mv.from_pixels();
144 if best_dist <= $threshold {
145 return (best_mv, best_dist);
19cfcd2f 146 }
19cfcd2f
KS
147 }
148
149 // subpel refinement
150 $self.set_new_point(best_mv, best_dist);
151 loop {
152 let mut cur_best_dist = best_dist;
153 for (dist, &point) in $self.dist.iter_mut().zip($self.point.iter()) {
154 if *dist == MAX_DIST {
155 *dist = $mv_est.$sad_func($cur_blk, $mb_x, $mb_y, point, cur_best_dist);
156 cur_best_dist = cur_best_dist.min(*dist);
c5d5793c 157 if *dist <= $threshold {
19cfcd2f
KS
158 break;
159 }
160 }
161 }
162 min_dist = $self.dist[0];
163 min_idx = 0;
164 for (i, &dist) in $self.dist.iter().enumerate().skip(1) {
165 if dist < min_dist {
166 min_dist = dist;
167 min_idx = i;
c5d5793c 168 if dist <= $threshold {
19cfcd2f
KS
169 break;
170 }
171 }
172 }
c5d5793c 173 if min_dist <= $threshold || min_idx == 0 || best_dist == min_dist || $self.point[min_idx].x.abs() >= $mv_est.mv_range * 8 || $self.point[min_idx].y.abs() >= $mv_est.mv_range * 8 {
19cfcd2f
KS
174 break;
175 }
176 best_dist = min_dist;
177 $self.update($self.steps[min_idx]);
178 }
179 best_dist = min_dist;
180 best_mv = $self.point[min_idx];
c5d5793c 181 if best_dist <= $threshold {
19cfcd2f
KS
182 return (best_mv, best_dist);
183 }
184 for &step in REFINEMENT.iter() {
185 let mv = best_mv + step;
186 let dist = $mv_est.$sad_func($cur_blk, $mb_x, $mb_y, mv, MAX_DIST);
187 if best_dist > dist {
188 best_dist = dist;
189 best_mv = mv;
190 }
191 }
192 (best_mv, best_dist)
c5d5793c 193 });
19cfcd2f 194}