From: Kostya Shishkov Date: Tue, 14 Feb 2023 15:32:35 +0000 (+0100) Subject: indeo3enc: rework the logic with motion cells X-Git-Url: https://git.nihav.org/?a=commitdiff_plain;h=bafe9cd4a4016dc550e2cd5311628024c8368023;p=nihav.git indeo3enc: rework the logic with motion cells Previously motion vectors were ranged by the number of cells using them and if the vector outside the allowed number of MVs was close enough to a popular one the latter was used instead. Now the MVs are ranked by the total area of cells using them and the vectors not in the list are pruned entirely. Cells with those vectors are replaced with the intra-coded ones. --- diff --git a/nihav-indeo/src/codecs/indeo3enc/mod.rs b/nihav-indeo/src/codecs/indeo3enc/mod.rs index 5566228..84764ec 100644 --- a/nihav-indeo/src/codecs/indeo3enc/mod.rs +++ b/nihav-indeo/src/codecs/indeo3enc/mod.rs @@ -339,14 +339,17 @@ impl NAEncoder for Indeo3Encoder { // prepare plane data structure for &planeno in PLANE_ORDER.iter() { let ref_plane = &self.pframe.plane[planeno]; - let tree = self.cframe.plane[planeno].find_cells(is_intra, ref_plane, &self.mv_est); + let mut tree = self.cframe.plane[planeno].find_cells(is_intra, ref_plane, &self.mv_est); if self.debug_tree { println!(" tree for plane {}:", planeno); tree.print(); } - trees.push(tree); let mvs = &mut self.cframe.plane[planeno].mvs; - compact_mvs(mvs); + if mvs.len() > 256 { + compact_mvs(mvs); + self.cframe.plane[planeno].prune_extra_mvs(&mut tree); + } + trees.push(tree); } self.encode_planes(&mut dbuf, &trees, is_intra)?; @@ -587,7 +590,7 @@ mod test { let enc_options = &[ NAOption { name: super::TRY_AGAIN_OPTION, value: NAValue::Bool(true) }, ]; - encode_test("indeo3.avi", enc_options, Some(4), &[0xd62f9996, 0x7fb4ba1b, 0x1f552801, 0xfd4e4726]); + encode_test("indeo3.avi", enc_options, Some(4), &[0xc23464a1, 0xd319a38f, 0x7421165e, 0x42786e50]); } /*#[test] fn test_indeo3_roundtrip() { diff --git a/nihav-indeo/src/codecs/indeo3enc/mv.rs b/nihav-indeo/src/codecs/indeo3enc/mv.rs index 498a6ef..d234e94 100644 --- a/nihav-indeo/src/codecs/indeo3enc/mv.rs +++ b/nihav-indeo/src/codecs/indeo3enc/mv.rs @@ -118,36 +118,16 @@ fn calc_mv_score(cur: &Plane, prev: &Plane, cell: Indeo3Cell, mv: MV) -> u32 { calc_cell_diff(cur_ptr, ref_ptr, cur.width, cell.get_width(), cell.get_height()) } -fn get_mv_diff(mv1: MV, mv2: MV) -> i8 { - (mv1.x - mv2.x).abs() + (mv1.y - mv2.y).abs() -} - pub fn compact_mvs(mvs: &mut Vec<(MV, u16)>) { - mvs.sort_by(|a, b| a.1.cmp(&b.1)); - while mvs.len() > 256 { - let (mv, _) = mvs.pop().unwrap(); - if let Some(idx) = find_mv(mv, mvs) { - mvs[usize::from(idx)].1 += 1; - } - } + mvs.sort_by(|a, b| b.1.cmp(&a.1)); + mvs.truncate(256); } pub fn find_mv(mv: MV, mvs: &[(MV, u16)]) -> Option { - let mut best_idx = None; - let mut best_cand_diff = 42; - let mut best_cand_count = 0; - for (i, &(cand_mv, count)) in mvs.iter().enumerate() { + for (i, &(cand_mv, _)) in mvs.iter().enumerate() { if cand_mv == mv { return Some(i as u8); } - let diff = get_mv_diff(mv, cand_mv); - if diff <= 2 { - if diff < best_cand_diff || (diff == best_cand_diff && count > best_cand_count) { - best_idx = Some(i as u8); - best_cand_diff = diff; - best_cand_count = count; - } - } } - best_idx + None } diff --git a/nihav-indeo/src/codecs/indeo3enc/tree.rs b/nihav-indeo/src/codecs/indeo3enc/tree.rs index 8b1bb68..e91880f 100644 --- a/nihav-indeo/src/codecs/indeo3enc/tree.rs +++ b/nihav-indeo/src/codecs/indeo3enc/tree.rs @@ -1,6 +1,7 @@ use super::Indeo3Writer; use super::mv::*; use super::cell::{CellEncoder, MAX_CELL_SIZE}; +use std::ops::DerefMut; pub enum Indeo3PrimaryTree { VSplit(Box, Box), @@ -208,7 +209,7 @@ impl Plane { } else { if !cell.intra { if let Some((mv, flat)) = mv_est.mv_search(self, pplane, cell) { - return self.add_mv_tree(mv, flat); + return self.add_mv_tree(mv, flat, cell); } // try splitting once to see if it improves situation @@ -223,13 +224,13 @@ impl Plane { let search2 = mv_est.mv_search(self, pplane, cell2); if search1.is_some() || search2.is_some() { let tree1 = if let Some((mv, flat)) = search1 { - self.add_mv_tree(mv, flat) + self.add_mv_tree(mv, flat, cell1) } else { let sec = self.split_sec(cell1); Box::new(Indeo3PrimaryTree::AbsFill(sec)) }; let tree2 = if let Some((mv, flat)) = search2 { - self.add_mv_tree(mv, flat) + self.add_mv_tree(mv, flat, cell2) } else { let sec = self.split_sec(cell2); Box::new(Indeo3PrimaryTree::AbsFill(sec)) @@ -246,7 +247,7 @@ impl Plane { Box::new(Indeo3PrimaryTree::AbsFill(sec)) } } - fn add_mv_tree(&mut self, mv: MV, flat: bool) -> Box { + fn add_mv_tree(&mut self, mv: MV, flat: bool, cell: Indeo3Cell) -> Box { let sec = if flat { Box::new(Indeo3SecondaryTree::VQNull(0)) } else { @@ -256,7 +257,7 @@ impl Plane { let mut found = false; for (ref cmv, ref mut count) in self.mvs.iter_mut() { if cmv == &mv { - *count += 1; + *count += u16::from(cell.w) * u16::from(cell.h); found = true; break; } @@ -327,6 +328,31 @@ impl Plane { let area = (w * h) as u32; (hdiff * 16 / area, vdiff * 16 / area) } + pub fn prune_extra_mvs(&mut self, tree: &mut Box) { + let cell = Indeo3Cell::new(self.width, self.height, true); + self.prune_pri(cell, tree) + } + fn prune_pri(&mut self, cell: Indeo3Cell, tree: &mut Box) { + match tree.deref_mut() { + Indeo3PrimaryTree::HSplit(ref mut tree1, ref mut tree2) => { + let (cell1, cell2) = cell.split_h(); + self.prune_pri(cell1, tree1); + self.prune_pri(cell2, tree2); + }, + Indeo3PrimaryTree::VSplit(ref mut tree1, ref mut tree2) => { + let (cell1, cell2) = cell.split_v(self.stripw); + self.prune_pri(cell1, tree1); + self.prune_pri(cell2, tree2); + }, + Indeo3PrimaryTree::AbsFill(_) => {}, + Indeo3PrimaryTree::RelFill(ref mv, ref _sec) => { + if find_mv(*mv, &self.mvs).is_none() { + let sec = self.split_sec(cell); + *tree = Box::new(Indeo3PrimaryTree::AbsFill(sec)); + } + }, + } + } pub fn encode_tree(&mut self, iw: &mut Indeo3Writer, tree: &Indeo3PrimaryTree, cenc: &mut CellEncoder, is_intra: bool, refp: &Plane) { let cell = Indeo3Cell::new(self.width, self.height, is_intra); self.encode_pri(iw, cell, tree, cenc, refp);