Commit 689efd90 authored by Andrey Filippov's avatar Andrey Filippov

CLAUDE: per-SEQUENCE zero-DC level - fix rotation-synced DC flicker (the rotating ghost)

POSE-10 forensics: RT-RENDER merged horizon pk-pk 234 counts (sky 187) with
the dominant period 165.7 scenes = EXACTLY one camera revolution (21.83 rpm
@ 60 fps -> 164.9); POSE-RT-COMPOSITE real slices the same. Cause: the
per-SCENE zero-DC sky median (Config.dc_level=NaN auto) oscillates with
azimuth as warm terrain sweeps through the sky half; downstream temporal
averages convert the wobble into content-correlated residuals - the faint
rotating ghost ('real image treated as FPN'). Oracle MERGED-DBG shows a
separate, 3-6x smaller wobble at the same period (pre-existing, prepared
path - not addressed here).

Fix (Andrey's rule: one constant per sequence, from the average scene):
- CuasRT.sequenceDcLevel(): manual curt.dc_center override when nonzero,
  else the RT-STATE value derived by appropriateCenter() from the borrowed
  center (= the sequence average) - scenes and center shift by the
  IDENTICAL constant.
- CuasPoseRT (A2 ingest) + CuasRender (render test) now pass a Config with
  that dc_level instead of null; NaN fallback (per-scene) kept but warns
  loudly. Config.dc_level doc updated.
Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
parent 424ac6f9
...@@ -66,7 +66,7 @@ public class CuasConditioning { ...@@ -66,7 +66,7 @@ public class CuasConditioning {
// NaN = AUTO (the global mean over all sensors of this scene). A calibration- // NaN = AUTO (the global mean over all sensors of this scene). A calibration-
// maintained fixed level (no scene-to-scene DC flicker) is the production target. // maintained fixed level (no scene-to-scene DC flicker) is the production target.
public boolean zero_dc = true; // By Claude on 07/05/2026 public boolean zero_dc = true; // By Claude on 07/05/2026
public double dc_level = Double.NaN; // NaN = auto (per-scene global mean) // By Claude on 07/05/2026 public double dc_level = Double.NaN; // the PER-SEQUENCE constant (CuasRT.sequenceDcLevel - RT-STATE/appropriateCenter). NaN = per-scene sky median FALLBACK ONLY: it oscillates with rotation (terrain sweeps the sky half; 234-count pk-pk @ 1 rev period, POSE-10) and downstream temporal averages turn that into content ghosts. // By Claude on 07/05/2026
public Config() {} public Config() {}
} }
......
...@@ -742,6 +742,19 @@ public class CuasPoseRT { ...@@ -742,6 +742,19 @@ public class CuasPoseRT {
center_CLT.getGeometryCorrection().pixelSize; center_CLT.getGeometryCorrection().pixelSize;
final double [] px_per_rad = {px_per_rad_at, px_per_rad_at, px_per_rad_r}; final double [] px_per_rad = {px_per_rad_at, px_per_rad_at, px_per_rad_r};
// PER-SEQUENCE DC level for the A2 scene conditioning - NOT per-scene: a per-scene
// sky median oscillates with rotation (terrain sweeps the sky half; 234-count pk-pk
// at exactly 1 rev period, POSE-10 forensics) and downstream temporal averages turn
// it into content ghosts. ONE constant (CuasRT.appropriateCenter derived it from
// the borrowed center = the average scene). By Claude on 07/05/2026, Andrey's rule.
final CuasConditioning.Config cond_cfg = new CuasConditioning.Config();
if (clt_parameters.curt.pose_raw) {
cond_cfg.dc_level = CuasRT.sequenceDcLevel(clt_parameters, center_CLT);
if (Double.isNaN(cond_cfg.dc_level)) {
System.out.println("CuasPoseRT: WARNING - no per-sequence DC level (RT-STATE missing, curt.dc_center==0):"+
" falling back to the PER-SCENE sky median (rotation-synced DC flicker!)");
}
}
// ---- Ascending per-scene loop (the RT iterator) ---- // ---- Ascending per-scene loop (the RT iterator) ----
final int tilesX = center_CLT.getTilesX(); final int tilesX = center_CLT.getTilesX();
final int tilesY = center_CLT.getTilesY(); final int tilesY = center_CLT.getTilesY();
...@@ -861,7 +874,7 @@ public class CuasPoseRT { ...@@ -861,7 +874,7 @@ public class CuasPoseRT {
if (clt_parameters.curt.pose_raw) { if (clt_parameters.curt.pose_raw) {
final boolean raw_ok = CuasConditioning.conditionSceneToGpu( final boolean raw_ok = CuasConditioning.conditionSceneToGpu(
quadCLTs[nscene], // QuadCLT scene (jp4 + calibration + FPN) quadCLTs[nscene], // QuadCLT scene (jp4 + calibration + FPN)
null, // Config (defaults: rowcol + photometric + FPN) cond_cfg, // Config: defaults + the PER-SEQUENCE dc_level // By Claude on 07/05/2026
ImageDtt.THREADS_MAX, // read threads ImageDtt.THREADS_MAX, // read threads
debugLevel - 2); // debug debugLevel - 2); // debug
if (!raw_ok) { if (!raw_ok) {
......
...@@ -306,6 +306,29 @@ public class CuasRT { ...@@ -306,6 +306,29 @@ public class CuasRT {
") - downstream consumers see the appropriated center only"); ") - downstream consumers see the appropriated center only");
} }
/**
* Resolve the PER-SEQUENCE DC level for scene conditioning. The fix for the
* rotation-synced DC flicker (POSE-10 forensics, 07/05/2026): a per-SCENE sky median
* oscillates with azimuth as warm terrain sweeps through the sky half - RT-RENDER
* horizon pk-pk 234 counts with the period exactly one camera revolution, and any
* temporal-average step downstream converts that wobble into content-correlated
* residuals ("real image treated as FPN" ghosts). ONE constant per sequence instead
* (Andrey's rule: from the average scene): manual curt.dc_center override when
* nonzero, else the RT-STATE value derived by {@link #appropriateCenter} from the
* borrowed center (which IS the sequence average) - so the scenes and the center
* shift by the IDENTICAL constant.
* @return the level, or NaN when neither source is available (conditioning then
* falls back to the per-scene median - callers warn loudly).
* By Claude on 07/05/2026.
*/
public static double sequenceDcLevel(
final CLTParameters clt_parameters,
final QuadCLT center_CLT) {
if (clt_parameters.curt.dc_center != 0.0) return clt_parameters.curt.dc_center;
final CuasRtState state = CuasRtState.load(center_CLT.getX3dDirectory(), center_CLT.getImageName());
return (state.dc_center != null) ? state.dc_center : Double.NaN;
}
/** /**
* Optional EXCLUSIVE diagnostic modes - each replaces the production run. Precedence * Optional EXCLUSIVE diagnostic modes - each replaces the production run. Precedence
* (unchanged from the OpticalFlow inline era): rend_test > pose_test > cond_test. * (unchanged from the OpticalFlow inline era): rend_test > pose_test > cond_test.
......
...@@ -276,6 +276,18 @@ public class CuasRender { ...@@ -276,6 +276,18 @@ public class CuasRender {
System.out.println("testRenderSequence(): motion-blur compensation "+ System.out.println("testRenderSequence(): motion-blur compensation "+
(mb_en ? ("ON (tau="+mb_tau+" s, max gain "+mb_max_gain+") - compare vs the MB oracle DBG") : (mb_en ? ("ON (tau="+mb_tau+" s, max gain "+mb_max_gain+") - compare vs the MB oracle DBG") :
"OFF - compare vs the -NOMB oracle DBG")); "OFF - compare vs the -NOMB oracle DBG"));
// PER-SEQUENCE DC level for the scene conditioning - NOT per-scene: a per-scene sky
// median oscillates with rotation (terrain sweeps the sky half; 234-count pk-pk at
// exactly 1 rev period measured in THIS product, POSE-10 forensics) and downstream
// temporal averages turn it into content ghosts. ONE constant per sequence
// (CuasRT.appropriateCenter derived it from the borrowed center = the average
// scene). By Claude on 07/05/2026, Andrey's rule.
final CuasConditioning.Config cond_cfg = new CuasConditioning.Config();
cond_cfg.dc_level = CuasRT.sequenceDcLevel(clt_parameters, center_CLT);
if (Double.isNaN(cond_cfg.dc_level)) {
System.out.println("testRenderSequence(): WARNING - no per-sequence DC level (RT-STATE missing, curt.dc_center==0):"+
" falling back to the PER-SCENE sky median (rotation-synced DC flicker!)");
}
final ArrayList<float [][]> rendered = new ArrayList<float [][]>(); final ArrayList<float [][]> rendered = new ArrayList<float [][]>();
final ArrayList<String> ts_names = new ArrayList<String>(); final ArrayList<String> ts_names = new ArrayList<String>();
final float [] nan_slice = new float [img_width * img_height]; final float [] nan_slice = new float [img_width * img_height];
...@@ -301,7 +313,7 @@ public class CuasRender { ...@@ -301,7 +313,7 @@ public class CuasRender {
// borrowed (stored); in production they come analytically from pose + rpm. // borrowed (stored); in production they come analytically from pose + rpm.
final boolean raw_ok = CuasConditioning.conditionSceneToGpuCuas( final boolean raw_ok = CuasConditioning.conditionSceneToGpuCuas(
quadCLTs[nscene], quadCLTs[nscene],
null, // Config (defaults: rowcol + photometric + FPN) cond_cfg, // Config: defaults + the PER-SEQUENCE dc_level // By Claude on 07/05/2026
(ers_atr_dt != null) ? ers_atr_dt : ZERO3.clone(), // omegas (ers_atr_dt != null) ? ers_atr_dt : ZERO3.clone(), // omegas
(ers_xyz_dt != null) ? ers_xyz_dt : ZERO3.clone(), // velocities (provision) (ers_xyz_dt != null) ? ers_xyz_dt : ZERO3.clone(), // velocities (provision)
ImageDtt.THREADS_MAX, ImageDtt.THREADS_MAX,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment