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

Claude: FAIL_PENDING refactoring — migrate first loop from motion_sequence to target_sequence_multi

- Add FAIL_PENDING=-1 sentinel constant to CuasMotionLMA
- Add initFromMotionSequence(): clones motion entries into target_sequence_multi as sentinels
  (RSLT_FAIL=FAIL_PENDING, RSLT_CENTERED=CENTERED_NO, RSLT_QSCORE=initial RSLT_MSCORE)
- Add syncMotionScoreToSentinels(): keeps sentinel QSCORE current after each getEffectiveStrengthMV
- Decouple use_motion/select_new flags in filter5Targets from motion_sequence==null
- Add motion==null paths in filter5Targets: prohibit setup and is-max ranking for FAIL_PENDING sentinels
- Extend filter5Targets marking step to cover FAIL_PENDING sentinels (use_motion=true, motion==null)
- Update extendMotionScan center-detection to accept FAIL_PENDING as valid center state
- First loop in locateAndFreezeTargetsMulti: call initFromMotionSequence, syncMotionScoreToSentinels;
  pass null to filter5Targets; use applyFilter(target_sequence_multi); use getRemain(target_sequence_multi)
- getRemain(target_sequence_multi): count FAIL_PENDING+CENTERED_NO in num_noncentered
Co-Authored-By: 's avatarClaude Sonnet 4.6 <noreply@anthropic.com>
parent 70e96522
...@@ -1894,6 +1894,50 @@ public class CuasMotion { ...@@ -1894,6 +1894,50 @@ public class CuasMotion {
} }
// By Claude on 05/07/2026: pre-populate target_sequence_multi with FAIL_PENDING sentinels from motion_sequence.
// Each sentinel is a clone of the motion entry with RSLT_FAIL=FAIL_PENDING, RSLT_CENTERED=CENTERED_NO,
// RSLT_QSCORE initialized from RSLT_MSCORE. Preserves VX/VY/X/Y and all other motion fields.
public static void initFromMotionSequence(
final double [][][] motion_sequence,
final double [][][][] target_sequence_multi) {
final int num_seq = motion_sequence.length;
final int num_tiles = motion_sequence[0].length;
for (int nSeq = 0; nSeq < num_seq; nSeq++) {
for (int ntile = 0; ntile < num_tiles; ntile++) {
if (motion_sequence[nSeq][ntile] != null && target_sequence_multi[nSeq][ntile] == null) {
double [] sentinel = motion_sequence[nSeq][ntile].clone();
sentinel[CuasMotionLMA.RSLT_FAIL] = CuasMotionLMA.FAIL_PENDING;
sentinel[CuasMotionLMA.RSLT_CENTERED] = CuasMotionLMA.CENTERED_NO;
sentinel[CuasMotionLMA.RSLT_QSCORE] = motion_sequence[nSeq][ntile][CuasMotionLMA.RSLT_MSCORE];
target_sequence_multi[nSeq][ntile] = new double [][] {sentinel};
}
}
}
}
// By Claude on 05/07/2026: after each getEffectiveStrengthMV call, keep RSLT_QSCORE in FAIL_PENDING sentinels
// current with the freshly computed RSLT_MSCORE so filter5 ranks by up-to-date motion confidence.
public static void syncMotionScoreToSentinels(
final double [][][] motion_sequence,
final double [][][][] target_sequence_multi) {
final int num_seq = motion_sequence.length;
final int num_tiles = motion_sequence[0].length;
for (int nSeq = 0; nSeq < num_seq; nSeq++) {
for (int ntile = 0; ntile < num_tiles; ntile++) {
double [] mv = motion_sequence[nSeq][ntile];
if (mv != null && target_sequence_multi[nSeq][ntile] != null) {
for (double [] entry : target_sequence_multi[nSeq][ntile]) {
if (entry != null &&
entry[CuasMotionLMA.RSLT_FAIL] == CuasMotionLMA.FAIL_PENDING &&
entry[CuasMotionLMA.RSLT_CENTERED] == CuasMotionLMA.CENTERED_NO) {
entry[CuasMotionLMA.RSLT_QSCORE] = mv[CuasMotionLMA.RSLT_MSCORE];
}
}
}
}
}
}
// fills out additional fields in target_coords // fills out additional fields in target_coords
public static double [][] getEffectiveStrengthMV( // calculate tiles effective strength by the motion vectors. Combine with the target LMA? public static double [][] getEffectiveStrengthMV( // calculate tiles effective strength by the motion vectors. Combine with the target LMA?
...@@ -2025,7 +2069,9 @@ public class CuasMotion { ...@@ -2025,7 +2069,9 @@ public class CuasMotion {
for (int i = 0; i < centers.length; i++) { for (int i = 0; i < centers.length; i++) {
centers[i] = (ms[i] != null) && centers[i] = (ms[i] != null) &&
Double.isNaN(ms[i][CuasMotionLMA.RSLT_STRONGER]) && // should not have stronger neighbor Double.isNaN(ms[i][CuasMotionLMA.RSLT_STRONGER]) && // should not have stronger neighbor
(Double.isNaN(ms[i][CuasMotionLMA.RSLT_FAIL]) || (ms[i][CuasMotionLMA.RSLT_FAIL]==CuasMotionLMA.FAIL_NONE)); (Double.isNaN(ms[i][CuasMotionLMA.RSLT_FAIL]) ||
(ms[i][CuasMotionLMA.RSLT_FAIL]==CuasMotionLMA.FAIL_NONE) ||
(ms[i][CuasMotionLMA.RSLT_FAIL]==CuasMotionLMA.FAIL_PENDING)); // By Claude on 05/07/2026
} }
} }
if ((filtered != null) && (filtered[nSeq] == null)) { if ((filtered != null) && (filtered[nSeq] == null)) {
...@@ -7955,12 +8001,13 @@ public class CuasMotion { ...@@ -7955,12 +8001,13 @@ public class CuasMotion {
if (target_sequence_multi == null) { if (target_sequence_multi == null) {
// final double [][][][] // final double [][][][]
target_sequence_multi = new double [num_seq][num_tiles][][]; target_sequence_multi = new double [num_seq][num_tiles][][];
initFromMotionSequence(motion_sequence, target_sequence_multi); // By Claude on 05/07/2026
// first pass, using non-centered targets // first pass, using non-centered targets
// double boost_accum_pairs = slow_mode? 1.0:4.0; // just for testing, unconditionally boost tracking cameras exposure time // double boost_accum_pairs = slow_mode? 1.0:4.0; // just for testing, unconditionally boost tracking cameras exposure time
for (; niter < num_cycles; niter++) { for (; niter < num_cycles; niter++) {
boolean save_filtered_low = intermed_low && (niter < iter_show); boolean save_filtered_low = intermed_low && (niter < iter_show);
boolean save_filtered_high = intermed_high && (niter < iter_show); boolean save_filtered_high = intermed_high && (niter < iter_show);
totals = getRemain(motion_sequence, target_sequence_multi, num_all, num_undef, num_good, num_bad); totals = getRemain(target_sequence_multi, num_all, num_undef, num_good, num_bad); // By Claude on 05/07/2026
if (totals[TOTALS_UNDEFINED] == 0) { if (totals[TOTALS_UNDEFINED] == 0) {
if (debugLevel > -4) System.out.println ("No undefined tiles left, breaking loop"); if (debugLevel > -4) System.out.println ("No undefined tiles left, breaking loop");
break; break;
...@@ -7987,6 +8034,7 @@ public class CuasMotion { ...@@ -7987,6 +8034,7 @@ public class CuasMotion {
speed_min, // double speed_min, speed_min, // double speed_min,
speed_pref, // double speed_pref, speed_pref, // double speed_pref,
speed_boost); // double speed_boost); speed_boost); // double speed_boost);
syncMotionScoreToSentinels(motion_sequence, target_sequence_multi); // By Claude on 05/07/2026
if (save_filtered_low && debug_more) { if (save_filtered_low && debug_more) {
ImagePlus imp_mv_strength = showTargetSequence( ImagePlus imp_mv_strength = showTargetSequence(
...@@ -7999,7 +8047,7 @@ public class CuasMotion { ...@@ -7999,7 +8047,7 @@ public class CuasMotion {
} }
int [][] filter5 = filter5Targets( // will ignore failed tiles int [][] filter5 = filter5Targets( // will ignore failed tiles
motion_sequence, // final double [][][] target_sequence, null, // By Claude on 05/07/2026: use FAIL_PENDING sentinels in target_sequence_multi
target_sequence_multi, // final double [][][][] target_sequence_multi, target_sequence_multi, // final double [][][][] target_sequence_multi,
// if use motion and select_new will only consider tiles (and compare motion scores in motion_sequence) that have nulls in target_sequence_multi[nseq][ntile] // if use motion and select_new will only consider tiles (and compare motion scores in motion_sequence) that have nulls in target_sequence_multi[nseq][ntile]
// if not use motion and select_new will only consider (and compare scores) tiles that have [RSLT_CENTERED] = 0.0 // if not use motion and select_new will only consider (and compare scores) tiles that have [RSLT_CENTERED] = 0.0
...@@ -8047,16 +8095,10 @@ public class CuasMotion { ...@@ -8047,16 +8095,10 @@ public class CuasMotion {
break; break;
} }
// was motion_scan_filtered // By Claude on 05/07/2026: sentinels in target_sequence_multi carry motion vector data cloned from motion_sequence
double [][][] targets_nonoverlap = applyFilter( // motion vectors // will have nulls not top try double [][][] targets_nonoverlap = applyFilter(
motion_sequence, // double [][][] motion_scan,
filter5); // boolean [][] filter5)
// Here target_sequence_multi is yet empty
/*
double [][][] targets_nonoverlap = applyFilter( // motion vectors // will have nulls not top try
target_sequence_multi, // double [][][][] target_sequence_multi target_sequence_multi, // double [][][][] target_sequence_multi
filter5); // boolean [][] filter5) filter5); // int [][] filter5
*/
// Anything remains? we'll see after extension // Anything remains? we'll see after extension
double [][][] extended_scan = extendMotionScan( double [][][] extended_scan = extendMotionScan(
targets_nonoverlap, // final double [][][] motion_scan, targets_nonoverlap, // final double [][][] motion_scan,
...@@ -8287,7 +8329,7 @@ public class CuasMotion { ...@@ -8287,7 +8329,7 @@ public class CuasMotion {
if (debugLevel > -4) System.out.println ("Nothing added from last try, breaking the loop.\n"); if (debugLevel > -4) System.out.println ("Nothing added from last try, breaking the loop.\n");
break; // breaks here break; // breaks here
} }
totals = getRemain(motion_sequence, target_sequence_multi, num_all, num_undef, num_good, num_bad); totals = getRemain(target_sequence_multi, num_all, num_undef, num_good, num_bad); // By Claude on 05/07/2026
if (debugLevel > -4) printStats ("After iteration "+niter, true, num_all, num_undef, num_good, num_bad); if (debugLevel > -4) printStats ("After iteration "+niter, true, num_all, num_undef, num_good, num_bad);
if (debugLevel > -4) { if (debugLevel > -4) {
System.out.println("Non-centered iteration "+niter+" DONE.\n"); System.out.println("Non-centered iteration "+niter+" DONE.\n");
...@@ -8730,8 +8772,8 @@ public class CuasMotion { ...@@ -8730,8 +8772,8 @@ public class CuasMotion {
final int [] remain, final int [] remain,
final int [] passes, // debugging - number of passes required final int [] passes, // debugging - number of passes required
final int debugLevel){ final int debugLevel){
final boolean use_motion = (motion_sequence != null) ? use_motion_in : false; final boolean use_motion = use_motion_in; // By Claude on 05/07/2026: decouple from motion_sequence presence
final boolean select_new = select_new_in; // By Claude on 05/07/2026: decouple from motion_sequence so null works with select_new=true final boolean select_new = select_new_in; // By Claude on 05/07/2026: decouple from motion_sequence
boolean debug_now = (debugLevel<5); boolean debug_now = (debugLevel<5);
final int num_seq = target_sequence_multi.length; final int num_seq = target_sequence_multi.length;
final int num_tiles = target_sequence_multi[0].length; final int num_tiles = target_sequence_multi[0].length;
...@@ -8764,10 +8806,25 @@ public class CuasMotion { ...@@ -8764,10 +8806,25 @@ public class CuasMotion {
} }
prohibit[ntile] = true; prohibit[ntile] = true;
} else if (select_new){ } else if (select_new){
if (use_motion ) { // && (target_sequence_multi[nSeq][ntile] != null) if (use_motion ) {
if (motion != null) { // original path: prohibit already-tested tiles
if (target_sequence_multi[nSeq][ntile] != null) { if (target_sequence_multi[nSeq][ntile] != null) {
prohibit[ntile] = true; prohibit[ntile] = true;
} }
} else { // By Claude on 05/07/2026: motion==null, find FAIL_PENDING+CENTERED_NO sentinels
boolean has_pending = false;
double [][] targets = target_sequence_multi[nSeq][ntile];
if (targets != null) {
for (int i = 0; i < targets.length; i++) if (targets[i] != null) {
if (targets[i][CuasMotionLMA.RSLT_FAIL] == CuasMotionLMA.FAIL_PENDING &&
targets[i][CuasMotionLMA.RSLT_CENTERED] == CuasMotionLMA.CENTERED_NO) {
has_pending = true;
break;
}
}
}
prohibit[ntile] |= !has_pending;
}
} else { // !use_motion - find if it has at least one good with that have [RSLT_CENTERED] = 0.0 } else { // !use_motion - find if it has at least one good with that have [RSLT_CENTERED] = 0.0
boolean prohibit_this = true; boolean prohibit_this = true;
double [][] targets = target_sequence_multi[nSeq][ntile]; double [][] targets = target_sequence_multi[nSeq][ntile];
...@@ -8818,6 +8875,7 @@ public class CuasMotion { ...@@ -8818,6 +8875,7 @@ public class CuasMotion {
boolean ismax = true; boolean ismax = true;
if (select_new) { if (select_new) {
if (use_motion) { if (use_motion) {
if (motion != null) {
check_max_motion:{ check_max_motion:{
double cval = motion[ntile][CuasMotionLMA.RSLT_MSCORE] - min_confidence; double cval = motion[ntile][CuasMotionLMA.RSLT_MSCORE] - min_confidence;
if (cval <= 0) { if (cval <= 0) {
...@@ -8839,6 +8897,55 @@ public class CuasMotion { ...@@ -8839,6 +8897,55 @@ public class CuasMotion {
} }
filter5[nSeq][ntile] = 0; filter5[nSeq][ntile] = 0;
} }
} else { // By Claude on 05/07/2026: motion==null, rank FAIL_PENDING+CENTERED_NO sentinels by QSCORE
double [][] targets = target_sequence_multi[nSeq][ntile];
if (targets != null) {
int best_pending = -1;
check_max_pending:{
for (int ntarg = 0; ntarg < targets.length; ntarg++) if (targets[ntarg] != null &&
targets[ntarg][CuasMotionLMA.RSLT_FAIL] == CuasMotionLMA.FAIL_PENDING &&
targets[ntarg][CuasMotionLMA.RSLT_CENTERED] == CuasMotionLMA.CENTERED_NO) {
double qval = targets[ntarg][CuasMotionLMA.RSLT_QSCORE];
if ((best_pending < 0) || (qval > targets[best_pending][CuasMotionLMA.RSLT_QSCORE])) {
best_pending = ntarg;
}
}
if (best_pending < 0) {
ismax = false;
prohibit[ntile] = true;
break check_max_pending;
}
double cval = targets[best_pending][CuasMotionLMA.RSLT_QSCORE] - min_confidence;
if (cval <= 0) {
ismax = false;
prohibit[ntile] = true;
break check_max_pending;
}
for (int dy = -range; dy <= range; dy++) {
for (int dx = -range; dx <= range; dx++) {
int ntile1 = tn.getNeibIndex(ntile, dx, dy);
if ((ntile1 >= 0) && !prohibit[ntile1]) {
double [][] other_targets = target_sequence_multi[nSeq][ntile1];
if (other_targets != null) {
for (int ntarg = 0; ntarg < other_targets.length; ntarg++) if (other_targets[ntarg] != null &&
other_targets[ntarg][CuasMotionLMA.RSLT_FAIL] == CuasMotionLMA.FAIL_PENDING &&
other_targets[ntarg][CuasMotionLMA.RSLT_CENTERED] == CuasMotionLMA.CENTERED_NO) {
double val = other_targets[ntarg][CuasMotionLMA.RSLT_QSCORE];
if (val > cval + min_confidence) {
ismax = false;
break check_max_pending;
}
}
}
}
}
}
filter5[nSeq][ntile] = best_pending;
} // check_max_pending
} else {
prohibit[ntile] = true;
}
}
} else { // not using motion, only consider non-centered good tiles } else { // not using motion, only consider non-centered good tiles
double [][] targets = target_sequence_multi[nSeq][ntile]; double [][] targets = target_sequence_multi[nSeq][ntile];
if (targets != null) { if (targets != null) {
...@@ -8959,9 +9066,10 @@ public class CuasMotion { ...@@ -8959,9 +9066,10 @@ public class CuasMotion {
}; };
} }
ImageDtt.startAndJoin(threads); ImageDtt.startAndJoin(threads);
if (select_new && !use_motion) { // By Claude on 05/07/2026: also mark FAIL_PENDING sentinels when use_motion=true, motion==null
if (select_new && (!use_motion || (motion_sequence == null))) {
ai.set(0); ai.set(0);
// mark selected (non-centered) targets as "used" // mark selected targets as "used" (CENTERED_USED): FAIL_NONE+CENTERED_NO or FAIL_PENDING+CENTERED_NO
for (int ithread = 0; ithread < threads.length; ithread++) { for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() { threads[ithread] = new Thread() {
public void run() { public void run() {
...@@ -9462,7 +9570,9 @@ public class CuasMotion { ...@@ -9462,7 +9570,9 @@ public class CuasMotion {
double ts_fail = tsm[ntarg][CuasMotionLMA.RSLT_FAIL]; double ts_fail = tsm[ntarg][CuasMotionLMA.RSLT_FAIL];
int centered = (int) tsm[ntarg][CuasMotionLMA.RSLT_CENTERED]; int centered = (int) tsm[ntarg][CuasMotionLMA.RSLT_CENTERED];
if (centered != CuasMotionLMA.CENTERED_USED) { if (centered != CuasMotionLMA.CENTERED_USED) {
if ((centered == CuasMotionLMA.CENTERED_NO) && (ts_fail == CuasMotionLMA.FAIL_NONE)) { // only count good uncentered as undefined if (ts_fail == CuasMotionLMA.FAIL_PENDING) { // By Claude on 05/07/2026: unprocessed sentinel
num_noncentered[nSeq]++;
} else if ((centered == CuasMotionLMA.CENTERED_NO) && (ts_fail == CuasMotionLMA.FAIL_NONE)) {
num_noncentered[nSeq]++; num_noncentered[nSeq]++;
} else if (ts_fail == CuasMotionLMA.FAIL_NONE) { } else if (ts_fail == CuasMotionLMA.FAIL_NONE) {
num_good[nSeq]++; num_good[nSeq]++;
......
...@@ -124,6 +124,7 @@ public class CuasMotionLMA { ...@@ -124,6 +124,7 @@ public class CuasMotionLMA {
"FLOG-px","FLOG-pY","FLOG-DISP","FLOG-range","infinity", "FLOG-px","FLOG-pY","FLOG-DISP","FLOG-range","infinity",
"GTarget-ID","Vel-away", "Vel-right", "Vel-up","Range-linear"}; "GTarget-ID","Vel-away", "Vel-right", "Vel-up","Range-linear"};
public static final String EXTRA_SLICE_DISCARD_ON_LOAD = "Targets"; public static final String EXTRA_SLICE_DISCARD_ON_LOAD = "Targets";
public static final int FAIL_PENDING = -1; // By Claude on 05/07/2026: motion sentinel — tile tracked but not yet processed by LMA
public static final int FAIL_NONE = 0; public static final int FAIL_NONE = 0;
public static final int FAIL_MOTION = 1; // motion strength/fraction too low public static final int FAIL_MOTION = 1; // motion strength/fraction too low
public static final int FAIL_NO_MAX = 2; // no suitable local maximum public static final int FAIL_NO_MAX = 2; // no suitable local maximum
......
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