1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use crate::frame::*;
use crate::tiling::*;
use crate::util::*;
#[derive(Debug, Default, Clone)]
pub struct ActivityMask {
variances: Vec<f64>,
width: usize,
height: usize,
granularity: usize,
}
impl ActivityMask {
pub fn from_plane<T: Pixel>(luma_plane: &Plane<T>) -> ActivityMask {
let PlaneConfig { width, height, .. } = luma_plane.cfg;
let granularity = 3;
let aligned_luma = Rect {
x: 0_isize,
y: 0_isize,
width: (width >> granularity) << granularity,
height: (height >> granularity) << granularity,
};
let luma = PlaneRegion::new(luma_plane, aligned_luma);
let mut variances =
Vec::with_capacity((height >> granularity) * (width >> granularity));
for y in 0..height >> granularity {
for x in 0..width >> granularity {
let block_rect = Area::Rect {
x: (x << granularity) as isize,
y: (y << granularity) as isize,
width: 8,
height: 8,
};
let block = luma.subregion(block_rect);
let mean: f64 = block
.rows_iter()
.flatten()
.map(|&pix| {
let pix: i16 = CastFromPrimitive::cast_from(pix);
pix as f64
})
.sum::<f64>()
/ 64.0_f64;
let variance: f64 = block
.rows_iter()
.flatten()
.map(|&pix| {
let pix: i16 = CastFromPrimitive::cast_from(pix);
(pix as f64 - mean).powi(2)
})
.sum::<f64>();
variances.push(variance);
}
}
ActivityMask { variances, width, height, granularity }
}
pub fn variance_at(&self, x: usize, y: usize) -> Option<f64> {
let (dec_width, dec_height) =
(self.width >> self.granularity, self.height >> self.granularity);
if x > dec_width || y > dec_height {
None
} else {
Some(*self.variances.get(x + dec_width * y).unwrap())
}
}
pub fn mean_activity_of(&self, rect: Rect) -> Option<f64> {
let Rect { x, y, width, height } = rect;
let (x, y) = (x as usize, y as usize);
let granularity = self.granularity;
let (dec_x, dec_y) = (x >> granularity, y >> granularity);
let (dec_width, dec_height) =
(width >> granularity, height >> granularity);
if x > self.width
|| y > self.height
|| (x + width) > self.width
|| (y + height) > self.height
|| dec_width == 0
|| dec_height == 0
{
None
} else {
let activity = self
.variances
.chunks_exact(self.width >> granularity)
.skip(dec_y)
.take(dec_height)
.map(|row| row.iter().skip(dec_x).take(dec_width).sum::<f64>())
.sum::<f64>()
/ (dec_width as f64 * dec_height as f64);
Some(activity.cbrt().sqrt())
}
}
}