diff --git a/src/api/mod.rs b/src/api/mod.rs index 64f8733cb3..fe38ecbc01 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -111,6 +111,7 @@ pub struct EncoderConfig { pub speed_settings: SpeedSettings, pub show_psnr: bool, pub train_rdo: bool, + pub uv_m_q8: u8, } /// Default preset for EncoderConfig: it is a balance between quality and speed. @@ -163,6 +164,7 @@ impl EncoderConfig { speed_settings: SpeedSettings::from_preset(speed), show_psnr: false, train_rdo: false, + uv_m_q8: 81, } } diff --git a/src/bin/common.rs b/src/bin/common.rs index 4b24b2070e..84cafccd64 100644 --- a/src/bin/common.rs +++ b/src/bin/common.rs @@ -303,6 +303,12 @@ pub fn parse_cli() -> Result { Arg::with_name("train-rdo") .long("train-rdo") ) + .arg( + Arg::with_name("UV_M_Q8") + .long("uv-m-q8") + .takes_value(true) + .default_value("81") + ) .subcommand(SubCommand::with_name("advanced") .setting(AppSettings::Hidden) .about("Advanced features") @@ -547,6 +553,7 @@ fn parse_config(matches: &ArgMatches<'_>) -> Result { cfg.low_latency = matches.is_present("LOW_LATENCY"); cfg.train_rdo = train_rdo; + cfg.uv_m_q8 = matches.value_of("UV_M_Q8").unwrap().parse().unwrap(); Ok(cfg) } diff --git a/src/rate.rs b/src/rate.rs index 679e3f5156..96667b6b08 100644 --- a/src/rate.rs +++ b/src/rate.rs @@ -559,21 +559,21 @@ const Q57_SQUARE_EXP_SCALE: f64 = (2.0 * ::std::f64::consts::LN_2) / ((1i64 << 57) as f64); // Daala style log-offset for chroma quantizers -fn chroma_offset(log_target_q: i64) -> (i64, i64) { +fn chroma_offset(log_target_q: i64, m_q8: u8) -> (i64, i64) { let x = log_target_q.max(0); - // Gradient 0.266 optimized for CIEDE2000+PSNR on subset3 - let y = (x >> 2) + (x >> 6); + let m_q12 = (m_q8 as i64) << 4; + let y = m_q12 * (x >> 12); // blog64(7) - blog64(4); blog64(5) - blog64(4) (0x19D_5D9F_D501_0B37 - y, 0xA4_D3C2_5E68_DC58 - y) } impl QuantizerParameters { fn new_from_log_q( - log_base_q: i64, log_target_q: i64, bit_depth: usize, + log_base_q: i64, log_target_q: i64, bit_depth: usize, uv_m_q8: u8 ) -> QuantizerParameters { let scale = q57(QSCALE + bit_depth as i32 - 8); let quantizer = bexp64(log_target_q + scale); - let (offset_u, offset_v) = chroma_offset(log_target_q); + let (offset_u, offset_v) = chroma_offset(log_target_q, uv_m_q8); let log_target_q_u = log_target_q + offset_u + scale; let log_target_q_v = log_target_q + offset_v + scale; let quantizer_u = bexp64(log_target_q_u); @@ -749,6 +749,7 @@ impl RCState { &self, ctx: &ContextInner, output_frameno: u64, fti: usize, maybe_prev_log_base_q: Option, ) -> QuantizerParameters { + let uv_m_q8 = ctx.config.uv_m_q8; // Is rate control active? if self.target_bitrate <= 0 { // Rate control is not active. @@ -772,7 +773,7 @@ impl RCState { // Adjust the quantizer for the frame type, result is Q57: let log_q = ((log_base_q + (1i64 << 11)) >> 12) * (MQP_Q12[fti] as i64) + DQP_Q57[fti]; - QuantizerParameters::new_from_log_q(log_base_q, log_q, bit_depth) + QuantizerParameters::new_from_log_q(log_base_q, log_q, bit_depth, uv_m_q8) } else { let mut nframes: [i32; FRAME_NSUBTYPES + 1] = [0; FRAME_NSUBTYPES + 1]; let mut log_scale: [i64; FRAME_NSUBTYPES] = self.log_scale; @@ -790,6 +791,7 @@ impl RCState { self.pass1_log_base_q, log_q, ctx.config.bit_depth, + uv_m_q8, ); } // Second pass of 2-pass mode: we know exactly how much of each frame @@ -1052,7 +1054,7 @@ impl RCState { // If that target is unreasonable, oh well; we'll have to drop. } } - QuantizerParameters::new_from_log_q(log_base_q, log_q, bit_depth) + QuantizerParameters::new_from_log_q(log_base_q, log_q, bit_depth, uv_m_q8) } }