diff options
-rw-r--r-- | Cargo.lock | 21 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | difftree/Cargo.toml | 4 | ||||
-rw-r--r-- | difftree/src/main.rs | 157 | ||||
-rwxr-xr-x | difftree/test | 4 | ||||
-rw-r--r-- | evc/src/bin/encode.rs | 2 | ||||
-rw-r--r-- | evc/src/codec/decode.rs | 4 | ||||
-rw-r--r-- | evc/src/codec/encode/mod.rs | 2 | ||||
-rw-r--r-- | framework/src/lib.rs | 142 | ||||
-rw-r--r-- | framework/src/vector.rs | 128 | ||||
-rw-r--r-- | readme.md | 7 | ||||
-rwxr-xr-x | test | 4 |
12 files changed, 360 insertions, 116 deletions
@@ -447,6 +447,15 @@ dependencies = [ ] [[package]] +name = "difftree" +version = "0.1.0" +dependencies = [ + "framework", + "rand", + "rayon", +] + +[[package]] name = "either" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -554,6 +563,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] +name = "framework" +version = "0.1.0" + +[[package]] name = "futures-core" version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1399,14 +1412,6 @@ dependencies = [ ] [[package]] -name = "tweak" -version = "0.1.0" -dependencies = [ - "rand", - "rayon", -] - -[[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4,6 +4,7 @@ members = [ "vgcodec", "difftree", "evc", + "framework", "bv1/app", "bv1/codec", "bv1/codec-web", diff --git a/difftree/Cargo.toml b/difftree/Cargo.toml index 2c19efb..0aae03b 100644 --- a/difftree/Cargo.toml +++ b/difftree/Cargo.toml @@ -1,8 +1,10 @@ [package] -name = "tweak" +name = "difftree" version = "0.1.0" edition = "2021" [dependencies] rayon = "1.8.0" rand = "0.8.5" + +framework = { path = "../framework" } diff --git a/difftree/src/main.rs b/difftree/src/main.rs index b2144a3..36faa44 100644 --- a/difftree/src/main.rs +++ b/difftree/src/main.rs @@ -1,40 +1,38 @@ -use rayon::join; -use std::{ - io::{stdin, stdout, Read, Write}, - time::Instant, +use framework::{ + vector::{UVec2, Vec2}, + Frame, Framework, Pixel, }; +use rayon::join; const WIDTH: usize = 1920; const HEIGHT: usize = 1080; fn main() { - let mut oframe = Frame::new(); - let mut dframe = Frame::new(); - let mut out = Vec::<u8>::new(); - let do_debug = false; - loop { - let timer = Instant::now(); - let iframe = Frame::read(stdin()); + let (mut framework, params) = Framework::init(); - let t = encode(&oframe, &iframe, Area::root()); - decode(&mut oframe, Area::root(), &t); + match params.mode { + framework::CodecMode::Encode => { + let mut oframe = Frame::new(params.width, params.height); + let mut dframe = Frame::new(params.width, params.height); + let mut out = Vec::<u8>::new(); + while let Some(iframe) = framework.next_frame() { + let t = encode(&oframe, &iframe, Area::root()); + decode(&mut oframe, Area::root(), &t); - write(&mut out, &t); + write(&mut out, &t); - if do_debug { - dframe.0.copy_from_slice(&oframe.0); - debug(&mut dframe, Area::root(), &t); - dframe.write(stdout()); - } else { - oframe.write(stdout()); - } + if params.debug > 0 { + dframe.pixels.copy_from_slice(&oframe.pixels); + debug(&mut dframe, Area::root(), &t); + framework.decode_done(&dframe); + } else { + framework.decode_done(&oframe) + } - eprintln!( - "compression={:.01} time={:?}", - (WIDTH * HEIGHT * 3) as f64 / out.len() as f64, - timer.elapsed() - ); - out.clear(); + out.clear(); + } + } + framework::CodecMode::Decode => todo!(), } } @@ -45,7 +43,7 @@ pub fn write(w: &mut Vec<u8>, t: &DiffTree) { write(w, a); write(w, b); } - DiffTree::Diff(d) => w.extend([1, d.0[0] as u8, d.0[1] as u8, d.0[2] as u8]), + DiffTree::Diff(d) => w.extend([1, d.r as u8, d.g as u8, d.b as u8]), } } @@ -57,7 +55,7 @@ pub fn decode(f: &mut Frame, area: Area, tree: &DiffTree) { decode(f, ab, tb); } DiffTree::Diff(diff) => { - f.apply_area_diff(area, *diff); + apply_area_diff(f, area, *diff); } } } @@ -72,10 +70,7 @@ pub fn debug(f: &mut Frame, area: Area, tree: &DiffTree) { let Area { x1, y1, x2, y2 } = area; for x in x1..x2 { for y in y1..y2 { - let o = (x + y * WIDTH) * 3; - f.0[o + 0] = 255; - f.0[o + 1] = 0; - f.0[o + 2] = 255; + f[(x, y)] = Pixel { r: 0, g: 0, b: 255 } } } } @@ -84,24 +79,31 @@ pub fn debug(f: &mut Frame, area: Area, tree: &DiffTree) { pub fn encode(a: &Frame, b: &Frame, area: Area) -> DiffTree { if area.area() == 1 { - DiffTree::Diff(Frame::diff_pixel(a, b, area.x1, area.y1)) + DiffTree::Diff(diff_pixel( + a, + b, + Vec2 { + x: area.x1, + y: area.y1, + }, + )) } else { let (aa, ba) = area.split(); let (at, bt) = join(|| encode(a, b, aa), || encode(a, b, ba)); match (&at, &bt) { (DiffTree::Diff(ad), DiffTree::Diff(bd)) => { - let d_r = ad.0[0].abs_diff(bd.0[0]); - let d_g = ad.0[1].abs_diff(bd.0[1]); - let d_b = ad.0[2].abs_diff(bd.0[2]); + let d_r = ad.r.abs_diff(bd.r); + let d_g = ad.g.abs_diff(bd.g); + let d_b = ad.b.abs_diff(bd.b); let visdiff = (d_r as usize + d_g as usize + d_b as usize) * aa.area(); if visdiff < 100 { - return DiffTree::Diff(PixelDiff([ - ((ad.0[0] as i16 + bd.0[0] as i16) / 2) as i8, - ((ad.0[1] as i16 + bd.0[1] as i16) / 2) as i8, - ((ad.0[2] as i16 + bd.0[2] as i16) / 2) as i8, - ])); + return DiffTree::Diff(Pixel { + r: ((ad.r as i16 + bd.r as i16) / 2) as i8, + g: ((ad.g as i16 + bd.g as i16) / 2) as i8, + b: ((ad.b as i16 + bd.b as i16) / 2) as i8, + }); } } _ => (), @@ -112,13 +114,9 @@ pub fn encode(a: &Frame, b: &Frame, area: Area) -> DiffTree { pub enum DiffTree { Split([Box<DiffTree>; 2]), - Diff(PixelDiff), + Diff(Pixel<i8>), } -#[derive(Debug)] -pub struct Frame(Vec<u8>); -#[derive(Debug, Clone, Copy)] -pub struct PixelDiff([i8; 3]); #[derive(Debug, Clone, Copy)] pub struct Area { x1: usize, @@ -135,61 +133,26 @@ fn diff_clamp(x: u8, y: u8) -> i8 { -((x - y).min(128) as i8) } } - -impl Frame { - pub fn new() -> Self { - Self(vec![0u8; WIDTH * HEIGHT * 3]) - } - pub fn read(mut r: impl Read) -> Self { - let mut f = Frame::new(); - r.read_exact(&mut f.0).unwrap(); - f - } - pub fn write(&self, mut w: impl Write) { - w.write_all(&self.0).unwrap() - } - - pub fn diff_pixel(a: &Frame, b: &Frame, x: usize, y: usize) -> PixelDiff { - let o = (x + y * WIDTH) * 3; - PixelDiff([ - diff_clamp(a.0[o + 0], b.0[o + 0]), - diff_clamp(a.0[o + 1], b.0[o + 1]), - diff_clamp(a.0[o + 2], b.0[o + 2]), - ]) +pub fn diff_pixel(a: &Frame, b: &Frame, p: UVec2) -> Pixel<i8> { + let (ap, bp) = (a[p], b[p]); + Pixel { + r: diff_clamp(ap.r, bp.r), + g: diff_clamp(ap.g, bp.g), + b: diff_clamp(ap.b, bp.b), } +} - pub fn apply_area_diff(&mut self, Area { x1, y1, x2, y2 }: Area, p: PixelDiff) { - for x in x1..x2 { - for y in y1..y2 { - let o = (x + y * WIDTH) * 3; - self.0[o + 0] = self.0[o + 0].saturating_add_signed(p.0[0]); - self.0[o + 1] = self.0[o + 1].saturating_add_signed(p.0[1]); - self.0[o + 2] = self.0[o + 2].saturating_add_signed(p.0[2]); - } +pub fn apply_area_diff(frame: &mut Frame, Area { x1, y1, x2, y2 }: Area, diff: Pixel<i8>) { + for x in x1..x2 { + for y in y1..y2 { + let p = &mut frame[(x, y)]; + p.r = p.r.saturating_add_signed(diff.r); + p.g = p.g.saturating_add_signed(diff.g); + p.b = p.b.saturating_add_signed(diff.b); } } - // pub fn average_area_diff( - // &self, - // other: &Frame, - // area @ Area { x1, y1, x2, y2 }: Area, - // ) -> PixelDiff { - // let (mut r, mut g, mut b) = (0i32, 0i32, 0i32); - // for x in x1..x2 { - // for y in y1..y2 { - // let o = (x + y * WIDTH) * 3; - // r += other.0[o + 0] as i32 - self.0[o + 0] as i32; - // g += other.0[o + 1] as i32 - self.0[o + 1] as i32; - // b += other.0[o + 2] as i32 - self.0[o + 2] as i32; - // } - // } - // let a = area.area() as i32; - // PixelDiff([ - // (r / a).clamp(i8::MIN as i32, i8::MAX as i32) as i8, - // (g / a).clamp(i8::MIN as i32, i8::MAX as i32) as i8, - // (b / a).clamp(i8::MIN as i32, i8::MAX as i32) as i8, - // ]) - // } } + impl Area { pub fn area(&self) -> usize { self.width() as usize * self.height() as usize diff --git a/difftree/test b/difftree/test deleted file mode 100755 index 4bfc7d8..0000000 --- a/difftree/test +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/fish -ffmpeg -loglevel quiet -i $argv[1] -vf 'scale=1920x1080,format=rgb24' -f rawvideo pipe:1 \ - | cargo run --release \ - | ffplay -loglevel quiet -video_size 1920x1080 -pixel_format rgb24 -f rawvideo pipe:0 diff --git a/evc/src/bin/encode.rs b/evc/src/bin/encode.rs index 6d18992..43f2c57 100644 --- a/evc/src/bin/encode.rs +++ b/evc/src/bin/encode.rs @@ -3,7 +3,6 @@ use clap::Parser; use indicatif::ProgressBar; use libreschmux::{ codec::{ - compress::compress_block, decode::{decode_block, DecodeConfig}, encode::{encode_block, EncodeConfig, EncodeMode}, }, @@ -12,7 +11,6 @@ use libreschmux::{ helpers::vector::Vec2, }; use log::info; -use rayon::{ThreadPool, ThreadPoolBuilder}; use std::io::{BufReader, BufWriter}; #[derive(Parser)] diff --git a/evc/src/codec/decode.rs b/evc/src/codec/decode.rs index b1f6a3d..c042278 100644 --- a/evc/src/codec/decode.rs +++ b/evc/src/codec/decode.rs @@ -1,7 +1,5 @@ use super::compress::lit_decompress; -use crate::{ - block::Block, frame::Frame, helpers::threading::both_par, refsampler::Sampler, view::View, -}; +use crate::{block::Block, frame::Frame, refsampler::Sampler, view::View}; pub struct DecodeConfig {} diff --git a/evc/src/codec/encode/mod.rs b/evc/src/codec/encode/mod.rs index 43607aa..30342f0 100644 --- a/evc/src/codec/encode/mod.rs +++ b/evc/src/codec/encode/mod.rs @@ -4,7 +4,7 @@ pub mod simple; use crate::{ block::Block, frame::Frame, - helpers::{pixel::Pixel, threading::both_par, vector::Vec2}, + helpers::{pixel::Pixel, vector::Vec2}, view::View, }; use clap::ValueEnum; diff --git a/framework/src/lib.rs b/framework/src/lib.rs index 139597f..89fcc10 100644 --- a/framework/src/lib.rs +++ b/framework/src/lib.rs @@ -1,2 +1,144 @@ +use std::{ + io::{self, stdin, stdout, Read, Write}, + ops::{Index, IndexMut}, + time::Instant, +}; +use vector::{UVec2, Vec2}; +pub mod vector; +#[derive(Clone)] +pub struct CodecParams { + pub width: usize, + pub height: usize, + pub debug: usize, + pub mode: CodecMode, +} +#[derive(Clone)] +pub enum CodecMode { + Encode, + Decode, +} + +pub struct Framework { + process_start: Instant, + params: CodecParams, +} + +impl Framework { + pub fn init() -> (Self, CodecParams) { + let width = std::env::var("V_WIDTH").unwrap().parse().unwrap(); + let height = std::env::var("V_HEIGHT").unwrap().parse().unwrap(); + let debug = std::env::var("V_DEBUG").unwrap().parse().unwrap(); + let mode = match std::env::var("V_MODE").unwrap().as_str() { + "encode" => CodecMode::Encode, + "decode" => CodecMode::Decode, + _ => panic!("invalid mode"), + }; + let params = CodecParams { + height, + width, + debug, + mode, + }; + ( + Self { + params: params.clone(), + process_start: Instant::now(), + }, + params, + ) + } + + pub fn next_frame(&mut self) -> Option<Frame> { + self.process_start = Instant::now(); + Some(Frame::read(self.params.width, self.params.height, stdin()).unwrap()) + } + pub fn encode_done(&mut self, output: &[u8]) { + let el = self.process_start.elapsed(); + eprintln!("size={}Kb t={el:?}", output.len() / 1000 * 8) + } + + pub fn next_chunk(&mut self, buffer: &mut Vec<u8>) -> bool { + true + } + pub fn decode_done(&mut self, output: &Frame) { + output.write(stdout()).unwrap(); + } +} + +#[derive(Debug, Clone)] +pub struct Frame { + pub width: usize, + pub height: usize, + pub pixels: Vec<Pixel<u8>>, +} + +#[derive(Debug, Clone, Copy)] +pub struct Pixel<T> { + pub r: T, + pub g: T, + pub b: T, +} +pub type EPixel = Pixel<u8>; + +impl Frame { + pub fn new(width: usize, height: usize) -> Self { + Self { + height, + width, + pixels: vec![Pixel::BLACK; width * height], + } + } + pub fn read(width: usize, height: usize, mut r: impl Read) -> io::Result<Self> { + let mut f = Frame::new(width, height); + let p = unsafe { + std::slice::from_raw_parts_mut( + f.pixels.as_mut_slice().as_mut_ptr() as *mut u8, + width * height * 3, + ) + }; + r.read_exact(p)?; + Ok(f) + } + pub fn write(&self, mut w: impl Write) -> io::Result<()> { + let p = unsafe { + std::slice::from_raw_parts( + self.pixels.as_slice().as_ptr() as *const u8, + self.width * self.height * 3, + ) + }; + w.write_all(p) + } +} + +impl Index<(usize, usize)> for Frame { + type Output = Pixel<u8>; + #[inline] + fn index(&self, (x, y): (usize, usize)) -> &Self::Output { + &self.pixels[x + y * self.width] + } +} +impl IndexMut<(usize, usize)> for Frame { + #[inline] + fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut Self::Output { + &mut self.pixels[x + y * self.width] + } +} +impl Index<UVec2> for Frame { + type Output = Pixel<u8>; + #[inline] + fn index(&self, Vec2 { x, y }: UVec2) -> &Self::Output { + &self.pixels[x + y * self.width] + } +} +impl IndexMut<UVec2> for Frame { + #[inline] + fn index_mut(&mut self, Vec2 { x, y }: UVec2) -> &mut Self::Output { + &mut self.pixels[x + y * self.width] + } +} + +impl Pixel<u8> { + pub const BLACK: Pixel<u8> = Pixel { r: 0, g: 0, b: 0 }; +} diff --git a/framework/src/vector.rs b/framework/src/vector.rs new file mode 100644 index 0000000..5227d04 --- /dev/null +++ b/framework/src/vector.rs @@ -0,0 +1,128 @@ +pub type UVec2 = Vec2<usize>; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Vec2<T> { + pub x: T, + pub y: T, +} + +impl From<Vec2<isize>> for Vec2<u16> { + fn from(value: Vec2<isize>) -> Self { + Self { + x: value.x as u16, + y: value.y as u16, + } + } +} +impl From<Vec2<u16>> for Vec2<isize> { + fn from(value: Vec2<u16>) -> Self { + Self { + x: value.x as isize, + y: value.y as isize, + } + } +} +impl From<Vec2<f32>> for Vec2<isize> { + fn from(value: Vec2<f32>) -> Self { + Self { + x: value.x as isize, + y: value.y as isize, + } + } +} + +impl Vec2<isize> { + pub const ZERO: Vec2<isize> = Vec2 { x: 0, y: 0 }; + pub const UP: Vec2<isize> = Vec2 { x: 0, y: -1 }; + pub const LEFT: Vec2<isize> = Vec2 { x: -1, y: 0 }; +} +impl Vec2<f32> { + pub const ZERO: Vec2<f32> = Vec2 { x: 0.0, y: 0.0 }; + pub const UP: Vec2<f32> = Vec2 { x: 0.0, y: -1.0 }; + pub const LEFT: Vec2<f32> = Vec2 { x: -1.0, y: 0.0 }; +} + +impl<T: std::ops::Div<Output = T> + Copy> Vec2<T> { + pub fn downscale(&self, f: T) -> Self { + Self { + x: self.x / f, + y: self.y / f, + } + } +} + +impl<T: std::ops::Mul<Output = T> + Copy> Vec2<T> { + pub fn scale(&self, f: T) -> Self { + Self { + x: self.x * f, + y: self.y * f, + } + } + pub fn area(&self) -> T { + self.x * self.y + } +} + +impl Vec2<isize> { + pub fn x_only(&self) -> Self { + Self { x: self.x, y: 0 } + } + pub fn y_only(&self) -> Self { + Self { x: 0, y: self.y } + } +} + +impl Into<Vec2<f32>> for Vec2<isize> { + fn into(self) -> Vec2<f32> { + Vec2 { + x: self.x as f32, + y: self.y as f32, + } + } +} +impl From<(isize, isize)> for Vec2<f32> { + fn from((x, y): (isize, isize)) -> Self { + Vec2 { + x: x as f32, + y: y as f32, + } + } +} + +impl<T: std::ops::Add> std::ops::Add for Vec2<T> { + type Output = Vec2<T::Output>; + #[inline] + fn add(self, rhs: Self) -> Self::Output { + Vec2 { + x: self.x + rhs.x, + y: self.y + rhs.y, + } + } +} +impl<T: std::ops::Sub> std::ops::Sub for Vec2<T> { + type Output = Vec2<T::Output>; + #[inline] + fn sub(self, rhs: Self) -> Self::Output { + Vec2 { + x: self.x - rhs.x, + y: self.y - rhs.y, + } + } +} +impl<T: std::ops::Mul> std::ops::Mul for Vec2<T> { + type Output = Vec2<T::Output>; + #[inline] + fn mul(self, rhs: Self) -> Self::Output { + Vec2 { + x: self.x * rhs.x, + y: self.y * rhs.y, + } + } +} + +impl<T> From<(T, T)> for Vec2<T> { + #[inline] + fn from((x, y): (T, T)) -> Self { + Vec2 { x, y } + } +} @@ -14,3 +14,10 @@ H.264. - `vgcodec` approximates images by drawing circles (on the GPU). - (`dhwt-codec` tries to compress using a discrete haar wavelet across all three dimensions. that doesnt work well) + +## the test framework + +| Variable | Description | +| ---------- | ------------ | +| `V_WIDTH` | Video width | +| `V_HEIGHT` | Video height | @@ -0,0 +1,4 @@ +#!/bin/fish +ffmpeg -loglevel quiet -i $argv[2] -vf 'scale=1920x1080,format=rgb24' -f rawvideo pipe:1 \ + | V_WIDTH=1920 V_HEIGHT=1080 V_DEBUG=0 V_MODE=encode cargo run --release --bin $argv[1] \ + | ffplay -loglevel quiet -video_size 1920x1080 -pixel_format rgb24 -f rawvideo pipe:0 |