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, 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 native_row_change: Option<&'a [f64]>,
66 pub native_col_change: Option<&'a [f64]>,
67 pub reference: Option<Rgba<'a>>,
68}
69
70#[derive(Clone, Copy, PartialEq, Eq, Debug)]
71pub enum Kind {
72 Moving,
73 Fixed,
74 Ignore,
75}
76
77pub struct RegionOut {
83 pub id: String,
84 pub name: String,
85 pub kind: Kind,
86 pub rect: Rect,
87 pub cells: Option<Vec<i64>>,
88 pub mask: Option<Vec<u8>>,
89 pub mask_width: usize,
90 pub mask_height: usize,
91 pub manual: bool,
92 pub unassigned: bool,
93 pub exclusions: Vec<Rect>,
94 pub crop: Option<Rect>,
95 pub solid: bool,
96 pub factor: u32,
97 pub band_side: Option<u8>,
102}
103
104#[inline]
105fn intersect(a: Rect, b: Rect) -> Rect {
106 let x = a.x.max(b.x);
107 let y = a.y.max(b.y);
108 Rect {
109 x,
110 y,
111 width: ((a.x + a.width).min(b.x + b.width) - x).max(0.0),
112 height: ((a.y + a.height).min(b.y + b.height) - y).max(0.0),
113 }
114}
115
116#[inline]
119fn stat_at(stats: &[f64], index: i64) -> f64 {
120 if index < 0 || index as usize >= stats.len() {
121 f64::NAN
122 } else {
123 stats[index as usize]
124 }
125}
126
127pub fn finish(
135 input: &FinishInput<'_>,
136 native_width: f64,
137 native_height: f64,
138 factor: u32,
139) -> Vec<RegionOut> {
140 let width = input.width as i64;
141 let height = input.height as i64;
142 let cell = input.cell as i64;
143 let cols = (width + cell - 1) / cell.max(1);
144 let rows = (height + cell - 1) / cell.max(1);
145 let reference = input.reference.as_ref();
146 let bands = detect_bands(
147 width,
148 height,
149 input.informative_frames,
150 input.row_change,
151 input.col_moving,
152 input.col_mean,
153 reference,
154 native_width,
155 native_height,
156 factor as f64,
157 );
158 let vertical_cut = strongest_cut(input.col_gain, input.informative_frames);
159 let horizontal_cut = strongest_cut(input.horizontal_gain, input.informative_frames);
160 let mut ds = union_find_cells(
161 cols,
162 rows,
163 cell,
164 width,
165 height,
166 bands.top,
167 bands.bottom,
168 bands.left,
169 bands.right,
170 vertical_cut,
171 horizontal_cut,
172 input.evidence,
173 input.split,
174 input.informative_frames,
175 );
176 let n = cols * rows;
177 let (large, labels) = group_and_label(&mut ds, n, cols);
178 let mut regions = build_regions(
179 &large,
180 &labels,
181 cols,
182 cell,
183 width,
184 height,
185 bands.top,
186 bands.bottom,
187 bands.left,
188 bands.right,
189 input.activity,
190 input.observations,
191 input.informative_frames,
192 factor,
193 );
194 let (divider_start, divider_end) = apply_pane_divider(
195 &mut regions,
196 &labels,
197 cols,
198 rows,
199 cell,
200 width,
201 height,
202 bands.top,
203 bands.bottom,
204 vertical_cut,
205 input.col_change,
206 input.informative_frames,
207 factor,
208 );
209 expand_bands(
210 &mut regions,
211 width,
212 height,
213 bands.top,
214 bands.bottom,
215 bands.left,
216 bands.right,
217 native_width,
218 native_height,
219 cols,
220 cell,
221 factor,
222 );
223 apply_cell_granularity(
224 &mut regions,
225 width,
226 height,
227 cell,
228 bands.top,
229 bands.bottom,
230 bands.left,
231 bands.right,
232 );
233 let edges = compute_native_edges(
234 &bands,
235 input.native_row_change,
236 input.native_col_change,
237 input.native_frames,
238 height,
239 width,
240 factor as f64,
241 native_width,
242 native_height,
243 );
244 assign_native_crops(
245 &mut regions,
246 &edges,
247 native_width,
248 native_height,
249 cols,
250 cell,
251 width,
252 height,
253 bands.top,
254 bands.bottom,
255 bands.left,
256 bands.right,
257 );
258 refine_vertical_cut_crops(
259 &mut regions,
260 &edges,
261 vertical_cut,
262 cell,
263 cols,
264 divider_start,
265 divider_end,
266 input.native_col_change,
267 input.native_frames,
268 native_width,
269 factor as f64,
270 );
271 recompute_bounding_boxes(
272 &mut regions,
273 width,
274 height,
275 native_width,
276 native_height,
277 factor as f64,
278 );
279 let valid: Vec<usize> = (0..regions.len())
280 .filter(|&i| regions[i].rect.width > 0.0 && regions[i].rect.height > 0.0)
281 .collect();
282 merge_small_regions(&mut regions, &valid, factor as f64);
283 cleanup_sticky_headers(
284 &mut regions,
285 &valid,
286 edges.content,
287 cell,
288 factor as f64,
289 reference,
290 );
291 finalize(regions, &valid, edges.content)
292}