From: Kostya Shishkov Date: Mon, 18 Oct 2021 15:31:09 +0000 (+0200) Subject: core/scale: add options support X-Git-Url: https://git.nihav.org/?p=nihav.git;a=commitdiff_plain;h=25e0bf9a974b1bb5a8f8b38e23b4a7e91db3eab0 core/scale: add options support --- diff --git a/nihav-core/src/scale/colorcvt.rs b/nihav-core/src/scale/colorcvt.rs index bc6c4b6..00ae2a5 100644 --- a/nihav-core/src/scale/colorcvt.rs +++ b/nihav-core/src/scale/colorcvt.rs @@ -17,6 +17,33 @@ const YUV_PARAMS: &[[f32; 2]] = &[ [ 0.2627, 0.0593 ], // ITU-R BT2020 ]; +fn parse_yuv_mat(name: &str) -> usize { + match name { + "rgb" => 0, + "bt709" => 1, + "bt601" => 4, + "bt470" => 5, + "smpte170m" => 6, + "smpte240m" => 7, + "ycocg" => 8, + "bt2020" => 9, + _ => 2, + } +} + +/*fn get_yuv_mat(id: usize) -> &'static str { + match id { + 1 => "bt709", + 4 => "bt601", + 5 => "bt470", + 6 => "smpte170m", + 7 => "smpte240m", + 8 => "ycocg", + 9 => "bt2020", + _ => "rgb", + } +}*/ + const BT_PAL_COEFFS: [f32; 2] = [ 0.493, 0.877 ]; const SMPTE_NTSC_COEFFS: &[f32; 4] = &[ -0.268, 0.7358, 0.4127, 0.4778 ]; @@ -163,6 +190,7 @@ fn matrix_mul(mat: &[[f32; 3]; 3], a: f32, b: f32, c: f32) -> (f32, f32, f32) { #[derive(Default)] struct RgbToYuv { matrix: [[f32; 3]; 3], + mode: usize, } impl RgbToYuv { @@ -171,10 +199,23 @@ impl RgbToYuv { #[allow(clippy::many_single_char_names)] impl Kernel for RgbToYuv { - fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo) -> ScaleResult { + fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo, options: &[(String, String)]) -> ScaleResult { + let mut debug = false; + let mut mode = DEFAULT_YUV; + for (name, value) in options.iter() { + match (name.as_str(), value.as_str()) { + ("debug", "") => { debug = true; }, + ("debug", "true") => { debug = true; }, + ("rgb2yuv.mode", ymode) => { + mode = parse_yuv_mat(ymode); + }, + _ => {}, + } + } + self.mode = mode; + let mut df = dest_fmt.fmt; -//todo coeff selection - make_rgb2yuv(YUV_PARAMS[DEFAULT_YUV][0], YUV_PARAMS[DEFAULT_YUV][1], &mut self.matrix); + make_rgb2yuv(YUV_PARAMS[mode][0], YUV_PARAMS[mode][1], &mut self.matrix); if let ColorModel::YUV(yuvsm) = df.get_model() { match yuvsm { YUVSubmodel::YCbCr => {}, @@ -192,7 +233,9 @@ impl Kernel for RgbToYuv { chr.v_ss = 0; } } -println!(" [intermediate format {}]", df); + if debug { + println!(" [intermediate format {}]", df); + } let res = alloc_video_buffer(NAVideoInfo::new(in_fmt.width, in_fmt.height, false, df), 3); if res.is_err() { return Err(ScaleError::AllocError); } Ok(res.unwrap()) @@ -272,6 +315,7 @@ pub fn create_rgb2yuv() -> Box { #[derive(Default)] struct YuvToRgb { matrix: [[f32; 3]; 3], + mode: usize, yscale: Vec, r_chr: Vec, g_u: Vec, @@ -285,7 +329,21 @@ impl YuvToRgb { #[allow(clippy::many_single_char_names)] impl Kernel for YuvToRgb { - fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo) -> ScaleResult { + fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo, options: &[(String, String)]) -> ScaleResult { + let mut debug = false; + let mut mode = DEFAULT_YUV; + for (name, value) in options.iter() { + match (name.as_str(), value.as_str()) { + ("debug", "") => { debug = true; }, + ("debug", "true") => { debug = true; }, + ("yuv2rgb.mode", ymode) => { + mode = parse_yuv_mat(ymode); + }, + _ => {}, + } + } + self.mode = mode; + let mut df = dest_fmt.fmt; df.palette = false; if !df.is_unpacked() || df.get_max_depth() != 8 || df.get_total_depth() != df.get_num_comp() as u8 * 8 { @@ -303,8 +361,7 @@ impl Kernel for YuvToRgb { df.comp_info[3] = Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: false, depth: 8, shift: 0, comp_offs: 3, next_elem: 1 }); } } -//todo coeff selection - make_yuv2rgb(YUV_PARAMS[DEFAULT_YUV][0], YUV_PARAMS[DEFAULT_YUV][1], &mut self.matrix); + make_yuv2rgb(YUV_PARAMS[mode][0], YUV_PARAMS[mode][1], &mut self.matrix); if let ColorModel::YUV(yuvsm) = in_fmt.fmt.get_model() { match yuvsm { YUVSubmodel::YCbCr => {}, @@ -345,7 +402,9 @@ impl Kernel for YuvToRgb { chr.comp_offs = i as u8; } } -println!(" [intermediate format {}]", df); + if debug { + println!(" [intermediate format {}]", df); + } let res = alloc_video_buffer(NAVideoInfo::new(in_fmt.width, in_fmt.height, false, df), 3); if res.is_err() { return Err(ScaleError::AllocError); } Ok(res.unwrap()) diff --git a/nihav-core/src/scale/kernel.rs b/nihav-core/src/scale/kernel.rs index 84a69b4..abc7835 100644 --- a/nihav-core/src/scale/kernel.rs +++ b/nihav-core/src/scale/kernel.rs @@ -2,6 +2,6 @@ use crate::frame::*; use super::{ScaleInfo, ScaleResult}; pub trait Kernel { - fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo) -> ScaleResult; + fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo, options: &[(String, String)]) -> ScaleResult; fn process(&mut self, pic_in: &NABufferType, pic_out: &mut NABufferType); } diff --git a/nihav-core/src/scale/mod.rs b/nihav-core/src/scale/mod.rs index cc2388e..6bf6e27 100644 --- a/nihav-core/src/scale/mod.rs +++ b/nihav-core/src/scale/mod.rs @@ -109,9 +109,9 @@ pub fn get_scale_fmt_from_pic(pic: &NABufferType) -> ScaleInfo { } impl Stage { - fn new(name: &str, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo) -> ScaleResult { + fn new(name: &str, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo, options: &[(String, String)]) -> ScaleResult { let mut worker = KernelDesc::find(name)?; - let tmp_pic = worker.init(in_fmt, dest_fmt)?; + let tmp_pic = worker.init(in_fmt, dest_fmt, options)?; let fmt_out = get_scale_fmt_from_pic(&tmp_pic); Ok(Self { fmt_out, tmp_pic, next: None, worker }) } @@ -260,11 +260,21 @@ fn fmt_needs_scale(ifmt: &NAPixelFormaton, ofmt: &NAPixelFormaton) -> bool { } false } -fn build_pipeline(ifmt: &ScaleInfo, ofmt: &ScaleInfo, just_convert: bool) -> ScaleResult> { +fn build_pipeline(ifmt: &ScaleInfo, ofmt: &ScaleInfo, just_convert: bool, options: &[(String, String)]) -> ScaleResult> { + let mut debug = false; + for (name, value) in options.iter() { + if name == "debug" && (value == "" || value == "true") { + debug = true; + break; + } + } + let inname = ifmt.fmt.get_model().get_short_name(); let outname = ofmt.fmt.get_model().get_short_name(); -println!("convert {} -> {}", ifmt, ofmt); + if debug { + println!("convert {} -> {}", ifmt, ofmt); + } let needs_scale = if fmt_needs_scale(&ifmt.fmt, &ofmt.fmt) { true } else { @@ -282,46 +292,60 @@ println!("convert {} -> {}", ifmt, ofmt); let mut cur_fmt = *ifmt; if needs_unpack { -println!("[adding unpack]"); + if debug { + println!("[adding unpack]"); + } let new_stage = if !cur_fmt.fmt.is_paletted() { - Stage::new("unpack", &cur_fmt, &ofmt)? + Stage::new("unpack", &cur_fmt, &ofmt, options)? } else { - Stage::new("depal", &cur_fmt, &ofmt)? + Stage::new("depal", &cur_fmt, &ofmt, options)? }; cur_fmt = new_stage.fmt_out; add_stage!(stages, new_stage); } if needs_scale && scale_before_cvt { -println!("[adding scale]"); - let new_stage = Stage::new("scale", &cur_fmt, &ofmt)?; + if debug { + println!("[adding scale]"); + } + let new_stage = Stage::new("scale", &cur_fmt, &ofmt, options)?; cur_fmt = new_stage.fmt_out; add_stage!(stages, new_stage); } if needs_convert { -println!("[adding convert]"); + if debug { + println!("[adding convert]"); + } let cvtname = format!("{}_to_{}", inname, outname); -println!("[{}]", cvtname); - let new_stage = Stage::new(&cvtname, &cur_fmt, &ofmt)?; + if debug { + println!("[{}]", cvtname); + } + let new_stage = Stage::new(&cvtname, &cur_fmt, &ofmt, options)?; //todo if fails try converting via RGB or YUV cur_fmt = new_stage.fmt_out; add_stage!(stages, new_stage); //todo alpha plane copy/add } if needs_scale && !scale_before_cvt { -println!("[adding scale]"); - let new_stage = Stage::new("scale", &cur_fmt, &ofmt)?; + if debug { + println!("[adding scale]"); + } + let new_stage = Stage::new("scale", &cur_fmt, &ofmt, options)?; cur_fmt = new_stage.fmt_out; add_stage!(stages, new_stage); } if needs_pack && !needs_palettise { -println!("[adding pack]"); - let new_stage = Stage::new("pack", &cur_fmt, &ofmt)?; + if debug { + println!("[adding pack]"); + } + let new_stage = Stage::new("pack", &cur_fmt, &ofmt, options)?; //cur_fmt = new_stage.fmt_out; add_stage!(stages, new_stage); } if needs_palettise { -println!("[adding palettise]"); - let new_stage = Stage::new("palette", &cur_fmt, &ofmt)?; + if debug { + println!("[adding palettise]"); + } + let new_stage = Stage::new("palette", &cur_fmt, &ofmt, options)?; //cur_fmt = new_stage.fmt_out; add_stage!(stages, new_stage); } @@ -417,7 +441,18 @@ impl NAScale { let pipeline; let just_convert = (fmt_in.width == fmt_out.width) && (fmt_in.height == fmt_out.height); if fmt_in != fmt_out { - pipeline = build_pipeline(&fmt_in, &fmt_out, just_convert)?; + pipeline = build_pipeline(&fmt_in, &fmt_out, just_convert, &[])?; + } else { + pipeline = None; + } + Ok(Self { fmt_in, fmt_out, just_convert, pipeline }) + } + /// Constructs a new `NAScale` instance taking into account provided options. + pub fn new_with_options(fmt_in: ScaleInfo, fmt_out: ScaleInfo, options: &[(String, String)]) -> ScaleResult { + let pipeline; + let just_convert = (fmt_in.width == fmt_out.width) && (fmt_in.height == fmt_out.height); + if fmt_in != fmt_out { + pipeline = build_pipeline(&fmt_in, &fmt_out, just_convert, options)?; } else { pipeline = None; } diff --git a/nihav-core/src/scale/palette/mod.rs b/nihav-core/src/scale/palette/mod.rs index 2005cd8..f9b0ee9 100644 --- a/nihav-core/src/scale/palette/mod.rs +++ b/nihav-core/src/scale/palette/mod.rs @@ -220,7 +220,29 @@ impl PalettiseKernel { } impl Kernel for PalettiseKernel { - fn init(&mut self, in_fmt: &ScaleInfo, _dest_fmt: &ScaleInfo) -> ScaleResult { + fn init(&mut self, in_fmt: &ScaleInfo, _dest_fmt: &ScaleInfo, options: &[(String, String)]) -> ScaleResult { + for (name, value) in options.iter() { + match name.as_str() { + "pal.quant" => { + self.qmode = match value.as_str() { + "mediancut" => QuantisationMode::MedianCut, + "elbg" => QuantisationMode::ELBG, + "neuquant" => QuantisationMode::NeuQuant(3), + _ => QuantisationMode::default(), + }; + }, + "pal.search" => { + self.palmode = match value.as_str() { + "full" => PaletteSearchMode::Full, + "local" => PaletteSearchMode::Local, + "kdtree" => PaletteSearchMode::KDTree, + _ => PaletteSearchMode::default(), + }; + }, + _ => {}, + }; + } + self.pixels = Vec::with_capacity(in_fmt.width * in_fmt.height); let res = alloc_video_buffer(NAVideoInfo::new(in_fmt.width, in_fmt.height, false, PAL8_FORMAT), 0); if res.is_err() { return Err(ScaleError::AllocError); } diff --git a/nihav-core/src/scale/repack.rs b/nihav-core/src/scale/repack.rs index a9d354a..8cdb8ea 100644 --- a/nihav-core/src/scale/repack.rs +++ b/nihav-core/src/scale/repack.rs @@ -33,7 +33,7 @@ impl PackKernel { } impl Kernel for PackKernel { - fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo) -> ScaleResult { + fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo, _options: &[(String, String)]) -> ScaleResult { self.ncomps = in_fmt.fmt.components.min(dest_fmt.fmt.components) as usize; for i in 0..self.ncomps { let ichr = in_fmt.fmt.comp_info[i].unwrap(); @@ -120,7 +120,16 @@ impl UnpackKernel { } impl Kernel for UnpackKernel { - fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo) -> ScaleResult { + fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo, options: &[(String, String)]) -> ScaleResult { + let mut debug = false; + for (name, value) in options.iter() { + match (name.as_str(), value.as_str()) { + ("debug", "") => { debug = true; }, + ("debug", "true") => { debug = true; }, + _ => {}, + } + } + self.ncomps = in_fmt.fmt.components.min(dest_fmt.fmt.components) as usize; let mut chr: Vec> = Vec::with_capacity(MAX_CHROMATONS); for i in 0..self.ncomps { @@ -146,7 +155,9 @@ impl Kernel for UnpackKernel { df.comp_info[..self.ncomps].clone_from_slice(&chr[..self.ncomps]); df.components = self.ncomps as u8; df.palette = false; -println!(" [intermediate format {}]", df); + if debug { + println!(" [intermediate format {}]", df); + } let res = alloc_video_buffer(NAVideoInfo::new(in_fmt.width, in_fmt.height, false, df), 3); if res.is_err() { return Err(ScaleError::AllocError); } Ok(res.unwrap()) @@ -256,7 +267,16 @@ impl DepalKernel { } impl Kernel for DepalKernel { - fn init(&mut self, in_fmt: &ScaleInfo, _dest_fmt: &ScaleInfo) -> ScaleResult { + fn init(&mut self, in_fmt: &ScaleInfo, _dest_fmt: &ScaleInfo, options: &[(String, String)]) -> ScaleResult { + let mut debug = false; + for (name, value) in options.iter() { + match (name.as_str(), value.as_str()) { + ("debug", "") => { debug = true; }, + ("debug", "true") => { debug = true; }, + _ => {}, + } + } + //todo select output more fitting for dest_fmt if possible self.ncomps = in_fmt.fmt.components as usize; let mut chr: Vec> = Vec::with_capacity(MAX_CHROMATONS); @@ -276,7 +296,9 @@ impl Kernel for DepalKernel { let mut df = in_fmt.fmt; df.palette = false; df.comp_info[..self.ncomps].clone_from_slice(&chr[..self.ncomps]); -println!(" [intermediate format {}]", df); + if debug { + println!(" [intermediate format {}]", df); + } let res = alloc_video_buffer(NAVideoInfo::new(in_fmt.width, in_fmt.height, false, df), 3); if res.is_err() { return Err(ScaleError::AllocError); } Ok(res.unwrap()) diff --git a/nihav-core/src/scale/scale.rs b/nihav-core/src/scale/scale.rs index fc97bd7..a0bd0ca 100644 --- a/nihav-core/src/scale/scale.rs +++ b/nihav-core/src/scale/scale.rs @@ -100,7 +100,7 @@ macro_rules! scale_loop { } impl Kernel for NNResampler { - fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo) -> ScaleResult { + fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo, _options: &[(String, String)]) -> ScaleResult { let res = alloc_video_buffer(NAVideoInfo::new(dest_fmt.width, dest_fmt.height, false, in_fmt.fmt), 3); if res.is_err() { return Err(ScaleError::AllocError); } Ok(res.unwrap())