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 {
}
// 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
public static double [][] getEffectiveStrengthMV( // calculate tiles effective strength by the motion vectors. Combine with the target LMA?
......@@ -2025,7 +2069,9 @@ public class CuasMotion {
for (int i = 0; i < centers.length; i++) {
centers[i] = (ms[i] != null) &&
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)) {
......@@ -7955,12 +8001,13 @@ public class CuasMotion {
if (target_sequence_multi == null) {
// final double [][][][]
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
// double boost_accum_pairs = slow_mode? 1.0:4.0; // just for testing, unconditionally boost tracking cameras exposure time
for (; niter < num_cycles; niter++) {
boolean save_filtered_low = intermed_low && (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 (debugLevel > -4) System.out.println ("No undefined tiles left, breaking loop");
break;
......@@ -7987,6 +8034,7 @@ public class CuasMotion {
speed_min, // double speed_min,
speed_pref, // double speed_pref,
speed_boost); // double speed_boost);
syncMotionScoreToSentinels(motion_sequence, target_sequence_multi); // By Claude on 05/07/2026
if (save_filtered_low && debug_more) {
ImagePlus imp_mv_strength = showTargetSequence(
......@@ -7999,7 +8047,7 @@ public class CuasMotion {
}
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,
// 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
......@@ -8047,16 +8095,10 @@ public class CuasMotion {
break;
}
// was motion_scan_filtered
double [][][] targets_nonoverlap = applyFilter( // motion vectors // will have nulls not top try
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
// By Claude on 05/07/2026: sentinels in target_sequence_multi carry motion vector data cloned from motion_sequence
double [][][] targets_nonoverlap = applyFilter(
target_sequence_multi, // double [][][][] target_sequence_multi
filter5); // boolean [][] filter5)
*/
filter5); // int [][] filter5
// Anything remains? we'll see after extension
double [][][] extended_scan = extendMotionScan(
targets_nonoverlap, // final double [][][] motion_scan,
......@@ -8287,7 +8329,7 @@ public class CuasMotion {
if (debugLevel > -4) System.out.println ("Nothing added from last try, breaking the loop.\n");
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) {
System.out.println("Non-centered iteration "+niter+" DONE.\n");
......@@ -8730,8 +8772,8 @@ public class CuasMotion {
final int [] remain,
final int [] passes, // debugging - number of passes required
final int debugLevel){
final boolean use_motion = (motion_sequence != null) ? use_motion_in : false;
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 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
boolean debug_now = (debugLevel<5);
final int num_seq = target_sequence_multi.length;
final int num_tiles = target_sequence_multi[0].length;
......@@ -8764,10 +8806,25 @@ public class CuasMotion {
}
prohibit[ntile] = true;
} 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) {
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
boolean prohibit_this = true;
double [][] targets = target_sequence_multi[nSeq][ntile];
......@@ -8818,6 +8875,7 @@ public class CuasMotion {
boolean ismax = true;
if (select_new) {
if (use_motion) {
if (motion != null) {
check_max_motion:{
double cval = motion[ntile][CuasMotionLMA.RSLT_MSCORE] - min_confidence;
if (cval <= 0) {
......@@ -8839,6 +8897,55 @@ public class CuasMotion {
}
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
double [][] targets = target_sequence_multi[nSeq][ntile];
if (targets != null) {
......@@ -8959,9 +9066,10 @@ public class CuasMotion {
};
}
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);
// 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++) {
threads[ithread] = new Thread() {
public void run() {
......@@ -9462,7 +9570,9 @@ public class CuasMotion {
double ts_fail = tsm[ntarg][CuasMotionLMA.RSLT_FAIL];
int centered = (int) tsm[ntarg][CuasMotionLMA.RSLT_CENTERED];
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]++;
} else if (ts_fail == CuasMotionLMA.FAIL_NONE) {
num_good[nSeq]++;
......
......@@ -124,6 +124,7 @@ public class CuasMotionLMA {
"FLOG-px","FLOG-pY","FLOG-DISP","FLOG-range","infinity",
"GTarget-ID","Vel-away", "Vel-right", "Vel-up","Range-linear"};
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_MOTION = 1; // motion strength/fraction too low
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