diff options
Diffstat (limited to 'evc')
-rwxr-xr-x | evc/scripts/bench_modes | 17 | ||||
-rw-r--r-- | evc/src/block.rs | 2 | ||||
-rw-r--r-- | evc/src/codec/encode.rs | 209 | ||||
-rw-r--r-- | evc/src/codec/encode/advanced.rs | 109 | ||||
-rw-r--r-- | evc/src/codec/encode/mod.rs | 105 | ||||
-rw-r--r-- | evc/src/codec/encode/simple.rs | 64 |
6 files changed, 296 insertions, 210 deletions
diff --git a/evc/scripts/bench_modes b/evc/scripts/bench_modes new file mode 100755 index 0000000..f6105db --- /dev/null +++ b/evc/scripts/bench_modes @@ -0,0 +1,17 @@ +#!/bin/fish +set w $argv[1] +set h $argv[2] +set t $argv[3] + +ffmpeg -hide_banner -i $argv[4] -to {$t} -vf scale={$w}x{$h},fps=30,format=rgb24 -f rawvideo pipe:1 > samples/raw + +echo "file: "$argv[4] +echo "resolution: "{$w}x{$h} +echo "frames: "(math $t \* 30) +for mode in trivial simple-exhaustive simple-fast advanced advanced-partial + echo ----------- + echo "mode: $mode" + echo "time: $(command time -f %U ./target/release/encode -W {$w} -H {$h} --mode $mode $argv[5..] <samples/raw >samples/encoded-$mode 2>| tail -n 1)s" + echo "size: $(du -h samples/encoded-$mode | cut -f 1)" +end + diff --git a/evc/src/block.rs b/evc/src/block.rs index 04ee3d2..8d7686f 100644 --- a/evc/src/block.rs +++ b/evc/src/block.rs @@ -115,7 +115,7 @@ impl Default for AdvancedReference { c: 0, d: 4, }, - value_scale: 4, // so 1 + value_scale: 0, } } } diff --git a/evc/src/codec/encode.rs b/evc/src/codec/encode.rs deleted file mode 100644 index 6b6f8a3..0000000 --- a/evc/src/codec/encode.rs +++ /dev/null @@ -1,209 +0,0 @@ -use crate::{ - block::{AdvancedReference, Block}, - frame::Frame, - helpers::{pixel::Pixel, threading::both_par, vector::Vec2}, - refsampler::Sampler, - view::View, -}; -use clap::ValueEnum; - -#[derive(Debug, Clone)] -pub struct EncodeConfig { - pub mode: EncodeMode, - pub ref_thres: f64, - pub max_diff_area: isize, - pub min_block_size: isize, - pub max_threads: usize, - pub weight_factor: f64, - pub do_value_scale: bool, - pub do_matrix_transform: bool, -} - -#[derive(Debug, Clone, ValueEnum)] -pub enum EncodeMode { - Trivial, - Fast, - Default, - Advanced, -} - -#[inline] -pub fn pk<F: FnMut(&mut AdvancedReference) -> ()>( - view: &View<&Frame>, - prev: &View<&Frame>, - diff: &mut f64, - params: &mut AdvancedReference, - initial_params: &AdvancedReference, - mut f: F, -) { - let mut p = initial_params.clone(); - f(&mut p); - let sampler = Sampler::from_refblock(prev.clone(), &p); - let d = View::diff_sampler(view, &sampler); - if d < *diff { - *diff = d; - *params = p; - } -} - -pub fn encode_block(view: View<&Frame>, prev: View<&Frame>, config: &EncodeConfig) -> Block { - let (diff, refblock) = if view.area() > config.max_diff_area { - ( - f64::INFINITY, - Block::Reference { - translation: Vec2::<isize>::ZERO, - }, - ) - } else { - let weight = importance(&view).max(0.5); - let irrelevance = config.weight_factor / weight; - let (diff, refblock) = match config.mode { - EncodeMode::Trivial => ( - View::diff(&view, &prev) / view.area() as f64, - Block::Reference { - translation: Vec2::<isize>::ZERO, - }, - ), - EncodeMode::Default => { - let mut diff = f64::INFINITY; - let mut translation = Vec2::<isize>::ZERO; - const OFFSETS: &[isize] = - &[-64, -32, -16, -8, -4, -2, -1, 0, 1, 2, 4, 8, 16, 32, 64]; - for x in OFFSETS { - for y in OFFSETS { - let t = Vec2 { x: *x, y: *y }; - let d = View::diff(&view, &prev.offset(t)); - if d < diff { - translation = t; - diff = d; - } - } - } - (diff, Block::Reference { translation }) - } - EncodeMode::Fast => { - let mut pm = AdvancedReference::default(); - let sampler = Sampler::from_refblock(prev.clone(), &pm); - let mut diff = View::diff_sampler(&view, &sampler); - if diff - irrelevance < config.ref_thres { - (diff, Block::REFZERO) - } else { - loop { - let (mut d, mut p) = (diff, pm.clone()); - pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x += 2); - pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x -= 2); - pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y += 2); - pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y -= 2); - pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x += 1); - pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x -= 1); - pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y += 1); - pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y -= 1); - if d >= diff { - break (diff, Block::AdvancedReference(pm)); - } - diff = d; - pm = p; - } - } - } - EncodeMode::Advanced => { - let mut pm = AdvancedReference::default(); - let sampler = Sampler::from_refblock(prev.clone(), &pm); - let mut diff = View::diff_sampler(&view, &sampler); - if diff - irrelevance < config.ref_thres { - (diff, Block::REFZERO) - } else { - loop { - let (mut d, mut p) = (diff, pm.clone()); - - pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x += 4); - pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x -= 4); - pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y += 4); - pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y -= 4); - pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x += 2); - pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x -= 2); - pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y += 2); - pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y -= 2); - pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x += 1); - pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x -= 1); - pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y += 1); - pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y -= 1); - if config.do_value_scale { - // pk(&view, &prev, &mut d, &mut p, &pm, |p| p.value_scale += 1); - // pk(&view, &prev, &mut d, &mut p, &pm, |p| p.value_scale -= 1); - } - if config.do_matrix_transform { - pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.a -= 1); - pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.a += 1); - pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.b -= 1); - pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.b += 1); - pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.c -= 1); - pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.c += 1); - pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.d -= 1); - pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.d += 1); - } - - if d >= diff { - break (diff, Block::AdvancedReference(pm)); - } - - diff = d; - pm = p; - } - } - } - }; - (diff - irrelevance, refblock) - }; - if diff < config.ref_thres { - refblock - } else { - if view.size.x < config.min_block_size || view.size.y < config.min_block_size { - Block::Literal(view.pixels()) - } else { - let [av, bv] = - unsafe { std::mem::transmute::<_, [View<&'static Frame>; 2]>(view.split()) }; - let [ap, bp] = - unsafe { std::mem::transmute::<_, [View<&'static Frame>; 2]>(prev.split()) }; - let config = unsafe { std::mem::transmute::<_, &'static EncodeConfig>(config) }; - - // only bother to do multithreading, when the block is big. - let (a, b) = // if view.size.x > 64 { - both_par( - || encode_block(av, ap, config), - || encode_block(bv, bp, config), - config.max_threads, - ) - // } else { - // (encode_block(av, ap, config), encode_block(bv, bp, config)) - // } - ; - - if a.is_literal() && b.is_literal() { - Block::Literal(view.pixels()) - } else if a.is_ref_without_translation() && b.is_ref_without_translation() { - Block::Reference { - translation: Vec2::<isize>::ZERO, - } - } else { - Block::Split(Box::new([a, b])) - } - } - } -} - -pub fn importance(view: &View<&Frame>) -> f64 { - let mut acc = 0; - for x in 0..view.size.x { - for y in 0..view.size.y { - let p = Vec2 { x, y }; - if x > 0 { - acc += Pixel::distance(view[p], view[p + Vec2::<isize>::LEFT]); - } - if y > 0 { - acc += Pixel::distance(view[p], view[p + Vec2::<isize>::UP]); - } - } - } - (acc / view.area() as usize) as f64 -} diff --git a/evc/src/codec/encode/advanced.rs b/evc/src/codec/encode/advanced.rs new file mode 100644 index 0000000..e86a1b8 --- /dev/null +++ b/evc/src/codec/encode/advanced.rs @@ -0,0 +1,109 @@ +use super::EncodeConfig; +use crate::{ + block::{AdvancedReference, Block}, + frame::Frame, + refsampler::Sampler, + view::View, +}; + +pub fn default( + view: &View<&Frame>, + prev: &View<&Frame>, + config: &EncodeConfig, + max_diff: f64, +) -> (f64, Block) { + let mut pm = AdvancedReference::default(); + let sampler = Sampler::from_refblock(prev.clone(), &pm); + let mut diff = View::diff_sampler(&view, &sampler); + if diff < max_diff { + (diff, Block::REFZERO) + } else { + loop { + let (mut d, mut p) = (diff, pm.clone()); + + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x += 4); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x -= 4); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y += 4); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y -= 4); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x += 2); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x -= 2); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y += 2); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y -= 2); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x += 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x -= 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y += 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y -= 1); + if config.do_value_scale { + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.value_scale += 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.value_scale -= 1); + } + if config.do_matrix_transform { + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.a -= 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.a += 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.b -= 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.b += 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.c -= 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.c += 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.d -= 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.transform.d += 1); + } + + if d >= diff { + break (diff, Block::AdvancedReference(pm)); + } + + diff = d; + pm = p; + } + } +} + +pub fn partial( + view: &View<&Frame>, + prev: &View<&Frame>, + _config: &EncodeConfig, + max_diff: f64, +) -> (f64, Block) { + let mut pm = AdvancedReference::default(); + let sampler = Sampler::from_refblock(prev.clone(), &pm); + let mut diff = View::diff_sampler(&view, &sampler); + if diff < max_diff { + (diff, Block::REFZERO) + } else { + loop { + let (mut d, mut p) = (diff, pm.clone()); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x += 2); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x -= 2); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y += 2); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y -= 2); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x += 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.x -= 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y += 1); + pk(&view, &prev, &mut d, &mut p, &pm, |p| p.translation.y -= 1); + if d >= diff { + break (diff, Block::AdvancedReference(pm)); + } + diff = d; + pm = p; + } + } +} + +#[inline] +fn pk<F: FnMut(&mut AdvancedReference) -> ()>( + view: &View<&Frame>, + prev: &View<&Frame>, + diff: &mut f64, + params: &mut AdvancedReference, + initial_params: &AdvancedReference, + mut f: F, +) { + let mut p = initial_params.clone(); + f(&mut p); + let sampler = Sampler::from_refblock(prev.clone(), &p); + let d = View::diff_sampler(view, &sampler); + if d < *diff { + *diff = d; + *params = p; + } +} diff --git a/evc/src/codec/encode/mod.rs b/evc/src/codec/encode/mod.rs new file mode 100644 index 0000000..2ccac64 --- /dev/null +++ b/evc/src/codec/encode/mod.rs @@ -0,0 +1,105 @@ +pub mod advanced; +pub mod simple; + +use crate::{ + block::Block, + frame::Frame, + helpers::{pixel::Pixel, threading::both_par, vector::Vec2}, + view::View, +}; +use clap::ValueEnum; + +#[derive(Debug, Clone)] +pub struct EncodeConfig { + pub mode: EncodeMode, + pub ref_thres: f64, + pub max_diff_area: isize, + pub min_block_size: isize, + pub max_threads: usize, + pub weight_factor: f64, + pub do_value_scale: bool, + pub do_matrix_transform: bool, +} + +#[derive(Debug, Clone, ValueEnum)] +pub enum EncodeMode { + Trivial, + SimpleFast, + SimpleExhaustive, + Advanced, + AdvancedPartial, +} + +pub fn encode_block(view: View<&Frame>, prev: View<&Frame>, config: &EncodeConfig) -> Block { + let (diff, refblock) = if view.area() > config.max_diff_area { + ( + f64::INFINITY, + Block::Reference { + translation: Vec2::<isize>::ZERO, + }, + ) + } else { + let weight = importance(&view).max(0.5); + let irrelevance = config.weight_factor / weight; + let max_diff = config.ref_thres - irrelevance; + let (diff, refblock) = match config.mode { + EncodeMode::Trivial => ( + View::diff(&view, &prev), + Block::Reference { + translation: Vec2::<isize>::ZERO, + }, + ), + EncodeMode::SimpleExhaustive => simple::exhaustive(&view, &prev, config, max_diff), + EncodeMode::SimpleFast => simple::fast(&view, &prev, config, max_diff), + EncodeMode::Advanced => advanced::default(&view, &prev, config, max_diff), + EncodeMode::AdvancedPartial => advanced::partial(&view, &prev, config, max_diff), + }; + (diff - irrelevance, refblock) + }; + if diff < config.ref_thres { + refblock + } else { + if view.size.x < config.min_block_size || view.size.y < config.min_block_size { + Block::Literal(view.pixels()) + } else { + let [av, bv] = + unsafe { std::mem::transmute::<_, [View<&'static Frame>; 2]>(view.split()) }; + let [ap, bp] = + unsafe { std::mem::transmute::<_, [View<&'static Frame>; 2]>(prev.split()) }; + let config = unsafe { std::mem::transmute::<_, &'static EncodeConfig>(config) }; + + // only bother to do multithreading, when the block is big. + let (a, b) = both_par( + || encode_block(av, ap, config), + || encode_block(bv, bp, config), + config.max_threads, + ); + + if a.is_literal() && b.is_literal() { + Block::Literal(view.pixels()) + } else if a.is_ref_without_translation() && b.is_ref_without_translation() { + Block::Reference { + translation: Vec2::<isize>::ZERO, + } + } else { + Block::Split(Box::new([a, b])) + } + } + } +} + +pub fn importance(view: &View<&Frame>) -> f64 { + let mut acc = 0; + for x in 0..view.size.x { + for y in 0..view.size.y { + let p = Vec2 { x, y }; + if x > 0 { + acc += Pixel::distance(view[p], view[p + Vec2::<isize>::LEFT]); + } + if y > 0 { + acc += Pixel::distance(view[p], view[p + Vec2::<isize>::UP]); + } + } + } + (acc / view.area() as usize) as f64 +} diff --git a/evc/src/codec/encode/simple.rs b/evc/src/codec/encode/simple.rs new file mode 100644 index 0000000..8ae1abb --- /dev/null +++ b/evc/src/codec/encode/simple.rs @@ -0,0 +1,64 @@ +use crate::{block::Block, frame::Frame, helpers::vector::Vec2, view::View}; + +use super::EncodeConfig; + +pub fn exhaustive( + view: &View<&Frame>, + prev: &View<&Frame>, + _config: &EncodeConfig, + _max_diff: f64, +) -> (f64, Block) { + let mut diff = f64::INFINITY; + let mut translation = Vec2::<isize>::ZERO; + const OFFSETS: &[isize] = &[-64, -32, -16, -8, -4, -2, -1, 0, 1, 2, 4, 8, 16, 32, 64]; + for x in OFFSETS { + for y in OFFSETS { + let t = Vec2 { x: *x, y: *y }; + let d = View::diff(&view, &prev.offset(t)); + if d < diff { + translation = t; + diff = d; + } + } + } + (diff, Block::Reference { translation }) +} + +pub fn fast( + view: &View<&Frame>, + prev: &View<&Frame>, + _config: &EncodeConfig, + max_diff: f64, +) -> (f64, Block) { + let mut offset = Vec2::<isize>::ZERO; + let mut diff = View::diff(&view, &prev); + if diff < max_diff { + (diff, Block::REFZERO) + } else { + loop { + let (mut d, mut o) = (diff, offset); + let mut probe = |test_o: Vec2<isize>| { + let test_d = View::diff(&prev.clone().offset(test_o), &view); + if test_d < d { + d = test_d; + o = test_o; + } + }; + + probe(offset + Vec2 { x: 2, y: 0 }); + probe(offset + Vec2 { x: -2, y: 0 }); + probe(offset + Vec2 { x: 0, y: 2 }); + probe(offset + Vec2 { x: 0, y: -2 }); + probe(offset + Vec2 { x: 1, y: 0 }); + probe(offset + Vec2 { x: -1, y: 0 }); + probe(offset + Vec2 { x: 0, y: 1 }); + probe(offset + Vec2 { x: 0, y: -1 }); + + if d >= diff { + break (diff, Block::Reference { translation: o }); + } + diff = d; + offset = o; + } + } +} |