Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add start frame and frames option to video comparison #45

Merged
merged 8 commits into from
Dec 3, 2024
15 changes: 14 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ struct Cli {
}

#[derive(Subcommand, Debug)]
#[allow(clippy::large_enum_variant)]
enum Commands {
/// Compare two still images. Resolutions must be identical.
Image {
Expand All @@ -42,9 +43,17 @@ enum Commands {

/// How many worker threads to use for decoding & calculating scores.
/// Note: Memory usage increases linearly with the number of workers.
#[arg(long, short)]
#[arg(long, short, verbatim_doc_comment)]
frame_threads: Option<usize>,

/// The amount of frames to skip.
#[arg(long, default_value_t = 0)]
skip_frames: usize,

/// Limit the amount of frames to compare.
#[arg(long)]
frames: Option<usize>,

/// How to increment current frame count; e.g. 10 will read every 10th frame.
#[arg(long, short)]
increment: Option<usize>,
Expand Down Expand Up @@ -99,6 +108,8 @@ fn main() {
source,
distorted,
frame_threads,
skip_frames,
frames,
increment,
graph,
verbose,
Expand Down Expand Up @@ -135,6 +146,8 @@ fn main() {
&source,
&distorted,
frame_threads,
skip_frames,
frames,
inc,
graph,
verbose,
Expand Down
105 changes: 81 additions & 24 deletions src/video.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,34 +115,61 @@ fn pretty_spinner_style() -> ProgressStyle {
.progress_chars(PROGRESS_CHARS)
}

type VideoCompareMutex<E, F> = Arc<Mutex<VideoCompare<E, F>>>;

struct VideoCompare<E: Decoder, F: Decoder> {
current_frame: usize,
next_frame: usize,
source: E,
distorted: F,
}

fn calc_score<S: Pixel, D: Pixel, E: Decoder, F: Decoder>(
mtx: &Mutex<(usize, (E, F))>,
mtx: &VideoCompareMutex<E, F>,
src_yuvcfg: &YuvConfig,
dst_yuvcfg: &YuvConfig,
inc: usize,
end_frame: Option<usize>,
verbose: bool,
) -> Option<(usize, f64)> {
let (frame_idx, (src_frame, dst_frame)) = {
let mut guard = mtx.lock().unwrap();
let curr_frame = guard.0;
let mut curr_frame = guard.current_frame;
// We passed this as start frame.
// However, we are going to use it to store the next frame we should compute.
let mut next_frame = guard.next_frame;

//println!("{curr_frame} < {next_frame}");
FreezyLemon marked this conversation as resolved.
Show resolved Hide resolved

while curr_frame < next_frame {
FreezyLemon marked this conversation as resolved.
Show resolved Hide resolved
let _src_frame = guard.source.read_video_frame::<S>();
let _dst_frame = guard.distorted.read_video_frame::<D>();
if _src_frame.is_none() || _dst_frame.is_none() {
break;
}
if verbose {
println!("Frame {}: skip", curr_frame);
}
curr_frame += 1;
}

let src_frame = guard.1 .0.read_video_frame::<S>();
let dst_frame = guard.1 .1.read_video_frame::<D>();
next_frame = curr_frame + inc;

if let (Some(sf), Some(df)) = (src_frame, dst_frame) {
// skip remaining frames in increment size
for ii in 1..inc {
let _src_frame = guard.1 .0.read_video_frame::<S>();
let _dst_frame = guard.1 .1.read_video_frame::<D>();
if _src_frame.is_none() || _dst_frame.is_none() {
break;
}
if verbose {
println!("Frame {}: skip", curr_frame + ii);
}
if let Some(end_frame) = end_frame {
FreezyLemon marked this conversation as resolved.
Show resolved Hide resolved
if next_frame > end_frame {
return None;
}
}

let src_frame = guard.source.read_video_frame::<S>();
let dst_frame = guard.distorted.read_video_frame::<D>();

guard.current_frame = curr_frame + 1;
guard.next_frame = next_frame;

guard.0 += inc;
//println!("current: {}, next: {}", curr_frame, next_frame);

if let (Some(sf), Some(df)) = (src_frame, dst_frame) {
(curr_frame, (sf, df))
} else {
return None;
Expand All @@ -163,6 +190,8 @@ pub fn compare_videos(
source: &str,
distorted: &str,
frame_threads: usize,
skip_frames: usize,
frames_to_compare: Option<usize>,
inc: usize,
graph: bool,
verbose: bool,
Expand Down Expand Up @@ -196,6 +225,8 @@ pub fn compare_videos(
None,
distorted_frame_count,
frame_threads,
skip_frames,
frames_to_compare,
inc,
graph,
verbose,
Expand Down Expand Up @@ -227,6 +258,8 @@ pub fn compare_videos(
source_frame_count,
None,
frame_threads,
skip_frames,
frames_to_compare,
inc,
graph,
verbose,
Expand Down Expand Up @@ -267,6 +300,8 @@ pub fn compare_videos(
source_frame_count,
distorted_frame_count,
frame_threads,
skip_frames,
frames_to_compare,
inc,
graph,
verbose,
Expand All @@ -288,6 +323,8 @@ fn compare_videos_inner<D: Decoder + 'static, E: Decoder + 'static>(
source_frame_count: Option<usize>,
distorted_frame_count: Option<usize>,
frame_threads: usize,
skip_frames: usize,
frames_to_compare: Option<usize>,
inc: usize,
graph: bool,
verbose: bool,
Expand Down Expand Up @@ -362,40 +399,57 @@ fn compare_videos_inner<D: Decoder + 'static, E: Decoder + 'static>(
let dst_bd = dst_config.bit_depth;

let current_frame = 0usize;
let decoders = Arc::new(Mutex::new((current_frame, (source, distorted))));
let end_frame = frames_to_compare
.map(|frames_to_compare| skip_frames + (frames_to_compare * inc));

let video_compare = Arc::new(
Mutex::new(
VideoCompare {
current_frame,
next_frame: skip_frames,
source,
distorted,
}
)
);

for _ in 0..frame_threads {
let decoders = Arc::clone(&decoders);
let video_compare = Arc::clone(&video_compare);
let result_tx = result_tx.clone();

std::thread::spawn(move || {
loop {
let score = match (src_bd, dst_bd) {
(8, 8) => calc_score::<u8, u8, _, _>(
&decoders,
&video_compare,
&src_config,
&dst_config,
inc,
end_frame,
verbose,
),
(8, _) => calc_score::<u8, u16, _, _>(
&decoders,
&video_compare,
&src_config,
&dst_config,
inc,
end_frame,
verbose,
),
(_, 8) => calc_score::<u16, u8, _, _>(
&decoders,
&video_compare,
&src_config,
&dst_config,
inc,
end_frame,
verbose,
),
(_, _) => calc_score::<u16, u16, _, _>(
&decoders,
&video_compare,
&src_config,
&dst_config,
inc,
end_frame,
verbose,
),
};
Expand All @@ -416,7 +470,10 @@ fn compare_videos_inner<D: Decoder + 'static, E: Decoder + 'static>(
let progress = if stderr().is_tty() && !verbose {
let frame_count = source_frame_count.or(distorted_frame_count);
let pb = if let Some(frame_count) = frame_count {
ProgressBar::new(frame_count as u64)
let fc = frames_to_compare.unwrap_or(frame_count - skip_frames)
.min(((frame_count - skip_frames) as f64 / inc as f64).ceil() as usize);

ProgressBar::new(fc as u64)
.with_style(pretty_progress_style())
.with_message(", avg: N/A")
} else {
Expand All @@ -443,7 +500,7 @@ fn compare_videos_inner<D: Decoder + 'static, E: Decoder + 'static>(
results.insert(score.0, score.1);
avg = avg + (score.1 - avg) / (min(results.len(), 10) as f64);
progress.set_message(format!(", avg: {:.1$}", avg, 2));
progress.inc(inc.try_into().unwrap());
progress.inc(1);
}

progress.finish();
Expand Down