Projection chips redraw the preview; auto-crop as the DNG default crop
Picking a chip stored the choice for the merge and changed nothing on screen — the chip did not even highlight, since the selected property was never written back. Now the pick is reflected, and the job, waiting for its decision, takes a Preview request, draws the alignment on the chosen surface at proxy cost and reports again; the drain puts the new picture and its size up. Auto is the surface the field of view suggests. Also: The largest rectangle inside the frames' coverage is found a row at a time — a histogram of consecutive covered rows and a stack pass per row — so the composite is never held to be measured (FR-MRG-11). It is written as DefaultCropOrigin/DefaultCropSize (FR-MRG-4): the file opens on the picture, the border is still in it, and resetting the crop shows it. rawler reports the crop as the picture, which the test checks. FR-MRG-4 records the question raised the same day — fill the border rather than crop it — as open: a non-generative fill through the heal, or a generative inpainter with its licence and weights. Neither decided.
This commit is contained in:
+35
-19
@@ -110,6 +110,10 @@ impl MergeRequest {
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Decision {
|
||||
Merge { projection: Option<Projection> },
|
||||
/// Draw the alignment again on another surface, and report it again.
|
||||
/// The page's projection chips: what a choice looks like before it is
|
||||
/// confirmed, at proxy cost rather than the merge's.
|
||||
Preview { projection: Option<Projection> },
|
||||
Abandon,
|
||||
}
|
||||
|
||||
@@ -279,8 +283,7 @@ fn run_inner(
|
||||
let projection = request
|
||||
.projection
|
||||
.unwrap_or_else(|| Projection::suggest(hfov, vfov));
|
||||
let bounds = projection::bounds(projection, focal_full, &cameras, frame_size)
|
||||
.ok_or("the frames project nowhere")?;
|
||||
let suggested = projection;
|
||||
|
||||
// 4. Gains, before the report so the preview shows them.
|
||||
let gains = if alignment.is_complete() {
|
||||
@@ -288,23 +291,25 @@ fn run_inner(
|
||||
} else {
|
||||
vec![1.0; frames.len()]
|
||||
};
|
||||
let preview = if alignment.is_complete() {
|
||||
progress("Drawing the preview", 0, 1);
|
||||
Some(preview(&colour, &proxies, &alignment, &gains, projection, &frames[0].raw))
|
||||
} else {
|
||||
None
|
||||
let report = |projection: Projection| -> Result<AlignmentReport, String> {
|
||||
let bounds = projection::bounds(projection, focal_full, &cameras, frame_size)
|
||||
.ok_or("the frames project nowhere")?;
|
||||
let preview = alignment
|
||||
.is_complete()
|
||||
.then(|| preview(&colour, &proxies, &alignment, &gains, projection, &frames[0].raw));
|
||||
Ok(AlignmentReport {
|
||||
focal_mm: focal_full * 36.0 / full_long,
|
||||
rms_px: alignment.rms_px,
|
||||
projection,
|
||||
width: bounds.width().ceil() as u32,
|
||||
height: bounds.height().ceil() as u32,
|
||||
frames: describe(&alignment),
|
||||
links: alignment.links.len(),
|
||||
preview,
|
||||
})
|
||||
};
|
||||
let report = AlignmentReport {
|
||||
focal_mm: focal_full * 36.0 / full_long,
|
||||
rms_px: alignment.rms_px,
|
||||
projection,
|
||||
width: bounds.width().ceil() as u32,
|
||||
height: bounds.height().ceil() as u32,
|
||||
frames: describe(&alignment),
|
||||
links: alignment.links.len(),
|
||||
preview,
|
||||
};
|
||||
let _ = events.send(MergeEvent::Aligned(report));
|
||||
progress("Drawing the preview", 0, 1);
|
||||
let _ = events.send(MergeEvent::Aligned(report(suggested)?));
|
||||
if !alignment.is_complete() {
|
||||
let names: Vec<String> = alignment
|
||||
.unaligned
|
||||
@@ -322,7 +327,10 @@ fn run_inner(
|
||||
return Ok(None);
|
||||
}
|
||||
match decision.recv_timeout(std::time::Duration::from_millis(100)) {
|
||||
Ok(Decision::Merge { projection: p }) => break p.unwrap_or(projection),
|
||||
Ok(Decision::Merge { projection: p }) => break p.unwrap_or(suggested),
|
||||
Ok(Decision::Preview { projection: p }) => {
|
||||
let _ = events.send(MergeEvent::Aligned(report(p.unwrap_or(suggested))?));
|
||||
}
|
||||
Ok(Decision::Abandon) => return Ok(None),
|
||||
Err(std::sync::mpsc::RecvTimeoutError::Timeout) => continue,
|
||||
Err(std::sync::mpsc::RecvTimeoutError::Disconnected) => return Ok(None),
|
||||
@@ -388,6 +396,10 @@ fn run_inner(
|
||||
// other's pace more than one band.
|
||||
let (band_tx, band_rx) = std::sync::mpsc::sync_channel::<Vec<u16>>(1);
|
||||
let rows_per_strip = request.chunk.1.max(1);
|
||||
// The crop (FR-MRG-4), found as the bands go by; the writer reads it
|
||||
// once the last strip is in, by which time every band has been seen.
|
||||
let inscribed = Arc::new(std::sync::Mutex::new(dr_export::Inscribed::new(out_w)));
|
||||
let inscribed_for_writer = inscribed.clone();
|
||||
let file = std::fs::File::create(&out_path).map_err(|e| format!("{}: {e}", out_path.display()))?;
|
||||
let writer = std::thread::spawn(move || -> Result<(), String> {
|
||||
let mut file = std::io::BufWriter::new(file);
|
||||
@@ -405,6 +417,7 @@ fn run_inner(
|
||||
buf.extend_from_slice(&band);
|
||||
Ok(())
|
||||
},
|
||||
|| inscribed_for_writer.lock().ok().map(|i| i.best()),
|
||||
)
|
||||
.map_err(|e| e.to_string())
|
||||
});
|
||||
@@ -435,6 +448,9 @@ fn run_inner(
|
||||
|band| {
|
||||
bands_done += 1;
|
||||
progress("Merging", bands_done, total_bands);
|
||||
if let Ok(mut i) = inscribed.lock() {
|
||||
i.push_rows(band.covered, band.rows);
|
||||
}
|
||||
band_tx
|
||||
.send(band.rgb.to_vec())
|
||||
.map_err(|_| dr_gpu::GpuError::Readback("the writer stopped".into()))
|
||||
|
||||
Reference in New Issue
Block a user