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 { ...@@ -88,8 +88,9 @@ public class CuasPoseRT {
/** /**
* Derive the tile selection from the per-tile max-residual calibration array * 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; * (-POSE-RT-MAXDXY: max over scenes of measured dxy; NaN = unusable - either not
* NaN = tile not measured). Two scale-free stages, both adapt to footage quality * 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): * and sequence length (no absolute pixel constants):
* 1 - robust outlier gate: keep finite values <= median + k_nmad*NMAD of the * 1 - robust outlier gate: keep finite values <= median + k_nmad*NMAD of the
* finite population (the "single high bump + long tail" histogram rule); * finite population (the "single high bump + long tail" histogram rule);
...@@ -201,12 +202,13 @@ public class CuasPoseRT { ...@@ -201,12 +202,13 @@ public class CuasPoseRT {
System.out.println("CuasPoseRT.testPoseSequence(): num_reliable="+num_reliable+ System.out.println("CuasPoseRT.testPoseSequence(): num_reliable="+num_reliable+
" (strength > "+min_str+")"); " (strength > "+min_str+")");
} }
// Two-pass workflow: optionally derive the selection from the -POSE-RT-MAXDXY // Automatic FPN-style calibration reuse: if -POSE-RT-MAXDXY exists (and not forced to
// calibration saved by a previous full run (per-tile max residual, +inf where any // recalc), derive the selection from it (per-tile max residual; NMAD outlier gate +
// scene was NaN) - NMAD outlier gate + rank-N budget, then AND with the strength mask. // 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 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 = clt_parameters.imp.curt_pose_recalc ? null :
final ImagePlus imp_max = center_CLT.readImagePlusFromModelDirectory("-POSE-RT-MAXDXY"); center_CLT.readImagePlusFromModelDirectory("-POSE-RT-MAXDXY");
if (imp_max != null) { if (imp_max != null) {
final float [] fmax = (float []) imp_max.getProcessor().getPixels(); final float [] fmax = (float []) imp_max.getProcessor().getPixels();
final boolean [] filt = deriveSelection( final boolean [] filt = deriveSelection(
...@@ -220,12 +222,12 @@ public class CuasPoseRT { ...@@ -220,12 +222,12 @@ public class CuasPoseRT {
if (reliable_ref[i]) num_filt++; if (reliable_ref[i]) num_filt++;
} }
full_selection = false; full_selection = false;
System.out.println("CuasPoseRT.testPoseSequence(): applied -POSE-RT-MAXDXY-derived selection, "+ System.out.println("CuasPoseRT.testPoseSequence(): reusing -POSE-RT-MAXDXY calibration, filtered to "+
num_filt+" tiles remain (of "+num_reliable+" by strength)"); num_filt+" tiles (of "+num_reliable+" by strength)");
} else { } else {
System.out.println("CuasPoseRT.testPoseSequence(): curt_pose_use_filt is ON but "+ System.out.println("CuasPoseRT.testPoseSequence(): "+
"-POSE-RT-MAXDXY.tiff not found - FULL run (will generate the calibration)"); (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( final double [][] pXpYD_center = OpticalFlow.transformToScenePxPyD(
null, // final Rectangle [] extra_woi, null, // final Rectangle [] extra_woi,
...@@ -477,7 +479,10 @@ public class CuasPoseRT { ...@@ -477,7 +479,10 @@ public class CuasPoseRT {
if (Float.isNaN(d)) has_nan = true; if (Float.isNaN(d)) has_nan = true;
else { has = true; if (d > mx) mx = d; } 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.saveImagePlusInModelDirectory(null, new ImagePlus(
center_CLT.getImageName()+"-POSE-RT-MAXDXY", new FloatProcessor(tilesX, tilesY, fmax))); center_CLT.getImageName()+"-POSE-RT-MAXDXY", new FloatProcessor(tilesX, tilesY, fmax)));
......
...@@ -1131,7 +1131,7 @@ min_str_neib_fpn 0.35 ...@@ -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_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 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 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 === //=== LoG prefilter ===
public double curt_psf_radius = 1.0; // sensor PSF radius for LoG pre-filter 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 public double curt_n_sigma = 4.0; // cutoff LoG kernel array, number of sigmas
...@@ -3429,8 +3429,8 @@ min_str_neib_fpn 0.35 ...@@ -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."); "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 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."); "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 gd.addCheckbox ("Pose test force recalc calibration", this.curt_pose_recalc, // 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)."); "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.addMessage("=== LoG prefilter ===");
gd.addNumericField("Optical PSF radius", this.curt_psf_radius, 6,8,"pix", gd.addNumericField("Optical PSF radius", this.curt_psf_radius, 6,8,"pix",
...@@ -5027,7 +5027,7 @@ min_str_neib_fpn 0.35 ...@@ -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_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_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_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_psf_radius = gd.getNextNumber();
this.curt_n_sigma = gd.getNextNumber(); this.curt_n_sigma = gd.getNextNumber();
...@@ -6398,7 +6398,7 @@ min_str_neib_fpn 0.35 ...@@ -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_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_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_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_psf_radius", this.curt_psf_radius+""); // double
properties.setProperty(prefix+"curt_n_sigma", this.curt_n_sigma+""); // double properties.setProperty(prefix+"curt_n_sigma", this.curt_n_sigma+""); // double
...@@ -6803,7 +6803,7 @@ min_str_neib_fpn 0.35 ...@@ -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_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_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_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_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")); 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 ...@@ -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_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_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_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_psf_radius = this.curt_psf_radius;
imp.curt_n_sigma = this.curt_n_sigma; 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 // 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