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

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: default avatarClaude Sonnet 4.6 <noreply@anthropic.com>
parent 70e96522
Loading
Loading
Loading
Loading
+152 −42
Original line number Diff line number Diff line
@@ -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]++;
+1 −0
Original line number Diff line number Diff line
@@ -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