Commit ee86f0fc authored by Andrey Filippov's avatar Andrey Filippov

CLAUDE: CUAS-RT overhaul - real-base synthetic-grid injection, level/time...

CLAUDE: CUAS-RT overhaul - real-base synthetic-grid injection, level/time ROIs, 3d3 bypass, DNN offset/stride/window

Reworks the synthetic B-measurement and detector flow on the real pyramid base:

- Load: the real -CUAS-MERGED-CUAS stack is always the pyramid base (full-length
  levels); when curt_synth_src, the synthetic grid is loaded separately (scaled,
  NOT LoG'd) and injected TILED into each selected pyramid level AFTER the pure
  pyramid is built - same grid at every level, real noise averaged (sqrt(2)/level,
  printed). Both file picks use newestFile(). The old load-time mixing and the
  curt_synth_bg_avg (N=) decimation are retired.
- Level selection unified on curt_c5_levels for ALL heavy paths (3d3/conv5d,
  C5P-direct, DNN); cheap temporalAverage pyramid still builds up to the max
  selected level. Pyramid-depth guard stops building when a level would have <1
  frame (was NegativeArraySizeException on short sequences).
- Timing ROI curt_time_from/to (timestamp seconds; last-4-of-seconds shortcut,
  rollover-aware, 3-decimal fixed display) clips saved RECT/HYPER/DNN/C5P stacks
  and gates the DNN inference (compute-window, not just save).
- curt_3d3_en (default false) bypasses the 3d3 coarse-velocity path and its
  ~W*H*9 arrays. Full-velocities hyperstack render is guarded by a free-heap
  estimate (it is also an unfilled stub) and skipped if it would not fit.
- DNN: curt_dnn_stride (1 testing / 4 production 50%-overlap), and -DNN-OFFSET
  visualization (dx, dy, s over the ROI) from the offset channels.
Co-authored-by: 's avatarClaude Opus 4.8 (1M context) <noreply@anthropic.com>
parent f2b3b3b5
...@@ -48,6 +48,7 @@ public class CuasDetectRT { ...@@ -48,6 +48,7 @@ public class CuasDetectRT {
int width; int width;
int height; int height;
String [] time_stamps; String [] time_stamps;
float [][] synth_pixels = null; // synthetic reference grid (scaled, NOT LoG'd), injected per pyramid level; null = real-only // By Claude on 06/14/2026
double infinity; double infinity;
public CuasDetectRT( public CuasDetectRT(
...@@ -59,17 +60,19 @@ public class CuasDetectRT { ...@@ -59,17 +60,19 @@ public class CuasDetectRT {
this.clt_parameters = clt_parameters; this.clt_parameters = clt_parameters;
this.model_directory = model_directory; this.model_directory = model_directory;
this.infinity = clt_parameters.imp.cuas_infinity; this.infinity = clt_parameters.imp.cuas_infinity;
final String fpixels_suffix = clt_parameters.imp.curt_synth_src ? SUFFIX_SYNTH_TIFF : SUFFIX_FPIXELS_TIFF; // By Claude on 06/11/2026 // The REAL stack is always the pyramid base (gives the full-length levels); the synthetic // By Claude on 06/14/2026
// reference grid (if curt_synth_src) is loaded separately below and injected per-level.
final String fpixels_suffix = SUFFIX_FPIXELS_TIFF; // By Claude on 06/14/2026
String [] fpixels_paths = CorrectionParameters.getFilesByExtensionAsArray(model_directory, fpixels_suffix); // By Claude on 06/11/2026 String [] fpixels_paths = CorrectionParameters.getFilesByExtensionAsArray(model_directory, fpixels_suffix); // By Claude on 06/11/2026
String [] dbg_slices=null; String [] dbg_slices=null;
float [][] dbg_pixels=null; float [][] dbg_pixels=null;
boolean save_copy = false; // true; boolean save_copy = false; // true;
if (fpixels_paths.length > 0) { if (fpixels_paths.length > 0) {
fpixels_file = newestFile(fpixels_paths); // pick up the newest matching file // By Claude on 06/14/2026
if (fpixels_paths.length > 1) { if (fpixels_paths.length > 1) {
System.out.println((fpixels_paths.length)+" files ending with \""+fpixels_suffix+"\" found in "+model_directory+", will use the first one."); // By Claude on 06/11/2026 System.out.println((fpixels_paths.length)+" files ending with \""+fpixels_suffix+"\" found in "+model_directory+", using the newest: "+fpixels_file); // By Claude on 06/14/2026
} }
fpixels_file = fpixels_paths[0];
ImagePlus imp = new ImagePlus(fpixels_file); ImagePlus imp = new ImagePlus(fpixels_file);
width = imp.getWidth(); width = imp.getWidth();
height = imp.getHeight(); height = imp.getHeight();
...@@ -95,84 +98,34 @@ public class CuasDetectRT { ...@@ -95,84 +98,34 @@ public class CuasDetectRT {
} }
String fpixels_name = Path.of(fpixels_file).getFileName().toString(); String fpixels_name = Path.of(fpixels_file).getFileName().toString();
base_name = fpixels_name.substring(0,fpixels_name.length() - fpixels_suffix.length()); // By Claude on 06/11/2026 base_name = fpixels_name.substring(0,fpixels_name.length() - fpixels_suffix.length()); // By Claude on 06/11/2026
if (clt_parameters.imp.curt_synth_src) { // mark all output titles - synthetic and real runs share the directory // By Claude on 06/11/2026 if (clt_parameters.imp.curt_synth_src) { // load the synthetic reference grid separately (NOT mixed here) // By Claude on 06/14/2026
// Runtime mixing: synthetic file holds NORMALIZED (peak 1) clean targets; // By Claude on 06/12/2026 // Real (loaded above) is the pyramid base. The synthetic file holds NORMALIZED
// scale to the wanted peak (counts) and optionally add the real scene // (peak 1) clean targets / a velocity-reference grid: load + scale it, keep it
// (label-matched frames of *-CUAS-MERGED-CUAS.tiff) as background. // aside (NO LoG - it has no slowly-varying background), and inject it TILED into
// each pyramid level just before conv5d/DNN, so it is never averaged across levels
// and looks the same at every level while the real noise is averaged (SNR up ~sqrt2/level).
double synth_scale = clt_parameters.imp.curt_synth_scale; double synth_scale = clt_parameters.imp.curt_synth_scale;
boolean synth_bg = clt_parameters.imp.curt_synth_bg; String [] synth_paths = CorrectionParameters.getFilesByExtensionAsArray(model_directory, SUFFIX_SYNTH_TIFF);
for (int i = 0; i < fpixels.length; i++) { if (synth_paths.length > 0) {
float [] fpix_scaled = new float [fpixels[i].length]; // do not modify the stack's arrays String synth_file = newestFile(synth_paths); // pick up the newest synthetic // By Claude on 06/14/2026
for (int pix = 0; pix < fpixels[i].length; pix++) { ImagePlus imp_s = new ImagePlus(synth_file);
fpix_scaled[pix] = (float) (fpixels[i][pix] * synth_scale); ImageStack ss = imp_s.getStack();
} int s_first = 1;
fpixels[i] = fpix_scaled; for (; !Character.isDigit(ss.getSliceLabel(s_first).charAt(0)); s_first++);
} int n_synth = ss.getSize() - s_first + 1;
int bg_avg = Math.max(1, clt_parameters.imp.curt_synth_bg_avg); // By Claude on 06/13/2026 synth_pixels = new float [n_synth][];
if (synth_bg) { for (int i = 0; i < n_synth; i++) {
String [] real_paths = CorrectionParameters.getFilesByExtensionAsArray(model_directory, SUFFIX_FPIXELS_TIFF); float [] sp = (float[]) ss.getPixels(s_first + i);
if (real_paths.length > 0) { float [] sc = new float [sp.length];
ImagePlus imp_real = new ImagePlus(real_paths[0]); for (int k = 0; k < sp.length; k++) sc[k] = (float)(sp[k] * synth_scale);
ImageStack real_stack = imp_real.getStack(); synth_pixels[i] = sc;
java.util.HashMap<String,Integer> real_idx = new java.util.HashMap<String,Integer>();
for (int sl = 1; sl <= real_stack.getSize(); sl++) {
real_idx.put(real_stack.getSliceLabel(sl), sl);
}
if (bg_avg <= 1) { // By Claude on 06/13/2026 (original label-matched path)
int n_added = 0;
for (int i = 0; i < time_stamps.length; i++) {
Integer sl = real_idx.get(time_stamps[i]);
if (sl != null) {
float [] real_pix = (float[]) real_stack.getPixels(sl);
for (int pix = 0; pix < fpixels[i].length; pix++) {
fpixels[i][pix] += real_pix[pix];
}
n_added++;
}
}
System.out.println("Synthetic mixing: scale "+synth_scale+", real background added to "+
n_added+" of "+time_stamps.length+" frames from "+real_paths[0]);
} else { // By Claude on 06/13/2026
// Pyramid-level emulation by DECIMATION: output frame i is the mean of
// bg_avg consecutive frames [i*N .. i*N+N-1] of BOTH the scaled synthetic
// target AND the real background (non-overlapping groups). This matches
// level log2(N): the sequence is DECIMATED by N (fewer output frames), the
// moving target is motion-averaged like the real pyramid, and the noise
// statistics are those of an N-frame average. Timestamp = LAST real frame
// of each group (pyramid-level convention), so labels increment by N/fps.
Integer sl0 = real_idx.get(time_stamps[0]);
if (sl0 == null) sl0 = 1; // fall back to the start of the real stack
int n_synth = time_stamps.length; // By Claude on 06/13/2026
int npix_frame = width * height;
int real_groups = (real_stack.getSize() - sl0 + 1) / bg_avg;
int num_out = Math.min(n_synth / bg_avg, real_groups); // complete groups only
float [][] dec_pixels = new float [num_out][npix_frame];
String [] dec_ts = new String [num_out];
double [] acc = new double [npix_frame];
for (int i = 0; i < num_out; i++) {
java.util.Arrays.fill(acc, 0.0);
for (int n = 0; n < bg_avg; n++) {
float [] syn_pix = fpixels[i * bg_avg + n]; // already scaled by synth_scale
float [] real_pix = (float[]) real_stack.getPixels(sl0 + i * bg_avg + n);
for (int pix = 0; pix < npix_frame; pix++) acc[pix] += syn_pix[pix] + real_pix[pix];
}
for (int pix = 0; pix < npix_frame; pix++) dec_pixels[i][pix] = (float) (acc[pix] / bg_avg);
dec_ts[i] = real_stack.getSliceLabel(sl0 + i * bg_avg + bg_avg - 1);
}
fpixels = dec_pixels;
time_stamps = dec_ts;
System.out.println("Synthetic mixing: scale "+synth_scale+", pyramid-level emulation by decimation x"+bg_avg+
" (mean of "+bg_avg+" consecutive synth+real frames per output frame), "+
num_out+" output frames from "+n_synth+" synthetic / "+real_stack.getSize()+" real");
}
} else {
System.out.println("Synthetic mixing: real background requested but no \""+SUFFIX_FPIXELS_TIFF+"\" found - synthetic only");
synth_bg = false;
} }
System.out.println("Synthetic grid: loaded "+n_synth+" frames (scale "+synth_scale+", no LoG) from "+synth_file+
" - injected per pyramid level, tiled (synth[j % "+n_synth+"])");
} else { } else {
System.out.println("Synthetic mixing: scale "+synth_scale+", no background (clean targets)"); System.out.println("curt_synth_src set but no \""+SUFFIX_SYNTH_TIFF+"\" found in "+model_directory+" - real only");
} }
base_name += "-SYNTH"+d2s(synth_scale)+(synth_bg ? "B" : "")+((synth_bg && (bg_avg > 1)) ? ("AVG"+bg_avg) : ""); // By Claude on 06/13/2026 base_name += "-SYNTH"+d2s(synth_scale)+"B"; // real is always the averaged background base // By Claude on 06/14/2026
} }
System.out.println("Read image data from "+fpixels_file); System.out.println("Read image data from "+fpixels_file);
} else { } else {
...@@ -227,6 +180,82 @@ public class CuasDetectRT { ...@@ -227,6 +180,82 @@ public class CuasDetectRT {
public static String now() { // By Claude on 06/11/2026 public static String now() { // By Claude on 06/11/2026
return new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(Calendar.getInstance().getTime()); return new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(Calendar.getInstance().getTime());
} }
/** Pick the most recently modified file among matches ("pick up the new one"). // By Claude on 06/14/2026 */
private static String newestFile(String [] paths) {
if ((paths == null) || (paths.length == 0)) return null;
String best = paths[0];
long bestT = new java.io.File(best).lastModified();
for (int i = 1; i < paths.length; i++) {
long t = new java.io.File(paths[i]).lastModified();
if (t > bestT) { bestT = t; best = paths[i]; }
}
return best;
}
/** Parse a slice-label timestamp "sec_micro..." (ignoring any " f<idx>" / "-N" tail) to seconds. // By Claude on 06/14/2026 */
private static double parseTs(String label) {
if (label == null) return Double.NaN;
try {
int us = label.indexOf('_');
if (us < 0) {
int i = 0; while ((i < label.length()) && Character.isDigit(label.charAt(i))) i++;
return (i > 0) ? Double.parseDouble(label.substring(0, i)) : Double.NaN;
}
long sec = Long.parseLong(label.substring(0, us));
int j = us + 1; while ((j < label.length()) && Character.isDigit(label.charAt(j))) j++;
double frac = (j > us + 1) ? Double.parseDouble("0." + label.substring(us + 1, j)) : 0.0;
return sec + frac;
} catch (Exception e) { return Double.NaN; }
}
/** Expand a timing-ROI spec: <=0 open; 0<v<10000 = last-4 of seconds (from the range, rollover-aware); >=10000 explicit. // By Claude on 06/14/2026 */
private static double expandTimeSpec(double v, double minSec, boolean isEnd) {
if (v <= 0) return isEnd ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
if (v >= 10000) return v; // explicit full timestamp (sub-second fraction preserved)
long min = (long) Math.floor(minSec);
long high = min - (min % 10000);
long tail = (long) Math.floor(v); // integer seconds tail (last 4 digits)
double frac = v - tail; // sub-second fraction, e.g. 1/60 s steps
long cand = high + tail;
if (cand < min) cand += 10000; // rolled over within the available range (...9999 -> ...+1 0000)
if (frac > 0) return cand + frac; // explicit sub-second given - use it for both ends
return isEnd ? (cand + 0.999999) : (double) cand; // whole 'to' second when entered as an integer
}
/** [i0,i1) scene-index window for the timing ROI (curt_time_from/to), or full range if open/empty. // By Claude on 06/14/2026 */
private int [] timeWindow(String [] ts) {
int n = (ts == null) ? 0 : ts.length;
double from = clt_parameters.imp.curt_time_from;
double to = clt_parameters.imp.curt_time_to;
if ((n == 0) || ((from <= 0) && (to <= 0))) return new int[]{0, n};
double minSec = Double.POSITIVE_INFINITY;
double [] tv = new double [n];
for (int i = 0; i < n; i++) { tv[i] = parseTs(ts[i]); if (tv[i] < minSec) minSec = tv[i]; }
double f = expandTimeSpec(from, minSec, false);
double t = expandTimeSpec(to, minSec, true);
int i0 = 0; while ((i0 < n) && (tv[i0] < f)) i0++;
int i1 = i0; while ((i1 < n) && (tv[i1] <= t)) i1++;
if (i0 >= n) { i0 = Math.max(0, n - 1); i1 = n; } // window past end - keep last scene
if (i1 <= i0) i1 = Math.min(n, i0 + 1); // keep at least one scene
return new int[]{i0, i1};
}
/** Shallow sub-range of a rendered-ROI array / its labels for the timing ROI (no copy if full). // By Claude on 06/14/2026 */
private static double [][][][] win4(double [][][][] a, int [] w) {
return ((w[0] == 0) && (w[1] == a.length)) ? a : java.util.Arrays.copyOfRange(a, w[0], w[1]);
}
private static String [] winS(String [] a, int [] w) {
return ((w[0] == 0) && (w[1] == a.length)) ? a : java.util.Arrays.copyOfRange(a, w[0], w[1]);
}
/** Level selector for the heavy per-level paths (3d3/conv5d, C5P-direct, DNN): // By Claude on 06/13/2026
* empty/null curt_c5_levels = all built levels, otherwise only the listed ones. */
private static boolean c5LevelSelected(int [] c5_levels, int nlev) { // By Claude on 06/13/2026
if ((c5_levels == null) || (c5_levels.length == 0)) return true;
for (int l : c5_levels) if (l == nlev) return true;
return false;
}
public int getWidth() { public int getWidth() {
...@@ -627,7 +656,8 @@ public class CuasDetectRT { ...@@ -627,7 +656,8 @@ public class CuasDetectRT {
int dim3d3 = (2*cuasRTUtils.get3d3Radius()+1); int dim3d3 = (2*cuasRTUtils.get3d3Radius()+1);
// int size3x3 = dim3d3 * dim3d3; // int size3x3 = dim3d3 * dim3d3;
double [][][] dpixels_3d3 = new double [dpixels.length-1][width*height][dim3d3 * dim3d3]; boolean use_3d3 = clt_parameters.imp.curt_3d3_en; // bypass the 3d3 coarse-velocity path (and its ~W*H*9 arrays) when off - C5P-direct/DNN use dpixels_pyramid, not 3d3 // By Claude on 06/14/2026
double [][][] dpixels_3d3 = use_3d3 ? (new double [dpixels.length-1][width*height][dim3d3 * dim3d3]) : null; // By Claude on 06/14/2026
String title_conv3d3 = getBaseName()+SUFFIX_CONV3D3+"-PSF"+d2s(cuasRTUtils.getPsfRadius())+ String title_conv3d3 = getBaseName()+SUFFIX_CONV3D3+"-PSF"+d2s(cuasRTUtils.getPsfRadius())+
"-KR"+d2s(cuasRTUtils.getKernel2dRadius())+"-ALPHA0_"+d2s(alpha0)+ "-KR"+d2s(cuasRTUtils.getKernel2dRadius())+"-ALPHA0_"+d2s(alpha0)+
"-ALPHAPYR_"+d2s(alpha_pyr)+"-M"+curt_3d3_mode+"-F"+d2s(curt_3d3_frac)+"-T"+d2s(curt_3d3_thrsh)+"-P"+d2s(curt_3d3_power)+ "-ALPHAPYR_"+d2s(alpha_pyr)+"-M"+curt_3d3_mode+"-F"+d2s(curt_3d3_frac)+"-T"+d2s(curt_3d3_thrsh)+"-P"+d2s(curt_3d3_power)+
...@@ -635,6 +665,7 @@ public class CuasDetectRT { ...@@ -635,6 +665,7 @@ public class CuasDetectRT {
"-ALPHA1_"+d2s(alpha1); "-ALPHA1_"+d2s(alpha1);
// getTimeStamps(int from_indx) // getTimeStamps(int from_indx)
int dbg_seq = 480; int dbg_seq = 480;
if (use_3d3) { // By Claude on 06/14/2026
System.out.println(now()+" detectTargets(): convolve3D3LReLU"); // By Claude on 06/11/2026 System.out.println(now()+" detectTargets(): convolve3D3LReLU"); // By Claude on 06/11/2026
for (int nseq = 1; nseq < dpixels_log.length; nseq++) { for (int nseq = 1; nseq < dpixels_log.length; nseq++) {
int dbg_lev = (nseq == dbg_seq) ? 1 : -10; int dbg_lev = (nseq == dbg_seq) ? 1 : -10;
...@@ -658,13 +689,18 @@ public class CuasDetectRT { ...@@ -658,13 +689,18 @@ public class CuasDetectRT {
title_conv3d3); // String title) title_conv3d3); // String title)
QuadCLTCPU.saveImagePlusInDirectory(imp_3d3, getModelDirectory()); QuadCLTCPU.saveImagePlusInDirectory(imp_3d3, getModelDirectory());
} }
} else { // By Claude on 06/14/2026
System.out.println(now()+" detectTargets(): 3d3 coarse-velocity path bypassed (curt_3d3_en=false)"); // By Claude on 06/14/2026
}
// building pyramid // building pyramid
int pyramid_levels = clt_parameters.imp.curt_pyramid; int pyramid_levels = clt_parameters.imp.curt_pyramid;
if (clt_parameters.imp.curt_synth_src) { // B-measurement on synthetic needs only level 0 - bounds memory and runtime (lift this when synthetic LEV2+ tests are wanted) // By Claude on 06/11/2026 // Single level selector for ALL heavy per-level paths (3d3/conv5d, C5P-direct, DNN): // By Claude on 06/13/2026
pyramid_levels = 1; // curt_c5_levels = {} -> all built levels; {k} -> run the heavy stages only at level k
System.out.println("Synthetic input: limiting processing to pyramid level 0"); // (the cheap temporalAverageLReLU/convolve3D3LReLU pyramid chain still builds up to k).
} // Synthetic input now uses the same pyramid + level selection as real data (the old
// pyramid_levels=1 force and the curt_synth_bg_avg N= decimation were retired).
final int [] c5_levels = clt_parameters.imp.curt_c5_levels;
double [][][] dpixels_pyramid = new double [pyramid_levels][][]; double [][][] dpixels_pyramid = new double [pyramid_levels][][];
String [][] ts_pyramid = new String [pyramid_levels][]; String [][] ts_pyramid = new String [pyramid_levels][];
...@@ -696,7 +732,22 @@ public class CuasDetectRT { ...@@ -696,7 +732,22 @@ public class CuasDetectRT {
filt_suffix + filt_suffix +
"-SR"+curt_vel_suppr_rad+"-D"+curt_vel_dbg_min; "-SR"+curt_vel_suppr_rad+"-D"+curt_vel_dbg_min;
// Level-0 5D convolution (fine velocity from coarse-velocity history) // Level-0 5D convolution (fine velocity from coarse-velocity history)
int num_5d_lev0 = Math.max(0, dpixels_3d3.length - num_hist_5d + 1); int num_5d_lev0 = Math.max(0, (dpixels.length - 1) - num_hist_5d + 1); // dpixels_3d3 length (= dpixels.length-1), valid even when 3d3 bypassed // By Claude on 06/14/2026
// Full whole-image conv5d render is ~num_vout * num_scenes * (W*sub)*(H*sub) * 8 B (x2 with the // By Claude on 06/14/2026
// compute array), and is currently an unfilled stub - guard it so it cannot OOM the heap.
if (curt_save_c5full && (num_5d_lev0 > 0)) {
int subdim = cuasRTUtils.getSubDim();
long full_pix = (long)(width * subdim) * (height * subdim);
long need = (long) cuasRTUtils.getNumVout() * num_5d_lev0 * full_pix * 8L * 2L;
Runtime rt = Runtime.getRuntime();
long headroom = rt.maxMemory() - (rt.totalMemory() - rt.freeMemory());
if (need > (headroom / 2)) {
System.out.println("WARNING: full velocities hyperstack (~"+(need >> 30)+" GB) exceeds safe heap (~"+(headroom >> 30)+
" GB free) - skipping it (it is also an unfilled stub; use the ROI -RECT/-HYPER-RECT outputs).");
curt_save_c5full = false;
}
}
save_5d_pixels = curt_save_c5full; // re-sync after the guard (declared above) // By Claude on 06/14/2026
dpixels_5d_pyramid[0] = new double[num_5d_lev0][][][]; dpixels_5d_pyramid[0] = new double[num_5d_lev0][][][];
dpixels_5d_roi_pyramid[0] = new double[num_5d_lev0][][][]; dpixels_5d_roi_pyramid[0] = new double[num_5d_lev0][][][];
String[] ts_5d_lev0 = new String[num_5d_lev0]; String[] ts_5d_lev0 = new String[num_5d_lev0];
...@@ -705,6 +756,7 @@ public class CuasDetectRT { ...@@ -705,6 +756,7 @@ public class CuasDetectRT {
// every curt_recur_period-th output after each level's sequence is complete. // By Claude on 06/11/2026 // every curt_recur_period-th output after each level's sequence is complete. // By Claude on 06/11/2026
boolean run_recur = curt_recur_en && (curt_save_select != null); // By Claude on 06/09/2026 boolean run_recur = curt_recur_en && (curt_save_select != null); // By Claude on 06/09/2026
System.out.println(now()+" detectTargets(): will run convolve3d()"); // By Claude on 06/11/2026 System.out.println(now()+" detectTargets(): will run convolve3d()"); // By Claude on 06/11/2026
if (use_3d3 && c5LevelSelected(c5_levels, 0)) { // gate the LEV0 3d3-based conv5d by curt_3d3_en + curt_c5_levels // By Claude on 06/14/2026
for (int n5d = 0; n5d < num_5d_lev0; n5d++) { for (int n5d = 0; n5d < num_5d_lev0; n5d++) {
double tmp_min=curt_vel_dbg_min; double tmp_min=curt_vel_dbg_min;
/* /*
...@@ -749,16 +801,18 @@ public class CuasDetectRT { ...@@ -749,16 +801,18 @@ public class CuasDetectRT {
QuadCLTCPU.saveImagePlusInDirectory(imp_5d, getModelDirectory()); QuadCLTCPU.saveImagePlusInDirectory(imp_5d, getModelDirectory());
} }
if (curt_save_c5rect && num_5d_lev0 > 0) { if (curt_save_c5rect && num_5d_lev0 > 0) {
int [] win = timeWindow(ts_5d_lev0); // timing ROI (curt_time_from/to) // By Claude on 06/14/2026
double [][][][] roi_w = win4(dpixels_5d_roi_pyramid[0], win); String [] ts_w = winS(ts_5d_lev0, win); // By Claude on 06/14/2026
ImagePlus imp_5d_rect = cuasRTUtils.showConvKernel5d( ImagePlus imp_5d_rect = cuasRTUtils.showConvKernel5d(
dpixels_5d_roi_pyramid[0], roi_w,
curt_save_select, curt_save_select,
ts_5d_lev0, ts_w,
title_conv5d+"-RECT"); title_conv5d+"-RECT");
QuadCLTCPU.saveImagePlusInDirectory(imp_5d_rect, getModelDirectory()); QuadCLTCPU.saveImagePlusInDirectory(imp_5d_rect, getModelDirectory());
ImagePlus imp_5d_hyper = cuasRTUtils.showConvKernel5dHyperRect( ImagePlus imp_5d_hyper = cuasRTUtils.showConvKernel5dHyperRect(
dpixels_5d_roi_pyramid[0], roi_w,
curt_save_select, curt_save_select,
ts_5d_lev0, ts_w,
title_conv5d+"-HYPER-RECT"); title_conv5d+"-HYPER-RECT");
QuadCLTCPU.saveImagePlusInDirectory(imp_5d_hyper, getModelDirectory()); QuadCLTCPU.saveImagePlusInDirectory(imp_5d_hyper, getModelDirectory());
} }
...@@ -772,6 +826,7 @@ public class CuasDetectRT { ...@@ -772,6 +826,7 @@ public class CuasDetectRT {
title_conv5d, // String title_base, // By Claude on 06/11/2026 title_conv5d, // String title_base, // By Claude on 06/11/2026
true); // boolean save_individual // By Claude on 06/11/2026 true); // boolean save_individual // By Claude on 06/11/2026
} // By Claude on 06/11/2026 } // By Claude on 06/11/2026
} // end LEV0 conv5d gate (curt_c5_levels) // By Claude on 06/13/2026
for (int nlev = 0; nlev < pyramid_levels; nlev++) { for (int nlev = 0; nlev < pyramid_levels; nlev++) {
if (save_LoG_pixels) { if (save_LoG_pixels) {
String title_conv2d_lev = getBaseName()+SUFFIX_CONV2D+"-PSF"+d2s(cuasRTUtils.getPsfRadius())+ String title_conv2d_lev = getBaseName()+SUFFIX_CONV2D+"-PSF"+d2s(cuasRTUtils.getPsfRadius())+
...@@ -788,12 +843,19 @@ public class CuasDetectRT { ...@@ -788,12 +843,19 @@ public class CuasDetectRT {
// run next pyramid level (use half of the temporal slices) // run next pyramid level (use half of the temporal slices)
if (nlev < (pyramid_levels -1)) { if (nlev < (pyramid_levels -1)) {
System.out.println(now()+" detectTargets(): building pyramid level "+(nlev+1)); // By Claude on 06/11/2026
int num_lev_scenes = (dpixels_pyramid[nlev].length)/2 -1; int num_lev_scenes = (dpixels_pyramid[nlev].length)/2 -1;
if (num_lev_scenes < 1) { // sequence too short to build deeper levels - stop here // By Claude on 06/14/2026
System.out.println(now()+" detectTargets(): pyramid level "+(nlev+1)+
" would have "+num_lev_scenes+" frames (LEV"+nlev+" has "+dpixels_pyramid[nlev].length+
") - stopping pyramid build at LEV"+nlev);
break;
}
System.out.println(now()+" detectTargets(): building pyramid level "+(nlev+1)); // By Claude on 06/11/2026
dpixels_pyramid[nlev+1] = new double[num_lev_scenes][width*height]; dpixels_pyramid[nlev+1] = new double[num_lev_scenes][width*height];
dpixels_3d3_pyramid[nlev+1] = new double [num_lev_scenes][width*height][dim3d3 * dim3d3];
ts_pyramid[nlev+1] = new String[num_lev_scenes]; ts_pyramid[nlev+1] = new String[num_lev_scenes];
boolean lev_sel_next = use_3d3 && c5LevelSelected(c5_levels, nlev+1); // 3d3/conv5d only if enabled and this level is selected // By Claude on 06/14/2026
if (lev_sel_next) dpixels_3d3_pyramid[nlev+1] = new double [num_lev_scenes][width*height][dim3d3 * dim3d3]; // By Claude on 06/13/2026
for (int nseq = 0; nseq < num_lev_scenes; nseq++) { for (int nseq = 0; nseq < num_lev_scenes; nseq++) {
ts_pyramid[nlev+1][nseq] = ts_pyramid[nlev][2*(nseq + 1)]; ts_pyramid[nlev+1][nseq] = ts_pyramid[nlev][2*(nseq + 1)];
cuasRTUtils.temporalAverageLReLU( cuasRTUtils.temporalAverageLReLU(
...@@ -802,7 +864,7 @@ public class CuasDetectRT { ...@@ -802,7 +864,7 @@ public class CuasDetectRT {
dpixels_pyramid[nlev+1][nseq], // double [] result_in, dpixels_pyramid[nlev+1][nseq], // double [] result_in,
alpha_pyr); // final double alpha) alpha_pyr); // final double alpha)
cuasRTUtils.convolve3D3LReLU( if (lev_sel_next) cuasRTUtils.convolve3D3LReLU( // By Claude on 06/13/2026
dpixels_pyramid[nlev][2*nseq+2], // final double [] data, dpixels_pyramid[nlev][2*nseq+2], // final double [] data,
dpixels_pyramid[nlev][2*nseq], // final double [] data_prev, dpixels_pyramid[nlev][2*nseq], // final double [] data_prev,
dpixels_3d3_pyramid[nlev+1][nseq], // double [][] result_in, dpixels_3d3_pyramid[nlev+1][nseq], // double [][] result_in,
...@@ -815,6 +877,7 @@ public class CuasDetectRT { ...@@ -815,6 +877,7 @@ public class CuasDetectRT {
alpha1, // final double alpha) { alpha1, // final double alpha) {
debugLevel); // final int debugLevel) debugLevel); // final int debugLevel)
} }
if (lev_sel_next) { // gate 3d3-save + conv5d + saves + recurrent at this pyramid level (curt_c5_levels) // By Claude on 06/13/2026
if (save_3d3_pixels) { if (save_3d3_pixels) {
ImagePlus imp_3d3 = cuasRTUtils.render3d3Hyperstack( ImagePlus imp_3d3 = cuasRTUtils.render3d3Hyperstack(
dpixels_3d3_pyramid[nlev+1], // double [][][] coarse_vel_seq, // [nser][npix][nvel] dpixels_3d3_pyramid[nlev+1], // double [][][] coarse_vel_seq, // [nser][npix][nvel]
...@@ -863,16 +926,18 @@ public class CuasDetectRT { ...@@ -863,16 +926,18 @@ public class CuasDetectRT {
QuadCLTCPU.saveImagePlusInDirectory(imp_5d, getModelDirectory()); QuadCLTCPU.saveImagePlusInDirectory(imp_5d, getModelDirectory());
} }
if (curt_save_c5rect && num_5d_lev > 0) { if (curt_save_c5rect && num_5d_lev > 0) {
int [] win = timeWindow(ts_5d_lev); // timing ROI // By Claude on 06/14/2026
double [][][][] roi_w = win4(dpixels_5d_roi_pyramid[nlev+1], win); String [] ts_w = winS(ts_5d_lev, win); // By Claude on 06/14/2026
ImagePlus imp_5d_rect = cuasRTUtils.showConvKernel5d( ImagePlus imp_5d_rect = cuasRTUtils.showConvKernel5d(
dpixels_5d_roi_pyramid[nlev+1], roi_w,
curt_save_select, curt_save_select,
ts_5d_lev, ts_w,
title_conv5d+"-LEV"+(nlev+1)+"-RECT"); title_conv5d+"-LEV"+(nlev+1)+"-RECT");
QuadCLTCPU.saveImagePlusInDirectory(imp_5d_rect, getModelDirectory()); QuadCLTCPU.saveImagePlusInDirectory(imp_5d_rect, getModelDirectory());
ImagePlus imp_5d_hyper = cuasRTUtils.showConvKernel5dHyperRect( ImagePlus imp_5d_hyper = cuasRTUtils.showConvKernel5dHyperRect(
dpixels_5d_roi_pyramid[nlev+1], roi_w,
curt_save_select, curt_save_select,
ts_5d_lev, ts_w,
title_conv5d+"-LEV"+(nlev+1)+"-HYPER-RECT"); title_conv5d+"-LEV"+(nlev+1)+"-HYPER-RECT");
QuadCLTCPU.saveImagePlusInDirectory(imp_5d_hyper, getModelDirectory()); QuadCLTCPU.saveImagePlusInDirectory(imp_5d_hyper, getModelDirectory());
} }
...@@ -885,7 +950,37 @@ public class CuasDetectRT { ...@@ -885,7 +950,37 @@ public class CuasDetectRT {
title_conv5d+"-LEV"+(nlev+1), // String title_base, // By Claude on 06/11/2026 title_conv5d+"-LEV"+(nlev+1), // String title_base, // By Claude on 06/11/2026
false); // boolean save_individual // By Claude on 06/11/2026 false); // boolean save_individual // By Claude on 06/11/2026
} // By Claude on 06/11/2026 } // By Claude on 06/11/2026
} // end 3d3/conv5d gate at this pyramid level (curt_c5_levels) // By Claude on 06/13/2026
}
}
// --- Synthetic reference grid injection + sqrt(2) noise check ---------------------- // By Claude on 06/14/2026
if (clt_parameters.imp.curt_synth_src && (synth_pixels != null) && (synth_pixels.length > 0)) {
// sqrt(2) check: full-frame std of the PURE real frame 0 should drop ~/sqrt(2) per level.
for (int nlev = 0; nlev < pyramid_levels; nlev++) {
if ((dpixels_pyramid[nlev] == null) || (dpixels_pyramid[nlev].length == 0)) continue;
double [] f0 = dpixels_pyramid[nlev][0];
double s = 0, s2 = 0; int np = f0.length;
for (double v : f0) { s += v; s2 += v*v; }
double mean = s/np, var = s2/np - mean*mean;
System.out.println(String.format(" LEV%d real background std = %.5f (frame0, %d px) - expect ~/sqrt(2) per level", nlev, Math.sqrt(Math.max(0.0,var)), np));
}
// Inject the synthetic grid TILED (synth[j %% n]) into each SELECTED built level, AFTER
// the pure-real pyramid is complete - so it is identical at every level (never averaged
// across levels) while the real noise is averaged. Moving cells wrap every n_synth frames
// (a deliberate recurrent-decay test case). C5P/DNN read this; legacy 3d3-conv5d does not.
int n_synth = synth_pixels.length;
for (int nlev = 0; nlev < pyramid_levels; nlev++) {
if (!c5LevelSelected(c5_levels, nlev)) continue;
double [][] lev = dpixels_pyramid[nlev];
if (lev == null) continue;
for (int j = 0; j < lev.length; j++) {
float [] sp = synth_pixels[j % n_synth];
double [] d = lev[j];
int m = Math.min(d.length, sp.length);
for (int pix = 0; pix < m; pix++) d[pix] += sp[pix];
}
} }
System.out.println(now()+" detectTargets(): injected synthetic grid (tiled "+n_synth+" frames) into selected pyramid levels");
} }
// Direct pixel-domain fine-velocity path (-C5P): bypass the 3x3 coarse stage - // By Claude on 06/11/2026 // Direct pixel-domain fine-velocity path (-C5P): bypass the 3x3 coarse stage - // By Claude on 06/11/2026
// a single-stage velocity-matched filter straight on the LoG+LReLU frames of // By Claude on 06/11/2026 // a single-stage velocity-matched filter straight on the LoG+LReLU frames of // By Claude on 06/11/2026
...@@ -893,7 +988,6 @@ public class CuasDetectRT { ...@@ -893,7 +988,6 @@ public class CuasDetectRT {
// equal contrast). Parallel to the 3d3-based path for comparison. // By Claude on 06/11/2026 // equal contrast). Parallel to the 3d3-based path for comparison. // By Claude on 06/11/2026
if (clt_parameters.imp.curt_c5_from_pix && (curt_save_select != null)) { // By Claude on 06/11/2026 if (clt_parameters.imp.curt_c5_from_pix && (curt_save_select != null)) { // By Claude on 06/11/2026
int num_hist_p = cuasRTUtils.getNumHistDirect(); // By Claude on 06/11/2026 int num_hist_p = cuasRTUtils.getNumHistDirect(); // By Claude on 06/11/2026
int [] c5_levels = clt_parameters.imp.curt_c5_levels; // empty/null = all levels // By Claude on 06/13/2026
boolean white_en = clt_parameters.imp.curt_c5_white; // linear sharpening before nonlinearities // By Claude on 06/13/2026 boolean white_en = clt_parameters.imp.curt_c5_white; // linear sharpening before nonlinearities // By Claude on 06/13/2026
double white_sp = clt_parameters.imp.curt_c5_white_sp; // By Claude on 06/13/2026 double white_sp = clt_parameters.imp.curt_c5_white_sp; // By Claude on 06/13/2026
double white_vel = clt_parameters.imp.curt_c5_white_vel; // By Claude on 06/13/2026 double white_vel = clt_parameters.imp.curt_c5_white_vel; // By Claude on 06/13/2026
...@@ -904,9 +998,7 @@ public class CuasDetectRT { ...@@ -904,9 +998,7 @@ public class CuasDetectRT {
double [] op_log_prior = cuasRTUtils.getVelLogPrior( // shared by gate + sweep // By Claude on 06/13/2026 double [] op_log_prior = cuasRTUtils.getVelLogPrior( // shared by gate + sweep // By Claude on 06/13/2026
clt_parameters.imp.curt_c5_prior_r0, clt_parameters.imp.curt_c5_prior_r1); // By Claude on 06/13/2026 clt_parameters.imp.curt_c5_prior_r0, clt_parameters.imp.curt_c5_prior_r1); // By Claude on 06/13/2026
for (int nlev = 0; nlev < pyramid_levels; nlev++) { // By Claude on 06/11/2026 for (int nlev = 0; nlev < pyramid_levels; nlev++) { // By Claude on 06/11/2026
boolean lev_sel = (c5_levels == null) || (c5_levels.length == 0); // By Claude on 06/13/2026 if (!c5LevelSelected(c5_levels, nlev)) continue; // skip non-selected levels (speed) // By Claude on 06/13/2026
if (!lev_sel) for (int l : c5_levels) if (l == nlev) { lev_sel = true; break; } // By Claude on 06/13/2026
if (!lev_sel) continue; // skip non-selected levels (speed) // By Claude on 06/13/2026
double [][] frames = dpixels_pyramid[nlev]; // By Claude on 06/11/2026 double [][] frames = dpixels_pyramid[nlev]; // By Claude on 06/11/2026
if (frames == null) continue; // By Claude on 06/11/2026 if (frames == null) continue; // By Claude on 06/11/2026
int num_5dp = Math.max(0, frames.length - num_hist_p + 1); // By Claude on 06/11/2026 int num_5dp = Math.max(0, frames.length - num_hist_p + 1); // By Claude on 06/11/2026
...@@ -953,14 +1045,16 @@ public class CuasDetectRT { ...@@ -953,14 +1045,16 @@ public class CuasDetectRT {
(white_en ? ("-W"+white_sp+"_"+white_vel) : "")+ // linear whitening marker // By Claude on 06/13/2026 (white_en ? ("-W"+white_sp+"_"+white_vel) : "")+ // linear whitening marker // By Claude on 06/13/2026
(recur_soft ? ("-PG"+recur_lambda+"K"+recur_keep) : "")+ // posterior-gated recurrent feed marker // By Claude on 06/13/2026 (recur_soft ? ("-PG"+recur_lambda+"K"+recur_keep) : "")+ // posterior-gated recurrent feed marker // By Claude on 06/13/2026
((nlev > 0)? ("-LEV"+nlev) : ""); // By Claude on 06/12/2026 ((nlev > 0)? ("-LEV"+nlev) : ""); // By Claude on 06/12/2026
int [] win_c5p = timeWindow(ts_5dp); // timing ROI // By Claude on 06/14/2026
double [][][][] c5p_w = win4(c5p_roi, win_c5p); String [] ts_5dp_w = winS(ts_5dp, win_c5p); // By Claude on 06/14/2026
if (curt_save_c5rect) { // By Claude on 06/11/2026 if (curt_save_c5rect) { // By Claude on 06/11/2026
ImagePlus imp_c5p_rect = cuasRTUtils.showConvKernel5d( // By Claude on 06/11/2026 ImagePlus imp_c5p_rect = cuasRTUtils.showConvKernel5d( // By Claude on 06/11/2026
c5p_roi, curt_save_select, ts_5dp, title_c5p+"-RECT"); // By Claude on 06/11/2026 c5p_w, curt_save_select, ts_5dp_w, title_c5p+"-RECT"); // By Claude on 06/14/2026
QuadCLTCPU.saveImagePlusInDirectory(imp_c5p_rect, getModelDirectory()); // By Claude on 06/11/2026 QuadCLTCPU.saveImagePlusInDirectory(imp_c5p_rect, getModelDirectory()); // By Claude on 06/11/2026
} // By Claude on 06/11/2026 } // By Claude on 06/11/2026
if (clt_parameters.imp.curt_save_c5hyper) { // per-velocity view: bottom slider = velocity, second = time // By Claude on 06/13/2026 if (clt_parameters.imp.curt_save_c5hyper) { // per-velocity view: bottom slider = velocity, second = time // By Claude on 06/13/2026
ImagePlus imp_c5p_hyper = cuasRTUtils.showConvKernel5dHyperRect( // By Claude on 06/13/2026 ImagePlus imp_c5p_hyper = cuasRTUtils.showConvKernel5dHyperRect( // By Claude on 06/13/2026
c5p_roi, curt_save_select, ts_5dp, title_c5p+"-HYPER-RECT"); // By Claude on 06/13/2026 c5p_w, curt_save_select, ts_5dp_w, title_c5p+"-HYPER-RECT"); // By Claude on 06/14/2026
QuadCLTCPU.saveImagePlusInDirectory(imp_c5p_hyper, getModelDirectory()); // By Claude on 06/13/2026 QuadCLTCPU.saveImagePlusInDirectory(imp_c5p_hyper, getModelDirectory()); // By Claude on 06/13/2026
} // By Claude on 06/13/2026 } // By Claude on 06/13/2026
// Bayes posterior lambda sweep: per-cell score z^2/2+ln(prior) (-SCORE), // By Claude on 06/12/2026 // Bayes posterior lambda sweep: per-cell score z^2/2+ln(prior) (-SCORE), // By Claude on 06/12/2026
...@@ -1041,24 +1135,30 @@ public class CuasDetectRT { ...@@ -1041,24 +1135,30 @@ public class CuasDetectRT {
if (!clt_parameters.imp.curt_dnn_model.isEmpty() && (curt_save_select != null)) { // By Claude on 06/13/2026 if (!clt_parameters.imp.curt_dnn_model.isEmpty() && (curt_save_select != null)) { // By Claude on 06/13/2026
final int N_dnn = 8; // By Claude on 06/13/2026 final int N_dnn = 8; // By Claude on 06/13/2026
final int vr_dnn = clt_parameters.imp.curt_vel_radius; // By Claude on 06/13/2026 final int vr_dnn = clt_parameters.imp.curt_vel_radius; // By Claude on 06/13/2026
int [] c5_levels_dnn = clt_parameters.imp.curt_c5_levels; // By Claude on 06/13/2026 final int dnn_stride = Math.max(1, clt_parameters.imp.curt_dnn_stride); // 1 = every slice (testing), 4 = production 50% overlap // By Claude on 06/14/2026
try { // By Claude on 06/13/2026 try { // By Claude on 06/13/2026
CuasDnnInfer dnn = new CuasDnnInfer(clt_parameters.imp.curt_dnn_model, N_dnn); // By Claude on 06/13/2026 CuasDnnInfer dnn = new CuasDnnInfer(clt_parameters.imp.curt_dnn_model, N_dnn); // By Claude on 06/13/2026
for (int nlev = 0; nlev < pyramid_levels; nlev++) { // By Claude on 06/13/2026 for (int nlev = 0; nlev < pyramid_levels; nlev++) { // By Claude on 06/13/2026
boolean lev_sel = (c5_levels_dnn == null) || (c5_levels_dnn.length == 0); // By Claude on 06/13/2026 if (!c5LevelSelected(c5_levels, nlev)) continue; // level gating (curt_c5_levels) // By Claude on 06/13/2026
if (!lev_sel) for (int l : c5_levels_dnn) if (l == nlev) { lev_sel = true; break; } // By Claude on 06/13/2026
if (!lev_sel) continue; // By Claude on 06/13/2026
double [][] framesD = dpixels_pyramid[nlev]; // By Claude on 06/13/2026 double [][] framesD = dpixels_pyramid[nlev]; // By Claude on 06/13/2026
if (framesD == null) continue; // By Claude on 06/13/2026 if (framesD == null) continue; // By Claude on 06/13/2026
int num = Math.max(0, framesD.length - N_dnn + 1); // all available scenes // By Claude on 06/13/2026 // stride-4 (production) outputs every 4th level-slice (0..7, 4..11, ...); stride-1 (testing) every slice // By Claude on 06/14/2026
if (num == 0) continue; // By Claude on 06/13/2026 int num_all = (framesD.length >= N_dnn) ? ((framesD.length - N_dnn)/dnn_stride + 1) : 0; // By Claude on 06/14/2026
if (num_all == 0) continue; // By Claude on 06/13/2026
// timing ROI (curt_time_from/to): infer ONLY the in-window scenes (compute-window, not just save) // By Claude on 06/14/2026
String [] cand_ts = new String [num_all]; // By Claude on 06/14/2026
for (int k = 0; k < num_all; k++) cand_ts[k] = ts_pyramid[nlev][k * dnn_stride + N_dnn - 1]; // By Claude on 06/14/2026
int [] tw = timeWindow(cand_ts); int w0 = tw[0], num = tw[1] - tw[0]; // By Claude on 06/14/2026
if (num <= 0) continue; // By Claude on 06/14/2026
double dnn_thresh = clt_parameters.imp.curt_dnn_thresh; // By Claude on 06/13/2026 double dnn_thresh = clt_parameters.imp.curt_dnn_thresh; // By Claude on 06/13/2026
double [][][][] dnn_roi = new double [num][][][]; // By Claude on 06/13/2026 double [][][][] dnn_roi = new double [num][][][]; // By Claude on 06/13/2026
double [][][] dnn_off = new double [num][][]; // per scene: [roi_pix][{dx,dy,s}] - sub-pixel offset viz // By Claude on 06/14/2026
String [] ts_dnn = new String [num]; // By Claude on 06/13/2026 String [] ts_dnn = new String [num]; // By Claude on 06/13/2026
System.out.println(now()+" detectTargets(): DNN inferROI LEV"+nlev+" ("+num+" scenes, "+ // By Claude on 06/13/2026 System.out.println(now()+" detectTargets(): DNN inferROI LEV"+nlev+" ("+num+" of "+num_all+" scenes, stride "+dnn_stride+", "+ // By Claude on 06/14/2026
(curt_save_select.width*curt_save_select.height)+" px/scene, thresh="+dnn_thresh+")"); // By Claude on 06/13/2026 (curt_save_select.width*curt_save_select.height)+" px/scene, thresh="+dnn_thresh+")"); // By Claude on 06/13/2026
for (int n5d = 0; n5d < num; n5d++) { // By Claude on 06/13/2026 for (int j = 0; j < num; j++) { // By Claude on 06/14/2026
int newest = n5d + N_dnn - 1; // absolute newest-frame index in framesD // By Claude on 06/13/2026 int n5d = w0 + j; // in-window scene index // By Claude on 06/14/2026
int newest = n5d * dnn_stride + N_dnn - 1; // absolute newest-frame index in framesD (strided) // By Claude on 06/14/2026
float [][] window = new float [N_dnn][]; // By Claude on 06/13/2026 float [][] window = new float [N_dnn][]; // By Claude on 06/13/2026
for (int h = 0; h < N_dnn; h++) { // By Claude on 06/13/2026 for (int h = 0; h < N_dnn; h++) { // By Claude on 06/13/2026
double [] src = framesD[newest - h]; // h=0 newest // By Claude on 06/13/2026 double [] src = framesD[newest - h]; // h=0 newest // By Claude on 06/13/2026
...@@ -1066,19 +1166,42 @@ public class CuasDetectRT { ...@@ -1066,19 +1166,42 @@ public class CuasDetectRT {
for (int k = 0; k < src.length; k++) fw[k] = (float) src[k]; // By Claude on 06/13/2026 for (int k = 0; k < src.length; k++) fw[k] = (float) src[k]; // By Claude on 06/13/2026
window[h] = fw; // By Claude on 06/13/2026 window[h] = fw; // By Claude on 06/13/2026
} // By Claude on 06/13/2026 } // By Claude on 06/13/2026
dnn_roi[n5d] = dnn.inferROI(window, width, height, curt_save_select, vr_dnn, dnn_thresh); // By Claude on 06/13/2026 double [][] off = new double [curt_save_select.width * curt_save_select.height][]; // {dx,dy,s} per ROI pixel // By Claude on 06/14/2026
ts_dnn[n5d] = ts_pyramid[nlev][newest] + " f"+newest; // timestamp + abs frame index for matching // By Claude on 06/13/2026 dnn_roi[j] = dnn.inferROI(window, width, height, curt_save_select, vr_dnn, dnn_thresh, off); // By Claude on 06/14/2026
System.out.println(now()+" DNN scene "+(n5d+1)+"/"+num+" (frame "+newest+", "+ts_pyramid[nlev][newest]+") done"); // By Claude on 06/13/2026 dnn_off[j] = off; // By Claude on 06/14/2026
ts_dnn[j] = ts_pyramid[nlev][newest] + " f"+newest; // timestamp + abs frame index for matching // By Claude on 06/14/2026
System.out.println(now()+" DNN scene "+(j+1)+"/"+num+" (frame "+newest+", "+ts_pyramid[nlev][newest]+") done"); // By Claude on 06/14/2026
} // By Claude on 06/13/2026 } // By Claude on 06/13/2026
String title_dnn = title_conv5d+"-DNN"+((nlev > 0)?("-LEV"+nlev):""); // By Claude on 06/13/2026 String title_dnn = title_conv5d+"-DNN"+((nlev > 0)?("-LEV"+nlev):""); // By Claude on 06/13/2026
int [] win_dnn = timeWindow(ts_dnn); // timing ROI // By Claude on 06/14/2026
double [][][][] dnn_w = win4(dnn_roi, win_dnn); String [] ts_dnn_w = winS(ts_dnn, win_dnn); // By Claude on 06/14/2026
if (curt_save_c5rect) { // By Claude on 06/13/2026 if (curt_save_c5rect) { // By Claude on 06/13/2026
QuadCLTCPU.saveImagePlusInDirectory(cuasRTUtils.showConvKernel5d( // By Claude on 06/13/2026 QuadCLTCPU.saveImagePlusInDirectory(cuasRTUtils.showConvKernel5d( // By Claude on 06/13/2026
dnn_roi, curt_save_select, ts_dnn, title_dnn+"-RECT"), getModelDirectory()); // By Claude on 06/13/2026 dnn_w, curt_save_select, ts_dnn_w, title_dnn+"-RECT"), getModelDirectory()); // By Claude on 06/14/2026
} // By Claude on 06/13/2026 } // By Claude on 06/13/2026
if (clt_parameters.imp.curt_save_c5hyper) { // By Claude on 06/13/2026 if (clt_parameters.imp.curt_save_c5hyper) { // By Claude on 06/13/2026
QuadCLTCPU.saveImagePlusInDirectory(cuasRTUtils.showConvKernel5dHyperRect( // By Claude on 06/13/2026 QuadCLTCPU.saveImagePlusInDirectory(cuasRTUtils.showConvKernel5dHyperRect( // By Claude on 06/13/2026
dnn_roi, curt_save_select, ts_dnn, title_dnn+"-HYPER-RECT"), getModelDirectory()); // By Claude on 06/13/2026 dnn_w, curt_save_select, ts_dnn_w, title_dnn+"-HYPER-RECT"), getModelDirectory()); // By Claude on 06/14/2026
} // By Claude on 06/13/2026 } // By Claude on 06/13/2026
// -DNN-OFFSET: the per-pixel sub-pixel offset (dx,dy) + confidence s over the ROI, // By Claude on 06/14/2026
// to see whether a cluster's offsets converge on one fractional position. Channels
// (bottom slider): 0=dx, 1=dy (NaN where s<0.05 so only detections show), 2=s.
{
int rwid = curt_save_select.width, rhgt = curt_save_select.height, rnp = rwid * rhgt;
int nsc = win_dnn[1] - win_dnn[0];
double [][][] off_hyper = new double [3][nsc][rnp];
double vthr = 0.05;
for (int k = 0; k < nsc; k++) {
double [][] off = dnn_off[win_dnn[0] + k];
for (int p = 0; p < rnp; p++) {
double dx = Double.NaN, dy = Double.NaN, sv = 0.0;
if ((off != null) && (off[p] != null)) { sv = off[p][2]; if (sv >= vthr) { dx = off[p][0]; dy = off[p][1]; } }
off_hyper[0][k][p] = dx; off_hyper[1][k][p] = dy; off_hyper[2][k][p] = sv;
}
}
QuadCLTCPU.saveImagePlusInDirectory(ShowDoubleFloatArrays.showArraysHyperstack(
off_hyper, rwid, title_dnn+"-OFFSET", ts_dnn_w, new String[]{"dx","dy","s"}, false), getModelDirectory());
}
} // By Claude on 06/13/2026 } // By Claude on 06/13/2026
dnn.close(); // By Claude on 06/13/2026 dnn.close(); // By Claude on 06/13/2026
} catch (Exception e) { // By Claude on 06/13/2026 } catch (Exception e) { // By Claude on 06/13/2026
......
...@@ -1133,6 +1133,7 @@ min_str_neib_fpn 0.35 ...@@ -1133,6 +1133,7 @@ min_str_neib_fpn 0.35
public int curt_pyramid = 7; // temporal pyramid levels public int curt_pyramid = 7; // temporal pyramid levels
public double curt_rleak_pyr = 0.1; // 1-ReLU leak (0 - linear, 1.0 - classic ReLU) after averaging 2 consecutive samples public double curt_rleak_pyr = 0.1; // 1-ReLU leak (0 - linear, 1.0 - classic ReLU) after averaging 2 consecutive samples
// === Coarse velocities layer (from -1 to +1 in each direction, 9 total) === // === Coarse velocities layer (from -1 to +1 in each direction, 9 total) ===
public boolean curt_3d3_en = false; // compute the 3d3 coarse-velocity path (and its conv5d / dpixels_3d3 arrays); default false so old config files (without this key) bypass it and save memory/time - C5P-direct/DNN do not use it; set true when/if needed // By Claude on 06/14/2026
public int curt_3d3_mode = 2; // 0 (plain) integration, 1 - fraction to the winner, 2 use strength power public int curt_3d3_mode = 2; // 0 (plain) integration, 1 - fraction to the winner, 2 use strength power
public double curt_3d3_frac = 0.5; // for stronger signals give this fraction of the total (of 9) weight to the 1-of-9 winner public double curt_3d3_frac = 0.5; // for stronger signals give this fraction of the total (of 9) weight to the 1-of-9 winner
public double curt_3d3_thrsh = 1.0; // scale down curt_3d3_frac for weaker signals (in ratio of min(data[now]/curt_3d3_thrsh,1.0) public double curt_3d3_thrsh = 1.0; // scale down curt_3d3_frac for weaker signals (in ratio of min(data[now]/curt_3d3_thrsh,1.0)
...@@ -1179,16 +1180,18 @@ min_str_neib_fpn 0.35 ...@@ -1179,16 +1180,18 @@ min_str_neib_fpn 0.35
public double curt_c5_white_vel = 0.0; // whitening velocity strength a (3-tap unsharp in vx and vy); 0 - no velocity sharpening // By Claude on 06/13/2026 public double curt_c5_white_vel = 0.0; // whitening velocity strength a (3-tap unsharp in vx and vy); 0 - no velocity sharpening // By Claude on 06/13/2026
public String curt_dnn_model = ""; // C5P DNN front-end model (ONNX): empty = disabled; local path, scp user@host:path, or http(s) URL fetched to cache; overrides bundled resource (mirrors tile_processor_gpu) // By Claude on 06/13/2026 public String curt_dnn_model = ""; // C5P DNN front-end model (ONNX): empty = disabled; local path, scp user@host:path, or http(s) URL fetched to cache; overrides bundled resource (mirrors tile_processor_gpu) // By Claude on 06/13/2026
public double curt_dnn_thresh = 0.0; // DNN display threshold: zero the (Vx,Vy,s) field where detection s < this (0 - show all); cosmetic FP-suppression - real-clutter FPs need a retrain with real-bg negatives, not a threshold // By Claude on 06/13/2026 public double curt_dnn_thresh = 0.0; // DNN display threshold: zero the (Vx,Vy,s) field where detection s < this (0 - show all); cosmetic FP-suppression - real-clutter FPs need a retrain with real-bg negatives, not a threshold // By Claude on 06/13/2026
public int curt_dnn_stride = 1; // DNN temporal output stride (per pyramid-level slice): 1 = every step (testing, watch each temporal unit); 4 = production (50% overlap, matches the convolver's integer-pixel-shift cadence = curt_recur_period) // By Claude on 06/14/2026
public boolean curt_synth_src = true; // default set for the synthetic B-measurement experiment (set false for real-data runs); reads *-CUAS-SYNTHETIC-CUAS.tiff, output titles get -SYNTH // By Claude on 06/12/2026 public boolean curt_synth_src = true; // default set for the synthetic B-measurement experiment (set false for real-data runs); reads *-CUAS-SYNTHETIC-CUAS.tiff, output titles get -SYNTH // By Claude on 06/12/2026
public double curt_synth_scale = 5.0; // synthetic target peak, counts (synthetic file is peak-1 normalized; scaled at load) // By Claude on 06/12/2026 public double curt_synth_scale = 5.0; // synthetic target peak, counts (synthetic file is peak-1 normalized; scaled at load) // By Claude on 06/12/2026
public boolean curt_synth_bg = true; // add the real *-CUAS-MERGED-CUAS.tiff scene under the synthetic targets (label-matched frames); false - clean targets only // By Claude on 06/12/2026 public boolean curt_synth_bg = true; // add the real *-CUAS-MERGED-CUAS.tiff scene under the synthetic targets (label-matched frames); false - clean targets only // By Claude on 06/12/2026
public int curt_synth_bg_avg = 1; // pyramid-level emulation: background per synthetic frame = mean of N consecutive real frames (non-overlapping groups, level log2(N) noise stats); targets keep per-frame motion; 1 - off // By Claude on 06/13/2026
public double curt_min_frac = 0.0; // default 0 for linear synthetic/B runs (production value ~0.1); zeroes conv5d outputs where the newest frame contributes less than this fraction (NONLINEAR dark-frame gate) // By Claude on 06/12/2026 public double curt_min_frac = 0.0; // default 0 for linear synthetic/B runs (production value ~0.1); zeroes conv5d outputs where the newest frame contributes less than this fraction (NONLINEAR dark-frame gate) // By Claude on 06/12/2026
// debug/saving images // debug/saving images
public boolean curt_save_c5full = false; // save fine velocities [direction][scene][subpixels] public boolean curt_save_c5full = false; // save fine velocities [direction][scene][subpixels]
public boolean curt_save_c5rect = true; // save fine velocities for selected rectangle only [scene][flattened image] public boolean curt_save_c5rect = true; // save fine velocities for selected rectangle only [scene][flattened image]
public boolean curt_save_c5hyper = true; // save per-velocity HYPER-RECT hyperstacks (bottom slider=velocity, second=time) for C5P and posterior outputs - watch a low-SNR target evolve at fixed velocity // By Claude on 06/13/2026 public boolean curt_save_c5hyper = true; // save per-velocity HYPER-RECT hyperstacks (bottom slider=velocity, second=time) for C5P and posterior outputs - watch a low-SNR target evolve at fixed velocity // By Claude on 06/13/2026
public Rectangle curt_save_select = new Rectangle(98, 265, 82, 24); // Example selection for 1773135527_803834 public Rectangle curt_save_select = new Rectangle(98, 265, 82, 24); // Example selection for 1773135527_803834
public double curt_time_from = -1.0; // timing ROI start (timestamp seconds): <=0 open; 0<v<10000 = last-4 of seconds (expanded from the file's range, rollover-aware); >=10000 explicit. Clips saved RECT/HYPER/DNN/C5P stacks to the time window // By Claude on 06/14/2026
public double curt_time_to = -1.0; // timing ROI end (timestamp seconds): same convention as curt_time_from; the whole 'to' second is included // By Claude on 06/14/2026
// Airplane mode // Airplane mode
public boolean air_mode_en = false; // enable airplane mode public boolean air_mode_en = false; // enable airplane mode
...@@ -3428,6 +3431,8 @@ min_str_neib_fpn 0.35 ...@@ -3428,6 +3431,8 @@ min_str_neib_fpn 0.35
"1-ReLU leak (0 - linear, 1.0 - classic ReLU) after merging to consecutive samples from the previous level data."); "1-ReLU leak (0 - linear, 1.0 - classic ReLU) after merging to consecutive samples from the previous level data.");
gd.addMessage("=== Coarse velocities layer (from -1 to +1 in each direction, 9 total) ==="); gd.addMessage("=== Coarse velocities layer (from -1 to +1 in each direction, 9 total) ===");
gd.addCheckbox ("Compute 3d3 coarse-velocity path", this.curt_3d3_en, // By Claude on 06/14/2026
"Compute the 3x3 coarse-velocity (3d3) path and its conv5d outputs. Uncheck to BYPASS it - saves the ~W*H*9 arrays and time. The C5P-direct and DNN paths do not use it, so leave it off for those runs."); // By Claude on 06/14/2026
gd. addChoice("Coarse velocities non-linearities mode", CuasDetectRT.CONV3D3_MODES, CuasDetectRT.CONV3D3_MODES[this.curt_3d3_mode], gd. addChoice("Coarse velocities non-linearities mode", CuasDetectRT.CONV3D3_MODES, CuasDetectRT.CONV3D3_MODES[this.curt_3d3_mode],
"0 (plain) integration only, 1 - fraction to the winner, 2 - use velocities strength power."); "0 (plain) integration only, 1 - fraction to the winner, 2 - use velocities strength power.");
gd.addNumericField("Fraction for the winner", this.curt_3d3_frac, 6,8,"x", gd.addNumericField("Fraction for the winner", this.curt_3d3_frac, 6,8,"x",
...@@ -3522,14 +3527,14 @@ min_str_neib_fpn 0.35 ...@@ -3522,14 +3527,14 @@ min_str_neib_fpn 0.35
"Trained DNN front-end model location. Empty = disabled (use matched-filter/posterior path). Local path, scp user@host:/path (fetched to cache), or http(s) URL. Overrides any bundled resource - same default-vs-override scheme as the GPU kernel sources."); // By Claude on 06/13/2026 "Trained DNN front-end model location. Empty = disabled (use matched-filter/posterior path). Local path, scp user@host:/path (fetched to cache), or http(s) URL. Overrides any bundled resource - same default-vs-override scheme as the GPU kernel sources."); // By Claude on 06/13/2026
gd.addNumericField("DNN confidence threshold", this.curt_dnn_thresh, 6,8,"", // By Claude on 06/13/2026 gd.addNumericField("DNN confidence threshold", this.curt_dnn_thresh, 6,8,"", // By Claude on 06/13/2026
"Zero the DNN (Vx,Vy,s) field where detection confidence s < this (0 = show all). Display/FP-suppression only - real-clutter false positives need a retrain with real-background negatives, not a threshold."); // By Claude on 06/13/2026 "Zero the DNN (Vx,Vy,s) field where detection confidence s < this (0 = show all). Display/FP-suppression only - real-clutter false positives need a retrain with real-background negatives, not a threshold."); // By Claude on 06/13/2026
gd.addNumericField("DNN temporal stride", this.curt_dnn_stride, 0,3,"slices", // By Claude on 06/14/2026
"DNN output stride along the pyramid-level time axis: 1 = a fresh inference at every slice (testing, watch each temporal step); 4 = production (50% overlap of the window, matches the convolver's integer-pixel-shift cadence)."); // By Claude on 06/14/2026
gd.addCheckbox ("Use synthetic input", this.curt_synth_src, // By Claude on 06/11/2026 gd.addCheckbox ("Use synthetic input", this.curt_synth_src, // By Claude on 06/11/2026
"Read *-CUAS-SYNTHETIC-CUAS.tiff (generated test targets) instead of *-CUAS-MERGED-CUAS.tiff from the same model directory; all output titles get a -SYNTH mark."); // By Claude on 06/11/2026 "Read *-CUAS-SYNTHETIC-CUAS.tiff (generated test targets) instead of *-CUAS-MERGED-CUAS.tiff from the same model directory; all output titles get a -SYNTH mark."); // By Claude on 06/11/2026
gd.addNumericField("Synthetic target peak", this.curt_synth_scale, 6,8,"counts", // By Claude on 06/12/2026 gd.addNumericField("Synthetic target peak", this.curt_synth_scale, 6,8,"counts", // By Claude on 06/12/2026
"Scale applied to the (peak-1 normalized) synthetic targets at load = target peak in counts. Reference: birds ~3, background sigma ~1.7, strong targets >30."); // By Claude on 06/12/2026 "Scale applied to the (peak-1 normalized) synthetic targets at load = target peak in counts. Reference: birds ~3, background sigma ~1.7, strong targets >30."); // By Claude on 06/12/2026
gd.addCheckbox ("Synthetic over real background", this.curt_synth_bg, // By Claude on 06/12/2026 gd.addCheckbox ("Synthetic over real background", this.curt_synth_bg, // By Claude on 06/12/2026
"Add the real *-CUAS-MERGED-CUAS.tiff scene under the scaled synthetic targets (frames matched by timestamp label). Unchecked - clean targets on zero background (linear B measurement)."); // By Claude on 06/12/2026 "Add the real *-CUAS-MERGED-CUAS.tiff scene under the scaled synthetic targets (frames matched by timestamp label). Unchecked - clean targets on zero background (linear B measurement)."); // By Claude on 06/12/2026
gd.addNumericField("Background frame averaging", this.curt_synth_bg_avg, 0,3,"frames", // By Claude on 06/13/2026
"Pyramid-level emulation: background per synthetic frame = mean of this many CONSECUTIVE real frames (non-overlapping groups = level log2(N) noise statistics), while targets keep their per-frame motion (same relative velocity). Sequence truncates to complete groups. 1 - off (label-matched 1:1)."); // By Claude on 06/13/2026
gd.addNumericField("Min newest-frame fraction", this.curt_min_frac, 6,8,"", // By Claude on 06/12/2026 gd.addNumericField("Min newest-frame fraction", this.curt_min_frac, 6,8,"", // By Claude on 06/12/2026
"Zero fine-velocity outputs where the newest frame contributes less than this fraction of the accumulated response (dark-frame feedforward gate). NONLINEAR - punches holes into ridge maxima; set 0 for linear/synthetic B-measurement runs."); // By Claude on 06/12/2026 "Zero fine-velocity outputs where the newest frame contributes less than this fraction of the accumulated response (dark-frame feedforward gate). NONLINEAR - punches holes into ridge maxima; set 0 for linear/synthetic B-measurement runs."); // By Claude on 06/12/2026
...@@ -3541,6 +3546,10 @@ min_str_neib_fpn 0.35 ...@@ -3541,6 +3546,10 @@ min_str_neib_fpn 0.35
gd.addCheckbox ("Save per-velocity hyperstacks", this.curt_save_c5hyper, // By Claude on 06/13/2026 gd.addCheckbox ("Save per-velocity hyperstacks", this.curt_save_c5hyper, // By Claude on 06/13/2026
"HYPER-RECT views (bottom slider = velocity, second slider/scrollwheel = time) for the C5P output and one per posterior lambda - watch a low-SNR target evolve at a fixed velocity."); // By Claude on 06/13/2026 "HYPER-RECT views (bottom slider = velocity, second slider/scrollwheel = time) for the C5P output and one per posterior lambda - watch a low-SNR target evolve at a fixed velocity."); // By Claude on 06/13/2026
gd.addStringField ("Output selection Rectangle", rectangleToString(curt_save_select), 40, "Selection Rectangle:left, top, width,height"); gd.addStringField ("Output selection Rectangle", rectangleToString(curt_save_select), 40, "Selection Rectangle:left, top, width,height");
gd.addNumericField("Time ROI from (s)", this.curt_time_from, 3,12,"s", // 3 decimals (fixed) - stable round-trip on OK // By Claude on 06/14/2026
"Timing ROI start: clips saved RECT/HYPER/DNN/C5P stacks to this time window. -1 = open. Convenience: enter the last 4 digits of the seconds (0<v<10000) and the high digits are guessed from the file's timestamp range (rollover-aware); >=10000 = explicit seconds."); // By Claude on 06/14/2026
gd.addNumericField("Time ROI to (s)", this.curt_time_to, 3,12,"s", // 3 decimals (fixed) - stable round-trip on OK // By Claude on 06/14/2026
"Timing ROI end: same convention as 'from'. The whole 'to' second is included. -1 = open."); // By Claude on 06/14/2026
gd.addTab("Airplane","Airplane mode (fast forward movement, low vertical speed"); gd.addTab("Airplane","Airplane mode (fast forward movement, low vertical speed");
gd.addCheckbox ("Enable airplane mode", this.air_mode_en, gd.addCheckbox ("Enable airplane mode", this.air_mode_en,
...@@ -5021,6 +5030,7 @@ min_str_neib_fpn 0.35 ...@@ -5021,6 +5030,7 @@ min_str_neib_fpn 0.35
this.curt_pyramid = (int) gd.getNextNumber(); this.curt_pyramid = (int) gd.getNextNumber();
this.curt_rleak_pyr = gd.getNextNumber(); this.curt_rleak_pyr = gd.getNextNumber();
this.curt_3d3_en = gd.getNextBoolean(); // By Claude on 06/14/2026
this.curt_3d3_mode = gd.getNextChoiceIndex(); this.curt_3d3_mode = gd.getNextChoiceIndex();
this.curt_3d3_frac = gd.getNextNumber(); this.curt_3d3_frac = gd.getNextNumber();
this.curt_3d3_thrsh = gd.getNextNumber(); this.curt_3d3_thrsh = gd.getNextNumber();
...@@ -5068,16 +5078,18 @@ min_str_neib_fpn 0.35 ...@@ -5068,16 +5078,18 @@ min_str_neib_fpn 0.35
this.curt_c5_white_vel = gd.getNextNumber(); // By Claude on 06/13/2026 this.curt_c5_white_vel = gd.getNextNumber(); // By Claude on 06/13/2026
this.curt_dnn_model = gd.getNextString().trim(); // By Claude on 06/13/2026 this.curt_dnn_model = gd.getNextString().trim(); // By Claude on 06/13/2026
this.curt_dnn_thresh = gd.getNextNumber(); // By Claude on 06/13/2026 this.curt_dnn_thresh = gd.getNextNumber(); // By Claude on 06/13/2026
this.curt_dnn_stride = (int) gd.getNextNumber(); // By Claude on 06/14/2026
this.curt_synth_src = gd.getNextBoolean(); // By Claude on 06/11/2026 this.curt_synth_src = gd.getNextBoolean(); // By Claude on 06/11/2026
this.curt_synth_scale = gd.getNextNumber(); // By Claude on 06/12/2026 this.curt_synth_scale = gd.getNextNumber(); // By Claude on 06/12/2026
this.curt_synth_bg = gd.getNextBoolean(); // By Claude on 06/12/2026 this.curt_synth_bg = gd.getNextBoolean(); // By Claude on 06/12/2026
this.curt_synth_bg_avg = (int) gd.getNextNumber(); // By Claude on 06/13/2026
this.curt_min_frac = gd.getNextNumber(); // By Claude on 06/12/2026 this.curt_min_frac = gd.getNextNumber(); // By Claude on 06/12/2026
this.curt_save_c5full = gd.getNextBoolean(); this.curt_save_c5full = gd.getNextBoolean();
this.curt_save_c5rect = gd.getNextBoolean(); this.curt_save_c5rect = gd.getNextBoolean();
this.curt_save_c5hyper = gd.getNextBoolean(); // By Claude on 06/13/2026 this.curt_save_c5hyper = gd.getNextBoolean(); // By Claude on 06/13/2026
this.curt_save_select = stringToRectangle(gd.getNextString());// Rectangle this.curt_save_select = stringToRectangle(gd.getNextString());// Rectangle
this.curt_time_from = gd.getNextNumber(); // By Claude on 06/14/2026
this.curt_time_to = gd.getNextNumber(); // By Claude on 06/14/2026
this.air_mode_en = gd.getNextBoolean(); this.air_mode_en = gd.getNextBoolean();
this.air_sync_ims = gd.getNextBoolean(); this.air_sync_ims = gd.getNextBoolean();
...@@ -6388,6 +6400,7 @@ min_str_neib_fpn 0.35 ...@@ -6388,6 +6400,7 @@ min_str_neib_fpn 0.35
properties.setProperty(prefix+"curt_pyramid", this.curt_pyramid+""); // int properties.setProperty(prefix+"curt_pyramid", this.curt_pyramid+""); // int
properties.setProperty(prefix+"curt_rleak_pyr", this.curt_rleak_pyr+""); // double properties.setProperty(prefix+"curt_rleak_pyr", this.curt_rleak_pyr+""); // double
properties.setProperty(prefix+"curt_3d3_en", this.curt_3d3_en+""); // boolean // By Claude on 06/14/2026
properties.setProperty(prefix+"curt_3d3_mode", this.curt_3d3_mode+""); // int properties.setProperty(prefix+"curt_3d3_mode", this.curt_3d3_mode+""); // int
properties.setProperty(prefix+"curt_3d3_frac", this.curt_3d3_frac+""); // double properties.setProperty(prefix+"curt_3d3_frac", this.curt_3d3_frac+""); // double
properties.setProperty(prefix+"curt_3d3_thrsh", this.curt_3d3_thrsh+""); // double properties.setProperty(prefix+"curt_3d3_thrsh", this.curt_3d3_thrsh+""); // double
...@@ -6434,16 +6447,18 @@ min_str_neib_fpn 0.35 ...@@ -6434,16 +6447,18 @@ min_str_neib_fpn 0.35
properties.setProperty(prefix+"curt_c5_white_vel", this.curt_c5_white_vel+""); // double // By Claude on 06/13/2026 properties.setProperty(prefix+"curt_c5_white_vel", this.curt_c5_white_vel+""); // double // By Claude on 06/13/2026
properties.setProperty(prefix+"curt_dnn_model", this.curt_dnn_model); // String // By Claude on 06/13/2026 properties.setProperty(prefix+"curt_dnn_model", this.curt_dnn_model); // String // By Claude on 06/13/2026
properties.setProperty(prefix+"curt_dnn_thresh", this.curt_dnn_thresh+""); // double // By Claude on 06/13/2026 properties.setProperty(prefix+"curt_dnn_thresh", this.curt_dnn_thresh+""); // double // By Claude on 06/13/2026
properties.setProperty(prefix+"curt_dnn_stride", this.curt_dnn_stride+""); // int // By Claude on 06/14/2026
properties.setProperty(prefix+"curt_synth_src", this.curt_synth_src+""); // boolean // By Claude on 06/11/2026 properties.setProperty(prefix+"curt_synth_src", this.curt_synth_src+""); // boolean // By Claude on 06/11/2026
properties.setProperty(prefix+"curt_synth_scale", this.curt_synth_scale+""); // double // By Claude on 06/12/2026 properties.setProperty(prefix+"curt_synth_scale", this.curt_synth_scale+""); // double // By Claude on 06/12/2026
properties.setProperty(prefix+"curt_synth_bg", this.curt_synth_bg+""); // boolean // By Claude on 06/12/2026 properties.setProperty(prefix+"curt_synth_bg", this.curt_synth_bg+""); // boolean // By Claude on 06/12/2026
properties.setProperty(prefix+"curt_synth_bg_avg", this.curt_synth_bg_avg+""); // int // By Claude on 06/13/2026
properties.setProperty(prefix+"curt_min_frac", this.curt_min_frac+""); // double // By Claude on 06/12/2026 properties.setProperty(prefix+"curt_min_frac", this.curt_min_frac+""); // double // By Claude on 06/12/2026
properties.setProperty(prefix+"curt_save_c5full", this.curt_save_c5full+""); // boolean properties.setProperty(prefix+"curt_save_c5full", this.curt_save_c5full+""); // boolean
properties.setProperty(prefix+"curt_save_c5rect", this.curt_save_c5rect+""); // boolean properties.setProperty(prefix+"curt_save_c5rect", this.curt_save_c5rect+""); // boolean
properties.setProperty(prefix+"curt_save_c5hyper", this.curt_save_c5hyper+""); // boolean // By Claude on 06/13/2026 properties.setProperty(prefix+"curt_save_c5hyper", this.curt_save_c5hyper+""); // boolean // By Claude on 06/13/2026
properties.setProperty(prefix+"curt_save_select", rectangleToString(curt_save_select)+""); // Rectangle properties.setProperty(prefix+"curt_save_select", rectangleToString(curt_save_select)+""); // Rectangle
properties.setProperty(prefix+"curt_time_from", this.curt_time_from+""); // double // By Claude on 06/14/2026
properties.setProperty(prefix+"curt_time_to", this.curt_time_to+""); // double // By Claude on 06/14/2026
properties.setProperty(prefix+"air_mode_en", this.air_mode_en+""); // boolean properties.setProperty(prefix+"air_mode_en", this.air_mode_en+""); // boolean
properties.setProperty(prefix+"air_sync_ims", this.air_sync_ims+""); // boolean properties.setProperty(prefix+"air_sync_ims", this.air_sync_ims+""); // boolean
...@@ -6788,6 +6803,7 @@ min_str_neib_fpn 0.35 ...@@ -6788,6 +6803,7 @@ min_str_neib_fpn 0.35
if (properties.getProperty(prefix+"curt_pyramid")!=null) this.curt_pyramid=Integer.parseInt(properties.getProperty(prefix+"curt_pyramid")); if (properties.getProperty(prefix+"curt_pyramid")!=null) this.curt_pyramid=Integer.parseInt(properties.getProperty(prefix+"curt_pyramid"));
if (properties.getProperty(prefix+"curt_rleak_pyr")!=null) this.curt_rleak_pyr=Double.parseDouble(properties.getProperty(prefix+"curt_rleak_pyr")); if (properties.getProperty(prefix+"curt_rleak_pyr")!=null) this.curt_rleak_pyr=Double.parseDouble(properties.getProperty(prefix+"curt_rleak_pyr"));
if (properties.getProperty(prefix+"curt_3d3_en")!=null) this.curt_3d3_en=Boolean.parseBoolean(properties.getProperty(prefix+"curt_3d3_en")); // By Claude on 06/14/2026
if (properties.getProperty(prefix+"curt_3d3_mode")!=null) this.curt_3d3_mode=Integer.parseInt(properties.getProperty(prefix+"curt_3d3_mode")); if (properties.getProperty(prefix+"curt_3d3_mode")!=null) this.curt_3d3_mode=Integer.parseInt(properties.getProperty(prefix+"curt_3d3_mode"));
if (properties.getProperty(prefix+"curt_3d3_frac")!=null) this.curt_3d3_frac=Double.parseDouble(properties.getProperty(prefix+"curt_3d3_frac")); if (properties.getProperty(prefix+"curt_3d3_frac")!=null) this.curt_3d3_frac=Double.parseDouble(properties.getProperty(prefix+"curt_3d3_frac"));
if (properties.getProperty(prefix+"curt_3d3_thrsh")!=null) this.curt_3d3_thrsh=Double.parseDouble(properties.getProperty(prefix+"curt_3d3_thrsh")); if (properties.getProperty(prefix+"curt_3d3_thrsh")!=null) this.curt_3d3_thrsh=Double.parseDouble(properties.getProperty(prefix+"curt_3d3_thrsh"));
...@@ -6835,17 +6851,19 @@ min_str_neib_fpn 0.35 ...@@ -6835,17 +6851,19 @@ min_str_neib_fpn 0.35
if (properties.getProperty(prefix+"curt_c5_white_vel")!=null) this.curt_c5_white_vel=Double.parseDouble(properties.getProperty(prefix+"curt_c5_white_vel")); // By Claude on 06/13/2026 if (properties.getProperty(prefix+"curt_c5_white_vel")!=null) this.curt_c5_white_vel=Double.parseDouble(properties.getProperty(prefix+"curt_c5_white_vel")); // By Claude on 06/13/2026
if (properties.getProperty(prefix+"curt_dnn_model")!=null) this.curt_dnn_model=(String) properties.getProperty(prefix+"curt_dnn_model"); // By Claude on 06/13/2026 if (properties.getProperty(prefix+"curt_dnn_model")!=null) this.curt_dnn_model=(String) properties.getProperty(prefix+"curt_dnn_model"); // By Claude on 06/13/2026
if (properties.getProperty(prefix+"curt_dnn_thresh")!=null) this.curt_dnn_thresh=Double.parseDouble(properties.getProperty(prefix+"curt_dnn_thresh")); // By Claude on 06/13/2026 if (properties.getProperty(prefix+"curt_dnn_thresh")!=null) this.curt_dnn_thresh=Double.parseDouble(properties.getProperty(prefix+"curt_dnn_thresh")); // By Claude on 06/13/2026
if (properties.getProperty(prefix+"curt_dnn_stride")!=null) this.curt_dnn_stride=Integer.parseInt(properties.getProperty(prefix+"curt_dnn_stride")); // By Claude on 06/14/2026
if (properties.getProperty(prefix+"curt_synth_src")!=null) this.curt_synth_src=Boolean.parseBoolean(properties.getProperty(prefix+"curt_synth_src")); // By Claude on 06/11/2026 if (properties.getProperty(prefix+"curt_synth_src")!=null) this.curt_synth_src=Boolean.parseBoolean(properties.getProperty(prefix+"curt_synth_src")); // By Claude on 06/11/2026
if (properties.getProperty(prefix+"curt_synth_scale")!=null) this.curt_synth_scale=Double.parseDouble(properties.getProperty(prefix+"curt_synth_scale")); // By Claude on 06/12/2026 if (properties.getProperty(prefix+"curt_synth_scale")!=null) this.curt_synth_scale=Double.parseDouble(properties.getProperty(prefix+"curt_synth_scale")); // By Claude on 06/12/2026
if (properties.getProperty(prefix+"curt_synth_bg")!=null) this.curt_synth_bg=Boolean.parseBoolean(properties.getProperty(prefix+"curt_synth_bg")); // By Claude on 06/12/2026 if (properties.getProperty(prefix+"curt_synth_bg")!=null) this.curt_synth_bg=Boolean.parseBoolean(properties.getProperty(prefix+"curt_synth_bg")); // By Claude on 06/12/2026
if (properties.getProperty(prefix+"curt_synth_bg_avg")!=null) this.curt_synth_bg_avg=Integer.parseInt(properties.getProperty(prefix+"curt_synth_bg_avg")); // By Claude on 06/13/2026
if (properties.getProperty(prefix+"curt_min_frac")!=null) this.curt_min_frac=Double.parseDouble(properties.getProperty(prefix+"curt_min_frac")); // By Claude on 06/12/2026 if (properties.getProperty(prefix+"curt_min_frac")!=null) this.curt_min_frac=Double.parseDouble(properties.getProperty(prefix+"curt_min_frac")); // By Claude on 06/12/2026
if (properties.getProperty(prefix+"curt_save_c5full")!=null) this.curt_save_c5full=Boolean.parseBoolean(properties.getProperty(prefix+"curt_save_c5full")); if (properties.getProperty(prefix+"curt_save_c5full")!=null) this.curt_save_c5full=Boolean.parseBoolean(properties.getProperty(prefix+"curt_save_c5full"));
if (properties.getProperty(prefix+"curt_save_c5rect")!=null) this.curt_save_c5rect=Boolean.parseBoolean(properties.getProperty(prefix+"curt_save_c5rect")); if (properties.getProperty(prefix+"curt_save_c5rect")!=null) this.curt_save_c5rect=Boolean.parseBoolean(properties.getProperty(prefix+"curt_save_c5rect"));
if (properties.getProperty(prefix+"curt_save_c5hyper")!=null) this.curt_save_c5hyper=Boolean.parseBoolean(properties.getProperty(prefix+"curt_save_c5hyper")); // By Claude on 06/13/2026 if (properties.getProperty(prefix+"curt_save_c5hyper")!=null) this.curt_save_c5hyper=Boolean.parseBoolean(properties.getProperty(prefix+"curt_save_c5hyper")); // By Claude on 06/13/2026
if (properties.getProperty(prefix+"curt_save_select")!=null) this.curt_save_select=stringToRectangle((String) properties.getProperty(prefix+"curt_save_select")); if (properties.getProperty(prefix+"curt_save_select")!=null) this.curt_save_select=stringToRectangle((String) properties.getProperty(prefix+"curt_save_select"));
if (properties.getProperty(prefix+"curt_time_from")!=null) this.curt_time_from=Double.parseDouble(properties.getProperty(prefix+"curt_time_from")); // By Claude on 06/14/2026
if (properties.getProperty(prefix+"curt_time_to")!=null) this.curt_time_to=Double.parseDouble(properties.getProperty(prefix+"curt_time_to")); // By Claude on 06/14/2026
} }
public void getProperties(String prefix,Properties properties){ public void getProperties(String prefix,Properties properties){
...@@ -9072,6 +9090,7 @@ min_str_neib_fpn 0.35 ...@@ -9072,6 +9090,7 @@ min_str_neib_fpn 0.35
imp.curt_pyramid = this.curt_pyramid; imp.curt_pyramid = this.curt_pyramid;
imp.curt_rleak_pyr = this.curt_rleak_pyr; imp.curt_rleak_pyr = this.curt_rleak_pyr;
imp.curt_3d3_en = this.curt_3d3_en; // By Claude on 06/14/2026
imp.curt_3d3_mode = this.curt_3d3_mode; imp.curt_3d3_mode = this.curt_3d3_mode;
imp.curt_3d3_frac = this.curt_3d3_frac; imp.curt_3d3_frac = this.curt_3d3_frac;
imp.curt_3d3_thrsh = this.curt_3d3_thrsh; imp.curt_3d3_thrsh = this.curt_3d3_thrsh;
...@@ -9118,16 +9137,18 @@ min_str_neib_fpn 0.35 ...@@ -9118,16 +9137,18 @@ min_str_neib_fpn 0.35
imp.curt_c5_white_vel = this.curt_c5_white_vel; // By Claude on 06/13/2026 imp.curt_c5_white_vel = this.curt_c5_white_vel; // By Claude on 06/13/2026
imp.curt_dnn_model = this.curt_dnn_model; // By Claude on 06/13/2026 imp.curt_dnn_model = this.curt_dnn_model; // By Claude on 06/13/2026
imp.curt_dnn_thresh = this.curt_dnn_thresh; // By Claude on 06/13/2026 imp.curt_dnn_thresh = this.curt_dnn_thresh; // By Claude on 06/13/2026
imp.curt_dnn_stride = this.curt_dnn_stride; // By Claude on 06/14/2026
imp.curt_synth_src = this.curt_synth_src; // By Claude on 06/11/2026 imp.curt_synth_src = this.curt_synth_src; // By Claude on 06/11/2026
imp.curt_synth_scale = this.curt_synth_scale; // By Claude on 06/12/2026 imp.curt_synth_scale = this.curt_synth_scale; // By Claude on 06/12/2026
imp.curt_synth_bg = this.curt_synth_bg; // By Claude on 06/12/2026 imp.curt_synth_bg = this.curt_synth_bg; // By Claude on 06/12/2026
imp.curt_synth_bg_avg = this.curt_synth_bg_avg; // By Claude on 06/13/2026
imp.curt_min_frac = this.curt_min_frac; // By Claude on 06/12/2026 imp.curt_min_frac = this.curt_min_frac; // By Claude on 06/12/2026
imp.curt_save_c5full = this.curt_save_c5full; imp.curt_save_c5full = this.curt_save_c5full;
imp.curt_save_c5rect = this.curt_save_c5rect; imp.curt_save_c5rect = this.curt_save_c5rect;
imp.curt_save_c5hyper = this.curt_save_c5hyper; // By Claude on 06/13/2026 imp.curt_save_c5hyper = this.curt_save_c5hyper; // By Claude on 06/13/2026
imp.curt_save_select = new Rectangle(this.curt_save_select); imp.curt_save_select = new Rectangle(this.curt_save_select);
imp.curt_time_from = this.curt_time_from; // By Claude on 06/14/2026
imp.curt_time_to = this.curt_time_to; // By Claude on 06/14/2026
imp.air_mode_en = this.air_mode_en; imp.air_mode_en = this.air_mode_en;
imp.air_sync_ims = this.air_sync_ims; imp.air_sync_ims = this.air_sync_ims;
......
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