Leave a panorama frame out without leaving the page

A frame that did not fit ended the job with its name, and the only way
on was Back, a smaller selection and every frame read, demosaiced and
searched for keypoints again. Each row on the page now has a box. An
unticked frame is left out and the rest are solved again from what the
first pass measured.

dr_pano::align is split for it: match_pairs does the matching and the
pairwise RANSAC once over every frame (about 5 s for twelve), and solve
takes a subset of the frames and uses only the links among them (about
0.1 s). Solving a subset by re-aligning it also moved every RANSAC seed,
which are keyed on frame position, and on the fixture set that was
enough to lose a marginal link and strand a neighbour of the frame left
out.

A frame that cannot be placed no longer stops the job either. Its row
names it and Merge stays off until it is unticked; the headless example
leaves such frames out the same way, and takes --leave-out N to try it.
This commit is contained in:
2026-09-27 16:29:39 -04:00
parent e98b1def98
commit 621a6b8313
9 changed files with 616 additions and 202 deletions
+2
View File
@@ -616,6 +616,7 @@ export component AppWindow inherits Window {
callback merge-confirm();
callback merge-abandon();
callback merge-projection-picked(int);
callback merge-frame-toggled(int, bool);
callback merge-border-picked(int);
/// A fill knob turned: its name and the new value.
callback merge-fill-knob(string, float);
@@ -1182,6 +1183,7 @@ in property <bool> panel-visible: true;
merge() => { root.merge-confirm(); }
abandon() => { root.merge-abandon(); }
projection-picked(i) => { root.merge-projection-picked(i); }
frame-toggled(i, on) => { root.merge-frame-toggled(i, on); }
border-picked(i) => { root.merge-border-picked(i); }
fill-knob(name, value) => { root.merge-fill-knob(name, value); }
close() => { root.merge-close(); }
+24 -8
View File
@@ -7,11 +7,13 @@
// one. The difference is the pause in the middle: the job aligns, this
// page shows what it found, and nothing is written until the photographer
// presses Merge (FR-MRG-1). A frame that could not be placed is named on
// its row and the button stays off (FR-MRG-5).
// its row and the button stays off (FR-MRG-5) until it is unticked: each
// row's box keeps or leaves out its frame, and the job aligns again over
// the ones kept without reading any of them again.
import { Theme } from "theme.slint";
import { Button, PanelHeading, Label, Value, Caption, Panel, ProgressBar } from "widgets.slint";
import { Segmented, SliderRow } from "controls.slint";
import { Segmented, SliderRow, Check } from "controls.slint";
/// One source frame's line in the alignment table.
export struct MergeFrameRow {
@@ -19,6 +21,8 @@ export struct MergeFrameRow {
/// "yaw −55.9° · pitch −1.6°", or why it is not aligned.
detail: string,
aligned: bool,
/// Whether the frame is in the alignment; unticked, it is left out.
included: bool,
}
export component MergePage inherits Rectangle {
@@ -75,6 +79,8 @@ export component MergePage inherits Rectangle {
callback merge();
callback abandon();
callback projection-picked(int);
/// A frame's box: its index in `frames`, and whether to keep it.
callback frame-toggled(int, bool);
callback border-picked(int);
/// A knob turned: its name ("scale", "erosion", "coarse", "band",
/// "mirror", "feather") and the new value.
@@ -276,18 +282,28 @@ export component MergePage inherits Rectangle {
Panel {
PanelHeading { text: "Frames"; }
for row in root.frames: HorizontalLayout {
// Controlled: the tick follows the report the job sends
// back, so a box never claims a frame the alignment on
// screen does not have.
for row[i] in root.frames: HorizontalLayout {
spacing: Theme.gap;
Label {
text: row.name;
width: 160px;
overflow: elide;
Check {
label: row.name;
width: 200px;
checked: row.included;
controlled: true;
toggled(on) => {
if (!root.running && !root.done) {
root.frame-toggled(i, on);
}
}
}
Caption {
text: row.detail;
warn: !row.aligned;
warn: row.included && !row.aligned;
overflow: elide;
horizontal-stretch: 1;
vertical-alignment: center;
}
}