diff options
author | metamuffin <metamuffin@disroot.org> | 2025-05-05 15:09:54 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2025-05-05 15:09:54 +0200 |
commit | 306f96164784a8cbf405e72fa4364d6523366e95 (patch) | |
tree | 51717fc139871baa438aad806f4923669ae0896c /old/evc/src/codec | |
parent | 9cc089e2d6e841879e430b01d2f3d92c8820523e (diff) | |
download | video-codec-experiments-306f96164784a8cbf405e72fa4364d6523366e95.tar video-codec-experiments-306f96164784a8cbf405e72fa4364d6523366e95.tar.bz2 video-codec-experiments-306f96164784a8cbf405e72fa4364d6523366e95.tar.zst |
old dir
Diffstat (limited to 'old/evc/src/codec')
-rw-r--r-- | old/evc/src/codec/compress.rs | 155 | ||||
-rw-r--r-- | old/evc/src/codec/decode.rs | 34 | ||||
-rw-r--r-- | old/evc/src/codec/encode/advanced.rs | 115 | ||||
-rw-r--r-- | old/evc/src/codec/encode/mod.rs | 112 | ||||
-rw-r--r-- | old/evc/src/codec/encode/simple.rs | 65 | ||||
-rw-r--r-- | old/evc/src/codec/mod.rs | 3 |
6 files changed, 484 insertions, 0 deletions
diff --git a/old/evc/src/codec/compress.rs b/old/evc/src/codec/compress.rs new file mode 100644 index 0000000..09d1f29 --- /dev/null +++ b/old/evc/src/codec/compress.rs @@ -0,0 +1,155 @@ +use crate::{ + block::Block, + frame::Frame, + helpers::{pixel::Pixel, vector::Vec2}, + view::View, +}; +use rustdct::DctPlanner; + +pub fn compress_block(block: &mut Block, size: Vec2<usize>) { + match block { + Block::Literal(p) => { + let comp = lit_compress(size.x, size.y, &p); + *block = Block::CompressedLiteral(comp) + } + Block::Split(box [a, b]) => { + let vert = size.x > size.y; + compress_block( + a, + if vert { + Vec2 { + x: size.x / 2, + y: size.y, + } + } else { + Vec2 { + x: size.x, + y: size.y / 2, + } + }, + ); + compress_block( + b, + if vert { + Vec2 { + x: size.x - size.x / 2, + y: size.y, + } + } else { + Vec2 { + x: size.x, + y: size.y - size.y / 2, + } + }, + ); + } + _ => (), + } +} + +pub fn lit_compress(w: usize, h: usize, pixels: &[Pixel]) -> Vec<u8> { + let mut out = vec![]; + for ci in 0..3 { + let mut ch = vec![0.; w * h]; + for y in 0..h { + for x in 0..w { + let p = pixels[x + y * w]; + ch[x + y * w] = match ci { + 0 => p.r, + 1 => p.g, + 2 => p.b, + _ => unreachable!(), + } as f32 + } + } + + norm_dct_channel(w, h, &mut ch); + for i in 0..w * h { + out.extend(unsafe { std::mem::transmute::<_, [u8; 4]>(ch[i]) }); + } + } + out +} + +pub fn lit_decompress(compressed: &[u8], mut target: View<&mut Frame>) { + let (w, h) = (target.size.x as usize, target.size.y as usize); + for ci in 0..3 { + let mut ch = compressed[ci * w * h..(ci + 1) * w * h] + .chunks_exact(4) + .map(|v| unsafe { + std::mem::transmute::<_, f32>(TryInto::<[u8; 4]>::try_into(v).unwrap()) + }) + .collect::<Vec<_>>(); + norm_idct_channel(w, h, &mut ch); + for y in 0..h { + for x in 0..w { + let p = &mut target[Vec2 { + x: x as isize, + y: y as isize, + }]; + *(match ci { + 0 => &mut p.r, + 1 => &mut p.g, + 2 => &mut p.b, + _ => unreachable!(), + }) = ch[x + y * w] as u8 + } + } + } +} + +fn norm_dct_channel(w: usize, h: usize, values: &mut [f32]) { + let d = DctPlanner::new().plan_dct2(w); + let mut temp = vec![0.; d.get_scratch_len()]; + for c in values.chunks_exact_mut(w) { + d.process_dct2_with_scratch(c, &mut temp) + } + + let mut values_trans = transpose(w, h, values); + let d = DctPlanner::new().plan_dct2(h); + let mut temp = vec![0.; d.get_scratch_len()]; + for c in values_trans.chunks_exact_mut(h) { + d.process_dct2_with_scratch(c, &mut temp) + } + + let scale = 2. / (w as f32 * h as f32).sqrt(); + for (i, v) in values_trans.iter().enumerate() { + values[i] = *v * scale * 127.0 + } +} + +fn norm_idct_channel(h: usize, w: usize, values: &mut [f32]) { + let scale = 2. / (w as f32 * h as f32).sqrt(); + for v in values.iter_mut() { + *v /= scale * 127.0 + } + + let d = DctPlanner::new().plan_dct2(w); + let mut temp = vec![0.; d.get_scratch_len()]; + for c in values.chunks_exact_mut(w) { + d.process_dct3_with_scratch(c, &mut temp) + } + + let mut values_trans = transpose(w, h, values); + let d = DctPlanner::new().plan_dct2(h); + let mut temp = vec![0.; d.get_scratch_len()]; + for c in values_trans.chunks_exact_mut(h) { + d.process_dct3_with_scratch(c, &mut temp) + } + values.copy_from_slice(&values_trans) +} + +fn transpose(w: usize, h: usize, values: &[f32]) -> Vec<f32> { + let mut io = 0; + let mut it; + let mut transposed = vec![0.; values.len()]; + for row in 0..h { + it = row; + for _ in 0..w { + transposed[it] = values[io]; + io += 1; + it += h; + } + } + transposed +} diff --git a/old/evc/src/codec/decode.rs b/old/evc/src/codec/decode.rs new file mode 100644 index 0000000..c042278 --- /dev/null +++ b/old/evc/src/codec/decode.rs @@ -0,0 +1,34 @@ +use super::compress::lit_decompress; +use crate::{block::Block, frame::Frame, refsampler::Sampler, view::View}; + +pub struct DecodeConfig {} + +pub fn decode_block( + block: &Block, + mut target: View<&mut Frame>, + prev: View<&Frame>, + config: &DecodeConfig, +) { + match &block { + Block::Literal(pixels) => target.set_pixels(pixels), + Block::Split(box [a, b]) => { + let [a, b] = unsafe { std::mem::transmute::<_, [&'static Block; 2]>([a, b]) }; + let [at, bt] = unsafe { + std::mem::transmute::<_, [View<&'static mut Frame>; 2]>(target.split_mut_unsafe()) + }; + let [ap, bp] = + unsafe { std::mem::transmute::<_, [View<&'static Frame>; 2]>(prev.split()) }; + let config = unsafe { std::mem::transmute::<_, &'static DecodeConfig>(config) }; + + rayon::join( + move || decode_block(a, at, ap, config), + move || decode_block(b, bt, bp, config), + ); + } + Block::CompressedLiteral(data) => { + lit_decompress(&data, target); + } + Block::Reference { translation } => target.copy_from(&prev.offset(*translation)), + Block::AdvancedReference(r) => target.copy_from_sampler(&Sampler::from_refblock(prev, r)), + } +} diff --git a/old/evc/src/codec/encode/advanced.rs b/old/evc/src/codec/encode/advanced.rs new file mode 100644 index 0000000..0e45176 --- /dev/null +++ b/old/evc/src/codec/encode/advanced.rs @@ -0,0 +1,115 @@ +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()); + + if config.do_translate { + 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_linear_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); + + 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 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/old/evc/src/codec/encode/mod.rs b/old/evc/src/codec/encode/mod.rs new file mode 100644 index 0000000..30342f0 --- /dev/null +++ b/old/evc/src/codec/encode/mod.rs @@ -0,0 +1,112 @@ +pub mod advanced; +pub mod simple; + +use crate::{ + block::Block, + frame::Frame, + helpers::{pixel::Pixel, 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 weight_factor: f64, + + pub do_translate: bool, + pub do_value_scale: bool, + pub do_linear_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) -> (f64, 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 { + (diff, refblock) + } else { + if view.size.x < config.min_block_size || view.size.y < config.min_block_size { + (0.0, 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 ((ad, a), (bd, b)) = if view.area() > 100 { + rayon::join( + || encode_block(av, ap, config), + || encode_block(bv, bp, config), + ) + } else { + (encode_block(av, ap, config), encode_block(bv, bp, config)) + }; + + ( + ad + bd, + if a.is_literal() && b.is_literal() { + Block::Literal(view.pixels()) + } else if Block::identical_ref(&a, &b) { + 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/old/evc/src/codec/encode/simple.rs b/old/evc/src/codec/encode/simple.rs new file mode 100644 index 0000000..2a971af --- /dev/null +++ b/old/evc/src/codec/encode/simple.rs @@ -0,0 +1,65 @@ +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: 8, y: 0 }); + probe(offset + Vec2 { x: -8, y: 0 }); + probe(offset + Vec2 { x: 0, y: 8 }); + probe(offset + Vec2 { x: 0, y: -8 }); + + 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; + } + } +} diff --git a/old/evc/src/codec/mod.rs b/old/evc/src/codec/mod.rs new file mode 100644 index 0000000..3203e6e --- /dev/null +++ b/old/evc/src/codec/mod.rs @@ -0,0 +1,3 @@ +pub mod decode; +pub mod encode; +pub mod compress; |