189 lines
7.0 KiB
Markdown
189 lines
7.0 KiB
Markdown
# Documentation
|
|
- Class name: WeightAdjustIndivAttnMultNode
|
|
- Category: Animate Diff 🎭🅐🅓
|
|
- Output node: False
|
|
- Repo Ref: https://github.com/Kosinkadink/ComfyUI-AnimateDiff-Evolved.git
|
|
|
|
最初设计用于将多个动画帧组合成一个连贯的序列,此节点有助于从单个帧创建复杂的动画。
|
|
|
|
# Input types
|
|
## Required
|
|
|
|
- images
|
|
- 一个动画帧集合,组合成单个序列,支持从单个帧创建复杂的动画。
|
|
- Comfy dtype: IMAGE
|
|
- Python dtype: List[Image.Image]
|
|
|
|
- frame_rate
|
|
- 指定组合动画序列的播放速度,决定帧的显示速度。
|
|
- Comfy dtype: INT
|
|
- Python dtype: int
|
|
- loop_count
|
|
- 定义动画序列的重复次数,值为0表示无限循环。
|
|
- Comfy dtype: INT
|
|
- Python dtype: int
|
|
- filename_prefix
|
|
- 输出文件名的前缀,有助于组织和识别生成的动画。
|
|
- Comfy dtype: STRING
|
|
- Python dtype: str
|
|
- format
|
|
- 输出动画文件的格式,例如GIF,允许自定义输出类型。
|
|
- Comfy dtype: COMBO[STRING]
|
|
- Python dtype: str
|
|
- pingpong
|
|
- 一个布尔值,指示动画是否应前后播放,创建无缝循环效果。
|
|
- Comfy dtype: BOOLEAN
|
|
- Python dtype: bool
|
|
- save_image
|
|
- 确定是否将组合的动画序列保存为图像文件,提供灵活的输出处理方式。
|
|
- Comfy dtype: BOOLEAN
|
|
- Python dtype: bool
|
|
|
|
## Output types
|
|
- gif
|
|
- 生成的动画文件,根据指定参数组合输入帧。
|
|
- Comfy dtype: GIF
|
|
- Python dtype: str
|
|
|
|
# Source code
|
|
|
|
```python
|
|
class AnimateDiffCombine_Deprecated:
|
|
ffmpeg_warning_already_shown = False
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
ffmpeg_path = shutil.which("ffmpeg")
|
|
#Hide ffmpeg formats if ffmpeg isn't available
|
|
if ffmpeg_path is not None:
|
|
ffmpeg_formats = ["video/"+x[:-5] for x in folder_paths.get_filename_list(Folders.VIDEO_FORMATS)]
|
|
else:
|
|
ffmpeg_formats = []
|
|
if not s.ffmpeg_warning_already_shown:
|
|
# Deprecated node are now hidden, so no need to show warning unless node is used.
|
|
# logger.warning("This warning can be ignored, you should not be using the deprecated AnimateDiff Combine node anyway. If you are, use Video Combine from ComfyUI-VideoHelperSuite instead. ffmpeg could not be found. Outputs that require it have been disabled")
|
|
s.ffmpeg_warning_already_shown = True
|
|
return {
|
|
"required": {
|
|
"images": ("IMAGE",),
|
|
"frame_rate": (
|
|
"INT",
|
|
{"default": 8, "min": 1, "max": 24, "step": 1},
|
|
),
|
|
"loop_count": ("INT", {"default": 0, "min": 0, "max": 100, "step": 1}),
|
|
"filename_prefix": ("STRING", {"default": "AnimateDiff"}),
|
|
"format": (["image/gif", "image/webp"] + ffmpeg_formats,),
|
|
"pingpong": ("BOOLEAN", {"default": False}),
|
|
"save_image": ("BOOLEAN", {"default": True}),
|
|
},
|
|
"hidden": {
|
|
"prompt": "PROMPT",
|
|
"extra_pnginfo": "EXTRA_PNGINFO",
|
|
},
|
|
}
|
|
|
|
RETURN_TYPES = ("GIF",)
|
|
OUTPUT_NODE = True
|
|
CATEGORY = ""
|
|
FUNCTION = "generate_gif"
|
|
|
|
def generate_gif(
|
|
self,
|
|
images,
|
|
frame_rate: int,
|
|
loop_count: int,
|
|
filename_prefix="AnimateDiff",
|
|
format="image/gif",
|
|
pingpong=False,
|
|
save_image=True,
|
|
prompt=None,
|
|
extra_pnginfo=None,
|
|
):
|
|
logger.warning("Do not use AnimateDiff Combine node, it is deprecated. Use Video Combine node from ComfyUI-VideoHelperSuite instead. Video nodes from VideoHelperSuite are actively maintained, more feature-rich, and also automatically attempts to get ffmpeg.")
|
|
# convert images to numpy
|
|
frames: List[Image.Image] = []
|
|
for image in images:
|
|
img = 255.0 * image.cpu().numpy()
|
|
img = Image.fromarray(np.clip(img, 0, 255).astype(np.uint8))
|
|
frames.append(img)
|
|
|
|
# get output information
|
|
output_dir = (
|
|
folder_paths.get_output_directory()
|
|
if save_image
|
|
else folder_paths.get_temp_directory()
|
|
)
|
|
(
|
|
full_output_folder,
|
|
filename,
|
|
counter,
|
|
subfolder,
|
|
_,
|
|
) = folder_paths.get_save_image_path(filename_prefix, output_dir)
|
|
|
|
metadata = PngInfo()
|
|
if prompt is not None:
|
|
metadata.add_text("prompt", json.dumps(prompt))
|
|
if extra_pnginfo is not None:
|
|
for x in extra_pnginfo:
|
|
metadata.add_text(x, json.dumps(extra_pnginfo[x]))
|
|
|
|
# save first frame as png to keep metadata
|
|
file = f"{filename}_{counter:05}_.png"
|
|
file_path = os.path.join(full_output_folder, file)
|
|
frames[0].save(
|
|
file_path,
|
|
pnginfo=metadata,
|
|
compress_level=4,
|
|
)
|
|
if pingpong:
|
|
frames = frames + frames[-2:0:-1]
|
|
|
|
format_type, format_ext = format.split("/")
|
|
file = f"{filename}_{counter:05}_.{format_ext}"
|
|
file_path = os.path.join(full_output_folder, file)
|
|
if format_type == "image":
|
|
# Use pillow directly to save an animated image
|
|
frames[0].save(
|
|
file_path,
|
|
format=format_ext.upper(),
|
|
save_all=True,
|
|
append_images=frames[1:],
|
|
duration=round(1000 / frame_rate),
|
|
loop=loop_count,
|
|
compress_level=4,
|
|
)
|
|
else:
|
|
# Use ffmpeg to save a video
|
|
ffmpeg_path = shutil.which("ffmpeg")
|
|
if ffmpeg_path is None:
|
|
#Should never be reachable
|
|
raise ProcessLookupError("Could not find ffmpeg")
|
|
|
|
video_format_path = folder_paths.get_full_path("video_formats", format_ext + ".json")
|
|
with open(video_format_path, 'r') as stream:
|
|
video_format = json.load(stream)
|
|
file = f"{filename}_{counter:05}_.{video_format['extension']}"
|
|
file_path = os.path.join(full_output_folder, file)
|
|
dimensions = f"{frames[0].width}x{frames[0].height}"
|
|
args = [ffmpeg_path, "-v", "error", "-f", "rawvideo", "-pix_fmt", "rgb24",
|
|
"-s", dimensions, "-r", str(frame_rate), "-i", "-"] \
|
|
+ video_format['main_pass'] + [file_path]
|
|
|
|
env=os.environ.copy()
|
|
if "environment" in video_format:
|
|
env.update(video_format["environment"])
|
|
with subprocess.Popen(args, stdin=subprocess.PIPE, env=env) as proc:
|
|
for frame in frames:
|
|
proc.stdin.write(frame.tobytes())
|
|
|
|
previews = [
|
|
{
|
|
"filename": file,
|
|
"subfolder": subfolder,
|
|
"type": "output" if save_image else "temp",
|
|
"format": format,
|
|
}
|
|
]
|
|
return {"ui": {"gifs": previews}}
|
|
|
|
``` |