core/scale: add options support
authorKostya Shishkov <kostya.shishkov@gmail.com>
Mon, 18 Oct 2021 15:31:09 +0000 (17:31 +0200)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Mon, 18 Oct 2021 16:56:38 +0000 (18:56 +0200)
nihav-core/src/scale/colorcvt.rs
nihav-core/src/scale/kernel.rs
nihav-core/src/scale/mod.rs
nihav-core/src/scale/palette/mod.rs
nihav-core/src/scale/repack.rs
nihav-core/src/scale/scale.rs

index bc6c4b6586551fc1ff33e80c3b79cea15ee26bea..00ae2a56203b38e2c9ac315be0e1696edd341226 100644 (file)
@@ -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<NABufferType> {
+    fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo, options: &[(String, String)]) -> ScaleResult<NABufferType> {
+        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<dyn Kernel> {
 #[derive(Default)]
 struct YuvToRgb {
     matrix: [[f32; 3]; 3],
+    mode:   usize,
     yscale: Vec<i16>,
     r_chr:  Vec<i16>,
     g_u:    Vec<i16>,
@@ -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<NABufferType> {
+    fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo, options: &[(String, String)]) -> ScaleResult<NABufferType> {
+        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())
index 84a69b49a14d08a3c86f3fea1c93f8f824380443..abc783594bd473a0cbe47d73b99232b9fda2566f 100644 (file)
@@ -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<NABufferType>;
+    fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo, options: &[(String, String)]) -> ScaleResult<NABufferType>;
     fn process(&mut self, pic_in: &NABufferType, pic_out: &mut NABufferType);
 }
index cc2388e364ab9f7190f5e4aa34f760abe3a05740..6bf6e272273887dac36bad6147bd0147e34b88bc 100644 (file)
@@ -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<Self> {
+    fn new(name: &str, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo, options: &[(String, String)]) -> ScaleResult<Self> {
         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<Option<Stage>> {
+fn build_pipeline(ifmt: &ScaleInfo, ofmt: &ScaleInfo, just_convert: bool, options: &[(String, String)]) -> ScaleResult<Option<Stage>> {
+    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<Self> {
+        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;
         }
index 2005cd8c0fc12c6158511fc937ee5a55b69b496c..f9b0ee9b033fa9ae4f99435a3731291527a93efd 100644 (file)
@@ -220,7 +220,29 @@ impl PalettiseKernel {
 }
 
 impl Kernel for PalettiseKernel {
-    fn init(&mut self, in_fmt: &ScaleInfo, _dest_fmt: &ScaleInfo) -> ScaleResult<NABufferType> {
+    fn init(&mut self, in_fmt: &ScaleInfo, _dest_fmt: &ScaleInfo, options: &[(String, String)]) -> ScaleResult<NABufferType> {
+        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); }
index a9d354a8a2381c4e396a3cd603231907916dcac4..8cdb8ea91b164df63ecb22409fd196bdef12b4cf 100644 (file)
@@ -33,7 +33,7 @@ impl PackKernel {
 }
 
 impl Kernel for PackKernel {
-    fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo) -> ScaleResult<NABufferType> {
+    fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo, _options: &[(String, String)]) -> ScaleResult<NABufferType> {
         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<NABufferType> {
+    fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo, options: &[(String, String)]) -> ScaleResult<NABufferType> {
+        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<Option<NAPixelChromaton>> = 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<NABufferType> {
+    fn init(&mut self, in_fmt: &ScaleInfo, _dest_fmt: &ScaleInfo, options: &[(String, String)]) -> ScaleResult<NABufferType> {
+        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<Option<NAPixelChromaton>> = 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())
index fc97bd78e97230926f69255b034c60a597031c07..a0bd0cab078f9dab6d2793d45856bdee0f360bb0 100644 (file)
@@ -100,7 +100,7 @@ macro_rules! scale_loop {
 }
 
 impl Kernel for NNResampler {
-    fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo) -> ScaleResult<NABufferType> {
+    fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo, _options: &[(String, String)]) -> ScaleResult<NABufferType> {
         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())