Commit 079d4237 authored by Andrey Filippov's avatar Andrey Filippov

CLAUDE: Add Layer 2 recurrent accumulation buffer (CuasRecurrentBuffer)

Implements the recurrent 4D (px, py, vx, vy) accumulation buffer per the
2026-06-09 design (cuas_rt_gpu-internal CONTINUE.md, Step 4):

- Update rule: buf = leak*(1-w_eff)*shift(buf, v) + w_eff*conditioned(meas),
  applied every curt_recur_period (default 4) time units so the per-update
  pixel shift is integer-clean (1 px per minimum velocity step).
- shift(): each 4D point moves by its own velocity; predictions leaving the
  ROI are dropped.
- conditioned(): adaptive 4D spreading of filterConv5dROI survivors -
  dimensions spanned by adjacent survivors are "resolved" and not expanded;
  total spread weight per survivor is 1.
- w_eff: fixed curt_recur_w, or per-pixel adaptive w*tanh(strength/wthresh)
  when curt_recur_wthresh > 0 (dark frames then decay by curt_recur_leak
  alone - blind prediction).
- convolve3d() still runs at every time step with unchanged output files;
  the buffer consumes every curt_recur_period-th ROI output (level 0 only).
- New params (curt_recur_en/period/w/leak/wthresh/spread) plumbed through
  IntersceneMatchParameters (dialog, get/set, properties, copy).
- Buffer state snapshots saved as -RECUR-RECT and -RECUR-HYPER-RECT images.

