8.9 KiB
8.9 KiB
Documentation
- Class name: StableZero123_BatchSchedule
- Category: KJNodes/experimental
- Output node: False
- Repo Ref: https://github.com/kijai/ComfyUI-KJNodes.git
StableZero123_BatchSchedule 类的 encode 方法负责处理图像并生成潜在表示。它接受各种参数,如视觉模型输出、初始图像、VAE模型以及宽度、高度和批量大小等额外设置。该方法还根据提供的方位角和高度点进行角度插值,应用不同的缓动函数以实现平滑过渡。最终输出包括正负条件数据和潜在图像表示。
Input types
Required
- clip_vision
- 参数
clip_vision对于将初始图像编码为可以用于进一步处理的嵌入至关重要。它代表了来自 CLIP 视觉模型的输出,是图像处理流水线的基本部分。 - Comfy dtype: CLIP_VISION
- Python dtype: torch.Tensor
- 参数
- init_image
- 参数
init_image是需要处理的原始图像。它是生成潜在表示的起点,也是该节点操作的必需输入。 - Comfy dtype: IMAGE
- Python dtype: PIL.Image.Image
- 参数
- vae
- 参数
vae是一个变分自编码器(VAE)模型,用于将图像编码到潜在空间中。它在图像数据转换为下游任务中起着关键作用。 - Comfy dtype: VAE
- Python dtype: torch.nn.Module
- 参数
Optional
- width
- 参数
width指定处理后图像的期望宽度。它是决定最终图像分辨率的重要因素,并且与高度参数一起使用。 - Comfy dtype: INT
- Python dtype: int
- 参数
- height
- 参数
height与宽度参数一起,定义了处理后图像的尺寸。它是控制输出图像大小的关键设置。 - Comfy dtype: INT
- Python dtype: int
- 参数
- batch_size
- 参数
batch_size决定了一次处理的图像数量。它是优化节点性能和内存使用的重要考虑因素。 - Comfy dtype: INT
- Python dtype: int
- 参数
- interpolation
- 参数
interpolation允许在调整图像大小时选择不同的插值方法。它影响最终图像的平滑度和外观。 - Comfy dtype: COMBO[linear, ease_in, ease_out, ease_in_out]
- Python dtype: str
- 参数
- azimuth_points_string
- 参数
azimuth_points_string包含用于角度插值的方位点的字符串表示。它在定义图像处理中的角过渡中至关重要。 - Comfy dtype: STRING
- Python dtype: str
- 参数
- elevation_points_string
- 参数
elevation_points_string以字符串形式指定高度点,用于控制图像处理过程中的垂直角度过渡。 - Comfy dtype: STRING
- Python dtype: str
- 参数
Output types
- positive
- 输出
positive包含从图像处理中得到的正向条件数据。它用于引导生成与输入图像相似的图像。 - Comfy dtype: CONDITIONING
- Python dtype: List[Tuple[torch.Tensor, Dict[str, torch.Tensor]]]
- 输出
- negative
- 输出
negative提供了负向条件数据,用于引导生成与输入图像不同的图像。 - Comfy dtype: CONDITIONING
- Python dtype: List[Tuple[torch.Tensor, Dict[str, torch.Tensor]]]
- 输出
- latent
- 输出
latent表示节点生成的潜在图像样本。这些样本是输入图像的低维表示,适用于各种图像生成任务。 - Comfy dtype: LATENT
- Python dtype: Dict[str, torch.Tensor]
- 输出
Usage tips
- Infra type: GPU
Source code
class StableZero123_BatchSchedule:
@classmethod
def INPUT_TYPES(s):
return {'required': {'clip_vision': ('CLIP_VISION',), 'init_image': ('IMAGE',), 'vae': ('VAE',), 'width': ('INT', {'default': 256, 'min': 16, 'max': MAX_RESOLUTION, 'step': 8}), 'height': ('INT', {'default': 256, 'min': 16, 'max': MAX_RESOLUTION, 'step': 8}), 'batch_size': ('INT', {'default': 1, 'min': 1, 'max': 4096}), 'interpolation': (['linear', 'ease_in', 'ease_out', 'ease_in_out'],), 'azimuth_points_string': ('STRING', {'default': '0:(0.0),\n7:(1.0),\n15:(0.0)\n', 'multiline': True}), 'elevation_points_string': ('STRING', {'default': '0:(0.0),\n7:(0.0),\n15:(0.0)\n', 'multiline': True})}}
RETURN_TYPES = ('CONDITIONING', 'CONDITIONING', 'LATENT')
RETURN_NAMES = ('positive', 'negative', 'latent')
FUNCTION = 'encode'
CATEGORY = 'KJNodes/experimental'
def encode(self, clip_vision, init_image, vae, width, height, batch_size, azimuth_points_string, elevation_points_string, interpolation):
output = clip_vision.encode_image(init_image)
pooled = output.image_embeds.unsqueeze(0)
pixels = comfy.utils.common_upscale(init_image.movedim(-1, 1), width, height, 'bilinear', 'center').movedim(1, -1)
encode_pixels = pixels[:, :, :, :3]
t = vae.encode(encode_pixels)
def ease_in(t):
return t * t
def ease_out(t):
return 1 - (1 - t) * (1 - t)
def ease_in_out(t):
return 3 * t * t - 2 * t * t * t
azimuth_points = []
azimuth_points_string = azimuth_points_string.rstrip(',\n')
for point_str in azimuth_points_string.split(','):
(frame_str, azimuth_str) = point_str.split(':')
frame = int(frame_str.strip())
azimuth = float(azimuth_str.strip()[1:-1])
azimuth_points.append((frame, azimuth))
azimuth_points.sort(key=lambda x: x[0])
elevation_points = []
elevation_points_string = elevation_points_string.rstrip(',\n')
for point_str in elevation_points_string.split(','):
(frame_str, elevation_str) = point_str.split(':')
frame = int(frame_str.strip())
elevation_val = float(elevation_str.strip()[1:-1])
elevation_points.append((frame, elevation_val))
elevation_points.sort(key=lambda x: x[0])
next_point = 1
next_elevation_point = 1
positive_cond_out = []
positive_pooled_out = []
negative_cond_out = []
negative_pooled_out = []
for i in range(batch_size):
while next_point < len(azimuth_points) and i >= azimuth_points[next_point][0]:
next_point += 1
if next_point == len(azimuth_points):
next_point -= 1
prev_point = max(next_point - 1, 0)
if azimuth_points[next_point][0] != azimuth_points[prev_point][0]:
fraction = (i - azimuth_points[prev_point][0]) / (azimuth_points[next_point][0] - azimuth_points[prev_point][0])
if interpolation == 'ease_in':
fraction = ease_in(fraction)
elif interpolation == 'ease_out':
fraction = ease_out(fraction)
elif interpolation == 'ease_in_out':
fraction = ease_in_out(fraction)
interpolated_azimuth = interpolate_angle(azimuth_points[prev_point][1], azimuth_points[next_point][1], fraction)
else:
interpolated_azimuth = azimuth_points[prev_point][1]
next_elevation_point = 1
while next_elevation_point < len(elevation_points) and i >= elevation_points[next_elevation_point][0]:
next_elevation_point += 1
if next_elevation_point == len(elevation_points):
next_elevation_point -= 1
prev_elevation_point = max(next_elevation_point - 1, 0)
if elevation_points[next_elevation_point][0] != elevation_points[prev_elevation_point][0]:
fraction = (i - elevation_points[prev_elevation_point][0]) / (elevation_points[next_elevation_point][0] - elevation_points[prev_elevation_point][0])
if interpolation == 'ease_in':
fraction = ease_in(fraction)
elif interpolation == 'ease_out':
fraction = ease_out(fraction)
elif interpolation == 'ease_in_out':
fraction = ease_in_out(fraction)
interpolated_elevation = interpolate_angle(elevation_points[prev_elevation_point][1], elevation_points[next_elevation_point][1], fraction)
else:
interpolated_elevation = elevation_points[prev_elevation_point][1]
cam_embeds = camera_embeddings(interpolated_elevation, interpolated_azimuth)
cond = torch.cat([pooled, cam_embeds.repeat((pooled.shape[0], 1, 1))], dim=-1)
positive_pooled_out.append(t)
positive_cond_out.append(cond)
negative_pooled_out.append(torch.zeros_like(t))
negative_cond_out.append(torch.zeros_like(pooled))
final_positive_cond = torch.cat(positive_cond_out, dim=0)
final_positive_pooled = torch.cat(positive_pooled_out, dim=0)
final_negative_cond = torch.cat(negative_cond_out, dim=0)
final_negative_pooled = torch.cat(negative_pooled_out, dim=0)
final_positive = [[final_positive_cond, {'concat_latent_image': final_positive_pooled}]]
final_negative = [[final_negative_cond, {'concat_latent_image': final_negative_pooled}]]
latent = torch.zeros([batch_size, 4, height // 8, width // 8])
return (final_positive, final_negative, {'samples': latent})