158 lines
5.5 KiB
Python
158 lines
5.5 KiB
Python
import os
|
|
import hashlib
|
|
import random
|
|
import numpy as np
|
|
from PIL import Image, ImageOps
|
|
import torch
|
|
import cv2
|
|
from server import folder_paths
|
|
|
|
class LoadVideoPlus:
|
|
def __init__(self):
|
|
self.vid_extensions = [".mp4", ".avi", ".mov", ".mkv", ".webm"]
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"video": ("IMAGE", ("STRING", {"default": "X:/insert/path/here.mp4"})),
|
|
"use_random_video": ("BOOLEAN", {"default": False}),
|
|
"random_folder": ("STRING", {"default": "."}),
|
|
"n_videos": ("INT", {"default": 1, "min": 1, "max": 10}),
|
|
"seed": ("INT", {"default": 0, "min": 0, "max": 100000}),
|
|
"sort": ("BOOLEAN", {"default": False}),
|
|
"loop_sequence": ("BOOLEAN", {"default": False}),
|
|
}
|
|
}
|
|
|
|
CATEGORY = "🧔🏻♂️🇰 🇪 🇼 🇰 "
|
|
RETURN_TYPES = ("IMAGE", "MASK", "INT")
|
|
RETURN_NAMES = ("output_video", "output_mask", "frame_count")
|
|
FUNCTION = "load_video"
|
|
|
|
def load_video(self, video, use_random_video, random_folder, n_videos, seed, sort, loop_sequence):
|
|
if use_random_video:
|
|
output_video, output_mask, frame_count = self.load_random_video(random_folder, n_videos, seed, sort, loop_sequence)
|
|
else:
|
|
output_video, output_mask, frame_count = self.load_specific_video(video)
|
|
return (output_video, output_mask, frame_count)
|
|
|
|
def load_specific_video(self, video):
|
|
video_path = folder_paths.get_annotated_filepath(video) if hasattr(folder_paths, 'get_annotated_filepath') else video
|
|
cap = cv2.VideoCapture(video_path)
|
|
|
|
output_frames = []
|
|
w, h = None, None
|
|
frame_count = 0
|
|
|
|
while cap.isOpened():
|
|
ret, frame = cap.read()
|
|
if not ret:
|
|
break
|
|
|
|
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
|
frame = ImageOps.exif_transpose(Image.fromarray(frame))
|
|
frame = np.array(frame).astype(np.float32) / 255.0
|
|
|
|
if w is None and h is None:
|
|
w, h = frame.shape[1], frame.shape[0]
|
|
|
|
if frame.shape[1] != w or frame.shape[0] != h:
|
|
continue
|
|
|
|
frame = torch.from_numpy(frame)[None,]
|
|
output_frames.append(frame)
|
|
frame_count += 1
|
|
|
|
cap.release()
|
|
|
|
if not output_frames:
|
|
raise ValueError("No frames were extracted from the video.")
|
|
|
|
if len(output_frames) > 1:
|
|
output_video = torch.cat(output_frames, dim=0)
|
|
else:
|
|
output_video = output_frames[0]
|
|
|
|
# Create a dummy mask
|
|
output_mask = torch.zeros((output_video.shape[0], 64, 64), dtype=torch.float32, device="cpu")
|
|
|
|
return (output_video, output_mask, frame_count)
|
|
|
|
def load_random_video(self, folder, n_videos, seed, sort, loop_sequence):
|
|
files = [os.path.join(folder, f) for f in os.listdir(folder)]
|
|
files = [f for f in files if os.path.isfile(f)]
|
|
files = [f for f in files if any([f.endswith(ext) for ext in self.vid_extensions])]
|
|
|
|
random.seed(seed)
|
|
random.shuffle(files)
|
|
|
|
video_paths = files[:n_videos]
|
|
|
|
if sort:
|
|
video_paths = sorted(video_paths)
|
|
|
|
frames_list = []
|
|
frame_count = 0
|
|
|
|
for video_path in video_paths:
|
|
cap = cv2.VideoCapture(video_path)
|
|
while cap.isOpened():
|
|
ret, frame = cap.read()
|
|
if not ret:
|
|
break
|
|
|
|
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
|
frame = ImageOps.exif_transpose(Image.fromarray(frame))
|
|
frame = np.array(frame).astype(np.float32) / 255.0
|
|
frames_list.append(frame)
|
|
frame_count += 1
|
|
|
|
cap.release()
|
|
|
|
if not frames_list:
|
|
raise ValueError("No frames were extracted from the video(s).")
|
|
|
|
if loop_sequence:
|
|
frames_list.append(frames_list[0])
|
|
frame_count += 1
|
|
|
|
if len(frames_list) > 1:
|
|
frames_list = [torch.from_numpy(frame)[None,] for frame in frames_list]
|
|
output_video = torch.cat(frames_list, dim=0)
|
|
else:
|
|
output_video = torch.from_numpy(frames_list[0])[None,]
|
|
|
|
# Create a dummy mask
|
|
mask = torch.zeros((output_video.shape[0], 64, 64), dtype=torch.float32, device="cpu")
|
|
|
|
return (output_video, mask, frame_count)
|
|
|
|
@classmethod
|
|
def IS_CHANGED(cls, video, use_random_video, random_folder, n_videos, seed, sort, loop_sequence):
|
|
if use_random_video:
|
|
return seed # Return seed to indicate change when using random videos
|
|
else:
|
|
video_path = folder_paths.get_annotated_filepath(video) if hasattr(folder_paths, 'get_annotated_filepath') else video
|
|
m = hashlib.sha256()
|
|
with open(video_path, 'rb') as f:
|
|
m.update(f.read())
|
|
return m.digest().hex()
|
|
|
|
@classmethod
|
|
def VALIDATE_INPUTS(cls, video, use_random_video, random_folder, n_videos, seed, sort, loop_sequence):
|
|
if not use_random_video:
|
|
if not os.path.isfile(video):
|
|
return "Invalid video file: {}".format(video)
|
|
else:
|
|
if not os.path.isdir(random_folder):
|
|
return "Invalid folder path: {}".format(random_folder)
|
|
return True
|
|
|
|
NODE_CLASS_MAPPINGS = {
|
|
"LoadVideoPlus": LoadVideoPlus
|
|
}
|
|
|
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
|
"LoadVideoPlus": "Load Video Plus"
|
|
} |