use crate::kernels::geometry::Rect;
mod atlas;
mod band_fn;
mod bands;
mod cells;
mod construct;
mod crops;
mod merge;
#[cfg(test)]
mod tests;
pub use atlas::{label_atlas, AtlasMask, AtlasRegion};
use band_fn::band;
use bands::{detect_bands, strongest_cut, Bands};
use cells::{group_and_label, union_find_cells};
use construct::{apply_cell_granularity, apply_pane_divider, build_regions, expand_bands};
use crops::{
assign_native_crops, compute_native_edges, recompute_bounding_boxes, refine_vertical_cut_crops,
};
use merge::{cleanup_sticky_headers, finalize, freeze_stationary_regions, merge_small_regions};
pub struct Rgba<'a> {
pub width: usize,
pub height: usize,
pub data: &'a [u8],
}
pub struct FinishInput<'a> {
pub width: usize,
pub height: usize,
pub cell: usize,
pub informative_frames: f64,
pub native_frames: f64,
pub row_change: &'a [f64],
pub col_change: &'a [f64],
pub col_moving: &'a [f64],
pub col_mean: &'a [f64],
pub col_gain: &'a [f64],
pub horizontal_gain: &'a [f64],
pub split: &'a [f64],
pub evidence: &'a [f64],
pub activity: &'a [f64],
pub observations: &'a [f64],
pub pixel_fixed: &'a [f64],
pub pixel_moving: &'a [f64],
pub native_row_change: Option<&'a [f64]>,
pub native_col_change: Option<&'a [f64]>,
pub row_fixed: Option<&'a [f64]>,
pub row_moving: Option<&'a [f64]>,
pub reference: Option<Rgba<'a>>,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Kind {
Moving,
Fixed,
Ignore,
}
pub struct RegionOut {
pub id: String,
pub name: String,
pub kind: Kind,
pub rect: Rect,
pub cells: Option<Vec<i64>>,
pub mask: Option<Vec<u8>>,
pub mask_width: usize,
pub mask_height: usize,
pub manual: bool,
pub unassigned: bool,
pub exclusions: Vec<Rect>,
pub crop: Option<Rect>,
pub solid: bool,
pub factor: u32,
pub band_side: Option<u8>,
}
#[inline]
fn intersect(a: Rect, b: Rect) -> Rect {
let x = a.x.max(b.x);
let y = a.y.max(b.y);
Rect {
x,
y,
width: ((a.x + a.width).min(b.x + b.width) - x).max(0.0),
height: ((a.y + a.height).min(b.y + b.height) - y).max(0.0),
}
}
#[inline]
fn stat_at(stats: &[f64], index: i64) -> f64 {
if index < 0 || index as usize >= stats.len() {
f64::NAN
} else {
stats[index as usize]
}
}
pub fn finish(
input: &FinishInput<'_>,
native_width: f64,
native_height: f64,
factor: u32,
) -> Vec<RegionOut> {
let width = input.width as i64;
let height = input.height as i64;
let cell = input.cell as i64;
let cols = (width + cell - 1) / cell.max(1);
let rows = (height + cell - 1) / cell.max(1);
let reference = input.reference.as_ref();
let bands = detect_bands(
width,
height,
input.informative_frames,
input.row_change,
input.row_fixed.zip(input.row_moving),
(input.pixel_fixed, input.pixel_moving),
input.col_moving,
input.col_mean,
reference,
native_width,
native_height,
factor as f64,
);
let vertical_cut = strongest_cut(input.col_gain, input.informative_frames);
let horizontal_cut = strongest_cut(input.horizontal_gain, input.informative_frames);
let mut ds = union_find_cells(
cols,
rows,
cell,
width,
height,
bands.top,
bands.bottom,
bands.left,
bands.right,
vertical_cut,
horizontal_cut,
input.evidence,
input.split,
input.informative_frames,
);
let n = cols * rows;
let (large, labels) = group_and_label(&mut ds, n, cols);
let mut regions = build_regions(
&large,
&labels,
cols,
cell,
width,
height,
bands.top,
bands.bottom,
bands.left,
bands.right,
input.activity,
input.observations,
input.informative_frames,
factor,
);
let (divider_start, divider_end) = apply_pane_divider(
&mut regions,
&labels,
cols,
rows,
cell,
width,
height,
bands.top,
bands.bottom,
vertical_cut,
input.col_change,
input.informative_frames,
factor,
);
expand_bands(
&mut regions,
width,
height,
bands.top,
bands.bottom,
bands.left,
bands.right,
native_width,
native_height,
cols,
cell,
factor,
);
apply_cell_granularity(
&mut regions,
width,
height,
cell,
bands.top,
bands.bottom,
bands.left,
bands.right,
);
let edges = compute_native_edges(
&bands,
input.native_row_change,
input.native_col_change,
input.native_frames,
height,
width,
factor as f64,
native_width,
native_height,
);
assign_native_crops(
&mut regions,
&edges,
native_width,
native_height,
cols,
cell,
width,
height,
bands.top,
bands.bottom,
bands.left,
bands.right,
);
refine_vertical_cut_crops(
&mut regions,
&edges,
vertical_cut,
cell,
cols,
divider_start,
divider_end,
input.native_col_change,
input.native_frames,
native_width,
factor as f64,
);
recompute_bounding_boxes(
&mut regions,
width,
height,
native_width,
native_height,
factor as f64,
);
let valid: Vec<usize> = (0..regions.len())
.filter(|&i| regions[i].rect.width > 0.0 && regions[i].rect.height > 0.0)
.collect();
merge_small_regions(&mut regions, &valid, factor as f64);
cleanup_sticky_headers(
&mut regions,
&valid,
edges.content,
cell,
factor as f64,
reference,
);
freeze_stationary_regions(
&mut regions,
&valid,
input.pixel_fixed,
input.pixel_moving,
reference,
factor as f64,
);
finalize(regions, &valid, edges.content)
}