Skip to main content

av_scenechange/analyze/
importance.rs

1use std::sync::Arc;
2
3use v_frame::{
4    frame::Frame,
5    pixel::{CastFromPrimitive, Pixel},
6};
7
8use super::intra::BLOCK_TO_PLANE_SHIFT;
9use crate::data::plane::{Area, AsRegion, PlaneRegion, Rect};
10
11/// Size of blocks for the importance computation, in pixels.
12pub const IMPORTANCE_BLOCK_SIZE: usize =
13    1 << (IMPORTANCE_BLOCK_TO_BLOCK_SHIFT + BLOCK_TO_PLANE_SHIFT);
14pub const IMPORTANCE_BLOCK_TO_BLOCK_SHIFT: usize = 1;
15pub const IMP_BLOCK_MV_UNITS_PER_PIXEL: i64 = 8;
16pub const IMP_BLOCK_SIZE_IN_MV_UNITS: i64 =
17    IMPORTANCE_BLOCK_SIZE as i64 * IMP_BLOCK_MV_UNITS_PER_PIXEL;
18
19pub(crate) fn estimate_importance_block_difference<T: Pixel>(
20    frame: Arc<Frame<T>>,
21    ref_frame: Arc<Frame<T>>,
22) -> f64 {
23    let plane_org = &frame.planes[0];
24    let plane_ref = &ref_frame.planes[0];
25    let h_in_imp_b = plane_org.cfg.height / IMPORTANCE_BLOCK_SIZE;
26    let w_in_imp_b = plane_org.cfg.width / IMPORTANCE_BLOCK_SIZE;
27
28    let mut imp_block_costs = 0;
29
30    (0..h_in_imp_b).for_each(|y| {
31        (0..w_in_imp_b).for_each(|x| {
32            // Coordinates of the top-left corner of the reference block, in MV
33            // units.
34            let region_org = plane_org.region(Area::Rect(Rect {
35                x: (x * IMPORTANCE_BLOCK_SIZE) as isize,
36                y: (y * IMPORTANCE_BLOCK_SIZE) as isize,
37                width: IMPORTANCE_BLOCK_SIZE,
38                height: IMPORTANCE_BLOCK_SIZE,
39            }));
40
41            let region_ref = plane_ref.region(Area::Rect(Rect {
42                x: (x * IMPORTANCE_BLOCK_SIZE) as isize,
43                y: (y * IMPORTANCE_BLOCK_SIZE) as isize,
44                width: IMPORTANCE_BLOCK_SIZE,
45                height: IMPORTANCE_BLOCK_SIZE,
46            }));
47
48            let sum_8x8_block = |region: &PlaneRegion<T>| {
49                region
50                    .rows_iter()
51                    .map(|row| {
52                        // 16-bit precision is sufficient for an 8 px row,
53                        // as `IMPORTANCE_BLOCK_SIZE * (2^12 - 1) < 2^16 - 1`,
54                        // so overflow is not possible
55                        row.iter().map(|pixel| u16::cast_from(*pixel)).sum::<u16>() as i64
56                    })
57                    .sum::<i64>()
58            };
59
60            let histogram_org_sum = sum_8x8_block(&region_org);
61            let histogram_ref_sum = sum_8x8_block(&region_ref);
62
63            let count = (IMPORTANCE_BLOCK_SIZE * IMPORTANCE_BLOCK_SIZE) as i64;
64
65            let mean = (((histogram_org_sum + count / 2) / count)
66                - ((histogram_ref_sum + count / 2) / count))
67                .abs();
68
69            imp_block_costs += mean as u64;
70        });
71    });
72
73    imp_block_costs as f64 / (w_in_imp_b * h_in_imp_b) as f64
74}