horizontal and veritcal drift
This commit is contained in:
parent
d7bd2fe6cd
commit
750f7164c7
1 changed files with 89 additions and 2 deletions
|
|
@ -591,6 +591,83 @@ class StitchingScanner:
|
||||||
result[sh:] = base[blend_h:]
|
result[sh:] = base[blend_h:]
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def _detect_row_start_alignment(self, frame: np.ndarray, direction: ScanDirection) -> AlignmentOffset:
|
||||||
|
"""
|
||||||
|
Detect alignment at the start of a new row by comparing the current frame
|
||||||
|
with the corner region of the mosaic.
|
||||||
|
|
||||||
|
For LEFT direction (starting at right edge): compare against bottom-right corner
|
||||||
|
For RIGHT direction (starting at left edge): compare against bottom-left corner
|
||||||
|
|
||||||
|
This handles the combined X+Y offset from gear backlash during row transitions.
|
||||||
|
"""
|
||||||
|
offset = AlignmentOffset()
|
||||||
|
|
||||||
|
if self.mosaic is None:
|
||||||
|
return offset
|
||||||
|
|
||||||
|
mh, mw = self.mosaic.shape[:2]
|
||||||
|
fh, fw = frame.shape[:2]
|
||||||
|
|
||||||
|
# Use a corner region for alignment - this captures both X and Y offset
|
||||||
|
overlap_size = min(fw // 2, fh // 2, 200) # Square-ish overlap region
|
||||||
|
|
||||||
|
if overlap_size < 50:
|
||||||
|
self.log(f"Row start alignment: overlap too small ({overlap_size})")
|
||||||
|
return offset
|
||||||
|
|
||||||
|
if direction == ScanDirection.LEFT:
|
||||||
|
# Starting at right edge, going left
|
||||||
|
# Compare frame's top-right corner with mosaic's bottom-right corner
|
||||||
|
|
||||||
|
# Frame region: top-right corner
|
||||||
|
frame_region = frame[:overlap_size, fw - overlap_size:]
|
||||||
|
|
||||||
|
# Mosaic region: bottom-right corner
|
||||||
|
mosaic_region = self.mosaic[mh - overlap_size:mh, mw - overlap_size:mw]
|
||||||
|
|
||||||
|
else: # RIGHT direction
|
||||||
|
# Starting at left edge, going right
|
||||||
|
# Compare frame's top-left corner with mosaic's bottom-left corner
|
||||||
|
|
||||||
|
# Frame region: top-left corner
|
||||||
|
frame_region = frame[:overlap_size, :overlap_size]
|
||||||
|
|
||||||
|
# Mosaic region: bottom-left corner
|
||||||
|
mosaic_region = self.mosaic[mh - overlap_size:mh, :overlap_size]
|
||||||
|
|
||||||
|
# Ensure regions have the same size
|
||||||
|
min_h = min(mosaic_region.shape[0], frame_region.shape[0])
|
||||||
|
min_w = min(mosaic_region.shape[1], frame_region.shape[1])
|
||||||
|
|
||||||
|
if min_h < 50 or min_w < 50:
|
||||||
|
self.log(f"Row start alignment: region too small ({min_w}x{min_h})")
|
||||||
|
return offset
|
||||||
|
|
||||||
|
mosaic_region = mosaic_region[:min_h, :min_w]
|
||||||
|
frame_region = frame_region[:min_h, :min_w]
|
||||||
|
|
||||||
|
# Detect displacement with confidence
|
||||||
|
dx, dy, confidence = self._detect_displacement_with_confidence(mosaic_region, frame_region)
|
||||||
|
|
||||||
|
# Sanity check - reject very large displacements
|
||||||
|
max_adjust = 100 # Allow larger adjustment at row start
|
||||||
|
if abs(dx) > max_adjust or abs(dy) > max_adjust:
|
||||||
|
self.log(f"Row start alignment: displacement too large ({dx:.1f}, {dy:.1f}), ignoring")
|
||||||
|
return offset
|
||||||
|
|
||||||
|
offset.x_offset = dx
|
||||||
|
offset.y_offset = dy
|
||||||
|
offset.confidence = confidence
|
||||||
|
offset.valid = confidence > 0.05 # Lower threshold for row start
|
||||||
|
|
||||||
|
self.log(f"=== Row Start Alignment ({direction.value}) ===")
|
||||||
|
self.log(f" Mosaic: {mw}x{mh}, Frame: {fw}x{fh}")
|
||||||
|
self.log(f" Overlap region: {min_w}x{min_h}")
|
||||||
|
self.log(f" Detected offset: X={dx:.1f}, Y={dy:.1f}, conf={confidence:.3f}")
|
||||||
|
|
||||||
|
return offset
|
||||||
|
|
||||||
def _append_strip(self, frame: np.ndarray, direction: ScanDirection):
|
def _append_strip(self, frame: np.ndarray, direction: ScanDirection):
|
||||||
"""Append strip to mosaic based on accumulated displacement with continuous alignment."""
|
"""Append strip to mosaic based on accumulated displacement with continuous alignment."""
|
||||||
BLEND_WIDTH = 10
|
BLEND_WIDTH = 10
|
||||||
|
|
@ -774,6 +851,17 @@ class StitchingScanner:
|
||||||
# Serpentine: even rows right, odd rows left
|
# Serpentine: even rows right, odd rows left
|
||||||
h_direction = ScanDirection.RIGHT if row % 2 == 0 else ScanDirection.LEFT
|
h_direction = ScanDirection.RIGHT if row % 2 == 0 else ScanDirection.LEFT
|
||||||
|
|
||||||
|
# For rows after the first, detect row-start alignment
|
||||||
|
if row > 0:
|
||||||
|
frame = self._capture_frame()
|
||||||
|
row_alignment = self._detect_row_start_alignment(frame, h_direction)
|
||||||
|
if row_alignment.valid:
|
||||||
|
# Apply row-start alignment to cumulative
|
||||||
|
self._cumulative_align_x += row_alignment.x_offset
|
||||||
|
self._cumulative_align_y += row_alignment.y_offset
|
||||||
|
self.log(f"Applied row-start alignment: X={row_alignment.x_offset:.1f}, Y={row_alignment.y_offset:.1f}")
|
||||||
|
self.log(f"New cumulative: X={self._cumulative_align_x:.1f}, Y={self._cumulative_align_y:.1f}")
|
||||||
|
|
||||||
stop_reason = self._scan_direction(h_direction)
|
stop_reason = self._scan_direction(h_direction)
|
||||||
|
|
||||||
if not self.running:
|
if not self.running:
|
||||||
|
|
@ -803,7 +891,6 @@ class StitchingScanner:
|
||||||
self.motion.stop_all()
|
self.motion.stop_all()
|
||||||
with self._state_lock:
|
with self._state_lock:
|
||||||
self.state.is_scanning = False
|
self.state.is_scanning = False
|
||||||
|
|
||||||
def _scan_direction(self, direction: ScanDirection) -> str:
|
def _scan_direction(self, direction: ScanDirection) -> str:
|
||||||
"""Scan in a direction until edge or max dimension reached."""
|
"""Scan in a direction until edge or max dimension reached."""
|
||||||
self.log(f"Scanning {direction.value}...")
|
self.log(f"Scanning {direction.value}...")
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue