indeo3enc: rework the logic with motion cells
authorKostya Shishkov <kostya.shishkov@gmail.com>
Tue, 14 Feb 2023 15:32:35 +0000 (16:32 +0100)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Tue, 14 Feb 2023 17:47:18 +0000 (18:47 +0100)
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.

nihav-indeo/src/codecs/indeo3enc/mod.rs
nihav-indeo/src/codecs/indeo3enc/mv.rs
nihav-indeo/src/codecs/indeo3enc/tree.rs

index 55662289dbff03e3a72cd7c6f8e033077ea21fa5..84764eca64cb10e44d4baefcb4b66f69bba49ec5 100644 (file)
@@ -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() {
index 498a6efd597c876979f0c26bfa543f846921c677..d234e94f4aa41790c13dde2e0c45ddd38b95587d 100644 (file)
@@ -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<u8> {
-    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
 }
index 8b1bb68057d4cebe39084a8e2ee374f6d1200257..e91880fab3efb0cb115a3453e7e9faed9692c57f 100644 (file)
@@ -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<Indeo3PrimaryTree>, Box<Indeo3PrimaryTree>),
@@ -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<Indeo3PrimaryTree> {
+    fn add_mv_tree(&mut self, mv: MV, flat: bool, cell: Indeo3Cell) -> Box<Indeo3PrimaryTree> {
         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<Indeo3PrimaryTree>) {
+        let cell = Indeo3Cell::new(self.width, self.height, true);
+        self.prune_pri(cell, tree)
+    }
+    fn prune_pri(&mut self, cell: Indeo3Cell, tree: &mut Box<Indeo3PrimaryTree>) {
+        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);