@@ -434,25 +434,27 @@ def _check_sequences_duplication(
434434def _check_sequences_zigzag (
435435 input_sequences : T .Sequence [PointSequence ],
436436 window_size : int = 5 ,
437- backtrack_threshold : float = 0.8 ,
438- min_backtracks : int = 1 ,
437+ deviation_threshold : float = 0.8 ,
438+ min_deviations : int = 1 ,
439439 min_distance : float = 50.0 ,
440440) -> tuple [list [PointSequence ], list [types .ErrorMetadata ]]:
441441 """
442442 Check for zig-zag GPS patterns where images jump back and forth between locations.
443443
444- Detects spatial backtracking - when an image returns closer to earlier images
444+ Detects spatial deviations - when an image returns closer to earlier images
445445 than the previous image was. This catches zig-zag patterns where the sequence
446446 jumps to a different location and then returns.
447447
448+ Only marks deviation points as errors, keeping the main path intact.
449+
448450 Args:
449451 input_sequences: List of image sequences to check
450- window_size: Number of images to look back when checking for backtracking
451- backtrack_threshold : Ratio threshold - if dist_curr < dist_prev * threshold,
452- it's considered backtracking
453- min_backtracks : Minimum number of backtrack events to fail the sequence
452+ window_size: Number of images to look back when checking for deviations
453+ deviation_threshold : Ratio threshold - if dist_curr < dist_prev * threshold,
454+ it's considered a deviation
455+ min_deviations : Minimum number of deviation events to mark errors
454456 min_distance: Minimum distance (in meters) between consecutive images (prev to curr)
455- to consider for backtracking . This filters out small-scale
457+ to consider for deviation detection . This filters out small-scale
456458 movements like U-turns.
457459 """
458460 output_sequences : list [PointSequence ] = []
@@ -464,8 +466,8 @@ def _check_sequences_zigzag(
464466 output_sequences .append (sequence )
465467 continue
466468
467- backtrack_count = 0
468- backtrack_locations : list [ str ] = []
469+ # Track which indices are detected as deviations
470+ deviation_indices : set [ int ] = set ()
469471
470472 for i in range (window_size , len (sequence )):
471473 curr = sequence [i ]
@@ -481,36 +483,70 @@ def _check_sequences_zigzag(
481483 # Distance from previous image to reference
482484 dist_prev = geo .gps_distance ((prev .lat , prev .lon ), (ref .lat , ref .lon ))
483485
484- # Backtracking : current is closer to reference than previous was
486+ # Deviation : current is closer to reference than previous was
485487 # Only check if the jump between prev and curr is above min_distance
486488 if (
487489 dist_prev_curr > min_distance
488- and dist_curr < dist_prev * backtrack_threshold
490+ and dist_curr < dist_prev * deviation_threshold
489491 ):
490- backtrack_count += 1
491- backtrack_locations .append (curr .filename .name )
492492 LOG .debug (
493- f"Potential zigzag at { curr .filename .name } : "
494- f"dist_curr={ dist_curr :.1f} m < dist_prev={ dist_prev :.1f} m * { backtrack_threshold } , "
493+ f"Zigzag detected at { prev .filename .name } : "
494+ f"dist_curr={ dist_curr :.1f} m < dist_prev={ dist_prev :.1f} m * { deviation_threshold } , "
495495 f"jump={ dist_prev_curr :.1f} m"
496496 )
497497
498- if backtrack_count >= min_backtracks :
499- locations_preview = ", " .join (backtrack_locations [:5 ])
500- if len (backtrack_locations ) > 5 :
501- locations_preview += "..."
498+ # Mark prev as deviation (it's the point that jumped away)
499+ deviation_indices .add (i - 1 )
502500
503- ex = exceptions .MapillaryZigZagError (
504- f"GPS zig-zag pattern detected: { backtrack_count } backtrack events "
505- f"found (at: { locations_preview } )"
506- )
507- LOG .error (f"{ _sequence_name (sequence )} : { ex } " )
508- for image in sequence :
501+ # Walk backwards from prev to ref+1 to find more deviation points
502+ # We're looking for deviations between prev and ref
503+ # Use the same check as above: compare distance to ref and jump to curr
504+ for j in range (i - 2 , i - window_size , - 1 ): # Stop at ref+1
505+ point_j = sequence [j ]
506+
507+ # Distance from j to ref
508+ dist_j_to_ref = geo .gps_distance (
509+ (point_j .lat , point_j .lon ), (ref .lat , ref .lon )
510+ )
511+ # Distance from j to curr
512+ dist_j_to_curr = geo .gps_distance (
513+ (point_j .lat , point_j .lon ), (curr .lat , curr .lon )
514+ )
515+
516+ # Same check as original: j is a deviation if it's farther from ref
517+ # than curr is, and the jump from j to curr is significant
518+ if (
519+ dist_j_to_curr > min_distance
520+ and dist_curr < dist_j_to_ref * deviation_threshold
521+ ):
522+ deviation_indices .add (j )
523+ LOG .debug (
524+ f"Backwards walk: { point_j .filename .name } also marked as deviation"
525+ )
526+ else :
527+ # j is on the normal path, stop walking backwards
528+ break
529+
530+ if len (deviation_indices ) >= min_deviations :
531+ # Create errors only for deviation points
532+ for idx in sorted (deviation_indices ):
533+ image = sequence [idx ]
534+ ex = exceptions .MapillaryZigZagError ("GPS zig-zag deviation detected" )
535+ LOG .error (f"{ image .filename .name } : { ex } " )
509536 output_errors .append (
510537 types .describe_error_metadata (
511538 exc = ex , filename = image .filename , filetype = types .FileType .IMAGE
512539 )
513540 )
541+
542+ # Keep non-deviation points in output sequence
543+ non_deviation_points = [
544+ sequence [idx ]
545+ for idx in range (len (sequence ))
546+ if idx not in deviation_indices
547+ ]
548+ if non_deviation_points :
549+ output_sequences .append (non_deviation_points )
514550 else :
515551 output_sequences .append (sequence )
516552
@@ -823,8 +859,8 @@ def process_sequence_properties(
823859 sequences , errors = _check_sequences_zigzag (
824860 sequences ,
825861 window_size = constants .ZIGZAG_WINDOW_SIZE ,
826- backtrack_threshold = constants .ZIGZAG_BACKTRACK_THRESHOLD ,
827- min_backtracks = constants .ZIGZAG_MIN_BACKTRACKS ,
862+ deviation_threshold = constants .ZIGZAG_DEVIATION_THRESHOLD ,
863+ min_deviations = constants .ZIGZAG_MIN_DEVIATIONS ,
828864 min_distance = constants .ZIGZAG_MIN_DISTANCE ,
829865 )
830866 error_metadatas .extend (errors )
0 commit comments