diff options
author | metamuffin <metamuffin@disroot.org> | 2023-03-09 18:16:05 +0100 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2023-03-09 18:16:05 +0100 |
commit | ac27a58c87bea9480f6603a731448f69c9c3b0fb (patch) | |
tree | b437fb947a5d650c88d8db743c521e832f79611f /lvc/codec/src/encode.rs | |
parent | 4fbf4bb4310da4bede43c9428809ac9a8804982a (diff) | |
download | video-codec-experiments-ac27a58c87bea9480f6603a731448f69c9c3b0fb.tar video-codec-experiments-ac27a58c87bea9480f6603a731448f69c9c3b0fb.tar.bz2 video-codec-experiments-ac27a58c87bea9480f6603a731448f69c9c3b0fb.tar.zst |
rearrange files
Diffstat (limited to 'lvc/codec/src/encode.rs')
-rw-r--r-- | lvc/codec/src/encode.rs | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/lvc/codec/src/encode.rs b/lvc/codec/src/encode.rs new file mode 100644 index 0000000..030e882 --- /dev/null +++ b/lvc/codec/src/encode.rs @@ -0,0 +1,127 @@ +use crate::diff::{diff, pixel_diff}; +use crate::split::split; +use crate::{Block, Frame, Pixel, Ref, View, P2}; + +#[derive(Debug, Clone)] +pub struct EncodeConfig { + pub threshold: f32, + pub max_block_size: usize, + pub attention_split: u32, +} + +pub fn encode(last_frame: &Frame, frame: &Frame, view: View, config: &EncodeConfig) -> Block { + let view_area = view.size().area(); + if view_area > config.max_block_size + || (view_area > 64 && attention(frame, view) > config.attention_split) + { + let [av, bv] = split(view); + let (ab, bb) = rayon::join( + || Box::new(encode(last_frame, frame, av, config)), + || Box::new(encode(last_frame, frame, bv, config)), + ); + return Block::Split(ab, bb); + } + + let mut r = Ref::default(); + let mut d = diff([last_frame, frame], view, r); + + // let att = 1. - attention(frame, view) as f32 * 0.000001; + // let thres = (config.threshold as f32 * att.clamp(0.2, 1.0)) as u32; + let thres = (config.threshold * view_area as f32) as u32; + + let target_average = average_color(frame, view); + + for granularity in [2, 1, 2, 1, 2, 1, 2, 1] { + let (nd, nrp) = optimize_ref(last_frame, frame, view, r, granularity, target_average); + if nd < d { + r = nrp; + d = nd; + } else { + break; + } + } + + if d < thres { + return Block::Ref(r); + } else { + Block::Lit(frame.export(view)) + } +} + +pub fn optimize_ref( + last_frame: &Frame, + frame: &Frame, + view: View, + r: Ref, + g: i32, + target_average: Pixel, +) -> (u32, Ref) { + let g2 = g * 2; + [ + Some(r.apply(|r| r.pos_off += P2 { x: g, y: 0 })), + Some(r.apply(|r| r.pos_off += P2 { x: g, y: g })), + Some(r.apply(|r| r.pos_off += P2 { x: 0, y: g })), + Some(r.apply(|r| r.pos_off += P2 { x: -g, y: g })), + Some(r.apply(|r| r.pos_off += P2 { x: -g, y: 0 })), + Some(r.apply(|r| r.pos_off += P2 { x: -g, y: -g })), + Some(r.apply(|r| r.pos_off += P2 { x: 0, y: -g })), + Some(r.apply(|r| r.pos_off += P2 { x: g, y: -g })), + Some(r.apply(|r| r.pos_off += P2 { x: g2, y: 0 })), + Some(r.apply(|r| r.pos_off += P2 { x: g2, y: g2 })), + Some(r.apply(|r| r.pos_off += P2 { x: 0, y: g2 })), + Some(r.apply(|r| r.pos_off += P2 { x: -g2, y: g2 })), + Some(r.apply(|r| r.pos_off += P2 { x: -g2, y: 0 })), + Some(r.apply(|r| r.pos_off += P2 { x: -g2, y: -g2 })), + Some(r.apply(|r| r.pos_off += P2 { x: 0, y: -g2 })), + Some(r.apply(|r| r.pos_off += P2 { x: g2, y: -g2 })), + { + let mut r = r; + let last_avr = average_color(last_frame, view); + let diff = target_average - last_avr; + r.color_off = diff; + if diff != Pixel::BLACK { + Some(r) + } else { + None + } + }, + ] + .into_iter() + .flatten() + .map(|r| (diff([last_frame, frame], view, r), r)) + .min_by_key(|e| e.0) + .unwrap() +} + +pub fn attention(frame: &Frame, view: View) -> u32 { + let mut k = 0; + for y in view.a.y..view.b.y - 1 { + for x in view.a.x..view.b.x - 1 { + let p = P2 { x, y }; + k += pixel_diff(frame[p], frame[p + P2::X]).pow(2); + k += pixel_diff(frame[p], frame[p + P2::Y]).pow(2); + } + } + k +} + +pub fn average_color(frame: &Frame, view: View) -> Pixel { + let mut r = 0u32; + let mut g = 0u32; + let mut b = 0u32; + + for y in view.a.y..view.b.y { + for x in view.a.x..view.b.x { + let p = frame[P2 { x, y }]; + r += p.r as u32; + g += p.g as u32; + b += p.b as u32; + } + } + let area = view.size().area() as u32; + Pixel { + r: (r / area) as i16, + g: (g / area) as i16, + b: (b / area) as i16, + } +} |