Commit 1ff4add6 authored by Andrey Filippov's avatar Andrey Filippov

CLAUDE: CuasPoseRT: automatic MAXDXY reuse (FPN-style) + NaN (not +inf) for viewable calibration

Per Andrey: (1) calibration reuse is now automatic - if -POSE-RT-MAXDXY exists
it is used (filtered run), else a full run generates it; new
curt_pose_recalc flag forces regeneration (replaces the backwards
curt_pose_use_filt enable). Matches the FPN reuse pattern. (2) MAXDXY stores
NaN instead of +inf for NaN-in-any-scene tiles - deriveSelection rejects NaN
and +inf identically (non-finite), and NaN keeps the TIFF viewable in ImageJ
(+inf broke min/max autoscaling).

mvn compile clean (Eyesis closed).
Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
parent abca0375
......@@ -88,8 +88,9 @@ public class CuasPoseRT {
/**
* Derive the tile selection from the per-tile max-residual calibration array
* (-POSE-RT-MAXDXY: max over scenes of measured dxy; +inf = NaN in some scene;
* NaN = tile not measured). Two scale-free stages, both adapt to footage quality
* (-POSE-RT-MAXDXY: max over scenes of measured dxy; NaN = unusable - either not
* selected/measured or NaN in at least one scene, always rejected). Two scale-free
* stages, both adapt to footage quality
* and sequence length (no absolute pixel constants):
* 1 - robust outlier gate: keep finite values <= median + k_nmad*NMAD of the
* finite population (the "single high bump + long tail" histogram rule);
......@@ -201,31 +202,32 @@ public class CuasPoseRT {
System.out.println("CuasPoseRT.testPoseSequence(): num_reliable="+num_reliable+
" (strength > "+min_str+")");
}
// Two-pass workflow: optionally derive the selection from the -POSE-RT-MAXDXY
// calibration saved by a previous full run (per-tile max residual, +inf where any
// scene was NaN) - NMAD outlier gate + rank-N budget, then AND with the strength mask.
// Automatic FPN-style calibration reuse: if -POSE-RT-MAXDXY exists (and not forced to
// recalc), derive the selection from it (per-tile max residual; NMAD outlier gate +
// rank-N budget) and AND with the strength mask - a filtered run. Otherwise run FULL
// (all strength-selected tiles) and generate the calibration at the end.
boolean full_selection = true; // MAXDXY calibration is (re)saved only from a full-selection run
if (clt_parameters.imp.curt_pose_use_filt) {
final ImagePlus imp_max = center_CLT.readImagePlusFromModelDirectory("-POSE-RT-MAXDXY");
if (imp_max != null) {
final float [] fmax = (float []) imp_max.getProcessor().getPixels();
final boolean [] filt = deriveSelection(
fmax,
clt_parameters.imp.curt_pose_dxy_k,
clt_parameters.imp.curt_pose_num_tiles,
debugLevel);
int num_filt = 0;
for (int i = 0; i < reliable_ref.length; i++) {
reliable_ref[i] &= (i < filt.length) && filt[i];
if (reliable_ref[i]) num_filt++;
}
full_selection = false;
System.out.println("CuasPoseRT.testPoseSequence(): applied -POSE-RT-MAXDXY-derived selection, "+
num_filt+" tiles remain (of "+num_reliable+" by strength)");
} else {
System.out.println("CuasPoseRT.testPoseSequence(): curt_pose_use_filt is ON but "+
"-POSE-RT-MAXDXY.tiff not found - FULL run (will generate the calibration)");
final ImagePlus imp_max = clt_parameters.imp.curt_pose_recalc ? null :
center_CLT.readImagePlusFromModelDirectory("-POSE-RT-MAXDXY");
if (imp_max != null) {
final float [] fmax = (float []) imp_max.getProcessor().getPixels();
final boolean [] filt = deriveSelection(
fmax,
clt_parameters.imp.curt_pose_dxy_k,
clt_parameters.imp.curt_pose_num_tiles,
debugLevel);
int num_filt = 0;
for (int i = 0; i < reliable_ref.length; i++) {
reliable_ref[i] &= (i < filt.length) && filt[i];
if (reliable_ref[i]) num_filt++;
}
full_selection = false;
System.out.println("CuasPoseRT.testPoseSequence(): reusing -POSE-RT-MAXDXY calibration, filtered to "+
num_filt+" tiles (of "+num_reliable+" by strength)");
} else {
System.out.println("CuasPoseRT.testPoseSequence(): "+
(clt_parameters.imp.curt_pose_recalc ? "curt_pose_recalc ON" : "-POSE-RT-MAXDXY not found")+
" - FULL run, will generate the calibration");
}
final double [][] pXpYD_center = OpticalFlow.transformToScenePxPyD(
null, // final Rectangle [] extra_woi,
......@@ -477,7 +479,10 @@ public class CuasPoseRT {
if (Float.isNaN(d)) has_nan = true;
else { has = true; if (d > mx) mx = d; }
}
fmax[i] = has_nan ? Float.POSITIVE_INFINITY : (has ? mx : Float.NaN);
// NaN (not +inf) where any scene was NaN: deriveSelection rejects NaN and +inf
// identically (non-finite), and NaN keeps the saved TIFF viewable in ImageJ
// (+inf breaks min/max autoscaling). Unmeasured/unselected tiles are also NaN.
fmax[i] = (has_nan || !has) ? Float.NaN : mx;
}
center_CLT.saveImagePlusInModelDirectory(null, new ImagePlus(
center_CLT.getImageName()+"-POSE-RT-MAXDXY", new FloatProcessor(tilesX, tilesY, fmax)));
......
......@@ -1131,7 +1131,7 @@ min_str_neib_fpn 0.35
public double curt_pose_str = 1.0; // reliable-tile strength threshold over the combo-DSI strength for the pose test tile selection (1.0 ~ old getReliableTiles population). // By Claude on 07/03/2026
public double curt_pose_dxy_k = 0.75; // tile-selection outlier gate: keep tiles with max-over-scenes residual <= median + k*NMAD of the finite per-tile maxes (scale-free - adapts to footage quality/sequence length; NaN-in-any-scene = +inf, always rejected). <=0 - skip the gate. // By Claude on 07/04/2026
public int curt_pose_num_tiles = 150; // tile-selection compute budget: after the gate, keep this many BEST (smallest max-residual) tiles; threshold-free rank - always yields the best available population. <=0 - no cap. // By Claude on 07/04/2026
public boolean curt_pose_use_filt = false; // use the previously saved -POSE-RT-MAXDXY calibration (per-tile max residual, +inf where NaN): derive the selection (NMAD gate + rank-N) and AND with the strength threshold. Two-pass workflow: full run generates the calibration, next runs use it. // By Claude on 07/04/2026
public boolean curt_pose_recalc = false; // force a FULL-selection pose run that regenerates the -POSE-RT-MAXDXY calibration even if it exists. Default (false): automatic FPN-style reuse - if -POSE-RT-MAXDXY exists, derive the selection from it (NMAD gate + rank-N) and use it; if not, run full and generate it. // By Claude on 07/04/2026
//=== LoG prefilter ===
public double curt_psf_radius = 1.0; // sensor PSF radius for LoG pre-filter
public double curt_n_sigma = 4.0; // cutoff LoG kernel array, number of sigmas
......@@ -3429,8 +3429,8 @@ min_str_neib_fpn 0.35
"Keep tiles with max-over-scenes residual <= median + k*NMAD of finite per-tile maxes (scale-free; NaN in any scene always rejected). <=0 - skip the gate.");
gd.addNumericField("Pose test number of best tiles", this.curt_pose_num_tiles, 0,7,"", // By Claude on 07/04/2026
"After the gate keep this many best (smallest max-residual) tiles - the RT compute budget. <=0 - no cap.");
gd.addCheckbox ("Pose test use filtered selection", this.curt_pose_use_filt, // By Claude on 07/04/2026
"Load the previously saved -POSE-RT-MAXDXY.tiff calibration, derive the selection (NMAD gate + rank-N) and AND with the strength selection (two-pass workflow).");
gd.addCheckbox ("Pose test force recalc calibration", this.curt_pose_recalc, // By Claude on 07/04/2026
"Force a FULL pose run that regenerates -POSE-RT-MAXDXY even if it exists. Default OFF = automatic reuse: use the calibration if present (filtered run), else run full and generate it.");
gd.addMessage("=== LoG prefilter ===");
gd.addNumericField("Optical PSF radius", this.curt_psf_radius, 6,8,"pix",
......@@ -5027,7 +5027,7 @@ min_str_neib_fpn 0.35
this.curt_pose_str = gd.getNextNumber(); // By Claude on 07/03/2026
this.curt_pose_dxy_k = gd.getNextNumber(); // By Claude on 07/04/2026
this.curt_pose_num_tiles =(int) gd.getNextNumber(); // By Claude on 07/04/2026
this.curt_pose_use_filt = gd.getNextBoolean(); // By Claude on 07/04/2026
this.curt_pose_recalc = gd.getNextBoolean(); // By Claude on 07/04/2026
this.curt_psf_radius = gd.getNextNumber();
this.curt_n_sigma = gd.getNextNumber();
......@@ -6398,7 +6398,7 @@ min_str_neib_fpn 0.35
properties.setProperty(prefix+"curt_pose_str", this.curt_pose_str+""); // double // By Claude on 07/03/2026
properties.setProperty(prefix+"curt_pose_dxy_k", this.curt_pose_dxy_k+""); // double // By Claude on 07/04/2026
properties.setProperty(prefix+"curt_pose_num_tiles", this.curt_pose_num_tiles+""); // int // By Claude on 07/04/2026
properties.setProperty(prefix+"curt_pose_use_filt", this.curt_pose_use_filt+""); // boolean // By Claude on 07/04/2026
properties.setProperty(prefix+"curt_pose_recalc", this.curt_pose_recalc+""); // boolean // By Claude on 07/04/2026
properties.setProperty(prefix+"curt_psf_radius", this.curt_psf_radius+""); // double
properties.setProperty(prefix+"curt_n_sigma", this.curt_n_sigma+""); // double
......@@ -6803,7 +6803,7 @@ min_str_neib_fpn 0.35
if (properties.getProperty(prefix+"curt_pose_str")!=null) this.curt_pose_str=Double.parseDouble(properties.getProperty(prefix+"curt_pose_str")); // By Claude on 07/03/2026
if (properties.getProperty(prefix+"curt_pose_dxy_k")!=null) this.curt_pose_dxy_k=Double.parseDouble(properties.getProperty(prefix+"curt_pose_dxy_k")); // By Claude on 07/04/2026
if (properties.getProperty(prefix+"curt_pose_num_tiles")!=null) this.curt_pose_num_tiles=Integer.parseInt(properties.getProperty(prefix+"curt_pose_num_tiles")); // By Claude on 07/04/2026
if (properties.getProperty(prefix+"curt_pose_use_filt")!=null) this.curt_pose_use_filt=Boolean.parseBoolean(properties.getProperty(prefix+"curt_pose_use_filt")); // By Claude on 07/04/2026
if (properties.getProperty(prefix+"curt_pose_recalc")!=null) this.curt_pose_recalc=Boolean.parseBoolean(properties.getProperty(prefix+"curt_pose_recalc")); // By Claude on 07/04/2026
if (properties.getProperty(prefix+"curt_psf_radius")!=null) this.curt_psf_radius=Double.parseDouble(properties.getProperty(prefix+"curt_psf_radius"));
if (properties.getProperty(prefix+"curt_n_sigma")!=null) this.curt_n_sigma=Double.parseDouble(properties.getProperty(prefix+"curt_n_sigma"));
......@@ -9093,7 +9093,7 @@ min_str_neib_fpn 0.35
imp.curt_pose_str = this.curt_pose_str; // By Claude on 07/03/2026
imp.curt_pose_dxy_k = this.curt_pose_dxy_k; // By Claude on 07/04/2026
imp.curt_pose_num_tiles = this.curt_pose_num_tiles; // By Claude on 07/04/2026
imp.curt_pose_use_filt = this.curt_pose_use_filt; // By Claude on 07/04/2026
imp.curt_pose_recalc = this.curt_pose_recalc; // By Claude on 07/04/2026
imp.curt_psf_radius = this.curt_psf_radius;
imp.curt_n_sigma = this.curt_n_sigma;
// rleak0 imp.X copy removed 2026-06-20 (LReLU now LINEAR); predecessor code at git tag cuas-layer1. // By Claude on 06/20/2026
......
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