FR-MRG-4: the border is cropped or filled, the fill experimental; panorama.md §13 records what was built and measured
This commit is contained in:
@@ -388,3 +388,101 @@ the tablet. Three ways to make it viable, none built:
|
||||
Whichever, the fill is a *proposal* under FR-MRG-1's rule — shown, then
|
||||
confirmed — and it would sit beside the crop, not replace it: the crop is
|
||||
free and honest, the fill is invented pixels, and the photographer chooses.
|
||||
|
||||
## 13. The fill, built — 2026-09-19, evening
|
||||
|
||||
Built the same day on `merge/fill`, on the engine (S16) rather than tract,
|
||||
and FR-MRG-4 revised to admit it: the border is *cropped or filled*, the
|
||||
photographer's choice, the crop the default.
|
||||
|
||||
**What runs.** `dr_pano::fill` is the engine-independent half: an
|
||||
`Inpainter` trait (a 512-px tile in, the same tile out) and `fill_border`,
|
||||
which owns everything the model does not — which tiles, what context, how
|
||||
to blend. `dr_pano::migan::MiGan` is the trait over the shipped generator
|
||||
under `dr_inference_engine` with the new `Role::Inpainter`, so it takes
|
||||
whichever rung the device has. The merge job runs the fill at **half the
|
||||
composite's resolution**, in a display-ish space (white balance, camera
|
||||
matrix, gamma — invertible, so the result goes back to camera-linear and
|
||||
into the same linear DNG), and the full-resolution merge samples the fill
|
||||
where no frame reached.
|
||||
|
||||
**What the spike taught, tried in order and kept or dropped.**
|
||||
|
||||
1. *Context across the coverage edge.* MI-GAN was trained on holes inside
|
||||
pictures; given a hole at the picture's edge it invents a structure along
|
||||
the open side (white streaks in the sky, on the first try). The known
|
||||
content is therefore **mirrored** across the coverage edge into the hole
|
||||
and into a 256-px ring, column by column for the top and bottom bands
|
||||
and row by row for the sides; the model interpolates between real and
|
||||
mirrored sky rather than extrapolating into nothing. *Replicated* rows
|
||||
(the edge row continued flat) streaked the grass; a detrended mix (tone
|
||||
replicated, texture mirrored) smeared; a low-pass extrapolation banded.
|
||||
Mirror stays.
|
||||
2. *Coarse to fine.* One pass at the working resolution let the boundary
|
||||
leak in — each 512 tile saw only its own corner of the hole. So a
|
||||
**coarse pass at a quarter** decides the structure with the whole border
|
||||
in a few tiles, and **fine passes in 96-px bands** from the real edge
|
||||
outward regenerate texture, each band the only unknown with the previous
|
||||
band on its near side and the upsampled coarse fill on its far side.
|
||||
3. *The seam.* A hard cut between real and invented showed as a sharpness
|
||||
step. The known mask is eroded by a **24-px feather** (48 at half
|
||||
resolution) and the fill blended in across that margin by distance to
|
||||
the real edge, smoothstep.
|
||||
4. *Partial pixels.* The seams were still visible until the cause was found
|
||||
upstream of the fill: the camera-space tap stored **black with alpha 1**
|
||||
for a pixel the lens correction pushed off the sensor, and the warp
|
||||
averaged it in — a dark, poorly interpolated fringe along every frame's
|
||||
edge that the fill then continued. `OutputMode::CameraLinear` now
|
||||
stores alpha 0 for a pixel that is not there and the merge's warp
|
||||
weights by the sampled alpha, so the fringe never enters the composite.
|
||||
The mask erosion before the fill dropped from 16 px to 4.
|
||||
|
||||
5. *What is still wrong, and why it ships anyway.* With the seams gone the
|
||||
content itself is the problem in the deep corners: the model, trained
|
||||
on Places2, puts bright cloud-and-peak shapes into a sky hole and a
|
||||
water-like band under grass — its prior for "top of a picture" and
|
||||
"bottom of a landscape", not anything in the context (the same shapes
|
||||
appear with the mirror capped, uncapped, and on the CPU as on TensorRT).
|
||||
Thin borders are fine; that is most of a hand-held sweep. So the fill
|
||||
ships **experimental**: opt-in, previewed, its knobs on the page and
|
||||
in the sidecar, and `cargo run -p dr-ui --example fill` re-runs any
|
||||
merge's dumped input (`DR_FILL_DUMP=dir`) stage by stage in seconds so
|
||||
the next attempt is made from the picture, not from a seven-minute
|
||||
merge. Candidates for that attempt: a context that is not a mirror at
|
||||
all in deep holes (the coarse pass's own answer, iterated), a sky
|
||||
detector that fills sky by extrapolating the gradient and leaves the
|
||||
model to texture, or a different model.
|
||||
|
||||
**Measured, the fixture's twelve frames (22 991 × 5 978), 348 tiles at
|
||||
half resolution.** 312 s on ONNX Runtime's CPU pool on the reference
|
||||
desktop (≈ 0.8 s a tile). On TensorRT fp16: **100 s**, of which 60 ms a
|
||||
tile was the engine hashing the 28 MB model on every acquire (fixed, the
|
||||
hash is taken at open) and 150 ms a tile the GPU itself — throttled:
|
||||
`trtexec` on the same engine read 23 ms at noon on a cool machine and
|
||||
152 ms that evening after two hours of builds, nvidia-smi showing SW power
|
||||
cap and thermal slowdown. Cool, the fill is ~10 s. The TensorRT engine
|
||||
compiles once, in 13 minutes, cached under the inference directory.
|
||||
|
||||
**The runtime is a packaging matter.** Arch's `onnxruntime-opt-cuda` has
|
||||
no TensorRT provider ("not enabled in this build") and its CUDA provider
|
||||
does not load against cuDNN 9, so on this machine the app fell to ORT CPU
|
||||
until the official `onnxruntime-linux-x64-gpu_cuda13` tarball (1.30.0,
|
||||
which links the system CUDA 13.4 and TensorRT 10.16) was unpacked and
|
||||
named with `DARKROOM_ORT_DIR`; `/usr/lib/darkroom` is searched too, for a
|
||||
package that ships it. §12's point 2 for the tablet is unchanged.
|
||||
|
||||
**On the page.** A *Border* choice beside the projection — *Crop to the
|
||||
picture* / *Fill the border* — with a caption saying what the fill is; the
|
||||
preview re-renders filled when chosen, at preview resolution, so the
|
||||
choice is seen before it is confirmed (FR-MRG-1). Greyed out with the reason
|
||||
when `migan-512.onnx` is not in the model directory. Under the fill, while
|
||||
it is experimental, its six knobs as sliders — working scale, edge
|
||||
erosion, coarse pass, band width, mirror depth, seam feather — each
|
||||
committing a redraw of the preview. A filled merge's sidecar says `border
|
||||
filled` with the knobs used, and its default crop is still the inscribed
|
||||
rectangle.
|
||||
|
||||
**Ships.** `models/inpaint/migan-512.onnx` (LFS, 28 MB, MIT,
|
||||
`models/LICENCE.md`), installed by the PKGBUILD and unpacked by the APK
|
||||
beside the face and scene models; `tools/export-migan.sh` regenerates it
|
||||
from the upstream checkpoint.
|
||||
|
||||
Reference in New Issue
Block a user