Spaces:
Running on Zero
Running on Zero
| import numpy as np | |
| from PIL import Image | |
| def create_blend_weight( | |
| height, | |
| width, | |
| overlap | |
| ): | |
| """ | |
| Create a smooth 2D blending weight. | |
| Pixels near the center receive higher weight. | |
| Pixels near overlapping boundaries receive lower weight. | |
| """ | |
| if overlap <= 0: | |
| return np.ones( | |
| (height, width), | |
| dtype=np.float32 | |
| ) | |
| # Horizontal weights | |
| wx = np.ones(width, dtype=np.float32) | |
| transition = min(overlap, width // 2) | |
| if transition > 0: | |
| ramp = np.linspace( | |
| 0.01, | |
| 1.0, | |
| transition, | |
| dtype=np.float32 | |
| ) | |
| wx[:transition] = ramp | |
| wx[-transition:] = ramp[::-1] | |
| # Vertical weights | |
| wy = np.ones(height, dtype=np.float32) | |
| transition = min(overlap, height // 2) | |
| if transition > 0: | |
| ramp = np.linspace( | |
| 0.01, | |
| 1.0, | |
| transition, | |
| dtype=np.float32 | |
| ) | |
| wy[:transition] = ramp | |
| wy[-transition:] = ramp[::-1] | |
| return wy[:, None] * wx[None, :] | |
| def stitch_tiles( | |
| sr_tiles, | |
| scale=4, | |
| original_size=None, | |
| overlap=32 | |
| ): | |
| """ | |
| Stitch overlapping super-resolution tiles. | |
| Parameters | |
| ---------- | |
| sr_tiles : list of dictionaries | |
| Each dictionary must contain: | |
| image | |
| x | |
| y | |
| where x/y are coordinates in the ORIGINAL image. | |
| scale : int | |
| Super-resolution scale factor. | |
| original_size : tuple | |
| (width, height) of original image. | |
| overlap : int | |
| Overlap in ORIGINAL-image pixels. | |
| Returns | |
| ------- | |
| PIL.Image | |
| Final stitched SR image. | |
| """ | |
| if not sr_tiles: | |
| raise ValueError("No SR tiles supplied.") | |
| if original_size is None: | |
| raise ValueError( | |
| "original_size must be provided." | |
| ) | |
| original_width, original_height = original_size | |
| output_width = original_width * scale | |
| output_height = original_height * scale | |
| # Accumulate weighted RGB values | |
| canvas = np.zeros( | |
| ( | |
| output_height, | |
| output_width, | |
| 3 | |
| ), | |
| dtype=np.float32 | |
| ) | |
| # Accumulate weights | |
| weights = np.zeros( | |
| ( | |
| output_height, | |
| output_width | |
| ), | |
| dtype=np.float32 | |
| ) | |
| sr_overlap = overlap * scale | |
| for tile_info in sr_tiles: | |
| image = tile_info["image"] | |
| if not isinstance(image, Image.Image): | |
| image = Image.fromarray(image) | |
| image = image.convert("RGB") | |
| tile = np.asarray( | |
| image, | |
| dtype=np.float32 | |
| ) | |
| tile_height, tile_width = tile.shape[:2] | |
| # Original-image coordinates → SR coordinates | |
| x = int(tile_info["x"] * scale) | |
| y = int(tile_info["y"] * scale) | |
| # Do not allow the tile to exceed final canvas | |
| valid_width = min( | |
| tile_width, | |
| output_width - x | |
| ) | |
| valid_height = min( | |
| tile_height, | |
| output_height - y | |
| ) | |
| if valid_width <= 0 or valid_height <= 0: | |
| continue | |
| tile = tile[ | |
| :valid_height, | |
| :valid_width | |
| ] | |
| # -------------------------------------------------- | |
| # Build blending weight | |
| # -------------------------------------------------- | |
| weight = create_blend_weight( | |
| valid_height, | |
| valid_width, | |
| sr_overlap | |
| ) | |
| # -------------------------------------------------- | |
| # Accumulate | |
| # -------------------------------------------------- | |
| canvas[ | |
| y:y + valid_height, | |
| x:x + valid_width | |
| ] += tile * weight[..., None] | |
| weights[ | |
| y:y + valid_height, | |
| x:x + valid_width | |
| ] += weight | |
| # ------------------------------------------------------ | |
| # Normalize overlapping pixels | |
| # ------------------------------------------------------ | |
| weights = np.maximum( | |
| weights, | |
| 1e-8 | |
| ) | |
| canvas /= weights[..., None] | |
| canvas = np.clip( | |
| canvas, | |
| 0, | |
| 255 | |
| ).round().astype(np.uint8) | |
| return Image.fromarray( | |
| canvas, | |
| mode="RGB" | |
| ) | |