From 91d95561f601a7936fdaf111885e0809595d6d5a Mon Sep 17 00:00:00 2001 From: 2ManyProjects Date: Sat, 10 Jan 2026 03:28:36 -0600 Subject: [PATCH] stichting --- src/stitching_scanner.py | 98 +++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 51 deletions(-) diff --git a/src/stitching_scanner.py b/src/stitching_scanner.py index 212f63f..8036b60 100644 --- a/src/stitching_scanner.py +++ b/src/stitching_scanner.py @@ -160,88 +160,80 @@ class StitchingScanner: self.log(f"Initialized mosaic: {frame.shape[1]}x{frame.shape[0]}") def _blend_horizontal_at_y(self, base: np.ndarray, strip: np.ndarray, - blend_width: int, append_right: bool, x_offset: int = None, - y_offset: int = 0) -> np.ndarray: + blend_width: int, append_right: bool, x_offset: int = None, + y_offset: int = 0) -> np.ndarray: h_base, w_base = base.shape[:2] h_strip, w_strip = strip.shape[:2] - # Clamp y_offset - y_offset = max(0, min(y_offset, h_base - h_strip)) + # Calculate y_offset based on mosaic growth (mirror of vertical's x_offset calculation) + y_offset = max(0, h_base - self.state.mosaic_init_height) + blend_w = min(blend_width, w_strip, w_base) + # Create full-height strip with actual strip placed at y_offset + full_strip = np.zeros((h_base, w_strip, 3), dtype=np.uint8) + available_height = h_base - y_offset + copy_height = min(h_strip, available_height) + full_strip[y_offset:y_offset + copy_height, :] = strip[:copy_height, :] + + self.log(f"=== _blend_horizontal_at_y ===") + self.log(f" base: {w_base}x{h_base}, strip: {w_strip}x{h_strip}") + self.log(f" y_offset: {y_offset} (h_base - init_height = {h_base} - {self.state.mosaic_init_height})") + self.log(f" full_strip: {full_strip.shape[1]}x{full_strip.shape[0]}") + if append_right: # Expand mosaic to the right + if blend_w <= 0 or blend_w >= w_strip: + return np.hstack([base, full_strip]) + result_width = w_base + w_strip - blend_w result = np.zeros((h_base, result_width, 3), dtype=np.uint8) - - self.log(f"=== _blend_horizontal_at_y (append_right) ===") - self.log(f" base: {w_base}x{h_base}, strip: {w_strip}x{h_strip}") - self.log(f" y_offset: {y_offset}, blend_w: {blend_w}") - self.log(f" result: {result_width}x{h_base}") - - # Step 1: Copy entire base result[:, :w_base] = base - # Step 2: Copy non-overlap portion of strip at correct Y - x_place = w_base - self.log(f" Placing strip at X={w_base - blend_w}:{result_width}, Y={y_offset}:{y_offset + h_strip}") - result[y_offset:y_offset + h_strip, w_base:] = strip[:, blend_w:] - - # Step 3: Create blend alpha = np.linspace(1, 0, blend_w, dtype=np.float32)[np.newaxis, :, np.newaxis] - base_overlap = base[y_offset:y_offset + h_strip, -blend_w:].astype(np.float32) - strip_overlap = strip[:, :blend_w].astype(np.float32) + base_overlap = base[:, -blend_w:].astype(np.float32) + strip_overlap = full_strip[:, :blend_w].astype(np.float32) blended = (base_overlap * alpha + strip_overlap * (1 - alpha)).astype(np.uint8) - # Step 4: Place blend - result[y_offset:y_offset + h_strip, w_base - blend_w:w_base] = blended + result[:, w_base - blend_w:w_base] = blended + result[:, w_base:] = full_strip[:, blend_w:] + self.log(f" append_right: result {result_width}x{h_base}") return result else: # append_left - place at x_offset, NO width expansion - # x_offset is where the LEFT edge of the strip should go if x_offset is None: x_offset = 0 - # Clamp x_offset to valid range - x_offset = 0 - min(x_offset, w_base - blend_w) + # Clamp x_offset to valid range + x_offset = max(0, min(x_offset, w_base - w_strip)) - self.log(f"=== _blend_horizontal_at_y (append_left) ===") - self.log(f" base: {w_base}x{h_base}, strip: {w_strip}x{h_strip}") - self.log(f" x_offset: {x_offset}, y_offset: {y_offset}, blend_w: {blend_w}") + self.log(f" append_left: x_offset={x_offset}") # Result is same size as base (no expansion when going left) result = base.copy() - # Calculate placement bounds - strip_x_start = x_offset - strip_x_end = min(x_offset + w_strip, w_base) - strip_cols_to_copy = strip_x_end - strip_x_start + # Place full_strip at x_offset, blending on the RIGHT edge + # Non-blend portion + non_blend_width = w_strip - blend_w + if non_blend_width > 0: + result[:, x_offset:x_offset + non_blend_width] = full_strip[:, :non_blend_width] + self.log(f" Placed non-blend at X={x_offset}:{x_offset + non_blend_width}") - self.log(f" Placing strip at X={strip_x_start}:{strip_x_end}, Y={y_offset}:{y_offset + h_strip}") - self.log(f" Strip cols to copy: {strip_cols_to_copy}") + # Blend zone on the right edge of the strip + blend_x_start = x_offset + non_blend_width + blend_x_end = x_offset + w_strip - # Step 1: Copy strip content (non-blend portion) at correct position - # For LEFT scanning, blend is on the RIGHT side of the strip - non_blend_end = strip_cols_to_copy - blend_w - if non_blend_end > 0: - result[y_offset:y_offset + h_strip, strip_x_start:strip_x_start + non_blend_end] = strip[:, :non_blend_end] - self.log(f" Step 1: Placed non-blend at X={strip_x_start}:{strip_x_start + non_blend_end}") - - # Step 2: Create blend on the RIGHT edge of strip (blending with existing content) - blend_x_start = strip_x_end - blend_w - blend_x_end = strip_x_end - - if blend_w > 0 and blend_x_start >= strip_x_start: + if blend_w > 0 and blend_x_end <= w_base: alpha = np.linspace(1, 0, blend_w, dtype=np.float32)[np.newaxis, :, np.newaxis] - strip_overlap = strip[:, -blend_w:].astype(np.float32) - base_overlap = base[y_offset:y_offset + h_strip, blend_x_start:blend_x_end].astype(np.float32) + strip_overlap = full_strip[:, -blend_w:].astype(np.float32) + base_overlap = base[:, blend_x_start:blend_x_end].astype(np.float32) blended = (strip_overlap * alpha + base_overlap * (1 - alpha)).astype(np.uint8) - result[y_offset:y_offset + h_strip, blend_x_start:blend_x_end] = blended - self.log(f" Step 2: Blend zone at X={blend_x_start}:{blend_x_end}") + result[:, blend_x_start:blend_x_end] = blended + self.log(f" Blend zone at X={blend_x_start}:{blend_x_end}") - self.log(f" Final: Strip placed at X={strip_x_start}, Y={y_offset}, mosaic size unchanged: {w_base}x{h_base}") + self.log(f" Final: strip at X={x_offset}, Y={y_offset} (via full_strip)") return result def _blend_horizontal(self, base: np.ndarray, strip: np.ndarray, @@ -342,6 +334,8 @@ class StitchingScanner: result[h_strip - blend_h:h_strip, :] = blended result[h_strip:, :] = base[blend_h:, :] return result + + def _blend_vertical(self, base: np.ndarray, strip: np.ndarray, blend_height: int, append_below: bool) -> np.ndarray: mh, mw = base.shape[:2] @@ -422,8 +416,10 @@ class StitchingScanner: else: strip_end = min(w, append_width + BLEND_WIDTH) new_strip = frame[:, :strip_end] + x_offset = int(self.state.mosaic_width - abs(self.state.current_x) - new_strip.shape[1]) + x_offset = max(0, x_offset) self.mosaic = self._blend_horizontal_at_y( - self.mosaic, new_strip, BLEND_WIDTH, append_right=False, x_offset=int(self.state.current_x), y_offset=y_offset) + self.mosaic, new_strip, BLEND_WIDTH, append_right=False, x_offset=x_offset, y_offset=y_offset) self._displacement_since_append_x = fractional_remainder self._displacement_since_append_y = 0.0