publicdoublecurt_recur_w=0.25;// measurement weight w: buf = leak*(1-w)*shift(buf) + w*conditioned(meas) // By Claude on 06/09/2026
publicdoublecurt_recur_leak=1.0;// extra decay of the shifted prediction per update (gap decay = leak*(1-w) for fixed w) // By Claude on 06/09/2026
publicdoublecurt_recur_wthresh=0.0;// if >0: adaptive per-pixel weight w_eff=w*tanh(strength/wthresh); 0 - fixed w // By Claude on 06/09/2026
publicdoublecurt_recur_spread=0.5;// orthogonal 4D neighbor weight in adaptive spreading (diagonals get products) // By Claude on 06/09/2026
publicintcurt_recur_max_clust=5;// max 4D-connected survivor cluster size treated as one target (energy-normalized spreading); larger - linear pass-through // By Claude on 06/09/2026
publicdoublecurt_recur_spread=0.5;// predict-step velocity diffusion: 3-tap (spread,1,spread) normalized, lets the max migrate between velocity cells; <=0 - off // By Claude on 06/10/2026
publicintcurt_recur_max_clust=5;// max 4D-connected survivor cluster size treated as one target (half-cosine bump fit); larger - linear pass-through // By Claude on 06/10/2026
publicdoublecurt_recur_fit_rat=2.0;// max disagreement of per-survivor peak estimates (max/min) for the bump fit; failed fit - per-survivor single-point bumps // By Claude on 06/10/2026
publicdoublecurt_recur_rs_frac=0.5;// re-sharpen blob growth threshold as fraction of the blob max; <=0 - re-sharpening off // By Claude on 06/10/2026
publicdoublecurt_recur_rs_min=1.0;// re-sharpen minimal blob maximum to re-fit; weaker content passes through as residue // By Claude on 06/10/2026
// debug/saving images
publicbooleancurt_save_c5full=false;// save fine velocities [direction][scene][subpixels]
publicbooleancurt_save_c5rect=true;// save fine velocities for selected rectangle only [scene][flattened image]
...
...
@@ -3449,10 +3452,16 @@ min_str_neib_fpn 0.35
"Extra decay of the velocity-shifted prediction per update. Keep leak^n_gap >= 0.2-0.3 for expected dark gaps.");// By Claude on 06/09/2026
gd.addNumericField("Adaptive weight threshold",this.curt_recur_wthresh,6,8,"",// By Claude on 06/09/2026
"If >0: per-pixel adaptive weight w_eff = w*tanh(strength/wthresh), dark frames give w_eff~0 (pure prediction). 0 - fixed w.");// By Claude on 06/09/2026
gd.addNumericField("4D spreading weight",this.curt_recur_spread,6,8,"",// By Claude on 06/09/2026
"Weight of orthogonal 4D neighbors when spreading each surviving measurement point (diagonals get products of this).");// By Claude on 06/09/2026
gd.addNumericField("Velocity diffusion weight",this.curt_recur_spread,6,8,"",// By Claude on 06/10/2026
"Predict-step process noise: 3-tap (spread,1,spread) normalized diffusion over vx,vy after the shift. Lets the buffer maximum migrate between velocity cells (half-grid velocities). <=0 - off.");// By Claude on 06/10/2026
gd.addNumericField("Max target cluster size",this.curt_recur_max_clust,0,3,"",// By Claude on 06/09/2026
"Survivor clusters up to this size are one target: spread with total energy = cluster max (compensates non-energy-preserving filtering). Larger clusters (weak linear signal) pass through unchanged.");// By Claude on 06/09/2026
"Survivor clusters up to this size are one target: fit a 4D half-cosine bump (3-pix half-period, half-pixel-grid center) and inject its full grid sampling. Larger clusters (weak linear signal) pass through unchanged.");// By Claude on 06/10/2026
gd.addNumericField("Bump fit max peak ratio",this.curt_recur_fit_rat,6,8,"",// By Claude on 06/10/2026
"Reject the single-bump fit when per-survivor peak estimates (value/model_weight) disagree by more than this ratio; then each survivor injects its own single-point bump.");// By Claude on 06/10/2026
gd.addNumericField("Re-sharpen blob threshold fraction",this.curt_recur_rs_frac,6,8,"",// By Claude on 06/10/2026
"After merging the measurement, peel buffer blobs (4D-connected cells above this fraction of the blob max) and re-fit each as a canonical half-cosine at its fractional centroid, conserving total energy. <=0 - off.");// By Claude on 06/10/2026
gd.addNumericField("Re-sharpen min blob max",this.curt_recur_rs_min,6,8,"",// By Claude on 06/10/2026
"Stop peeling when the strongest remaining cell is below this (filter-output units); sub-threshold residue passes through unchanged.");// By Claude on 06/10/2026
gd.addMessage("=== Debug, saving images ===");
gd.addCheckbox("Save full velocities hyperstack",this.curt_save_c5full,
...
...
@@ -4966,6 +4975,9 @@ min_str_neib_fpn 0.35
this.curt_recur_wthresh=gd.getNextNumber();// By Claude on 06/09/2026
this.curt_recur_spread=gd.getNextNumber();// By Claude on 06/09/2026
this.curt_recur_max_clust=(int)gd.getNextNumber();// By Claude on 06/09/2026
this.curt_recur_fit_rat=gd.getNextNumber();// By Claude on 06/10/2026
this.curt_recur_rs_frac=gd.getNextNumber();// By Claude on 06/10/2026
this.curt_recur_rs_min=gd.getNextNumber();// By Claude on 06/10/2026
this.curt_save_c5full=gd.getNextBoolean();
this.curt_save_c5rect=gd.getNextBoolean();
...
...
@@ -6305,6 +6317,9 @@ min_str_neib_fpn 0.35
properties.setProperty(prefix+"curt_recur_wthresh",this.curt_recur_wthresh+"");// double // By Claude on 06/09/2026
properties.setProperty(prefix+"curt_recur_spread",this.curt_recur_spread+"");// double // By Claude on 06/09/2026
properties.setProperty(prefix+"curt_recur_max_clust",this.curt_recur_max_clust+"");// int // By Claude on 06/09/2026
properties.setProperty(prefix+"curt_recur_fit_rat",this.curt_recur_fit_rat+"");// double // By Claude on 06/10/2026
properties.setProperty(prefix+"curt_recur_rs_frac",this.curt_recur_rs_frac+"");// double // By Claude on 06/10/2026
properties.setProperty(prefix+"curt_recur_rs_min",this.curt_recur_rs_min+"");// double // By Claude on 06/10/2026
if(properties.getProperty(prefix+"curt_recur_wthresh")!=null)this.curt_recur_wthresh=Double.parseDouble(properties.getProperty(prefix+"curt_recur_wthresh"));// By Claude on 06/09/2026
if(properties.getProperty(prefix+"curt_recur_spread")!=null)this.curt_recur_spread=Double.parseDouble(properties.getProperty(prefix+"curt_recur_spread"));// By Claude on 06/09/2026
if(properties.getProperty(prefix+"curt_recur_max_clust")!=null)this.curt_recur_max_clust=Integer.parseInt(properties.getProperty(prefix+"curt_recur_max_clust"));// By Claude on 06/09/2026
if(properties.getProperty(prefix+"curt_recur_fit_rat")!=null)this.curt_recur_fit_rat=Double.parseDouble(properties.getProperty(prefix+"curt_recur_fit_rat"));// By Claude on 06/10/2026
if(properties.getProperty(prefix+"curt_recur_rs_frac")!=null)this.curt_recur_rs_frac=Double.parseDouble(properties.getProperty(prefix+"curt_recur_rs_frac"));// By Claude on 06/10/2026
if(properties.getProperty(prefix+"curt_recur_rs_min")!=null)this.curt_recur_rs_min=Double.parseDouble(properties.getProperty(prefix+"curt_recur_rs_min"));// By Claude on 06/10/2026