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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
//! Input configuration for [`grow_mask`](super::grow::grow_mask).
use ArrayView2;
/// Pixel adjacency used both for heap-neighbour expansion and for
/// annulus dilation. The two uses must share a single setting so that
/// the annuli are defined in the same topology the mask grows in.
/// Signal-to-noise stop criterion evaluated on the inner annulus.
///
/// Fires when the cumulative SNR inside the inner annulus
/// (`sum(flux) / sqrt(sum(err^2))`) stays strictly below `threshold` for
/// `hysteresis` consecutive checks. Requires `err` to be supplied to
/// [`grow_mask`](super::grow::grow_mask).
/// Radial-gradient flip stop criterion evaluated across the two annuli.
///
/// Fires when `band_mean(outer_annulus) / band_mean(inner_annulus)`
/// stays strictly above `ratio_threshold` for `hysteresis` consecutive
/// checks. This catches the case where the mask has reached the basin
/// between two sources and is about to climb the neighbour.
///
/// `band_mean` is the mean of each ring's pixels whose value falls in
/// the `[lo_percentile, hi_percentile]` percentile band of that ring.
/// The lower bound drops the sky pixels that otherwise dilute the ring
/// average (so a rising neighbour stays visible even when most of the
/// ring is background); the upper bound trims the brightest pixels
/// (cosmic rays / hot pixels) by count, which a bright multi-pixel
/// neighbour survives. `[0, 100]` recovers the plain ring mean.
/// The set of enabled stop criteria. At least one must be enabled.
///
/// The two criteria are combined with **OR** semantics: whichever
/// reaches its hysteresis count first terminates the growth. They
/// capture different failure modes (SNR = decaying into noise; gradient
/// = encountering another source) and in practice almost never trigger
/// simultaneously; if they do on the same check, [`SnrStop`] wins by
/// evaluation order.
/// Algorithm configuration for [`grow_mask`](super::grow::grow_mask).
///
/// All fields are required. No defaults are exposed at the Rust layer:
/// reasonable defaults are scenario-specific (e.g. JWST/NIRCam high-z
/// galaxies) and belong to the calling boundary (the Python binding).
///
/// # Shape regularisation
///
/// Three fields control how compact the mask stays, suppressing the
/// thin tendrils a pure brightest-pixel heap would chase along noise
/// ridges or bridges to neighbouring sources:
///
/// - `shape_weight` adds a soft reward for admitting pixels that already
/// have many in-mask neighbours, biasing the *order* of growth toward
/// filling concavities before extending arms.
/// - `min_neighbor_support` is a hard floor: once past the warm-up, a
/// pixel is refused admission until enough of its neighbours are in
/// the mask. This sets the minimum aperture "neck width" and directly
/// forbids one-pixel-wide filaments.
/// - `min_pixels_before_shape_gate` delays that floor so the seed core
/// can establish itself (a single seed's neighbours start with only
/// one in-mask neighbour, which the floor would otherwise deadlock).
///
/// A fourth field, `fill_min_cardinal_support`, is the morphological
/// *closing* counterpart to the `min_neighbor_support` *opening*: where
/// the floor forbids thin protrusions, the fill closes thin intrusions
/// (deep notches and enclosed holes) the flux-driven heap leaves behind.
/// Optional segmentation constraint passed to
/// [`grow_mask`](super::grow::grow_mask).
///
/// `map` is a per-pixel integer label image (shape must equal the input
/// data). `allowed` is the *complete* whitelist of labels the mask may
/// occupy — the caller must include the background label explicitly if
/// growth into background is desired.