Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/reco-autocam/src/panners/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1151,6 +1151,7 @@ mod tests {
x_rx: 0.0,
z_rz: 0.0,
blend_width: 0.05,
seam_offset: 0.0,
},
Framing {
axis_offset: 0.24,
Expand Down
1 change: 1 addition & 0 deletions crates/reco-autocam/src/panners/sweep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ mod tests {
x_rx: 0.0,
z_rz: 0.0,
blend_width: 0.05,
seam_offset: 0.0,
},
Framing {
axis_offset: 0.24,
Expand Down
1 change: 1 addition & 0 deletions crates/reco-calibrate/src/optimizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@ impl Optimizer for NelderMeadOptimizer {
},
z_rz: 0.0,
blend_width: reco_core::calibration::DEFAULT_BLEND_WIDTH,
seam_offset: 0.0,
};
let framing = Framing {
axis_offset: params.cam_d,
Expand Down
19 changes: 19 additions & 0 deletions crates/reco-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,21 @@ enum Commands {
#[arg(long, value_parser = parse_blend)]
blend: Option<f32>,

/// Manual nudge of the seam position, in the right plane's own
/// local UV units (same space as `--blend`). `0.0` (default) =
/// seam sits exactly where the calibration's geometry puts it.
/// Overrides the calibration's saved value; when omitted, the
/// calibration decides.
#[arg(long)]
seam_offset: Option<f32>,

/// Draw a thin debug line at the exact geometric seam position,
/// independent of how wide `--blend` currently feathers it. Pure
/// visualization aid for tuning calibration - has no effect on
/// the blend math itself.
#[arg(long, default_value_t = false)]
show_seam_line: bool,

/// Frame offset for temporal sync between cameras.
/// Positive: skip N right frames (right started first).
/// Negative: skip N left frames (left started first).
Expand Down Expand Up @@ -787,6 +802,8 @@ fn main() -> anyhow::Result<()> {
codec,
quality,
blend,
seam_offset,
show_seam_line,
sync_offset,
model,
detection_interval,
Expand All @@ -812,6 +829,8 @@ fn main() -> anyhow::Result<()> {
width,
height,
blend,
seam_offset,
show_seam_line,
start_time,
end_time,
max_frames,
Expand Down
8 changes: 8 additions & 0 deletions crates/reco-cli/src/stitch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ pub struct StitchArgs<'a> {
pub width: u32,
pub height: u32,
pub blend: Option<f32>,
pub seam_offset: Option<f32>,
pub show_seam_line: bool,
pub start_time: Option<f64>,
pub end_time: Option<f64>,
pub max_frames: Option<u64>,
Expand Down Expand Up @@ -126,6 +128,12 @@ pub fn run_stitch(args: StitchArgs<'_>, interrupted: &Arc<AtomicBool>) -> anyhow
if let Some(b) = args.blend {
job = job.blend_width(b);
}
if let Some(o) = args.seam_offset {
job = job.seam_offset(o);
}
if args.show_seam_line {
job = job.show_seam_line(true);
}
if let Some(t) = args.start_time {
job = job.start_time(t);
}
Expand Down
30 changes: 30 additions & 0 deletions crates/reco-core/src/calibration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,13 @@ pub struct Topology {
/// Seam blend width as a fraction of the plane overlap. `0.0` = hard seam.
#[serde(default = "default_blend_width")]
pub blend_width: f32,
/// Manual nudge of the seam position, in the right plane's own local
/// UV units (same space as `blend_width`). `0.0` (default) = seam sits
/// exactly where the plane geometry's own edge puts it. Shifts only
/// the alpha-crossfade threshold, never the plane geometry itself, so
/// it cannot open a coverage gap the way adjusting `intersect` can.
#[serde(default)]
pub seam_offset: f32,
}

/// Default seam blend width for calibrations that do not specify one.
Expand All @@ -237,6 +244,10 @@ fn default_blend_width() -> f32 {
DEFAULT_BLEND_WIDTH
}

/// Safe bound (both directions) for [`Topology::seam_offset`], in the
/// right plane's own local UV units.
pub const SEAM_OFFSET_RANGE: f32 = 0.3;

/// The virtual camera's calibrated coordinate frame: the axis/orientation that
/// panning evolves *within*. Pan (yaw/pitch) and output framing (fov/size) are
/// runtime state and are deliberately NOT stored here.
Expand Down Expand Up @@ -598,6 +609,24 @@ fn validate_topology(t: &Topology) -> Result<(), CalibrationError> {
});
}

if !t.seam_offset.is_finite() {
return Err(CalibrationError::NonFiniteFloat {
field: "topology.seam_offset".to_owned(),
value: format!("{}", t.seam_offset),
});
}
// Bounded to a safe range within the coverage the geometry already
// guarantees - well past this and the shift itself (not the smoothstep
// math) starts pushing the visible seam into unrelated content.
if !(-SEAM_OFFSET_RANGE..=SEAM_OFFSET_RANGE).contains(&t.seam_offset) {
return Err(CalibrationError::OutOfRange {
field: "topology.seam_offset".to_owned(),
value: t.seam_offset as f64,
min: -SEAM_OFFSET_RANGE as f64,
max: SEAM_OFFSET_RANGE as f64,
});
}

Ok(())
}

Expand Down Expand Up @@ -721,6 +750,7 @@ mod tests {
x_rx: 0.0,
z_rz: 0.0,
blend_width: 0.05,
seam_offset: 0.0,
},
framing: Framing {
axis_offset: 0.25,
Expand Down
12 changes: 12 additions & 0 deletions crates/reco-core/src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,18 @@ impl StitchCore {
self.executor.set_blend_width(width);
}

/// Set the manual seam-position nudge (per-frame uniform; coverage
/// unaffected). See [`crate::calibration::Topology::seam_offset`].
pub fn set_seam_offset(&mut self, offset: f32) {
self.executor.set_seam_offset(offset);
}

/// Toggle the seam debug line (GPU executor only; see
/// [`crate::stitch::Executor::set_show_seam_line`]).
pub fn set_show_seam_line(&mut self, show: bool) {
self.executor.set_show_seam_line(show);
}

/// Set the lens-correction strength on every lens (`0` = pinhole,
/// `1` = full KB4).
pub fn set_lens_correction_amount(&mut self, amount: f32) {
Expand Down
1 change: 1 addition & 0 deletions crates/reco-core/src/detect/panner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ mod tests {
x_rx: 0.0,
z_rz: 0.0,
blend_width: 0.05,
seam_offset: 0.0,
},
Framing {
axis_offset: 0.24,
Expand Down
11 changes: 9 additions & 2 deletions crates/reco-core/src/projection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,10 @@ impl Projection for LShapeProjection {
(Box::new(left), BlendRule::Opaque),
(
Box::new(right),
BlendRule::Smoothstep(calibration.topology.blend_width as f64),
BlendRule::Smoothstep {
width: calibration.topology.blend_width as f64,
offset: calibration.topology.seam_offset as f64,
},
),
]
}
Expand Down Expand Up @@ -497,6 +500,7 @@ mod tests {
x_rx: 0.0,
z_rz: 0.0,
blend_width: 0.05,
seam_offset: 0.0,
},
Framing {
axis_offset: 0.2398,
Expand Down Expand Up @@ -909,7 +913,10 @@ mod tests {
assert_eq!(surfaces[0].1, crate::stitch::BlendRule::Opaque);
assert_eq!(
surfaces[1].1,
crate::stitch::BlendRule::Smoothstep(cal.topology.blend_width as f64)
crate::stitch::BlendRule::Smoothstep {
width: cal.topology.blend_width as f64,
offset: cal.topology.seam_offset as f64,
}
);
}

Expand Down
25 changes: 25 additions & 0 deletions crates/reco-core/src/render/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ pub struct StitchPipeline {
pub(crate) calibration: Calibration,
/// Output viewport configuration.
pub(crate) viewport: ViewportConfig,
/// Draw a debug line at the exact seam position. Pure visualization
/// (calibration-tuning aid), not calibration state - deliberately not
/// part of `Calibration` so toggling it never touches the saved file.
pub(crate) show_seam_line: bool,
/// GPU renderer (textures, pipelines, bind groups).
renderer: Renderer,
/// Input frame dimensions.
Expand Down Expand Up @@ -161,6 +165,7 @@ impl StitchPipeline {
scene,
calibration,
viewport,
show_seam_line: false,
renderer,
input_width,
input_height,
Expand Down Expand Up @@ -271,6 +276,20 @@ impl StitchPipeline {
self.calibration.topology.blend_width = width;
}

/// Set the manual seam-position nudge (per-frame uniform; no scene
/// rebuild). See [`crate::calibration::Topology::seam_offset`].
pub fn set_seam_offset(&mut self, offset: f32) {
self.calibration.topology.seam_offset = offset;
}

/// Toggle the seam debug line (per-frame uniform; no scene rebuild).
/// Draws a thin highlight at the exact rendered seam position by
/// reusing the same alpha-threshold math the blend itself uses, so
/// it can never disagree with where the blend actually sits.
pub fn set_show_seam_line(&mut self, show: bool) {
self.show_seam_line = show;
}

/// Update calibration parameters. Recomputes [`SceneGeometry`] from the
/// new layout. Takes effect on the next render call (uniforms are rebuilt
/// each frame from the stored calibration and scene).
Expand Down Expand Up @@ -546,6 +565,7 @@ impl StitchPipeline {
&self.calibration,
&viewport,
self.calibration.topology.blend_width,
self.show_seam_line,
target_view,
);
Ok(())
Expand Down Expand Up @@ -583,6 +603,7 @@ impl StitchPipeline {
&self.calibration,
&viewport,
self.calibration.topology.blend_width,
self.show_seam_line,
target_view,
);
Ok(())
Expand Down Expand Up @@ -624,6 +645,7 @@ impl StitchPipeline {
&self.calibration,
&viewport,
self.calibration.topology.blend_width,
self.show_seam_line,
))
}

Expand Down Expand Up @@ -662,6 +684,7 @@ impl StitchPipeline {
&self.calibration,
&viewport,
self.calibration.topology.blend_width,
self.show_seam_line,
))
}

Expand Down Expand Up @@ -700,6 +723,7 @@ impl StitchPipeline {
&self.calibration,
&viewport,
self.calibration.topology.blend_width,
self.show_seam_line,
))
}

Expand Down Expand Up @@ -763,6 +787,7 @@ impl StitchPipeline {
&self.calibration,
&viewport,
self.calibration.topology.blend_width,
self.show_seam_line,
)
}

Expand Down
7 changes: 7 additions & 0 deletions crates/reco-core/src/render/renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,7 @@ impl Renderer {
calibration: &Calibration,
viewport: &ResolvedViewport,
blend_width: f32,
show_seam_line: bool,
target_view: &wgpu::TextureView,
aspect: f32,
encoder_label: &str,
Expand Down Expand Up @@ -852,6 +853,8 @@ impl Renderer {
self.is_full_range,
);
right_uniforms.lens_preview[0] = calibration.lenses[1].correction;
right_uniforms.lens_preview[2] = calibration.topology.seam_offset;
right_uniforms.lens_preview[3] = if show_seam_line { 1.0 } else { 0.0 };

gpu.queue.write_buffer(
&self.left.uniform_buffer,
Expand Down Expand Up @@ -920,6 +923,7 @@ impl Renderer {
calibration: &Calibration,
viewport: &ResolvedViewport,
blend_width: f32,
show_seam_line: bool,
) -> wgpu::CommandBuffer {
let aspect = self.output_width as f32 / self.output_height as f32;
let encoder = self.encode_stitch_pass(
Expand All @@ -928,6 +932,7 @@ impl Renderer {
calibration,
viewport,
blend_width,
show_seam_line,
&self.render_target_view,
aspect,
"stitch_to_target",
Expand All @@ -954,6 +959,7 @@ impl Renderer {
calibration: &Calibration,
viewport: &ResolvedViewport,
blend_width: f32,
show_seam_line: bool,
target_view: &wgpu::TextureView,
) {
let aspect = viewport.config.width as f32 / viewport.config.height as f32;
Expand All @@ -963,6 +969,7 @@ impl Renderer {
calibration,
viewport,
blend_width,
show_seam_line,
target_view,
aspect,
"preview_frame",
Expand Down
2 changes: 2 additions & 0 deletions crates/reco-core/src/render/scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ mod tests {
x_rx: 0.0,
z_rz: 0.0,
blend_width: 0.05,
seam_offset: 0.0,
}
}

Expand Down Expand Up @@ -164,6 +165,7 @@ mod tests {
x_rx: 0.0,
z_rz: 0.0,
blend_width: 0.05,
seam_offset: 0.0,
};

let geom = SceneGeometry::new(&topology, &framing(0.24), 16.0 / 9.0);
Expand Down
1 change: 1 addition & 0 deletions crates/reco-core/src/session/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ fn test_calibration() -> Calibration {
x_rx: 0.0,
z_rz: 0.0,
blend_width: 0.05,
seam_offset: 0.0,
},
Framing {
axis_offset: 0.25,
Expand Down
8 changes: 8 additions & 0 deletions crates/reco-core/src/session/wiring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,14 @@ impl StitchSession {
self.lookahead_frames = frames;
}

/// Toggle the seam debug line (see
/// [`StitchCore::set_show_seam_line`](crate::core::StitchCore::set_show_seam_line)).
/// Pure visualization aid - has no effect on the rendered seam blend
/// itself.
pub fn set_show_seam_line(&mut self, show: bool) {
self.core.set_show_seam_line(show);
}

/// Attach a stacked-video replay recorder.
///
/// Forwards to `StitchCore::set_stacked_recorder` on the
Expand Down
Loading
Loading