Skip to main content

av_scenechange/analyze/
fast.rs

1use std::{cmp, sync::Arc};
2
3use log::debug;
4use v_frame::{frame::Frame, pixel::Pixel, plane::Plane};
5
6use super::{fast_idiv, ScaleFunction, SceneChangeDetector, ScenecutResult};
7use crate::{data::sad::sad_plane, SceneDetectionSpeed};
8
9/// Experiments have determined this to be an optimal threshold
10pub(super) const FAST_THRESHOLD: f64 = 18.0;
11
12impl<T: Pixel> SceneChangeDetector<T> {
13    /// The fast algorithm detects fast cuts using a raw difference
14    /// in pixel values between the scaled frames.
15    pub(super) fn fast_scenecut(
16        &mut self,
17        frame1: Arc<Frame<T>>,
18        frame2: Arc<Frame<T>>,
19    ) -> ScenecutResult {
20        if let Some(scale_func) = &self.scale_func {
21            // downscale both frames for faster comparison
22            if let Some(frame_buffer) = &mut self.downscaled_frame_buffer {
23                frame_buffer.swap(0, 1);
24                (scale_func.downscale_in_place)(&frame2.planes[0], &mut frame_buffer[1]);
25            } else {
26                self.downscaled_frame_buffer = Some([
27                    (scale_func.downscale)(&frame1.planes[0]),
28                    (scale_func.downscale)(&frame2.planes[0]),
29                ]);
30            }
31
32            if let Some(frame_buffer) = &self.downscaled_frame_buffer {
33                let &[first, second] = &frame_buffer;
34                let delta = self.delta_in_planes(first, second);
35
36                ScenecutResult {
37                    threshold: self.threshold,
38                    inter_cost: delta,
39                    imp_block_cost: delta,
40                    forward_adjusted_cost: delta,
41                    backward_adjusted_cost: delta,
42                }
43            } else {
44                unreachable!()
45            }
46        } else {
47            let delta = self.delta_in_planes(&frame1.planes[0], &frame2.planes[0]);
48
49            ScenecutResult {
50                threshold: self.threshold,
51                inter_cost: delta,
52                imp_block_cost: delta,
53                backward_adjusted_cost: delta,
54                forward_adjusted_cost: delta,
55            }
56        }
57    }
58
59    /// Calculates the average sum of absolute difference (SAD) per pixel
60    /// between 2 planes
61    fn delta_in_planes(&self, plane1: &Plane<T>, plane2: &Plane<T>) -> f64 {
62        let delta = sad_plane(plane1, plane2, self.cpu_feature_level);
63
64        delta as f64 / self.scaled_pixels as f64
65    }
66}
67
68/// Scaling factor for frame in scene detection
69pub(super) fn detect_scale_factor<T: Pixel>(
70    resolution: (usize, usize),
71    speed_mode: SceneDetectionSpeed,
72) -> Option<ScaleFunction<T>> {
73    let small_edge = cmp::min(resolution.0, resolution.1);
74    let scale_func = if speed_mode == SceneDetectionSpeed::Fast {
75        match small_edge {
76            0..=240 => None,
77            241..=480 => Some(ScaleFunction::from_scale::<2>()),
78            481..=720 => Some(ScaleFunction::from_scale::<4>()),
79            721..=1080 => Some(ScaleFunction::from_scale::<8>()),
80            1081..=1600 => Some(ScaleFunction::from_scale::<16>()),
81            1601..=usize::MAX => Some(ScaleFunction::from_scale::<32>()),
82            _ => None,
83        }
84    } else {
85        None
86    };
87
88    if let Some(scale_factor) = scale_func.as_ref().map(|x| x.factor) {
89        debug!(
90            "Scene detection scale factor {}, [{},{}] -> [{},{}]",
91            scale_factor,
92            resolution.0,
93            resolution.1,
94            fast_idiv(resolution.0, scale_factor),
95            fast_idiv(resolution.1, scale_factor)
96        );
97    }
98
99    scale_func
100}