Let the fill show the model an open void: mirror depth 0 means no ring and nothing known beyond the band

This commit is contained in:
2026-09-20 10:56:38 +02:00
parent 3de109fbd1
commit b1c99b5796
8 changed files with 155 additions and 31 deletions
+86 -14
View File
@@ -89,12 +89,19 @@ pub struct Params {
pub coarse: usize, pub coarse: usize,
/// The fine passes' band width. /// The fine passes' band width.
pub band: usize, pub band: usize,
/// How deep into the picture the mirrored context reaches. A plain /// How deep into the picture the mirrored context reaches, or **zero
/// reflection of a deep hole pulls in whatever is that far from the /// for no mirrored context at all**: the void is then shown to the
/// edge — a ridge, a peak — and the model, told that is what lies /// model as it is — reaching the picture's edge with nothing beyond,
/// beyond, paints it upside down. Folding the reflection within this /// and, beyond the band being filled, still unknown. That is what the
/// band keeps the ring looking like the edge it continues (sky beside /// shipped model was trained on (a fine-tune of MI-GAN on voids cut
/// sky, grass beside grass) and nothing further away. /// from photographs the way a cylindrical merge cuts them, see
/// `docs/panorama.md` §14); a ring would give it a fold to continue.
///
/// Non-zero is the stock model's crutch: a plain reflection of a deep
/// hole pulls in whatever is that far from the edge — a ridge, a peak —
/// and the model, told that is what lies beyond, paints it upside down.
/// Folding the reflection within this band keeps the ring looking like
/// the edge it continues and nothing further away.
pub mirror_depth: usize, pub mirror_depth: usize,
/// How far inside the real edge the fill also regenerates, the two /// How far inside the real edge the fill also regenerates, the two
/// blended by distance. A hard cut between real pixels and invented /// blended by distance. A hard cut between real pixels and invented
@@ -108,9 +115,9 @@ pub struct Params {
impl Default for Params { impl Default for Params {
fn default() -> Self { fn default() -> Self {
Params { Params {
coarse: 4, coarse: 1,
band: 96, band: 192,
mirror_depth: 48, mirror_depth: 0,
feather: 24, feather: 24,
stride: 384, stride: 384,
} }
@@ -141,7 +148,10 @@ pub fn fill_border(
} = params; } = params;
let q = q.max(1); let q = q.max(1);
let band = band.max(8); let band = band.max(8);
let mirror_depth = mirror_depth.max(1); // No ring: the void beyond the band stays unknown, as in the model's
// training; with a ring the far side is the coarse fill, presented as
// known, which the stock model needed to see something there.
let open = mirror_depth == 0;
if width == 0 || height == 0 || rgb.len() != width * height * 3 || known.len() != width * height if width == 0 || height == 0 || rgb.len() != width * height * 3 || known.len() != width * height
{ {
return Err(PanoError::Input("fill: buffer sizes disagree".into())); return Err(PanoError::Input("fill: buffer sizes disagree".into()));
@@ -227,7 +237,11 @@ pub fn fill_border(
let mut any = false; let mut any = false;
for i in 0..width * height { for i in 0..width * height {
let in_band = !known[i] && dist[i] > lo && dist[i] <= hi; let in_band = !known[i] && dist[i] > lo && dist[i] <= hi;
band_known[i] = !in_band; band_known[i] = if open {
known[i] || dist[i] <= lo
} else {
!in_band
};
any |= in_band; any |= in_band;
} }
if !any { if !any {
@@ -369,7 +383,7 @@ fn fill_once(
if known[i] { if known[i] {
continue; continue;
} }
let p = (yy + RING) * pw + (xx + RING); let p = (yy + ctx.ring) * pw + (xx + ctx.ring);
if wsum[p] > 0.0 { if wsum[p] > 0.0 {
for ch in 0..3 { for ch in 0..3 {
rgb[i * 3 + ch] = (acc[p * 3 + ch] / wsum[p]).clamp(0.0, 1.0); rgb[i * 3 + ch] = (acc[p * 3 + ch] / wsum[p]).clamp(0.0, 1.0);
@@ -478,12 +492,25 @@ fn fold(d: usize, depth: usize) -> usize {
struct MirroredContext { struct MirroredContext {
width: usize, width: usize,
height: usize, height: usize,
/// The padding on every side: `RING` with mirrored context, 0 without.
ring: usize,
rgb: Vec<f32>, rgb: Vec<f32>,
hole: Vec<bool>, hole: Vec<bool>,
} }
impl MirroredContext { impl MirroredContext {
fn build(rgb: &[f32], width: usize, height: usize, known: &[bool], depth: usize) -> Self { fn build(rgb: &[f32], width: usize, height: usize, known: &[bool], depth: usize) -> Self {
if depth == 0 {
// Open: the picture as it is, the hole as it is. What the hole
// holds does not matter — the model masks it out.
return MirroredContext {
width,
height,
ring: 0,
rgb: rgb.to_vec(),
hole: known.iter().map(|&k| !k).collect(),
};
}
let fold = |d: usize| fold(d, depth); let fold = |d: usize| fold(d, depth);
let (pw, ph) = (width + 2 * RING, height + 2 * RING); let (pw, ph) = (width + 2 * RING, height + 2 * RING);
let mut canvas = vec![0.0f32; pw * ph * 3]; let mut canvas = vec![0.0f32; pw * ph * 3];
@@ -534,6 +561,7 @@ impl MirroredContext {
MirroredContext { MirroredContext {
width: pw, width: pw,
height: ph, height: ph,
ring: RING,
rgb: canvas, rgb: canvas,
hole, hole,
} }
@@ -634,7 +662,10 @@ mod tests {
200, 200,
&known, &known,
&mut model, &mut model,
test_params(0), Params {
mirror_depth: 48,
..test_params(0)
},
&mut |_, _| {}, &mut |_, _| {},
) )
.unwrap(); .unwrap();
@@ -648,6 +679,42 @@ mod tests {
} }
} }
#[test]
fn an_open_void_reaches_the_tile_edge_and_stays_unknown_beyond_the_band() {
// A 150-tall hole above and below; bands of 96. With no ring the
// first band's tiles sit at the picture's edge, so a tile's top
// row is unknown, and the rows deeper than the band are unknown
// too — not "known" coarse fill — exactly as the model was trained.
let (mut rgb, known) = picture(200, 500, 150);
let mut model = Flat {
tile: 64,
seen: Vec::new(),
};
fill_border(
&mut rgb,
200,
500,
&known,
&mut model,
Params {
band: 96,
..test_params(0)
},
&mut |_, _| {},
)
.unwrap();
// The first pass's tile at the picture's top edge is unknown
// through and through: the hole is 150 deep, the tile 64, and
// nothing beyond the band was presented as known. With a ring, or
// with the far side shown as coarse fill, no tile is ever all hole.
assert!(model.seen.iter().any(|(_, k)| k.iter().all(|&v| !v)));
for i in 0..200 * 500 {
if !known[i] {
assert!((rgb[i * 3] - 0.5).abs() < 1e-4, "pixel {i}");
}
}
}
#[test] #[test]
fn the_fine_passes_run_in_bands_after_the_coarse_one() { fn the_fine_passes_run_in_bands_after_the_coarse_one() {
// A 150-tall hole above and below a picture: the coarse pass sees // A 150-tall hole above and below a picture: the coarse pass sees
@@ -663,7 +730,12 @@ mod tests {
500, 500,
&known, &known,
&mut model, &mut model,
test_params(0), Params {
coarse: 4,
band: 96,
mirror_depth: 48,
..test_params(0)
},
&mut |_, _| {}, &mut |_, _| {},
) )
.unwrap(); .unwrap();
+37
View File
@@ -486,3 +486,40 @@ rectangle.
`models/LICENCE.md`), installed by the PKGBUILD and unpacked by the APK `models/LICENCE.md`), installed by the PKGBUILD and unpacked by the APK
beside the face and scene models; `tools/export-migan.sh` regenerates it beside the face and scene models; `tools/export-migan.sh` regenerates it
from the upstream checkpoint. from the upstream checkpoint.
## 14. The fill, trained — 2026-09-20
§13.5 named the remaining fault: in a deep corner the stock model puts its
Places2 prior — clouds, peaks, a water line — into a hole, because it was
trained on holes *inside* pictures and a panorama's border is a hole with
the picture on one side and nothing on the other. Every ring (mirror,
replicate, detrend) treated the symptom. The fix is a model that has seen
the real thing: **MI-GAN's 512 generator fine-tuned on border-shaped voids
cut from the user's own photographs**, in a separate repository
(`darkroom-infill`, beside this one), so the truth beyond the void is known
and the model learns one-sided extrapolation.
**What it was trained on.** Voids made the way this merge makes them:
frames with a yaw, a common pitch and per-frame roll, projected onto the
cylinder and rasterised, the canvas their union's bounding box, the void
the canvas outside the union — arcs where straight edges bent, cusps where
frames meet, the bow-tie wedge at a corner (a third of tiles are cut at a
canvas corner). Voids to 256 px deep at a 512 tile. Half the deep tiles
train the *second pass*: a no-grad first pass fills the tile, its nearest
band (64–256 px) is marked known, and the remaining void is the example —
so the model continues its own output without drift, which is how `fill`
runs deep voids. Data: ~7 400 pictures — the 1024-px proxy tier of the
library and ~2 000 raws sampled evenly across every year, developed at
half size. Loss: hole-weighted L1, VGG16 perceptual, a hinge PatchGAN.
One night on the reference desktop's RTX 3050.
**What changed here.** `FillParams::mirror_depth` **0** is now "no ring":
the void reaches the tile's edge with nothing beyond, and beyond the band
being filled the void stays *unknown* rather than presented as known coarse
fill — the two conditions the model was trained under. Defaults: mirror 0,
coarse 1 (the coarse pass seeds nothing the model is allowed to see), band
192. The ring remains on the page for the stock model's sake, at any depth
above zero. The model file is a drop-in (`models/inpaint/migan-512.onnx`,
same six operators, same tensors) and the engine loads it unchanged.
**Measured.** PLACEHOLDER
+1 -1
View File
@@ -107,7 +107,7 @@ A tag naming an ID `requirements.md` does not define — what renumbering produc
| FR-MRG-3 | [`core/dr-decode/examples/linear_dng.rs:1`](../core/dr-decode/examples/linear_dng.rs#L1), [`core/dr-decode/src/lib.rs:137`](../core/dr-decode/src/lib.rs#L137), [`core/dr-decode/src/lib.rs:144`](../core/dr-decode/src/lib.rs#L144), [`core/dr-decode/src/lib.rs:598`](../core/dr-decode/src/lib.rs#L598), [`core/dr-decode/src/profile.rs:395`](../core/dr-decode/src/profile.rs#L395), [`core/dr-export/src/dng.rs:1`](../core/dr-export/src/dng.rs#L1), [`core/dr-gpu/src/demosaic.rs:254`](../core/dr-gpu/src/demosaic.rs#L254), [`ui/dr-ui/src/merge.rs:1`](../ui/dr-ui/src/merge.rs#L1) | | FR-MRG-3 | [`core/dr-decode/examples/linear_dng.rs:1`](../core/dr-decode/examples/linear_dng.rs#L1), [`core/dr-decode/src/lib.rs:137`](../core/dr-decode/src/lib.rs#L137), [`core/dr-decode/src/lib.rs:144`](../core/dr-decode/src/lib.rs#L144), [`core/dr-decode/src/lib.rs:598`](../core/dr-decode/src/lib.rs#L598), [`core/dr-decode/src/profile.rs:395`](../core/dr-decode/src/profile.rs#L395), [`core/dr-export/src/dng.rs:1`](../core/dr-export/src/dng.rs#L1), [`core/dr-gpu/src/demosaic.rs:254`](../core/dr-gpu/src/demosaic.rs#L254), [`ui/dr-ui/src/merge.rs:1`](../ui/dr-ui/src/merge.rs#L1) |
| FR-MRG-4 | [`core/dr-export/src/inscribed.rs:1`](../core/dr-export/src/inscribed.rs#L1), [`core/dr-pano/src/fill.rs:1`](../core/dr-pano/src/fill.rs#L1), [`core/dr-pano/src/migan.rs:1`](../core/dr-pano/src/migan.rs#L1), [`core/dr-pano/src/projection.rs:1`](../core/dr-pano/src/projection.rs#L1), [`ui/dr-ui/src/library.rs:4457`](../ui/dr-ui/src/library.rs#L4457), [`ui/dr-ui/src/library.rs:4461`](../ui/dr-ui/src/library.rs#L4461), [`ui/dr-ui/src/merge.rs:143`](../ui/dr-ui/src/merge.rs#L143), [`ui/dr-ui/src/merge_ui.rs:1`](../ui/dr-ui/src/merge_ui.rs#L1), [`ui/dr-ui/ui/merge.slint:1`](../ui/dr-ui/ui/merge.slint#L1), [`ui/dr-ui/ui/merge.slint:46`](../ui/dr-ui/ui/merge.slint#L46) | | FR-MRG-4 | [`core/dr-export/src/inscribed.rs:1`](../core/dr-export/src/inscribed.rs#L1), [`core/dr-pano/src/fill.rs:1`](../core/dr-pano/src/fill.rs#L1), [`core/dr-pano/src/migan.rs:1`](../core/dr-pano/src/migan.rs#L1), [`core/dr-pano/src/projection.rs:1`](../core/dr-pano/src/projection.rs#L1), [`ui/dr-ui/src/library.rs:4457`](../ui/dr-ui/src/library.rs#L4457), [`ui/dr-ui/src/library.rs:4461`](../ui/dr-ui/src/library.rs#L4461), [`ui/dr-ui/src/merge.rs:143`](../ui/dr-ui/src/merge.rs#L143), [`ui/dr-ui/src/merge_ui.rs:1`](../ui/dr-ui/src/merge_ui.rs#L1), [`ui/dr-ui/ui/merge.slint:1`](../ui/dr-ui/ui/merge.slint#L1), [`ui/dr-ui/ui/merge.slint:46`](../ui/dr-ui/ui/merge.slint#L46) |
| FR-MRG-5 | [`core/dr-pano/src/align.rs:1`](../core/dr-pano/src/align.rs#L1), [`ui/dr-ui/src/merge.rs:1`](../ui/dr-ui/src/merge.rs#L1), [`ui/dr-ui/src/merge_ui.rs:1`](../ui/dr-ui/src/merge_ui.rs#L1), [`ui/dr-ui/ui/merge.slint:1`](../ui/dr-ui/ui/merge.slint#L1) | | FR-MRG-5 | [`core/dr-pano/src/align.rs:1`](../core/dr-pano/src/align.rs#L1), [`ui/dr-ui/src/merge.rs:1`](../ui/dr-ui/src/merge.rs#L1), [`ui/dr-ui/src/merge_ui.rs:1`](../ui/dr-ui/src/merge_ui.rs#L1), [`ui/dr-ui/ui/merge.slint:1`](../ui/dr-ui/ui/merge.slint#L1) |
| FR-MRG-6 | [`core/dr-pipeline/src/sidecar.rs:1033`](../core/dr-pipeline/src/sidecar.rs#L1033), [`core/dr-pipeline/src/sidecar.rs:113`](../core/dr-pipeline/src/sidecar.rs#L113), [`core/dr-pipeline/src/sidecar.rs:123`](../core/dr-pipeline/src/sidecar.rs#L123), [`core/dr-pipeline/src/sidecar.rs:2241`](../core/dr-pipeline/src/sidecar.rs#L2241), [`core/dr-pipeline/src/sidecar.rs:841`](../core/dr-pipeline/src/sidecar.rs#L841), [`ui/dr-ui/src/merge.rs:838`](../ui/dr-ui/src/merge.rs#L838) | | FR-MRG-6 | [`core/dr-pipeline/src/sidecar.rs:1033`](../core/dr-pipeline/src/sidecar.rs#L1033), [`core/dr-pipeline/src/sidecar.rs:113`](../core/dr-pipeline/src/sidecar.rs#L113), [`core/dr-pipeline/src/sidecar.rs:123`](../core/dr-pipeline/src/sidecar.rs#L123), [`core/dr-pipeline/src/sidecar.rs:2241`](../core/dr-pipeline/src/sidecar.rs#L2241), [`core/dr-pipeline/src/sidecar.rs:841`](../core/dr-pipeline/src/sidecar.rs#L841), [`ui/dr-ui/src/merge.rs:842`](../ui/dr-ui/src/merge.rs#L842) |
| FR-MRG-7 | [`ui/dr-ui/src/merge.rs:1`](../ui/dr-ui/src/merge.rs#L1), [`ui/dr-ui/src/merge_ui.rs:1`](../ui/dr-ui/src/merge_ui.rs#L1), [`ui/dr-ui/ui/app.slint:829`](../ui/dr-ui/ui/app.slint#L829), [`ui/dr-ui/ui/merge.slint:1`](../ui/dr-ui/ui/merge.slint#L1) | | FR-MRG-7 | [`ui/dr-ui/src/merge.rs:1`](../ui/dr-ui/src/merge.rs#L1), [`ui/dr-ui/src/merge_ui.rs:1`](../ui/dr-ui/src/merge_ui.rs#L1), [`ui/dr-ui/ui/app.slint:829`](../ui/dr-ui/ui/app.slint#L829), [`ui/dr-ui/ui/merge.slint:1`](../ui/dr-ui/ui/merge.slint#L1) |
| FR-MRG-8 | [`core/dr-pano/src/xfeat.rs:1`](../core/dr-pano/src/xfeat.rs#L1), [`core/dr-segment/examples/onnx_probe.rs:1`](../core/dr-segment/examples/onnx_probe.rs#L1) | | FR-MRG-8 | [`core/dr-pano/src/xfeat.rs:1`](../core/dr-pano/src/xfeat.rs#L1), [`core/dr-segment/examples/onnx_probe.rs:1`](../core/dr-segment/examples/onnx_probe.rs#L1) |
| FR-NC-1 | [`core/dr-sync-nextcloud/src/auth.rs:132`](../core/dr-sync-nextcloud/src/auth.rs#L132), [`core/dr-sync-nextcloud/src/auth.rs:44`](../core/dr-sync-nextcloud/src/auth.rs#L44), [`core/dr-sync-nextcloud/src/provider.rs:1`](../core/dr-sync-nextcloud/src/provider.rs#L1), [`core/dr-sync/src/account.rs:375`](../core/dr-sync/src/account.rs#L375), [`ui/dr-ui/src/launch.rs:316`](../ui/dr-ui/src/launch.rs#L316), [`ui/dr-ui/src/launch.rs:61`](../ui/dr-ui/src/launch.rs#L61), [`ui/dr-ui/src/launch_ui.rs:418`](../ui/dr-ui/src/launch_ui.rs#L418) | | FR-NC-1 | [`core/dr-sync-nextcloud/src/auth.rs:132`](../core/dr-sync-nextcloud/src/auth.rs#L132), [`core/dr-sync-nextcloud/src/auth.rs:44`](../core/dr-sync-nextcloud/src/auth.rs#L44), [`core/dr-sync-nextcloud/src/provider.rs:1`](../core/dr-sync-nextcloud/src/provider.rs#L1), [`core/dr-sync/src/account.rs:375`](../core/dr-sync/src/account.rs#L375), [`ui/dr-ui/src/launch.rs:316`](../ui/dr-ui/src/launch.rs#L316), [`ui/dr-ui/src/launch.rs:61`](../ui/dr-ui/src/launch.rs#L61), [`ui/dr-ui/src/launch_ui.rs:418`](../ui/dr-ui/src/launch_ui.rs#L418) |
+9 -4
View File
@@ -104,11 +104,16 @@ the dataset licence restricts models trained on it by name.
| File | Source | Trained on | Used by | | File | Source | Trained on | Used by |
|---|---|---|---| |---|---|---|---|
| `inpaint/migan-512.onnx` | `migan_512_places2.pt` from `https://github.com/Picsart-AI-Research/MI-GAN` (Sargsyan et al., ICCV 2023) | Places2, by the authors | the panorama border fill (FR-MRG-4) | | `inpaint/migan-512.onnx` | `migan_512_places2.pt` from `https://github.com/Picsart-AI-Research/MI-GAN` (Sargsyan et al., ICCV 2023), **fine-tuned** in the `darkroom-infill` repository (2026-09-20) | Places2 by the authors, then ~7 400 of the maintainer's own photographs with border-shaped voids | the panorama border fill (FR-MRG-4) |
Exported by `tools/export-migan.sh`: the bare 512 generator at a fixed The bare 512 generator at a fixed `1×4×512×512`, six operator types; the
`1×4×512×512`, six operator types. The tiling, the context and the blend are tiling, the context and the blend are Rust (`dr_pano::fill`). Since
Rust (`dr_pano::fill`). 2026-09-20 the shipped file is the fine-tune (`docs/panorama.md` §14),
exported by `python -m infill.export` in `darkroom-infill`;
`tools/export-migan.sh` still produces the stock generator from the upstream
checkpoint, which the fine-tune starts from. The fine-tuned weights are a
derivative of the MIT weights trained on photographs the maintainer owns,
and carry the same MIT grant.
**MIT, code and weights alike** — `LICENSE` and `LICENSE-WEIGHTS` in the **MIT, code and weights alike** — `LICENSE` and `LICENSE-WEIGHTS` in the
repository, both read on 2026-09-19, both the plain MIT text with no further repository, both read on 2026-09-19, both the plain MIT text with no further
+6
View File
@@ -8,6 +8,12 @@
# #
# ./tools/export-migan.sh # -> models/inpaint/migan-512.onnx # ./tools/export-migan.sh # -> models/inpaint/migan-512.onnx
# #
# Since 2026-09-20 the file that ships is not this export but a fine-tune of
# it on panorama-border voids (docs/panorama.md §14), made in the
# `darkroom-infill` repository with `python -m infill.export`. This script
# still yields the stock generator — the fine-tune's starting point, and the
# model the page's "mirror depth" knob above zero was built around.
#
# Requires `uv`. Everything else is fetched into a throwaway venv, including # Requires `uv`. Everything else is fetched into a throwaway venv, including
# a CPU-only torch and `gdown` for the checkpoint, which the authors keep on # a CPU-only torch and `gdown` for the checkpoint, which the authors keep on
# Google Drive (models/LICENCE.md has the licence; it is MIT). # Google Drive (models/LICENCE.md has the licence; it is MIT).
+5 -1
View File
@@ -181,7 +181,11 @@ impl FillSettings {
params: dr_pano::FillParams { params: dr_pano::FillParams {
coarse: self.params.coarse, coarse: self.params.coarse,
band: px(self.params.band).max(8), band: px(self.params.band).max(8),
mirror_depth: px(self.params.mirror_depth), mirror_depth: if self.params.mirror_depth == 0 {
0
} else {
px(self.params.mirror_depth)
},
feather: if self.params.feather == 0 { feather: if self.params.feather == 0 {
0 0
} else { } else {
+1 -1
View File
@@ -339,7 +339,7 @@ pub fn wire<S, C, F>(
"erosion" => f.erosion = v, "erosion" => f.erosion = v,
"coarse" => f.params.coarse = v.max(1), "coarse" => f.params.coarse = v.max(1),
"band" => f.params.band = v.max(8), "band" => f.params.band = v.max(8),
"mirror" => f.params.mirror_depth = v.max(1), "mirror" => f.params.mirror_depth = v,
"feather" => f.params.feather = v, "feather" => f.params.feather = v,
_ => return, _ => return,
} }
+10 -10
View File
@@ -63,9 +63,9 @@ export component MergePage inherits Rectangle {
/// seam. Working pixels, at the merge's working scale. /// seam. Working pixels, at the merge's working scale.
in property <float> fill-scale: 2; in property <float> fill-scale: 2;
in property <float> fill-erosion: 4; in property <float> fill-erosion: 4;
in property <float> fill-coarse: 4; in property <float> fill-coarse: 1;
in property <float> fill-band: 96; in property <float> fill-band: 192;
in property <float> fill-mirror: 48; in property <float> fill-mirror: 0;
in property <float> fill-feather: 48; in property <float> fill-feather: 48;
/// The composite, once written: its name for the status line, and /// The composite, once written: its name for the status line, and
@@ -240,26 +240,26 @@ export component MergePage inherits Rectangle {
SliderRow { SliderRow {
label: "Coarse pass"; label: "Coarse pass";
hint: "reduction of the structure pass; 1 skips it"; hint: "reduction of the structure pass; 1 skips it";
value: root.fill-coarse; default-value: 4; minimum: 1; maximum: 8; value: root.fill-coarse; default-value: 1; minimum: 1; maximum: 8;
enabled: !root.running && !root.done; enabled: !root.running && !root.done;
changed(v) => { root.fill-knob("coarse", v); } changed(v) => { root.fill-knob("coarse", v); }
reset => { root.fill-knob("coarse", 4); } reset => { root.fill-knob("coarse", 1); }
} }
SliderRow { SliderRow {
label: "Band width"; label: "Band width";
hint: "the fine passes' bands from the edge outward, working pixels"; hint: "the fine passes' bands from the edge outward, working pixels";
value: root.fill-band; default-value: 96; minimum: 16; maximum: 384; value: root.fill-band; default-value: 192; minimum: 16; maximum: 384;
enabled: !root.running && !root.done; enabled: !root.running && !root.done;
changed(v) => { root.fill-knob("band", v); } changed(v) => { root.fill-knob("band", v); }
reset => { root.fill-knob("band", 96); } reset => { root.fill-knob("band", 192); }
} }
SliderRow { SliderRow {
label: "Mirror depth"; label: "Mirror depth";
hint: "how far into the picture the mirrored context reaches"; hint: "0: the void as it is, what the model was trained on; otherwise how far into the picture a mirrored context reaches";
value: root.fill-mirror; default-value: 48; minimum: 4; maximum: 512; value: root.fill-mirror; default-value: 0; minimum: 0; maximum: 512;
enabled: !root.running && !root.done; enabled: !root.running && !root.done;
changed(v) => { root.fill-knob("mirror", v); } changed(v) => { root.fill-knob("mirror", v); }
reset => { root.fill-knob("mirror", 48); } reset => { root.fill-knob("mirror", 0); }
} }
SliderRow { SliderRow {
label: "Seam feather"; label: "Seam feather";