2022-04-20 20:24:55 +00:00
|
|
|
use std::cmp;
|
|
|
|
use std::cmp::Ordering;
|
|
|
|
use std::convert::TryInto;
|
|
|
|
use std::fmt::Error;
|
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use std::process::Stdio;
|
|
|
|
|
2022-02-22 20:17:37 +00:00
|
|
|
use ffmpeg::format::Pixel;
|
2021-07-07 14:01:34 +00:00
|
|
|
use splines::{Interpolation, Key, Spline};
|
2022-04-20 20:24:55 +00:00
|
|
|
|
|
|
|
use crate::broker::EncoderCrash;
|
|
|
|
use crate::chunk::Chunk;
|
|
|
|
use crate::settings::EncodeArgs;
|
|
|
|
use crate::vmaf::{self, read_weighted_vmaf};
|
|
|
|
use crate::Encoder;
|
2021-11-11 02:52:32 +00:00
|
|
|
|
|
|
|
const VMAF_PERCENTILE: f64 = 0.25;
|
2021-09-03 19:01:11 +00:00
|
|
|
|
2021-10-04 16:22:08 +00:00
|
|
|
// TODO: just make it take a reference to a `Project`
|
2021-08-12 22:51:30 +00:00
|
|
|
pub struct TargetQuality<'a> {
|
2021-10-05 17:08:18 +00:00
|
|
|
vmaf_res: &'a str,
|
2021-10-04 16:22:08 +00:00
|
|
|
vmaf_filter: Option<&'a str>,
|
2021-10-05 17:08:18 +00:00
|
|
|
vmaf_threads: usize,
|
2021-08-12 22:51:30 +00:00
|
|
|
model: Option<&'a Path>,
|
|
|
|
probing_rate: usize,
|
|
|
|
probes: u32,
|
2021-10-05 17:08:18 +00:00
|
|
|
target: f64,
|
2021-08-12 22:51:30 +00:00
|
|
|
min_q: u32,
|
|
|
|
max_q: u32,
|
|
|
|
encoder: Encoder,
|
2021-10-30 11:49:26 +00:00
|
|
|
pix_format: Pixel,
|
2021-08-12 22:51:30 +00:00
|
|
|
temp: String,
|
|
|
|
workers: usize,
|
|
|
|
video_params: Vec<String>,
|
|
|
|
probe_slow: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> TargetQuality<'a> {
|
2021-10-30 11:49:26 +00:00
|
|
|
pub fn new(args: &'a EncodeArgs) -> Self {
|
2021-08-12 22:51:30 +00:00
|
|
|
Self {
|
2021-10-30 11:49:26 +00:00
|
|
|
vmaf_res: args.vmaf_res.as_str(),
|
|
|
|
vmaf_filter: args.vmaf_filter.as_deref(),
|
|
|
|
vmaf_threads: args.vmaf_threads.unwrap_or(0),
|
|
|
|
model: args.vmaf_path.as_deref(),
|
|
|
|
probes: args.probes,
|
|
|
|
target: args.target_quality.unwrap(),
|
|
|
|
min_q: args.min_q.unwrap(),
|
|
|
|
max_q: args.max_q.unwrap(),
|
|
|
|
encoder: args.encoder,
|
2021-11-20 18:43:13 +00:00
|
|
|
pix_format: args.output_pix_format.format,
|
2021-10-30 11:49:26 +00:00
|
|
|
temp: args.temp.clone(),
|
|
|
|
workers: args.workers,
|
|
|
|
video_params: args.video_params.clone(),
|
|
|
|
probe_slow: args.probe_slow,
|
|
|
|
probing_rate: adapt_probing_rate(args.probing_rate as usize),
|
2021-08-12 22:51:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-11 02:52:32 +00:00
|
|
|
fn per_shot_target_quality(&self, chunk: &Chunk) -> Result<u32, EncoderCrash> {
|
2021-08-12 22:51:30 +00:00
|
|
|
let mut vmaf_cq = vec![];
|
|
|
|
let frames = chunk.frames;
|
|
|
|
|
|
|
|
let mut q_list = vec![];
|
|
|
|
|
|
|
|
// Make middle probe
|
|
|
|
let middle_point = (self.min_q + self.max_q) / 2;
|
|
|
|
q_list.push(middle_point);
|
|
|
|
let last_q = middle_point;
|
|
|
|
|
2021-11-11 02:52:32 +00:00
|
|
|
let mut score =
|
|
|
|
read_weighted_vmaf(self.vmaf_probe(chunk, last_q as usize)?, VMAF_PERCENTILE).unwrap();
|
2021-08-12 22:51:30 +00:00
|
|
|
vmaf_cq.push((score, last_q));
|
|
|
|
|
|
|
|
// Initialize search boundary
|
|
|
|
let mut vmaf_lower = score;
|
|
|
|
let mut vmaf_upper = score;
|
|
|
|
let mut vmaf_cq_lower = last_q;
|
|
|
|
let mut vmaf_cq_upper = last_q;
|
|
|
|
|
|
|
|
// Branch
|
2021-10-05 17:08:18 +00:00
|
|
|
let next_q = if score < self.target {
|
2021-08-12 22:51:30 +00:00
|
|
|
self.min_q
|
|
|
|
} else {
|
|
|
|
self.max_q
|
|
|
|
};
|
|
|
|
|
|
|
|
q_list.push(next_q);
|
|
|
|
|
|
|
|
// Edge case check
|
2021-11-11 02:52:32 +00:00
|
|
|
score = read_weighted_vmaf(self.vmaf_probe(chunk, next_q as usize)?, VMAF_PERCENTILE).unwrap();
|
2021-08-12 22:51:30 +00:00
|
|
|
vmaf_cq.push((score, next_q));
|
|
|
|
|
2021-10-05 17:08:18 +00:00
|
|
|
if (next_q == self.min_q && score < self.target)
|
|
|
|
|| (next_q == self.max_q && score > self.target)
|
2021-08-12 22:51:30 +00:00
|
|
|
{
|
|
|
|
log_probes(
|
2021-10-05 17:08:18 +00:00
|
|
|
&mut vmaf_cq,
|
2021-08-12 22:51:30 +00:00
|
|
|
frames as u32,
|
|
|
|
self.probing_rate as u32,
|
|
|
|
&chunk.name(),
|
|
|
|
next_q,
|
|
|
|
score,
|
2021-10-05 17:08:18 +00:00
|
|
|
if score < self.target {
|
Refactoring, allow compiling with Rust 1.53 (#346)
Switch back to an old version of sysinfo, as 0.20 requires 1.54 to compile it, which is too new for MSYS2. Many minor simplifications.
We also switch to structopt for now instead of the beta release of clap, as clap officially recommends to not use the beta yet, and it caused some problems when compiling on Windows or with an older compiler.
Some minor changes:
- The `decow_strings` function has been removed, and we deal with `Cow<str>` properly now.
- The `remove_patterns` function now takes a mutable reference to a `Vec<String>` instead of copying one and modifing it to reduce complexity and overhead.
- The structs used to serialize the VMAF json result have been renamed to `VmafResult`, `Metrics`, and `VmafScore` (they were previously `Foo`, `Bar`, and `Baz`)
- Using an enum instead of a string as the argument to `log_probes` for better type safety and less overhead
- `frame_check_output` now warns if there is a mismatch, and the message has been updated to be more clear that a frame mismatch has occurred
- Several other small changes
2021-08-29 15:25:57 +00:00
|
|
|
Skip::Low
|
2021-08-12 22:51:30 +00:00
|
|
|
} else {
|
Refactoring, allow compiling with Rust 1.53 (#346)
Switch back to an old version of sysinfo, as 0.20 requires 1.54 to compile it, which is too new for MSYS2. Many minor simplifications.
We also switch to structopt for now instead of the beta release of clap, as clap officially recommends to not use the beta yet, and it caused some problems when compiling on Windows or with an older compiler.
Some minor changes:
- The `decow_strings` function has been removed, and we deal with `Cow<str>` properly now.
- The `remove_patterns` function now takes a mutable reference to a `Vec<String>` instead of copying one and modifing it to reduce complexity and overhead.
- The structs used to serialize the VMAF json result have been renamed to `VmafResult`, `Metrics`, and `VmafScore` (they were previously `Foo`, `Bar`, and `Baz`)
- Using an enum instead of a string as the argument to `log_probes` for better type safety and less overhead
- `frame_check_output` now warns if there is a mismatch, and the message has been updated to be more clear that a frame mismatch has occurred
- Several other small changes
2021-08-29 15:25:57 +00:00
|
|
|
Skip::High
|
2021-08-12 22:51:30 +00:00
|
|
|
},
|
|
|
|
);
|
2021-11-11 02:52:32 +00:00
|
|
|
return Ok(next_q);
|
2021-08-12 22:51:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Set boundary
|
2021-10-05 17:08:18 +00:00
|
|
|
if score < self.target {
|
2021-08-12 22:51:30 +00:00
|
|
|
vmaf_lower = score;
|
|
|
|
vmaf_cq_lower = next_q;
|
|
|
|
} else {
|
|
|
|
vmaf_upper = score;
|
|
|
|
vmaf_cq_upper = next_q;
|
|
|
|
}
|
|
|
|
|
|
|
|
// VMAF search
|
|
|
|
for _ in 0..self.probes - 2 {
|
|
|
|
let new_point = weighted_search(
|
|
|
|
f64::from(vmaf_cq_lower),
|
|
|
|
vmaf_lower,
|
|
|
|
f64::from(vmaf_cq_upper),
|
|
|
|
vmaf_upper,
|
2021-10-05 17:08:18 +00:00
|
|
|
self.target,
|
2021-08-12 22:51:30 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
if vmaf_cq
|
|
|
|
.iter()
|
|
|
|
.map(|(_, x)| *x)
|
|
|
|
.any(|x| x == new_point as u32)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
q_list.push(new_point as u32);
|
2021-11-11 02:52:32 +00:00
|
|
|
score = read_weighted_vmaf(self.vmaf_probe(chunk, new_point)?, VMAF_PERCENTILE).unwrap();
|
2021-08-12 22:51:30 +00:00
|
|
|
vmaf_cq.push((score, new_point as u32));
|
|
|
|
|
|
|
|
// Update boundary
|
2021-10-05 17:08:18 +00:00
|
|
|
if score < self.target {
|
2021-08-12 22:51:30 +00:00
|
|
|
vmaf_lower = score;
|
|
|
|
vmaf_cq_lower = new_point as u32;
|
|
|
|
} else {
|
|
|
|
vmaf_upper = score;
|
|
|
|
vmaf_cq_upper = new_point as u32;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-05 17:08:18 +00:00
|
|
|
let (q, q_vmaf) = interpolated_target_q(vmaf_cq.clone(), self.target);
|
2021-08-12 22:51:30 +00:00
|
|
|
log_probes(
|
2021-10-05 17:08:18 +00:00
|
|
|
&mut vmaf_cq,
|
2021-08-12 22:51:30 +00:00
|
|
|
frames as u32,
|
|
|
|
self.probing_rate as u32,
|
|
|
|
&chunk.name(),
|
|
|
|
q as u32,
|
|
|
|
q_vmaf,
|
Refactoring, allow compiling with Rust 1.53 (#346)
Switch back to an old version of sysinfo, as 0.20 requires 1.54 to compile it, which is too new for MSYS2. Many minor simplifications.
We also switch to structopt for now instead of the beta release of clap, as clap officially recommends to not use the beta yet, and it caused some problems when compiling on Windows or with an older compiler.
Some minor changes:
- The `decow_strings` function has been removed, and we deal with `Cow<str>` properly now.
- The `remove_patterns` function now takes a mutable reference to a `Vec<String>` instead of copying one and modifing it to reduce complexity and overhead.
- The structs used to serialize the VMAF json result have been renamed to `VmafResult`, `Metrics`, and `VmafScore` (they were previously `Foo`, `Bar`, and `Baz`)
- Using an enum instead of a string as the argument to `log_probes` for better type safety and less overhead
- `frame_check_output` now warns if there is a mismatch, and the message has been updated to be more clear that a frame mismatch has occurred
- Several other small changes
2021-08-29 15:25:57 +00:00
|
|
|
Skip::None,
|
2021-08-12 22:51:30 +00:00
|
|
|
);
|
|
|
|
|
2021-11-11 02:52:32 +00:00
|
|
|
Ok(q as u32)
|
2021-08-12 22:51:30 +00:00
|
|
|
}
|
|
|
|
|
2021-11-11 02:52:32 +00:00
|
|
|
fn vmaf_probe(&self, chunk: &Chunk, q: usize) -> Result<PathBuf, EncoderCrash> {
|
2021-10-05 17:08:18 +00:00
|
|
|
let vmaf_threads = if self.vmaf_threads == 0 {
|
2021-08-12 22:51:30 +00:00
|
|
|
vmaf_auto_threads(self.workers)
|
|
|
|
} else {
|
2021-10-05 17:08:18 +00:00
|
|
|
self.vmaf_threads
|
2021-08-12 22:51:30 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let cmd = self.encoder.probe_cmd(
|
|
|
|
self.temp.clone(),
|
2021-12-10 16:12:03 +00:00
|
|
|
chunk.index,
|
2021-08-12 22:51:30 +00:00
|
|
|
q,
|
2021-10-30 11:49:26 +00:00
|
|
|
self.pix_format,
|
2021-08-12 22:51:30 +00:00
|
|
|
self.probing_rate,
|
2021-10-05 17:08:18 +00:00
|
|
|
vmaf_threads,
|
2021-08-12 22:51:30 +00:00
|
|
|
self.video_params.clone(),
|
|
|
|
self.probe_slow,
|
|
|
|
);
|
|
|
|
|
|
|
|
let future = async {
|
2021-10-04 16:22:08 +00:00
|
|
|
let mut source = if let [pipe_cmd, args @ ..] = &*chunk.source {
|
|
|
|
tokio::process::Command::new(pipe_cmd)
|
|
|
|
.args(args)
|
|
|
|
.stderr(Stdio::piped())
|
|
|
|
.stdout(Stdio::piped())
|
|
|
|
.spawn()
|
|
|
|
.unwrap()
|
|
|
|
} else {
|
|
|
|
unreachable!()
|
|
|
|
};
|
|
|
|
|
|
|
|
let source_pipe_stdout: Stdio = source.stdout.take().unwrap().try_into().unwrap();
|
|
|
|
|
2021-11-11 02:52:32 +00:00
|
|
|
let mut source_pipe = if let [ffmpeg, args @ ..] = &*cmd.0 {
|
2021-10-04 16:22:08 +00:00
|
|
|
tokio::process::Command::new(ffmpeg)
|
|
|
|
.args(args)
|
|
|
|
.stdin(source_pipe_stdout)
|
|
|
|
.stdout(Stdio::piped())
|
|
|
|
.stderr(Stdio::piped())
|
|
|
|
.spawn()
|
|
|
|
.unwrap()
|
|
|
|
} else {
|
|
|
|
unreachable!()
|
|
|
|
};
|
2021-08-12 22:51:30 +00:00
|
|
|
|
2021-11-11 02:52:32 +00:00
|
|
|
let source_pipe_stdout: Stdio = source_pipe.stdout.take().unwrap().try_into().unwrap();
|
2021-08-12 22:51:30 +00:00
|
|
|
|
2021-11-11 02:52:32 +00:00
|
|
|
let enc_pipe = if let [cmd, args @ ..] = &*cmd.1 {
|
2021-10-04 16:22:08 +00:00
|
|
|
tokio::process::Command::new(cmd.as_ref())
|
|
|
|
.args(args.iter().map(AsRef::as_ref))
|
2021-11-11 02:52:32 +00:00
|
|
|
.stdin(source_pipe_stdout)
|
2021-10-04 16:22:08 +00:00
|
|
|
.stdout(Stdio::piped())
|
|
|
|
.stderr(Stdio::piped())
|
|
|
|
.spawn()
|
|
|
|
.unwrap()
|
|
|
|
} else {
|
|
|
|
unreachable!()
|
|
|
|
};
|
2021-08-12 22:51:30 +00:00
|
|
|
|
2021-11-11 02:52:32 +00:00
|
|
|
let source_pipe_output = source_pipe.wait_with_output().await.unwrap();
|
|
|
|
|
|
|
|
// TODO: Expand EncoderCrash to handle io errors as well
|
|
|
|
let enc_output = enc_pipe.wait_with_output().await.unwrap();
|
|
|
|
|
|
|
|
if !enc_output.status.success() {
|
|
|
|
let e = EncoderCrash {
|
|
|
|
exit_status: enc_output.status,
|
|
|
|
stdout: enc_output.stdout.into(),
|
|
|
|
stderr: enc_output.stderr.into(),
|
2022-01-07 23:03:39 +00:00
|
|
|
source_pipe_stderr: source_pipe_output.stderr.into(),
|
|
|
|
ffmpeg_pipe_stderr: None,
|
2021-11-11 02:52:32 +00:00
|
|
|
};
|
|
|
|
error!("[chunk {}] {}", chunk.index, e);
|
|
|
|
return Err(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
2021-08-12 22:51:30 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let rt = tokio::runtime::Builder::new_current_thread()
|
|
|
|
.enable_io()
|
|
|
|
.build()
|
|
|
|
.unwrap();
|
|
|
|
|
2021-11-11 02:52:32 +00:00
|
|
|
rt.block_on(future)?;
|
2021-08-12 22:51:30 +00:00
|
|
|
|
2021-12-10 16:12:03 +00:00
|
|
|
let probe_name = Path::new(&chunk.temp)
|
|
|
|
.join("split")
|
|
|
|
.join(format!("v_{}_{}.ivf", q, chunk.index));
|
2021-08-12 22:51:30 +00:00
|
|
|
let fl_path = Path::new(&chunk.temp)
|
|
|
|
.join("split")
|
2021-12-10 16:12:03 +00:00
|
|
|
.join(format!("{}.json", chunk.index));
|
2021-08-12 22:51:30 +00:00
|
|
|
|
2021-10-04 16:22:08 +00:00
|
|
|
vmaf::run_vmaf(
|
2021-08-12 22:51:30 +00:00
|
|
|
&probe_name,
|
2021-10-04 16:22:08 +00:00
|
|
|
chunk.source.as_slice(),
|
2021-08-12 22:51:30 +00:00
|
|
|
&fl_path,
|
|
|
|
self.model.as_ref(),
|
2021-10-05 17:08:18 +00:00
|
|
|
self.vmaf_res,
|
2021-08-12 22:51:30 +00:00
|
|
|
self.probing_rate,
|
2021-10-04 16:22:08 +00:00
|
|
|
self.vmaf_filter,
|
2021-10-05 17:08:18 +00:00
|
|
|
self.vmaf_threads,
|
2021-11-11 02:52:32 +00:00
|
|
|
)?;
|
2021-08-12 22:51:30 +00:00
|
|
|
|
2021-11-11 02:52:32 +00:00
|
|
|
Ok(fl_path)
|
2021-08-12 22:51:30 +00:00
|
|
|
}
|
|
|
|
|
2021-11-11 02:52:32 +00:00
|
|
|
pub fn per_shot_target_quality_routine(&self, chunk: &mut Chunk) -> Result<(), EncoderCrash> {
|
|
|
|
chunk.tq_cq = Some(self.per_shot_target_quality(chunk)?);
|
|
|
|
Ok(())
|
2021-08-12 22:51:30 +00:00
|
|
|
}
|
|
|
|
}
|
2021-07-16 00:15:48 +00:00
|
|
|
|
2021-06-27 23:27:48 +00:00
|
|
|
pub fn weighted_search(num1: f64, vmaf1: f64, num2: f64, vmaf2: f64, target: f64) -> usize {
|
|
|
|
let dif1 = (transform_vmaf(target as f64) - transform_vmaf(vmaf2)).abs();
|
|
|
|
let dif2 = (transform_vmaf(target as f64) - transform_vmaf(vmaf1)).abs();
|
|
|
|
|
|
|
|
let tot = dif1 + dif2;
|
|
|
|
|
2021-07-24 17:09:21 +00:00
|
|
|
num1.mul_add(dif1 / tot, num2 * (dif2 / tot)).round() as usize
|
2021-06-27 23:27:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn transform_vmaf(vmaf: f64) -> f64 {
|
|
|
|
let x: f64 = 1.0 - vmaf / 100.0;
|
|
|
|
if vmaf < 99.99 {
|
|
|
|
-x.ln()
|
|
|
|
} else {
|
2021-07-02 21:39:53 +00:00
|
|
|
9.2
|
2021-06-27 23:27:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-06 23:40:00 +00:00
|
|
|
/// Returns auto detected amount of threads used for vmaf calculation
|
2021-06-10 20:06:30 +00:00
|
|
|
pub fn vmaf_auto_threads(workers: usize) -> usize {
|
2021-06-14 20:20:47 +00:00
|
|
|
const OVER_PROVISION_FACTOR: f64 = 1.25;
|
2021-06-10 20:06:30 +00:00
|
|
|
|
2021-06-14 20:20:47 +00:00
|
|
|
// Logical CPUs
|
|
|
|
let threads = num_cpus::get();
|
2021-06-10 20:06:30 +00:00
|
|
|
|
2021-10-05 17:08:18 +00:00
|
|
|
cmp::max(
|
2021-06-14 20:20:47 +00:00
|
|
|
((threads / workers) as f64 * OVER_PROVISION_FACTOR) as usize,
|
|
|
|
1,
|
|
|
|
)
|
2021-06-10 20:06:30 +00:00
|
|
|
}
|
2021-07-07 14:01:34 +00:00
|
|
|
|
2021-08-06 23:40:00 +00:00
|
|
|
/// Use linear interpolation to get q/crf values closest to the target value
|
2021-07-07 14:01:34 +00:00
|
|
|
pub fn interpolate_target_q(scores: Vec<(f64, u32)>, target: f64) -> Result<f64, Error> {
|
|
|
|
let mut sorted = scores;
|
|
|
|
sorted.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
|
|
|
|
|
|
|
|
let keys = sorted
|
|
|
|
.iter()
|
2021-07-16 00:15:48 +00:00
|
|
|
.map(|(x, y)| Key::new(*x, f64::from(*y), Interpolation::Linear))
|
2021-07-07 14:01:34 +00:00
|
|
|
.collect();
|
|
|
|
|
|
|
|
let spline = Spline::from_vec(keys);
|
|
|
|
|
|
|
|
Ok(spline.sample(target).unwrap())
|
|
|
|
}
|
|
|
|
|
2021-08-06 23:40:00 +00:00
|
|
|
/// Use linear interpolation to get vmaf value that expected from q
|
2021-07-07 14:01:34 +00:00
|
|
|
pub fn interpolate_target_vmaf(scores: Vec<(f64, u32)>, q: f64) -> Result<f64, Error> {
|
|
|
|
let mut sorted = scores;
|
2021-07-16 00:15:48 +00:00
|
|
|
sorted.sort_by(|(a, _), (b, _)| a.partial_cmp(b).unwrap_or(Ordering::Less));
|
2021-07-07 14:01:34 +00:00
|
|
|
|
|
|
|
let keys = sorted
|
|
|
|
.iter()
|
2021-07-24 17:09:21 +00:00
|
|
|
.map(|f| Key::new(f64::from(f.1), f.0 as f64, Interpolation::Linear))
|
2021-07-07 14:01:34 +00:00
|
|
|
.collect();
|
|
|
|
|
|
|
|
let spline = Spline::from_vec(keys);
|
|
|
|
|
|
|
|
Ok(spline.sample(q).unwrap())
|
|
|
|
}
|
2021-07-08 00:27:48 +00:00
|
|
|
|
2021-09-04 20:45:47 +00:00
|
|
|
#[derive(Copy, Clone)]
|
Refactoring, allow compiling with Rust 1.53 (#346)
Switch back to an old version of sysinfo, as 0.20 requires 1.54 to compile it, which is too new for MSYS2. Many minor simplifications.
We also switch to structopt for now instead of the beta release of clap, as clap officially recommends to not use the beta yet, and it caused some problems when compiling on Windows or with an older compiler.
Some minor changes:
- The `decow_strings` function has been removed, and we deal with `Cow<str>` properly now.
- The `remove_patterns` function now takes a mutable reference to a `Vec<String>` instead of copying one and modifing it to reduce complexity and overhead.
- The structs used to serialize the VMAF json result have been renamed to `VmafResult`, `Metrics`, and `VmafScore` (they were previously `Foo`, `Bar`, and `Baz`)
- Using an enum instead of a string as the argument to `log_probes` for better type safety and less overhead
- `frame_check_output` now warns if there is a mismatch, and the message has been updated to be more clear that a frame mismatch has occurred
- Several other small changes
2021-08-29 15:25:57 +00:00
|
|
|
pub enum Skip {
|
|
|
|
High,
|
|
|
|
Low,
|
|
|
|
None,
|
|
|
|
}
|
|
|
|
|
2021-07-08 00:27:48 +00:00
|
|
|
pub fn log_probes(
|
2021-10-05 17:08:18 +00:00
|
|
|
vmaf_cq_scores: &mut [(f64, u32)],
|
2021-07-08 00:27:48 +00:00
|
|
|
frames: u32,
|
|
|
|
probing_rate: u32,
|
2021-11-17 08:57:08 +00:00
|
|
|
chunk_idx: &str,
|
2021-07-08 00:27:48 +00:00
|
|
|
target_q: u32,
|
|
|
|
target_vmaf: f64,
|
Refactoring, allow compiling with Rust 1.53 (#346)
Switch back to an old version of sysinfo, as 0.20 requires 1.54 to compile it, which is too new for MSYS2. Many minor simplifications.
We also switch to structopt for now instead of the beta release of clap, as clap officially recommends to not use the beta yet, and it caused some problems when compiling on Windows or with an older compiler.
Some minor changes:
- The `decow_strings` function has been removed, and we deal with `Cow<str>` properly now.
- The `remove_patterns` function now takes a mutable reference to a `Vec<String>` instead of copying one and modifing it to reduce complexity and overhead.
- The structs used to serialize the VMAF json result have been renamed to `VmafResult`, `Metrics`, and `VmafScore` (they were previously `Foo`, `Bar`, and `Baz`)
- Using an enum instead of a string as the argument to `log_probes` for better type safety and less overhead
- `frame_check_output` now warns if there is a mismatch, and the message has been updated to be more clear that a frame mismatch has occurred
- Several other small changes
2021-08-29 15:25:57 +00:00
|
|
|
skip: Skip,
|
2021-07-08 00:27:48 +00:00
|
|
|
) {
|
2021-10-05 17:08:18 +00:00
|
|
|
vmaf_cq_scores.sort_by_key(|(_score, q)| *q);
|
2021-07-08 00:27:48 +00:00
|
|
|
|
2021-11-17 08:57:08 +00:00
|
|
|
// TODO: take chunk id as integer instead and format with {:05}
|
2022-01-18 23:20:01 +00:00
|
|
|
debug!(
|
2021-11-17 08:57:08 +00:00
|
|
|
"chunk {}: P-Rate={}, {} frames",
|
|
|
|
chunk_idx, probing_rate, frames
|
|
|
|
);
|
2022-01-18 23:20:01 +00:00
|
|
|
debug!(
|
2021-11-17 08:57:08 +00:00
|
|
|
"chunk {}: TQ-Probes: {:.2?}{}",
|
|
|
|
chunk_idx,
|
2021-10-05 17:08:18 +00:00
|
|
|
vmaf_cq_scores,
|
Refactoring, allow compiling with Rust 1.53 (#346)
Switch back to an old version of sysinfo, as 0.20 requires 1.54 to compile it, which is too new for MSYS2. Many minor simplifications.
We also switch to structopt for now instead of the beta release of clap, as clap officially recommends to not use the beta yet, and it caused some problems when compiling on Windows or with an older compiler.
Some minor changes:
- The `decow_strings` function has been removed, and we deal with `Cow<str>` properly now.
- The `remove_patterns` function now takes a mutable reference to a `Vec<String>` instead of copying one and modifing it to reduce complexity and overhead.
- The structs used to serialize the VMAF json result have been renamed to `VmafResult`, `Metrics`, and `VmafScore` (they were previously `Foo`, `Bar`, and `Baz`)
- Using an enum instead of a string as the argument to `log_probes` for better type safety and less overhead
- `frame_check_output` now warns if there is a mismatch, and the message has been updated to be more clear that a frame mismatch has occurred
- Several other small changes
2021-08-29 15:25:57 +00:00
|
|
|
match skip {
|
|
|
|
Skip::High => " Early Skip High Q",
|
|
|
|
Skip::Low => " Early Skip Low Q",
|
2021-09-04 20:45:47 +00:00
|
|
|
Skip::None => "",
|
Refactoring, allow compiling with Rust 1.53 (#346)
Switch back to an old version of sysinfo, as 0.20 requires 1.54 to compile it, which is too new for MSYS2. Many minor simplifications.
We also switch to structopt for now instead of the beta release of clap, as clap officially recommends to not use the beta yet, and it caused some problems when compiling on Windows or with an older compiler.
Some minor changes:
- The `decow_strings` function has been removed, and we deal with `Cow<str>` properly now.
- The `remove_patterns` function now takes a mutable reference to a `Vec<String>` instead of copying one and modifing it to reduce complexity and overhead.
- The structs used to serialize the VMAF json result have been renamed to `VmafResult`, `Metrics`, and `VmafScore` (they were previously `Foo`, `Bar`, and `Baz`)
- Using an enum instead of a string as the argument to `log_probes` for better type safety and less overhead
- `frame_check_output` now warns if there is a mismatch, and the message has been updated to be more clear that a frame mismatch has occurred
- Several other small changes
2021-08-29 15:25:57 +00:00
|
|
|
}
|
|
|
|
);
|
2022-01-18 23:20:01 +00:00
|
|
|
debug!(
|
2021-11-17 08:57:08 +00:00
|
|
|
"chunk {}: Target Q={:.0}, VMAF={:.2}",
|
|
|
|
chunk_idx, target_q, target_vmaf
|
|
|
|
);
|
2021-07-08 00:27:48 +00:00
|
|
|
}
|
2021-08-12 22:51:30 +00:00
|
|
|
|
|
|
|
pub const fn adapt_probing_rate(rate: usize) -> usize {
|
|
|
|
match rate {
|
|
|
|
1..=4 => rate,
|
|
|
|
_ => 4,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn interpolated_target_q(scores: Vec<(f64, u32)>, target: f64) -> (f64, f64) {
|
|
|
|
let q = interpolate_target_q(scores.clone(), target).unwrap();
|
|
|
|
|
|
|
|
let vmaf = interpolate_target_vmaf(scores, q).unwrap();
|
|
|
|
|
|
|
|
(q, vmaf)
|
|
|
|
}
|
2022-01-04 15:28:35 +00:00
|
|
|
|
|
|
|
#[allow(unused)]
|
|
|
|
fn lagrange_interpolate(p: &[(u32, f64)], x: u32) -> f64 {
|
|
|
|
p.iter()
|
|
|
|
.map(|(x0, y0)| {
|
|
|
|
let mut num = 1;
|
|
|
|
let mut den = 1;
|
|
|
|
for (x1, _y1) in p {
|
|
|
|
if x0 != x1 {
|
|
|
|
num *= i64::from(x) - i64::from(*x1);
|
|
|
|
den *= i64::from(*x0) - i64::from(*x1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
y0 * num as f64 / den as f64
|
|
|
|
})
|
|
|
|
.sum()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(unused)]
|
|
|
|
fn lagrange_bisect(p: &[(u32, f64)], y: f64) -> (u32, f64) {
|
|
|
|
assert!(p.len() >= 2);
|
|
|
|
|
|
|
|
// Re-center the samples at the target value
|
|
|
|
let mut sorted = Vec::from(p);
|
|
|
|
for v in &mut sorted {
|
|
|
|
v.1 -= y;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Order samples by distance from target value
|
|
|
|
sorted.sort_by(|a, b| a.1.abs().partial_cmp(&b.1.abs()).unwrap());
|
|
|
|
|
|
|
|
// Take the closest point
|
|
|
|
let (mut xb, mut yb) = sorted[0];
|
|
|
|
// Take the next close point that brackets the root
|
|
|
|
let (mut xa, mut ya) = sorted.iter().find(|&&v| v.1 * yb < 0.).unwrap_or(&(xb, yb));
|
|
|
|
|
|
|
|
loop {
|
|
|
|
let x0 = (xa + xb + 1) / 2;
|
|
|
|
if x0 == xb || x0 == xa {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
let y0 = lagrange_interpolate(&sorted, x0);
|
|
|
|
if ya * y0 < 0. {
|
|
|
|
xb = x0;
|
|
|
|
yb = y0;
|
|
|
|
} else {
|
|
|
|
xa = x0;
|
|
|
|
ya = y0;
|
|
|
|
}
|
|
|
|
if ya.abs() < yb.abs() {
|
|
|
|
std::mem::swap(&mut xa, &mut xb);
|
|
|
|
std::mem::swap(&mut ya, &mut yb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
(xb, yb + y)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use crate::target_quality::lagrange_bisect;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bisect() {
|
|
|
|
let sorted = vec![(0, 0.0), (1, 1.0), (256, 256.0 * 256.0)];
|
|
|
|
|
|
|
|
assert!(lagrange_bisect(&sorted, 0.0).0 == 0);
|
|
|
|
assert!(lagrange_bisect(&sorted, 1.0).0 == 1);
|
|
|
|
assert!(lagrange_bisect(&sorted, 256.0 * 256.0).0 == 256);
|
|
|
|
|
|
|
|
assert!(lagrange_bisect(&sorted, 8.0).0 == 3);
|
|
|
|
assert!(lagrange_bisect(&sorted, 9.0).0 == 3);
|
|
|
|
|
|
|
|
assert!(lagrange_bisect(&sorted, -1.0).0 == 0);
|
|
|
|
assert!(lagrange_bisect(&sorted, 2.0 * 256.0 * 256.0).0 == 256);
|
|
|
|
}
|
|
|
|
}
|