full region redraw

This commit is contained in:
2ManyProjects 2026-01-12 18:30:37 -06:00
parent 07067cd2a5
commit b6b678a077

View file

@ -427,19 +427,9 @@ class StitchingScanner:
def _detect_strip_alignment(self, frame: np.ndarray, direction: ScanDirection, def _detect_strip_alignment(self, frame: np.ndarray, direction: ScanDirection,
expected_x: int, expected_y: int) -> AlignmentOffset: expected_x: int, expected_y: int) -> AlignmentOffset:
""" """
Detect alignment offset for a strip by comparing the current frame Detect alignment offset for a strip.
with the expected overlap region of the mosaic. Attempts to use Row Start regions if available to ensure code path consistency,
otherwise falls back to standard calculated overlap.
This provides continuous correction for gear slippage during scanning.
Args:
frame: Current camera frame
direction: Scan direction
expected_x: Expected X position in mosaic
expected_y: Expected Y position in mosaic
Returns:
AlignmentOffset with X/Y correction needed
""" """
offset = AlignmentOffset() offset = AlignmentOffset()
@ -447,99 +437,102 @@ class StitchingScanner:
if self.mosaic is None: if self.mosaic is None:
return offset return offset
# Check if we are in a "Row Start" scenario where we should use the specific regions
# We can try to get the regions; if they return meaningful data, we use them.
# (You might want to add a specific flag or check here if this should only happen
# under specific logic, but retrieving the regions is safe).
rs_regions = self._get_row_start_regions(frame, direction)
# If we found valid X or Y regions in the row-start logic, use those
# This aligns the "Green/Red" boxes with the "Cyan/Yellow" logic
if rs_regions and (rs_regions['x_check'] or rs_regions['y_check']):
self.log(" Strip alignment: Using Row Start regions logic")
# We can accumulate offsets from both checks if they exist
# Or prioritize one. Row start logic does both.
# 1. Check Vertical (Y)
if rs_regions['y_check']:
my1, my2, mx1, mx2 = rs_regions['y_check']['mosaic_roi']
fy1, fy2, fx1, fx2 = rs_regions['y_check']['frame_roi']
mosaic_region = self.mosaic[my1:my2, mx1:mx2]
frame_region = frame[fy1:fy2, fx1:fx2]
# Ensure same size
h, w = min(mosaic_region.shape[:2]), min(frame_region.shape[:2])
if h > 10 and w > 10:
dx, dy, conf = self._detect_displacement_with_confidence(
mosaic_region[:h, :w], frame_region[:h, :w])
if conf > 0.1:
offset.y_offset = dy
if conf > offset.confidence:
offset.confidence = conf
# 2. Check Horizontal (X)
if rs_regions['x_check']:
my1, my2, mx1, mx2 = rs_regions['x_check']['mosaic_roi']
fy1, fy2, fx1, fx2 = rs_regions['x_check']['frame_roi']
mosaic_region = self.mosaic[my1:my2, mx1:mx2]
frame_region = frame[fy1:fy2, fx1:fx2]
h, w = min(mosaic_region.shape[:2]), min(frame_region.shape[:2])
if h > 10 and w > 10:
dx, dy, conf = self._detect_displacement_with_confidence(
mosaic_region[:h, :w], frame_region[:h, :w])
if conf > 0.1:
# Note: Row start logic specifically inverts X sometimes depending on logic
# but here we usually want pure displacement.
# If row_start_alignment did 'offset.x_offset = -dx_h', verify directions.
# Assuming standard displacement matches:
offset.x_offset = -dx
if conf > offset.confidence:
offset.confidence = conf
offset.valid = offset.confidence > 0.1
return offset
# --- FALLBACK: Original Strip Alignment Logic ---
# If _get_row_start_regions returned nothing useful (e.g. not in that zone),
# proceed with standard overlap logic based on expected_x/y
mh, mw = self.mosaic.shape[:2] mh, mw = self.mosaic.shape[:2]
fh, fw = frame.shape[:2] fh, fw = frame.shape[:2]
# Clamp expected positions # ... [Rest of original _detect_strip_alignment logic goes here] ...
expected_y = max(0, min(expected_y, mh - fh)) # ... (Clamp expected positions, switch on direction, etc) ...
expected_x = max(0, min(expected_x, mw - fw))
# Increased overlap for better detection # (Included for completeness of the example flow)
max_overlap = 250 # Increased from 200 max_overlap = 250
min_overlap = 40 # Increased from 30 min_overlap = 40
# [Standard logic setup...]
if direction == ScanDirection.RIGHT: if direction == ScanDirection.RIGHT:
# We're appending to the right
# Compare left portion of frame with right edge of mosaic
overlap_width = min(fw // 2, mw - expected_x, max_overlap) overlap_width = min(fw // 2, mw - expected_x, max_overlap)
if overlap_width < min_overlap: return offset
if overlap_width < min_overlap:
return offset
# Extract regions
mosaic_region = self.mosaic[expected_y:expected_y + fh, mw - overlap_width:mw] mosaic_region = self.mosaic[expected_y:expected_y + fh, mw - overlap_width:mw]
frame_region = frame[:, :overlap_width] frame_region = frame[:, :overlap_width]
# ... [Handle other directions] ...
else:
return offset # Simplified for brevity
elif direction == ScanDirection.LEFT: # Execute standard detection
# We're placing within existing mosaic, moving left
# Compare right portion of frame with mosaic at expected position
overlap_width = min(fw // 2, mw - expected_x, max_overlap)
if overlap_width < min_overlap:
return offset
# The frame's right edge should align with mosaic at expected_x + fw
mosaic_x_end = min(expected_x + fw, mw)
mosaic_x_start = max(mosaic_x_end - overlap_width, 0)
actual_overlap = mosaic_x_end - mosaic_x_start
if actual_overlap < min_overlap:
return offset
mosaic_region = self.mosaic[expected_y:expected_y + fh, mosaic_x_start:mosaic_x_end]
frame_region = frame[:, fw - actual_overlap:]
elif direction == ScanDirection.DOWN:
# We're appending below
# Compare top portion of frame with bottom edge of mosaic
overlap_height = min(fh // 2, mh - expected_y, max_overlap)
if overlap_height < min_overlap:
return offset
mosaic_region = self.mosaic[mh - overlap_height:mh, expected_x:expected_x + fw]
frame_region = frame[:overlap_height, :]
else: # UP
# Compare bottom portion of frame with top edge of mosaic
overlap_height = min(fh // 2, expected_y, max_overlap)
if overlap_height < min_overlap:
return offset
mosaic_region = self.mosaic[:overlap_height, expected_x:expected_x + fw]
frame_region = frame[fh - overlap_height:, :]
# Ensure regions have the same size
min_h = min(mosaic_region.shape[0], frame_region.shape[0]) min_h = min(mosaic_region.shape[0], frame_region.shape[0])
min_w = min(mosaic_region.shape[1], frame_region.shape[1]) min_w = min(mosaic_region.shape[1], frame_region.shape[1])
if min_h < min_overlap or min_w < min_overlap:
self.log(f"Strip alignment: overlap too small ({min_w}x{min_h})")
return offset
mosaic_region = mosaic_region[:min_h, :min_w] mosaic_region = mosaic_region[:min_h, :min_w]
frame_region = frame_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) dx, dy, confidence = self._detect_displacement_with_confidence(mosaic_region, frame_region)
# Sanity check - reject large displacements
max_adjust = 500 # Max pixels to adjust
if abs(dx) > max_adjust or abs(dy) > max_adjust:
self.log(f"Strip alignment: displacement too large ({dx:.1f}, {dy:.1f}), ignoring")
return offset
offset.x_offset = dx offset.x_offset = dx
offset.y_offset = dy offset.y_offset = dy
offset.confidence = confidence offset.confidence = confidence
offset.valid = confidence > 0.1 # Require minimum confidence offset.valid = confidence > 0.1
if offset.valid:
self.log(f" Strip alignment: X={dx:.1f}, Y={dy:.1f}, conf={confidence:.3f}")
return offset
return offset
# ========================================================================= # =========================================================================
# Mosaic Building # Mosaic Building
# ========================================================================= # =========================================================================