Spaces:
Running
Running
| """ | |
| Contains utility functions for image loading, preparation, and manipulation. | |
| Includes HEIC image format support via the optional 'pillow-heif' library. | |
| """ | |
| from PIL import Image, ImageOps, ImageDraw | |
| import os | |
| try: | |
| from pillow_heif import register_heif_opener | |
| register_heif_opener() | |
| print("HEIC opener registered successfully using pillow-heif.") | |
| _heic_support = True | |
| except ImportError: | |
| print("Warning: pillow-heif not installed. HEIC/HEIF support will be disabled.") | |
| _heic_support = False | |
| print("Loading Image Utils...") | |
| def prepare_image(image_filepath, target_size=512): | |
| """ | |
| Prepares an input image file for the diffusion pipeline. | |
| Loads an image from the given filepath (supports standard formats like | |
| JPG, PNG, WEBP, and HEIC/HEIF), | |
| ensures it's in RGB format, handles EXIF orientation, and performs | |
| a forced resize to a square target_size, ignoring the original aspect ratio. | |
| Args: | |
| image_filepath (str): The path to the image file. | |
| target_size (int): The target dimension for both width and height. | |
| Returns: | |
| PIL.Image.Image | None: The prepared image as a PIL Image object in RGB format, | |
| or None if loading or processing fails. | |
| """ | |
| if image_filepath is None: | |
| print("Warning: prepare_image received None filepath.") | |
| return None | |
| if not isinstance(image_filepath, str) or not os.path.exists(image_filepath): | |
| print(f"Error: Invalid filepath provided to prepare_image: {image_filepath}") | |
| if isinstance(image_filepath, Image.Image): | |
| print("Warning: Received PIL Image instead of filepath, proceeding...") | |
| image = image_filepath | |
| else: | |
| return None | |
| else: | |
| # --- Load Image from Filepath --- | |
| print(f"Loading image from path: {image_filepath}") | |
| try: | |
| image = Image.open(image_filepath) | |
| except ImportError as e: | |
| print(f"ImportError during Image.open: {e}. Is pillow-heif installed?") | |
| print("Cannot process image format.") | |
| return None | |
| except Exception as e: | |
| print(f"Error opening image file {image_filepath} with PIL: {e}") | |
| return None | |
| # --- Process PIL Image --- | |
| try: | |
| image = ImageOps.exif_transpose(image) | |
| image = image.convert("RGB") | |
| original_width, original_height = image.size | |
| final_width = target_size | |
| final_height = target_size | |
| resized_image = image.resize((final_width, final_height), Image.LANCZOS) | |
| print(f"Original size: ({original_width}, {original_height}), FORCED Resized to: ({final_width}, {final_height})") | |
| return resized_image | |
| except Exception as e: | |
| print(f"Error during PIL image processing steps: {e}") | |
| return None | |
| def create_blend_mask(tile_size=1024, overlap=256): | |
| """ | |
| Creates a feathered blending mask (alpha mask) for smooth tile stitching. | |
| Generates a square mask where the edges have a linear gradient ramp within | |
| the specified overlap zone, and the central area is fully opaque. | |
| Assumes overlap occurs equally on all four sides. | |
| Args: | |
| tile_size (int): The dimension (width and height) of the tiles being processed. | |
| overlap (int): The number of pixels that overlap between adjacent tiles. | |
| Returns: | |
| PIL.Image.Image: The blending mask as a PIL Image object in 'L' (grayscale) mode. | |
| White (255) areas are fully opaque, black (0) are transparent, | |
| gray values provide blending. | |
| """ | |
| if overlap >= tile_size // 2: | |
| print("Warning: Overlap is large relative to tile size, mask generation might be suboptimal.") | |
| overlap = tile_size // 2 - 1 | |
| mask = Image.new("L", (tile_size, tile_size), 0) | |
| draw = ImageDraw.Draw(mask) | |
| if overlap > 0: | |
| for i in range(overlap): | |
| alpha = int(255 * (i / float(overlap))) | |
| # Left edge ramp | |
| draw.line([(i, 0), (i, tile_size)], fill=alpha) | |
| # Right edge ramp | |
| draw.line([(tile_size - 1 - i, 0), (tile_size - 1 - i, tile_size)], fill=alpha) | |
| # Top edge ramp | |
| draw.line([(0, i), (tile_size, i)], fill=alpha) | |
| # Bottom edge ramp | |
| draw.line([(0, tile_size - 1 - i), (tile_size, tile_size - 1 - i)], fill=alpha) | |
| center_start = overlap | |
| center_end_x = tile_size - overlap | |
| center_end_y = tile_size - overlap | |
| if center_end_x > center_start and center_end_y > center_start: | |
| draw.rectangle( (center_start, center_start, center_end_x - 1, center_end_y - 1), fill=255 ) | |
| else: | |
| center_x, center_y = tile_size // 2, tile_size // 2 | |
| draw.point((center_x, center_y), fill=255) | |
| if tile_size % 2 == 0: | |
| draw.point((center_x-1, center_y), fill=255) | |
| draw.point((center_x, center_y-1), fill=255) | |
| draw.point((center_x-1, center_y-1), fill=255) | |
| print(f"Blend mask created (Size: {tile_size}x{tile_size}, Overlap: {overlap})") | |
| return mask |