#[cfg(not(target_env = "msvc"))]
use tikv_jemallocator::Jemalloc;
#[cfg(not(target_env = "msvc"))]
#[global_allocator]
static GLOBAL: Jemalloc = Jemalloc;
use std::{path::{PathBuf, Path}, time::Duration, io::{stderr, IsTerminal}};
use clap::{Parser, ValueEnum};
use indicatif::{ProgressBar, ProgressDrawTarget};
mod noise_estimate;
mod progress_stuff;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
enum Format {
Medians,
Median,
ISO,
Av1an
}
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
input: PathBuf,
#[arg(long, short, value_enum, default_value_t = Format::Medians)]
format: Format,
#[arg(long, short, default_value_t = false, verbatim_doc_comment)]
strength: bool,
#[arg(long, short, default_value_t = 1)]
threads: usize,
}
fn main() {
let cli = Cli::parse();
let (y, u, v) = run(&cli.input, cli.threads);
let avg_y: f64 = y.iter().sum::<f64>() / y.len() as f64;
let avg_u: f64 = u.iter().sum::<f64>() / u.len() as f64;
let avg_v: f64 = v.iter().sum::<f64>() / v.len() as f64;
match cli.format {
Format::Medians => {
println!("Y:{avg_y}");
println!("U:{avg_u}");
println!("V:{avg_v}");
},
Format::ISO => {
if avg_y > avg_u+avg_v {
let iso = iso_from_noise(avg_y, cli.strength);
println!("LUMA:{iso}");
} else {
let noise_floor = (avg_y+avg_u+avg_v)/3.0;
let iso = iso_from_noise(noise_floor, cli.strength);
println!("CHROMA:{iso}");
}
},
Format::Av1an => {
if avg_y > avg_u+avg_v {
let iso = iso_from_noise(avg_y, true);
println!("--photon-noise {iso}");
} else {
let noise_floor = (avg_y+avg_u+avg_v)/3.0;
let iso = iso_from_noise(noise_floor, true);
println!("--photon-noise {iso} --chroma-noise");
}
},
Format::Median => {
if avg_y > avg_u+avg_v {
println!("{avg_y}");
} else {
let noise = (avg_y+avg_u+avg_v)/3.0;
println!("{noise}");
}
},
}
}
fn iso_from_noise(noise: f64, stength: bool) -> usize {
let (a, b, c) = (0.745974214, 1570.33842, -5.16323056);
let res = libm::exp((noise-c)/a)-b;
let iso = libm::round(res/100.0).clamp(0.0, 64.0);
if stength { iso as usize } else { iso as usize * 100 }
}
fn run(path: &Path, threads: usize) -> (Vec<f64>, Vec<f64>, Vec<f64>) {
let (frame_count_opt, receiver) = noise_estimate::run(path, threads);
let progress = if stderr().is_terminal() {
let pb = if let Some(frame_count) = frame_count_opt {
ProgressBar::new(frame_count as u64)
.with_style(progress_stuff::pretty_progress_style())
} else {
ProgressBar::new_spinner().with_style(progress_stuff::pretty_spinner_style())
};
pb.set_draw_target(ProgressDrawTarget::stderr());
pb.enable_steady_tick(Duration::from_millis(100));
pb.reset();
pb.reset_eta();
pb.reset_elapsed();
pb.set_position(0);
pb
} else {
ProgressBar::hidden()
};
let mut results = Vec::new();
for score in receiver {
results.push(score);
progress.inc(1);
}
progress.finish();
let results_y: Vec<f64> = results.iter().map(|r| { r[0] }).collect();
let results_u: Vec<f64> = results.iter().map(|r| { r[1] }).collect();
let results_v: Vec<f64> = results.iter().map(|r| { r[2] }).collect();
(results_y, results_u, results_v)
}