Files
QL-boy-ComfyUI-Advanced-Til…/utils/blending.py
T
2025-12-18 18:35:43 +08:00

106 lines
3.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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