import torch import math def generate_weight_mask(shape, blend_mode="gaussian", feather=0.25, device="cpu"): """ 生成用于分块融合的权重遮罩 shape: (H, W) or (C, H, W) blend_mode: 'linear', 'gaussian', 'cosine', 'none' feather: 0.0 - 1.0 (羽化范围比例) """ if len(shape) == 3: h, w = shape[1], shape[2] else: h, w = shape[0], shape[1] # 创建基础网格 # linspace 生成从 0 到 1 的线性空间 x = torch.linspace(-1, 1, w, device=device) y = torch.linspace(-1, 1, h, device=device) grid_y, grid_x = torch.meshgrid(y, x, indexing='ij') # 调整羽化范围,feather越大,有效中心区域越小,边缘衰减越宽 # 我们通过缩放网格坐标来控制边缘 if feather <= 0: mask = torch.ones((h, w), device=device) else: # 映射使得边缘 feather% 的区域从 1 衰减到 0 # 这里的逻辑是:在 1-feather 范围内是 1,超过这个范围开始衰减 scale = 1.0 / (1.0 - feather + 1e-6) # 避免除零 if blend_mode == "gaussian": # 高斯分布: exp(-(x^2 + y^2) / sigma) # 调整 sigma 使得在边缘处接近 0 d = torch.sqrt(grid_x**2 + grid_y**2) sigma = 0.5 # 标准差 mask = torch.exp(-(d**2) / (2 * sigma**2)) # 归一化到 0-1 并截断边缘 mask = (mask - mask.min()) / (mask.max() - mask.min()) elif blend_mode == "cosine": # 余弦窗口 mask_x = torch.cos(grid_x * math.pi / 2) mask_y = torch.cos(grid_y * math.pi / 2) mask = mask_x * mask_y mask = torch.clamp(mask, 0, 1) elif blend_mode == "linear": # 线性金字塔 mask_x = 1.0 - torch.abs(grid_x) mask_y = 1.0 - torch.abs(grid_y) mask = torch.min(mask_x, mask_y) mask = torch.clamp(mask, 0, 1) else: # none mask = torch.ones((h, w), device=device) # 扩展维度以匹配 Image (H,W,C) 或 Latent (C,H,W) # 假设输入 shape 决定了输出 mask 的维度 if len(shape) == 3: # Latent: (C, H, W), mask 需要是 (1, H, W) 用于广播 return mask.unsqueeze(0) else: # Image 通常在 Comfy 中处理时如果是单张 mask 是 (H, W) return mask def get_tiled_splits(full_height, full_width, tile_size, overlap): """ 计算分块坐标。采用 "覆盖优先" 策略,最后一块可能会有较大的重叠, 以避免产生极小的边缘切片。 Returns: list of (y, x, h, w) """ stride = tile_size - overlap splits = [] # 简单的网格生成 y_coords = [] current_y = 0 while current_y < full_height: y_coords.append(current_y) if current_y + tile_size >= full_height: break current_y += stride # 修正最后一个坐标,确保不越界且覆盖边缘 if y_coords[-1] + tile_size > full_height: y_coords[-1] = max(0, full_height - tile_size) x_coords = [] current_x = 0 while current_x < full_width: x_coords.append(current_x) if current_x + tile_size >= full_width: break current_x += stride if x_coords[-1] + tile_size > full_width: x_coords[-1] = max(0, full_width - tile_size) for y in y_coords: for x in x_coords: # 实际切片大小(通常等于 tile_size,除非原图小于 tile_size) h = min(tile_size, full_height - y) w = min(tile_size, full_width - x) splits.append((y, x, h, w)) return splits