Single-threaded over the ROI for now; optimization deferred by design.
Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
parent 11658a73
......@@ -21,6 +21,7 @@ public class CuasDetectRT {
public static final String SUFFIX_CONV2D = "-CONV2D";
public static final String SUFFIX_CONV3D3 = "-CONV3D3";
public static final String SUFFIX_CONV5D = "-CONV5D";
public static final String SUFFIX_RECUR = "-RECUR"; // By Claude on 06/09/2026
public static final String [] CONV3D3_MODES = {"plain", "winner", "power"};
public static final int CONV3D3_MODE_PLAIN = 0;
public static final int CONV3D3_MODE_WINNER = 1;
......@@ -170,8 +171,14 @@ public class CuasDetectRT {
int curt_vel_suppr_rad =clt_parameters.imp.curt_vel_suppr_rad;
double curt_vel_athresh = clt_parameters.imp.curt_vel_athresh;
double curt_vel_rthresh = clt_parameters.imp.curt_vel_rthresh;
boolean curt_recur_en = clt_parameters.imp.curt_recur_en; // By Claude on 06/09/2026
int curt_recur_period = clt_parameters.imp.curt_recur_period; // By Claude on 06/09/2026
double curt_recur_w = clt_parameters.imp.curt_recur_w; // By Claude on 06/09/2026
double curt_recur_leak = clt_parameters.imp.curt_recur_leak; // By Claude on 06/09/2026
double curt_recur_wthresh =clt_parameters.imp.curt_recur_wthresh; // By Claude on 06/09/2026
double curt_recur_spread = clt_parameters.imp.curt_recur_spread; // By Claude on 06/09/2026
double[] curt_temp_weights = clt_parameters.imp.curt_temp_weights.clone();
boolean curt_save_c5full = clt_parameters.imp.curt_save_c5full;
......@@ -314,6 +321,26 @@ public class CuasDetectRT {
dpixels_5d_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];
// Layer 2 recurrent accumulation buffer (level 0, ROI only). convolve3d() still runs // By Claude on 06/09/2026
// at every time step; the buffer consumes every curt_recur_period-th output. // By Claude on 06/09/2026
boolean run_recur = curt_recur_en && (curt_save_select != null); // By Claude on 06/09/2026
CuasRecurrentBuffer recurrentBuffer = null; // By Claude on 06/09/2026
double [][][][] recur_states = null; // By Claude on 06/09/2026
String [] recur_ts = null; // By Claude on 06/09/2026
int num_recur = 0; // By Claude on 06/09/2026
if (run_recur) { // By Claude on 06/09/2026
recurrentBuffer = new CuasRecurrentBuffer( // By Claude on 06/09/2026
curt_save_select.width, // int roi_width, // By Claude on 06/09/2026
curt_save_select.height, // int roi_height, // By Claude on 06/09/2026
curt_pix_decimate * curt_pix_decimate, // int num_sub, // By Claude on 06/09/2026
curt_vel_radius, // int velocity_radius, // By Claude on 06/09/2026
curt_vel_decimate, // int velocity_decimate, // By Claude on 06/09/2026
curt_recur_period, // int period, // By Claude on 06/09/2026
curt_recur_spread); // double spread) // By Claude on 06/09/2026
int max_recur = num_5d_lev0 / curt_recur_period + 1; // By Claude on 06/09/2026
recur_states = new double[max_recur][][][]; // By Claude on 06/09/2026
recur_ts = new String[max_recur]; // By Claude on 06/09/2026
} // By Claude on 06/09/2026
System.out.println("detectTargets(): will run convolve3d()");
for (int n5d = 0; n5d < num_5d_lev0; n5d++) {
System.out.print ("n5d="+n5d+" ");
......@@ -326,9 +353,9 @@ public class CuasDetectRT {
window,
null,
min_frac_last); // final double min_frac_last){
}
if (curt_save_c5rect && (curt_save_select != null)) {
if ((curt_save_c5rect || run_recur) && (curt_save_select != null)) { // By Claude on 06/09/2026 (added run_recur)
dpixels_5d_roi_pyramid[0][n5d] = cuasRTUtils.convolve3d(
window,
curt_save_select,
......@@ -341,6 +368,15 @@ public class CuasDetectRT {
curt_vel_athresh,
curt_vel_rthresh);
}
if (run_recur && ((n5d % curt_recur_period) == 0)) { // By Claude on 06/09/2026
recurrentBuffer.update( // By Claude on 06/09/2026
dpixels_5d_roi_pyramid[0][n5d], // double [][][] measurement, // By Claude on 06/09/2026
curt_recur_w, // double w, // By Claude on 06/09/2026
curt_recur_leak, // double leak, // By Claude on 06/09/2026
curt_recur_wthresh); // double wthresh) // By Claude on 06/09/2026
recur_states[num_recur] = recurrentBuffer.getBufferCopy(); // By Claude on 06/09/2026
recur_ts[num_recur++] = ts_pyramid[0][n5d + num_hist_5d - 1]; // By Claude on 06/09/2026
} // By Claude on 06/09/2026
}
ts_5d_lev0[n5d] = ts_pyramid[0][n5d + num_hist_5d - 1];
}
......@@ -365,6 +401,27 @@ public class CuasDetectRT {
title_conv5d+"-HYPER-RECT");
QuadCLTCPU.saveImagePlusInDirectory(imp_5d_hyper, getModelDirectory());
}
if (run_recur && (num_recur > 0)) { // By Claude on 06/09/2026
double [][][][] recur_show = new double[num_recur][][][]; // By Claude on 06/09/2026
String [] recur_ts_show = new String[num_recur]; // By Claude on 06/09/2026
System.arraycopy(recur_states, 0, recur_show, 0, num_recur); // By Claude on 06/09/2026
System.arraycopy(recur_ts, 0, recur_ts_show, 0, num_recur); // By Claude on 06/09/2026
String title_recur = title_conv5d+ // By Claude on 06/09/2026
"-RP"+curt_recur_period+"-RW"+d2s(curt_recur_w)+"-RL"+d2s(curt_recur_leak)+ // By Claude on 06/09/2026
"-RWT"+d2s(curt_recur_wthresh)+"-RS"+d2s(curt_recur_spread)+SUFFIX_RECUR; // By Claude on 06/09/2026
ImagePlus imp_recur_rect = cuasRTUtils.showConvKernel5d( // By Claude on 06/09/2026
recur_show, // By Claude on 06/09/2026
curt_save_select, // By Claude on 06/09/2026
recur_ts_show, // By Claude on 06/09/2026
title_recur+"-RECT"); // By Claude on 06/09/2026
QuadCLTCPU.saveImagePlusInDirectory(imp_recur_rect, getModelDirectory()); // By Claude on 06/09/2026
ImagePlus imp_recur_hyper = cuasRTUtils.showConvKernel5dHyperRect( // By Claude on 06/09/2026
recur_show, // By Claude on 06/09/2026
curt_save_select, // By Claude on 06/09/2026
recur_ts_show, // By Claude on 06/09/2026
title_recur+"-HYPER-RECT"); // By Claude on 06/09/2026
QuadCLTCPU.saveImagePlusInDirectory(imp_recur_hyper, getModelDirectory()); // By Claude on 06/09/2026
} // By Claude on 06/09/2026
for (int nlev = 0; nlev < pyramid_levels; nlev++) {
if (save_LoG_pixels) {
String title_conv2d_lev = getBaseName()+SUFFIX_CONV2D+"-PSF"+d2s(cuasRTUtils.getPsfRadius())+
......
package com.elphel.imagej.cuas.rt;
/**
* Layer 2 recurrent accumulation buffer for the CUAS real-time detector.
* By Claude (Fable 5) on 06/09/2026, design decisions by Andrey Filippov (2026-06-09).
*
* Maintains a 4D state (px, py, vx, vy) over an ROI, same shape as the ROI output
* of CuasRTUtils.convolve3d()/filterConv5dROI(): [roi_npix][num_sub][vel_dim*vel_dim].
* The sub-pixel dimension is carried through unchanged (pixel_decimate is currently 1).
*
* Update rule (applied once per `period` time units):
* buf_new = leak * (1 - w_eff) * shift(buf_old, v) + w_eff * conditioned(measurement)
*
* - shift(buf, v): every 4D point moves by its own velocity vector. The pixel shift
* over `period` time units is (vx_off * period / velocity_decimate) - exactly
* integer (1 px per minimum velocity step) when period == velocity_decimate.
* Points predicted outside the ROI are dropped (target left the watched area).
* - conditioned(measurement): adaptive 4D spreading of the filterConv5dROI survivors,
* see condition(). Each survivor contributes its value with total spread weight 1.
* - w_eff: fixed `w` when wthresh <= 0; otherwise per-pixel adaptive
* w_eff = w * tanh(strength / wthresh), where strength is the strongest conditioned
* measurement at that pixel. Dark frames then give w_eff ~ 0 (pure prediction),
* and `leak` alone controls gap survival: leak^n_gap >= 0.2-0.3 recommended.
* With fixed w, the effective per-update gap decay is leak * (1 - w).
*
* This is intentionally a simplified fixed/adaptive-gain filter, NOT a Kalman filter -
* the 4D map state is too large; a proper KF is reserved for the per-target worker stage.
* Single-threaded for now (ROI is small); optimize later.
*/
public class CuasRecurrentBuffer {
private final int roi_width;
private final int roi_height;
private final int num_sub; // pixel_decimate^2
private final int vel_dim; // 2*velocity_radius + 1
private final int velocity_radius;
private final int velocity_decimate;
private final int period; // time units between updates
private final double spread; // weight of an orthogonal 4D neighbor when spreading (diagonals get products)
private double [][][] buffer; // [roi_width*roi_height][num_sub][vel_dim*vel_dim]
private int num_updates = 0;
public CuasRecurrentBuffer (
int roi_width,
int roi_height,
int num_sub,
int velocity_radius,
int velocity_decimate,
int period,
double spread) {
this.roi_width = roi_width;
this.roi_height = roi_height;
this.num_sub = num_sub;
this.velocity_radius = velocity_radius;
this.vel_dim = 2 * velocity_radius + 1;
this.velocity_decimate = velocity_decimate;
this.period = period;
this.spread = spread;
this.buffer = new double [roi_width*roi_height][num_sub][vel_dim*vel_dim];
}
public double [][][] getBuffer() {
return buffer;
}
public double [][][] getBufferCopy() {
double [][][] copy = new double [buffer.length][num_sub][vel_dim*vel_dim];
for (int p = 0; p < buffer.length; p++) for (int s = 0; s < num_sub; s++) {
System.arraycopy(buffer[p][s], 0, copy[p][s], 0, buffer[p][s].length);
}
return copy;
}
public int getNumUpdates() {
return num_updates;
}
/**
* One recurrent update step.
* @param measurement filterConv5dROI() output for the current time step,
* [roi_npix][num_sub][vel_dim*vel_dim]; may be null (dark/skipped
* frame) - then the update is pure decayed prediction.
* @param w measurement weight (fixed gain), 0..1
* @param leak extra decay of the predicted (shifted) buffer per update
* @param wthresh if > 0, per-pixel adaptive gain w_eff = w*tanh(strength/wthresh)
*/
public void update (
double [][][] measurement,
double w,
double leak,
double wthresh) {
double [][][] shifted = shiftByVelocity(buffer);
double [][][] cond = (measurement != null) ? condition(measurement) : null;
for (int p = 0; p < buffer.length; p++) {
double w_eff = w;
if (cond == null) {
w_eff = 0.0;
} else if (wthresh > 0) { // adaptive gain from the strongest conditioned measurement at this pixel
double strength = 0;
for (int s = 0; s < num_sub; s++) for (int v = 0; v < cond[p][s].length; v++) {
if (cond[p][s][v] > strength) strength = cond[p][s][v];
}
w_eff = w * Math.tanh(strength / wthresh);
}
for (int s = 0; s < num_sub; s++) {
for (int v = 0; v < buffer[p][s].length; v++) {
double pred = leak * (1.0 - w_eff) * shifted[p][s][v];
double meas = (cond != null) ? (w_eff * cond[p][s][v]) : 0.0;
buffer[p][s][v] = pred + meas;
}
}
}
num_updates++;
}
/**
* Blind prediction: every 4D point moves by its own velocity, pixel shift =
* velocity * period (integer-clean when period == velocity_decimate).
* Multiple sources landing on the same destination are summed.
*/
private double [][][] shiftByVelocity(double [][][] src) {
double [][][] dst = new double [src.length][num_sub][vel_dim*vel_dim];
for (int p = 0; p < src.length; p++) {
int px = p % roi_width;
int py = p / roi_width;
for (int s = 0; s < num_sub; s++) {
for (int v = 0; v < src[p][s].length; v++) {
double val = src[p][s][v];
if (val == 0) continue;
int vx_off = (v % vel_dim) - velocity_radius;
int vy_off = (v / vel_dim) - velocity_radius;
int dx = (int) Math.round(((double) vx_off * period) / velocity_decimate);
int dy = (int) Math.round(((double) vy_off * period) / velocity_decimate);
int nx = px + dx;
int ny = py + dy;
if ((nx < 0) || (nx >= roi_width) || (ny < 0) || (ny >= roi_height)) {
continue; // predicted out of the ROI - drop
}
dst[ny * roi_width + nx][s][v] += val;
}
}
}
return dst;
}
/**
* Adaptive 4D spreading of the filtered measurement ("conditioning").
* Each survivor (value > 0 after filterConv5dROI) is spread into its 4D
* (px, py, vx, vy) neighbourhood, except along "resolved" dimensions:
* a dimension is resolved when another survivor exists among the 3^4-1
* immediate 4D neighbors offset along that dimension - the survivors already
* span it, so expanding would only blur real resolution.
* - 1 isolated survivor: full 3x3x3x3 hypercube (orthogonal neighbors weight
* `spread`, diagonals `spread^2` etc.).
* - 2 survivors adjacent in velocity: that velocity axis not expanded (2x3x3x3).
* Total contributed weight per survivor is 1 regardless of geometry (weights are
* normalized over the in-bounds part of the adaptive kernel).
*/
private double [][][] condition(double [][][] meas) {
double [][][] out = new double [meas.length][num_sub][vel_dim*vel_dim];
for (int p = 0; p < meas.length; p++) {
int px = p % roi_width;
int py = p / roi_width;
for (int s = 0; s < num_sub; s++) {
for (int v = 0; v < meas[p][s].length; v++) {
double m = meas[p][s][v];
if (m <= 0) continue; // survivors only
int vx = v % vel_dim;
int vy = v / vel_dim;
// Find resolved dimensions: scan the 3^4-1 immediate 4D neighbors for other survivors
boolean res_x = false, res_y = false, res_vx = false, res_vy = false;
for (int dpy = -1; dpy <= 1; dpy++) {
int ny = py + dpy;
if ((ny < 0) || (ny >= roi_height)) continue;
for (int dpx = -1; dpx <= 1; dpx++) {
int nx = px + dpx;
if ((nx < 0) || (nx >= roi_width)) continue;
int np = ny * roi_width + nx;
for (int dvy = -1; dvy <= 1; dvy++) {
int nvy = vy + dvy;
if ((nvy < 0) || (nvy >= vel_dim)) continue;
for (int dvx = -1; dvx <= 1; dvx++) {
int nvx = vx + dvx;
if ((nvx < 0) || (nvx >= vel_dim)) continue;
if ((dpx == 0) && (dpy == 0) && (dvx == 0) && (dvy == 0)) continue;
if (meas[np][s][nvy * vel_dim + nvx] > 0) {
if (dpx != 0) res_x = true;
if (dpy != 0) res_y = true;
if (dvx != 0) res_vx = true;
if (dvy != 0) res_vy = true;
}
}
}
}
}
int rx0 = res_x ? 0 : -1, rx1 = res_x ? 0 : 1;
int ry0 = res_y ? 0 : -1, ry1 = res_y ? 0 : 1;
int rvx0 = res_vx ? 0 : -1, rvx1 = res_vx ? 0 : 1;
int rvy0 = res_vy ? 0 : -1, rvy1 = res_vy ? 0 : 1;
// First pass: total in-bounds weight of the adaptive kernel
double sum_w = 0;
for (int dpy = ry0; dpy <= ry1; dpy++) {
int ny = py + dpy;
if ((ny < 0) || (ny >= roi_height)) continue;
for (int dpx = rx0; dpx <= rx1; dpx++) {
int nx = px + dpx;
if ((nx < 0) || (nx >= roi_width)) continue;
for (int dvy = rvy0; dvy <= rvy1; dvy++) {
int nvy = vy + dvy;
if ((nvy < 0) || (nvy >= vel_dim)) continue;
for (int dvx = rvx0; dvx <= rvx1; dvx++) {
int nvx = vx + dvx;
if ((nvx < 0) || (nvx >= vel_dim)) continue;
sum_w += kernelWeight(dpx, dpy, dvx, dvy);
}
}
}
}
if (sum_w <= 0) continue;
// Second pass: spread m with total weight 1
double scale = m / sum_w;
for (int dpy = ry0; dpy <= ry1; dpy++) {
int ny = py + dpy;
if ((ny < 0) || (ny >= roi_height)) continue;
for (int dpx = rx0; dpx <= rx1; dpx++) {
int nx = px + dpx;
if ((nx < 0) || (nx >= roi_width)) continue;
int np = ny * roi_width + nx;
for (int dvy = rvy0; dvy <= rvy1; dvy++) {
int nvy = vy + dvy;
if ((nvy < 0) || (nvy >= vel_dim)) continue;
for (int dvx = rvx0; dvx <= rvx1; dvx++) {
int nvx = vx + dvx;
if ((nvx < 0) || (nvx >= vel_dim)) continue;
out[np][s][nvy * vel_dim + nvx] += scale * kernelWeight(dpx, dpy, dvx, dvy);
}
}
}
}
}
}
}
return out;
}
// Unnormalized spreading weight: center 1, each non-zero offset dimension multiplies by `spread`
// (orthogonal neighbor = spread, 2D diagonal = spread^2, ..., full 4D diagonal = spread^4).
private double kernelWeight(int dpx, int dpy, int dvx, int dvy) {
double w = 1.0;
if (dpx != 0) w *= spread;
if (dpy != 0) w *= spread;
if (dvx != 0) w *= spread;
if (dvy != 0) w *= spread;
return w;
}
}
......@@ -1148,7 +1148,14 @@ min_str_neib_fpn 0.35
public int curt_vel_suppr_rad = 3; // suppress weaker in this radius
public double curt_vel_athresh = 1.0; // absolute threshold: discard this pixel-velocity if it has immediate neighbor (of 80=81-1) stronger by this value
public double curt_vel_rthresh = 0.1; // relative threshold: discard this pixel-velocity if it has immediate neighbor stronger by this value times stronger value
public double[] curt_temp_weights = {1,1,1,1,1}; // historic weights for 5D convolution ([0] is the latest sample)
public double[] curt_temp_weights = {1,1,1,1,1}; // historic weights for 5D convolution ([0] is the latest sample)
// === Layer 2 recurrent accumulation buffer === // By Claude on 06/09/2026
public boolean curt_recur_en = true; // enable recurrent accumulation buffer (Layer 2) // By Claude on 06/09/2026
public int curt_recur_period = 4; // time units between buffer updates (= curt_vel_decimate for integer-pixel shifts) // By Claude on 06/09/2026
public double curt_recur_w = 0.25; // measurement weight w: buf = leak*(1-w)*shift(buf) + w*conditioned(meas) // By Claude on 06/09/2026
public double curt_recur_leak = 1.0; // extra decay of the shifted prediction per update (gap decay = leak*(1-w) for fixed w) // By Claude on 06/09/2026
public double curt_recur_wthresh = 0.0; // if >0: adaptive per-pixel weight w_eff=w*tanh(strength/wthresh); 0 - fixed w // By Claude on 06/09/2026
public double curt_recur_spread = 0.5; // orthogonal 4D neighbor weight in adaptive spreading (diagonals get products) // By Claude on 06/09/2026
// debug/saving images
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]
......@@ -3424,7 +3431,21 @@ min_str_neib_fpn 0.35
"Discard this pixel-velocity if it has immediate neighbor stronger by this value times stronger value.");
gd.addStringField ("Historic weight for 5D convolution", IntersceneMatchParameters.doublesToString(curt_temp_weights), 80,
"Historic weights for 5D convolution ([0] is the latest sample). Will be normalized");
gd.addMessage("=== Layer 2 recurrent accumulation buffer ==="); // By Claude on 06/09/2026
gd.addCheckbox ("Enable recurrent buffer", this.curt_recur_en, // By Claude on 06/09/2026
"Maintain a recurrent 4D (pixel, velocity) accumulation buffer over the selected rectangle."); // By Claude on 06/09/2026
gd.addNumericField("Update period", this.curt_recur_period, 0,3,"time units", // By Claude on 06/09/2026
"Time units between buffer updates. Equal to velocity decimation for integer-pixel shifts (1 px per minimum velocity)."); // By Claude on 06/09/2026
gd.addNumericField("Measurement weight", this.curt_recur_w, 6,8,"", // By Claude on 06/09/2026
"Update rule: buf = leak*(1-w)*shift(buf) + w*conditioned(measurement). Gap decay = leak*(1-w) with fixed w."); // By Claude on 06/09/2026
gd.addNumericField("Prediction leak", this.curt_recur_leak, 6,8,"", // By Claude on 06/09/2026
"Extra decay of the velocity-shifted prediction per update. Keep leak^n_gap >= 0.2-0.3 for expected dark gaps."); // By Claude on 06/09/2026
gd.addNumericField("Adaptive weight threshold", this.curt_recur_wthresh, 6,8,"", // By Claude on 06/09/2026
"If >0: per-pixel adaptive weight w_eff = w*tanh(strength/wthresh), dark frames give w_eff~0 (pure prediction). 0 - fixed w."); // By Claude on 06/09/2026
gd.addNumericField("4D spreading weight", this.curt_recur_spread, 6,8,"", // By Claude on 06/09/2026
"Weight of orthogonal 4D neighbors when spreading each surviving measurement point (diagonals get products of this)."); // By Claude on 06/09/2026
gd.addMessage("=== Debug, saving images ===");
gd.addCheckbox ("Save full velocities hyperstack", this.curt_save_c5full,
"Save fine velocities [direction][scene][subpixels].");
......@@ -4929,7 +4950,14 @@ min_str_neib_fpn 0.35
this.curt_vel_rthresh = gd.getNextNumber();
this.curt_temp_weights = IntersceneMatchParameters. StringToDoubles(gd.getNextString(), 0);
this.curt_recur_en = gd.getNextBoolean(); // By Claude on 06/09/2026
this.curt_recur_period = (int) gd.getNextNumber(); // By Claude on 06/09/2026
this.curt_recur_w = gd.getNextNumber(); // By Claude on 06/09/2026
this.curt_recur_leak = gd.getNextNumber(); // By Claude on 06/09/2026
this.curt_recur_wthresh = gd.getNextNumber(); // By Claude on 06/09/2026
this.curt_recur_spread = gd.getNextNumber(); // By Claude on 06/09/2026
this.curt_save_c5full = gd.getNextBoolean();
this.curt_save_c5rect = gd.getNextBoolean();
this.curt_save_select = stringToRectangle(gd.getNextString());// Rectangle
......@@ -6259,7 +6287,14 @@ min_str_neib_fpn 0.35
properties.setProperty(prefix+"curt_vel_athresh", this.curt_vel_athresh+""); // double
properties.setProperty(prefix+"curt_vel_rthresh", this.curt_vel_rthresh+""); // double
properties.setProperty(prefix+"curt_temp_weights", IntersceneMatchParameters.doublesToString(this.curt_temp_weights));
properties.setProperty(prefix+"curt_recur_en", this.curt_recur_en+""); // boolean // By Claude on 06/09/2026
properties.setProperty(prefix+"curt_recur_period", this.curt_recur_period+""); // int // By Claude on 06/09/2026
properties.setProperty(prefix+"curt_recur_w", this.curt_recur_w+""); // double // By Claude on 06/09/2026
properties.setProperty(prefix+"curt_recur_leak", this.curt_recur_leak+""); // double // By Claude on 06/09/2026
properties.setProperty(prefix+"curt_recur_wthresh", this.curt_recur_wthresh+""); // double // By Claude on 06/09/2026
properties.setProperty(prefix+"curt_recur_spread", this.curt_recur_spread+""); // double // By Claude on 06/09/2026
properties.setProperty(prefix+"curt_save_c5full", this.curt_save_c5full+""); // boolean
properties.setProperty(prefix+"curt_save_c5rect", this.curt_save_c5rect+""); // boolean
properties.setProperty(prefix+"curt_save_select", rectangleToString(curt_save_select)+""); // Rectangle
......@@ -7576,7 +7611,14 @@ min_str_neib_fpn 0.35
if (properties.getProperty(prefix+"curt_vel_rthresh")!=null) this.curt_vel_rthresh=Double.parseDouble(properties.getProperty(prefix+"curt_vel_rthresh"));
if (properties.getProperty(prefix+"curt_temp_weights")!=null) this.curt_temp_weights= IntersceneMatchParameters.StringToDoubles(properties.getProperty(prefix+"curt_temp_weights"),0); // use 0
if (properties.getProperty(prefix+"curt_recur_en")!=null) this.curt_recur_en=Boolean.parseBoolean(properties.getProperty(prefix+"curt_recur_en")); // By Claude on 06/09/2026
if (properties.getProperty(prefix+"curt_recur_period")!=null) this.curt_recur_period=Integer.parseInt(properties.getProperty(prefix+"curt_recur_period")); // By Claude on 06/09/2026
if (properties.getProperty(prefix+"curt_recur_w")!=null) this.curt_recur_w=Double.parseDouble(properties.getProperty(prefix+"curt_recur_w")); // By Claude on 06/09/2026
if (properties.getProperty(prefix+"curt_recur_leak")!=null) this.curt_recur_leak=Double.parseDouble(properties.getProperty(prefix+"curt_recur_leak")); // By Claude on 06/09/2026
if (properties.getProperty(prefix+"curt_recur_wthresh")!=null) this.curt_recur_wthresh=Double.parseDouble(properties.getProperty(prefix+"curt_recur_wthresh")); // By Claude on 06/09/2026
if (properties.getProperty(prefix+"curt_recur_spread")!=null) this.curt_recur_spread=Double.parseDouble(properties.getProperty(prefix+"curt_recur_spread")); // By Claude on 06/09/2026
if (properties.getProperty(prefix+"curt_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_select")!=null) this.curt_save_select=stringToRectangle((String) properties.getProperty(prefix+"curt_save_select"));
......@@ -8869,7 +8911,14 @@ min_str_neib_fpn 0.35
imp.curt_vel_athresh = this.curt_vel_athresh;
imp.curt_vel_rthresh = this.curt_vel_rthresh;
imp.curt_temp_weights = this.curt_temp_weights.clone();
imp.curt_recur_en = this.curt_recur_en; // By Claude on 06/09/2026
imp.curt_recur_period = this.curt_recur_period; // By Claude on 06/09/2026
imp.curt_recur_w = this.curt_recur_w; // By Claude on 06/09/2026
imp.curt_recur_leak = this.curt_recur_leak; // By Claude on 06/09/2026
imp.curt_recur_wthresh = this.curt_recur_wthresh; // By Claude on 06/09/2026
imp.curt_recur_spread = this.curt_recur_spread; // By Claude on 06/09/2026
imp.curt_save_c5full = this.curt_save_c5full;
imp.curt_save_c5rect = this.curt_save_c5rect;
imp.curt_save_select = new Rectangle(this.curt_save_select);
......
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