diff --git a/nodes/faceenhance.py b/nodes/faceenhance.py index 56aeb94..678c2fb 100644 --- a/nodes/faceenhance.py +++ b/nodes/faceenhance.py @@ -78,6 +78,7 @@ class MTB_LoadFaceEnhanceModel: RETURN_NAMES = ("model",) FUNCTION = "load_model" CATEGORY = "mtb/facetools" + DEPRECATED = True def load_model(self, model_name, upscale=2, bg_upsampler=None): from gfpgan import GFPGANer @@ -163,6 +164,7 @@ class MTB_RestoreFace: RETURN_TYPES = ("IMAGE",) FUNCTION = "restore" CATEGORY = "mtb/facetools" + DEPRECATED = True @classmethod def INPUT_TYPES(cls): diff --git a/nodes/faceswap.py b/nodes/faceswap.py index 069f681..218c4d0 100644 --- a/nodes/faceswap.py +++ b/nodes/faceswap.py @@ -40,6 +40,7 @@ class MTB_LoadFaceAnalysisModel: RETURN_TYPES = ("FACE_ANALYSIS_MODEL",) FUNCTION = "load_model" CATEGORY = "mtb/facetools" + DEPRECATED = True def load_model(self, faceswap_model: str): if faceswap_model == "antelopev2": @@ -77,6 +78,7 @@ class MTB_LoadFaceSwapModel: RETURN_TYPES = ("FACESWAP_MODEL",) FUNCTION = "load_model" CATEGORY = "mtb/facetools" + DEPRECATED = True def load_model(self, faceswap_model: str): model_path = get_model_path("insightface", faceswap_model) @@ -126,6 +128,7 @@ class MTB_FaceSwap: RETURN_TYPES = ("IMAGE",) FUNCTION = "swap" CATEGORY = "mtb/facetools" + DEPRECATED = True def swap( self, diff --git a/nodes/image_interpolation.py b/nodes/image_interpolation.py index c07c3d9..8189058 100644 --- a/nodes/image_interpolation.py +++ b/nodes/image_interpolation.py @@ -1,5 +1,4 @@ from pathlib import Path -from typing import List import comfy import comfy.model_management as model_management @@ -15,10 +14,13 @@ from ..utils import get_model_path class MTB_LoadFilmModel: - """Loads a FILM model""" + """Loads a FILM model + + [DEPRECATED] Use ComfyUI-FrameInterpolation instead + """ @staticmethod - def get_models() -> List[Path]: + def get_models() -> list[Path]: models_paths = get_model_path("FILM").iterdir() return [x for x in models_paths if x.suffix in [".onnx", ".pth"]] @@ -37,6 +39,7 @@ class MTB_LoadFilmModel: RETURN_TYPES = ("FILM_MODEL",) FUNCTION = "load_model" CATEGORY = "mtb/frame iterpolation" + DEPRECATED = True def load_model(self, film_model: str): model_path = get_model_path("FILM", film_model) @@ -56,7 +59,10 @@ class MTB_LoadFilmModel: class MTB_FilmInterpolation: - """Google Research FILM frame interpolation for large motion""" + """Google Research FILM frame interpolation for large motion + + [DEPRECATED] Use ComfyUI-FrameInterpolation instead + """ @classmethod def INPUT_TYPES(cls): @@ -71,6 +77,7 @@ class MTB_FilmInterpolation: RETURN_TYPES = ("IMAGE",) FUNCTION = "do_interpolation" CATEGORY = "mtb/frame iterpolation" + DEPRECATED = True def do_interpolation( self, diff --git a/nodes/io.py b/nodes/io.py index 69e0f09..a738a39 100644 --- a/nodes/io.py +++ b/nodes/io.py @@ -2,9 +2,9 @@ import json import subprocess import uuid from pathlib import Path -from typing import List, Optional import comfy.model_management as model_management +import comfy.utils import folder_paths import numpy as np import torch @@ -41,6 +41,7 @@ class MTB_ReadPlaylist: RETURN_TYPES = ("PLAYLIST",) FUNCTION = "read_playlist" CATEGORY = "mtb/IO" + EXPERIMENTAL = True def read_playlist( self, @@ -83,6 +84,7 @@ class MTB_AddToPlaylist: OUTPUT_NODE = True FUNCTION = "add_to_playlist" CATEGORY = "mtb/IO" + EXPERIMENTAL = True def add_to_playlist( self, @@ -117,7 +119,10 @@ class MTB_AddToPlaylist: class MTB_ExportWithFfmpeg: - """Export with FFmpeg (Experimental)""" + """Export with FFmpeg (Experimental). + + [DEPRACATED] Use VHS nodes instead + """ @classmethod def INPUT_TYPES(cls): @@ -143,6 +148,7 @@ class MTB_ExportWithFfmpeg: RETURN_TYPES = ("VIDEO",) OUTPUT_NODE = True FUNCTION = "export_prores" + DEPRECATED = True CATEGORY = "mtb/IO" def export_prores( @@ -151,10 +157,9 @@ class MTB_ExportWithFfmpeg: prefix: str, format: str, codec: str, - images: Optional[torch.Tensor] = None, - playlist: Optional[List[str]] = None, + images: torch.Tensor | None = None, + playlist: list[str] | None = None, ): - pix_fmt = "rgb48le" if codec == "prores_ks" else "yuv420p" file_ext = format file_id = f"{prefix}_{uuid.uuid4()}.{file_ext}" @@ -208,9 +213,11 @@ class MTB_ExportWithFfmpeg: frames = tensor2np(images) log.debug(f"Frames type {type(frames[0])}") log.debug(f"Exporting {len(frames)} frames") + height, width, channels = frames[0].shape + has_alpha = channels == 4 + out_path = (output_dir / file_id).as_posix() if codec == "gif": - out_path = (output_dir / file_id).as_posix() command = [ "ffmpeg", "-f", @@ -233,12 +240,28 @@ class MTB_ExportWithFfmpeg: process.stdin.close() process.wait() + return (out_path,) else: - frames = [frame.astype(np.uint16) * 257 for frame in frames] - - height, width, _ = frames[0].shape - - out_path = (output_dir / file_id).as_posix() + if has_alpha: + if codec in ["prores_ks", "libx264", "libx265"]: + pix_fmt = ( + "yuva444p" if codec == "prores_ks" else "yuva420p" + ) + frames = [ + frame.astype(np.uint16) * 257 for frame in frames + ] + else: + log.warning( + f"Alpha channel not supported for codec {codec}. Alpha will be ignored." + ) + frames = [ + frame[:, :, :3].astype(np.uint16) * 257 + for frame in frames + ] + pix_fmt = "rgb48le" if codec == "prores_ks" else "yuv420p" + else: + pix_fmt = "rgb48le" if codec == "prores_ks" else "yuv420p" + frames = [frame.astype(np.uint16) * 257 for frame in frames] # Prepare the FFmpeg command command = [ @@ -258,17 +281,26 @@ class MTB_ExportWithFfmpeg: "-", "-c:v", codec, - "-r", - str(fps), - "-y", - out_path, ] + if codec == "prores_ks": + command.extend(["-profile:v", "4444"]) + + command.extend( + [ + "-r", + str(fps), + "-y", + out_path, + ] + ) process = subprocess.Popen(command, stdin=subprocess.PIPE) + pbar = comfy.utils.ProgressBar(len(frames)) + for frame in frames: - model_management.throw_exception_if_processing_interrupted() process.stdin.write(frame.tobytes()) + pbar.update(1) process.stdin.close() process.wait() @@ -280,9 +312,9 @@ def prepare_animated_batch( batch: torch.Tensor, pingpong=False, resize_by=1.0, - resample_filter: Optional[Image.Resampling] = None, + resample_filter: Image.Resampling | None = None, image_type=np.uint8, -) -> List[Image.Image]: +) -> list[Image.Image]: images = tensor2np(batch) images = [frame.astype(image_type) for frame in images] @@ -308,7 +340,10 @@ def prepare_animated_batch( # todo: deprecate for apng class MTB_SaveGif: - """Save the images from the batch as a GIF""" + """Save the images from the batch as a GIF. + + [DEPRACATED] Use VHS nodes instead + """ @classmethod def INPUT_TYPES(cls): @@ -328,6 +363,7 @@ class MTB_SaveGif: OUTPUT_NODE = True CATEGORY = "mtb/IO" FUNCTION = "save_gif" + DEPRECATED = True def save_gif( self,