1use crate::kernels::geometry::Rect;
18
19mod atlas;
20mod band_fn;
21mod bands;
22mod cells;
23mod construct;
24mod crops;
25mod merge;
26#[cfg(test)]
27mod tests;
28
29pub use atlas::{label_atlas, AtlasMask, AtlasRegion};
30use band_fn::band;
31use bands::{detect_bands, strongest_cut, Bands};
32use cells::{group_and_label, union_find_cells};
33use construct::{apply_cell_granularity, apply_pane_divider, build_regions, expand_bands};
34use crops::{
35 assign_native_crops, compute_native_edges, recompute_bounding_boxes, refine_vertical_cut_crops,
36};
37use merge::{cleanup_sticky_headers, finalize, freeze_stationary_regions, merge_small_regions};
38
39pub struct Rgba<'a> {
41 pub width: usize,
42 pub height: usize,
43 pub data: &'a [u8],
44}
45
46pub struct FinishInput<'a> {
49 pub width: usize,
50 pub height: usize,
51 pub cell: usize,
52 pub informative_frames: f64,
53 pub native_frames: f64,
54 pub row_change: &'a [f64],
55 pub col_change: &'a [f64],
56 pub col_moving: &'a [f64],
58 pub col_mean: &'a [f64],
59 pub col_gain: &'a [f64],
60 pub horizontal_gain: &'a [f64],
61 pub split: &'a [f64],
62 pub evidence: &'a [f64],
63 pub activity: &'a [f64],
64 pub observations: &'a [f64],
65 pub pixel_fixed: &'a [f64],
67 pub pixel_moving: &'a [f64],
68 pub native_row_change: Option<&'a [f64]>,
69 pub native_col_change: Option<&'a [f64]>,
70 pub row_fixed: Option<&'a [f64]>,
73 pub row_moving: Option<&'a [f64]>,
74 pub reference: Option<Rgba<'a>>,
75}
76
77#[derive(Clone, Copy, PartialEq, Eq, Debug)]
78pub enum Kind {
79 Moving,
80 Fixed,
81 Ignore,
82}
83
84pub struct RegionOut {
90 pub id: String,
91 pub name: String,
92 pub kind: Kind,
93 pub rect: Rect,
94 pub cells: Option<Vec<i64>>,
95 pub mask: Option<Vec<u8>>,
96 pub mask_width: usize,
97 pub mask_height: usize,
98 pub manual: bool,
99 pub unassigned: bool,
100 pub exclusions: Vec<Rect>,
101 pub crop: Option<Rect>,
102 pub solid: bool,
103 pub factor: u32,
104 pub band_side: Option<u8>,
109}
110
111#[inline]
112fn intersect(a: Rect, b: Rect) -> Rect {
113 let x = a.x.max(b.x);
114 let y = a.y.max(b.y);
115 Rect {
116 x,
117 y,
118 width: ((a.x + a.width).min(b.x + b.width) - x).max(0.0),
119 height: ((a.y + a.height).min(b.y + b.height) - y).max(0.0),
120 }
121}
122
123#[inline]
126fn stat_at(stats: &[f64], index: i64) -> f64 {
127 if index < 0 || index as usize >= stats.len() {
128 f64::NAN
129 } else {
130 stats[index as usize]
131 }
132}
133
134pub fn finish(
142 input: &FinishInput<'_>,
143 native_width: f64,
144 native_height: f64,
145 factor: u32,
146) -> Vec<RegionOut> {
147 let width = input.width as i64;
148 let height = input.height as i64;
149 let cell = input.cell as i64;
150 let cols = (width + cell - 1) / cell.max(1);
151 let rows = (height + cell - 1) / cell.max(1);
152 let reference = input.reference.as_ref();
153 let bands = detect_bands(
154 width,
155 height,
156 input.informative_frames,
157 input.row_change,
158 input.row_fixed.zip(input.row_moving),
159 (input.pixel_fixed, input.pixel_moving),
160 input.col_moving,
161 input.col_mean,
162 reference,
163 native_width,
164 native_height,
165 factor as f64,
166 );
167 let vertical_cut = strongest_cut(input.col_gain, input.informative_frames);
168 let horizontal_cut = strongest_cut(input.horizontal_gain, input.informative_frames);
169 let mut ds = union_find_cells(
170 cols,
171 rows,
172 cell,
173 width,
174 height,
175 bands.top,
176 bands.bottom,
177 bands.left,
178 bands.right,
179 vertical_cut,
180 horizontal_cut,
181 input.evidence,
182 input.split,
183 input.informative_frames,
184 );
185 let n = cols * rows;
186 let (large, labels) = group_and_label(&mut ds, n, cols);
187 let mut regions = build_regions(
188 &large,
189 &labels,
190 cols,
191 cell,
192 width,
193 height,
194 bands.top,
195 bands.bottom,
196 bands.left,
197 bands.right,
198 input.activity,
199 input.observations,
200 input.informative_frames,
201 factor,
202 );
203 let (divider_start, divider_end) = apply_pane_divider(
204 &mut regions,
205 &labels,
206 cols,
207 rows,
208 cell,
209 width,
210 height,
211 bands.top,
212 bands.bottom,
213 vertical_cut,
214 input.col_change,
215 input.informative_frames,
216 factor,
217 );
218 expand_bands(
219 &mut regions,
220 width,
221 height,
222 bands.top,
223 bands.bottom,
224 bands.left,
225 bands.right,
226 native_width,
227 native_height,
228 cols,
229 cell,
230 factor,
231 );
232 apply_cell_granularity(
233 &mut regions,
234 width,
235 height,
236 cell,
237 bands.top,
238 bands.bottom,
239 bands.left,
240 bands.right,
241 );
242 let edges = compute_native_edges(
243 &bands,
244 input.native_row_change,
245 input.native_col_change,
246 input.native_frames,
247 height,
248 width,
249 factor as f64,
250 native_width,
251 native_height,
252 );
253 assign_native_crops(
254 &mut regions,
255 &edges,
256 native_width,
257 native_height,
258 cols,
259 cell,
260 width,
261 height,
262 bands.top,
263 bands.bottom,
264 bands.left,
265 bands.right,
266 );
267 refine_vertical_cut_crops(
268 &mut regions,
269 &edges,
270 vertical_cut,
271 cell,
272 cols,
273 divider_start,
274 divider_end,
275 input.native_col_change,
276 input.native_frames,
277 native_width,
278 factor as f64,
279 );
280 recompute_bounding_boxes(
281 &mut regions,
282 width,
283 height,
284 native_width,
285 native_height,
286 factor as f64,
287 );
288 let valid: Vec<usize> = (0..regions.len())
289 .filter(|&i| regions[i].rect.width > 0.0 && regions[i].rect.height > 0.0)
290 .collect();
291 merge_small_regions(&mut regions, &valid, factor as f64);
292 cleanup_sticky_headers(
293 &mut regions,
294 &valid,
295 edges.content,
296 cell,
297 factor as f64,
298 reference,
299 );
300 freeze_stationary_regions(
301 &mut regions,
302 &valid,
303 input.pixel_fixed,
304 input.pixel_moving,
305 reference,
306 factor as f64,
307 );
308 finalize(regions, &valid, edges.content)
309}