added list output for directory crawl and image randomizer + added image blank node

This commit is contained in:
Fill
2025-05-23 03:33:23 +09:00
parent 100c850308
commit 88a070d8c4
5 changed files with 95 additions and 38 deletions
+3
View File
@@ -127,9 +127,11 @@ from .nodes.FL_GPT_Image1_ADV import FL_GPT_Image1_ADV
from .nodes.FL_ImageBatch import FL_ImageBatch
from .nodes.FL_Hedra_API import FL_Hedra_API
from .nodes.FL_RunwayImageAPI import FL_RunwayImageAPI
from .nodes.FL_Image_Blank import FL_ImageBlank
NODE_CLASS_MAPPINGS = {
"FL_ImageBlank": FL_ImageBlank,
"FL_ImageRandomizer": FL_ImageRandomizer,
"FL_ImageCaptionSaver": FL_ImageCaptionSaver,
"FL_VideoCaptionSaver": FL_VideoCaptionSaver,
@@ -265,6 +267,7 @@ NODE_CLASS_MAPPINGS = {
}
NODE_DISPLAY_NAME_MAPPINGS = {
"FL_ImageBlank": "FL Image Blank",
"FL_ImageRandomizer": "FL Image Randomizer",
"FL_ImageCaptionSaver": "FL Image Caption Saver",
"FL_VideoCaptionSaver": "FL Video Caption Saver",
+44 -23
View File
@@ -17,7 +17,9 @@ class FL_DirectoryCrawl:
}
}
RETURN_TYPES = ("IMAGE", "STRING") # Output a batch of images or list of text contents
RETURN_TYPES = ("IMAGE", "STRING", "IMAGE")
RETURN_NAMES = ("image_batch", "text_content", "image_list")
OUTPUT_IS_LIST = (False, False, True)
FUNCTION = "load_batch"
CATEGORY = "🏵️Fill Nodes/utility"
@@ -27,52 +29,71 @@ class FL_DirectoryCrawl:
file_paths = self.crawl_directories(directory_path, file_type)
if not file_paths:
raise ValueError(f"No {file_type} found in the specified directory and its subdirectories.")
if file_type == "images":
return (torch.empty(0), "", []) # Return empty for all if no files
else: # text
return (torch.empty(0), "", [])
file_paths = file_paths[:max_files] # Limit the number of files
if file_type == "images":
return self.load_image_batch(file_paths)
else:
return self.load_text_batch(file_paths)
batch_tensor, image_list_tensors = self.load_images_data(file_paths)
return (batch_tensor, "", image_list_tensors)
else: # text
text_content = self.load_text_data(file_paths)
return (torch.empty(0), text_content, [])
def load_image_batch(self, image_paths):
batch_images = []
def load_images_data(self, image_paths):
individual_images_np = []
individual_image_tensors = []
pbar = ProgressBar(len(image_paths))
if not image_paths:
return torch.empty(0), []
for idx, img_path in enumerate(image_paths):
image = Image.open(img_path)
image = ImageOps.exif_transpose(image) # Correct orientation
image = image.convert("RGB")
image_np = np.array(image).astype(np.float32) / 255.0
batch_images.append(image_np)
individual_images_np.append(image_np)
# Create tensor for the list output (B, H, W, C) -> (1, H, W, C)
individual_image_tensors.append(torch.from_numpy(image_np)[None,])
pbar.update_absolute(idx)
# Pad images to the largest dimensions
max_h = max(img.shape[0] for img in batch_images)
max_w = max(img.shape[1] for img in batch_images)
# Pad images for batch output
max_h = max(img.shape[0] for img in individual_images_np)
max_w = max(img.shape[1] for img in individual_images_np)
padded_images = []
for img in batch_images:
h, w, c = img.shape
padded_images_for_batch = []
for img_np in individual_images_np:
h, w, c = img_np.shape
padded = np.zeros((max_h, max_w, c), dtype=np.float32)
padded[:h, :w, :] = img
padded_images.append(padded)
padded[:h, :w, :] = img_np
padded_images_for_batch.append(padded)
batch_images_np = np.stack(padded_images, axis=0)
batch_images_np = np.stack(padded_images_for_batch, axis=0)
batch_images_tensor = torch.from_numpy(batch_images_np)
return (batch_images_tensor, "")
return batch_images_tensor, individual_image_tensors
def load_text_batch(self, text_paths):
def load_text_data(self, text_paths):
text_contents = []
if not text_paths:
return ""
pbar = ProgressBar(len(text_paths))
for idx, txt_path in enumerate(text_paths):
with open(txt_path, 'r', encoding='utf-8') as file:
content = file.read()
text_contents.append(content)
try:
with open(txt_path, 'r', encoding='utf-8') as file:
content = file.read()
text_contents.append(content)
except Exception as e:
print(f"Warning: Could not read text file {txt_path}: {e}")
text_contents.append(f"Error reading file: {txt_path}")
pbar.update_absolute(idx)
return (torch.zeros(1), "\n---\n".join(text_contents)) # Return empty tensor for IMAGE type
return "\n---\n".join(text_contents)
def crawl_directories(self, directory, file_type):
if file_type == "images":
+36
View File
@@ -0,0 +1,36 @@
import torch
import numpy as np
from PIL import Image
class FL_ImageBlank:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"width": ("INT", {"default": 512, "min": 64, "max": 8192, "step": 64}),
"height": ("INT", {"default": 512, "min": 64, "max": 8192, "step": 64}),
"red": ("INT", {"default": 255, "min": 0, "max": 255, "step": 1}),
"green": ("INT", {"default": 255, "min": 0, "max": 255, "step": 1}),
"blue": ("INT", {"default": 255, "min": 0, "max": 255, "step": 1}),
}
}
RETURN_TYPES = ("IMAGE",)
FUNCTION = "create_blank_image"
CATEGORY = "🏵️Fill Nodes/Image"
def create_blank_image(self, width, height, red, green, blue):
# Create a 3-channel image (H, W, C)
image_np = np.full((height, width, 3), [red, green, blue], dtype=np.uint8)
# Convert to PIL Image first to handle potential mode issues if needed, then to numpy float32
# Though for a simple color, direct numpy to tensor is fine.
# image_pil = Image.fromarray(image_np, 'RGB')
# image_np_float = np.array(image_pil).astype(np.float32) / 255.0
image_np_float = image_np.astype(np.float32) / 255.0
# Convert to tensor and add batch dimension (B, H, W, C)
image_tensor = torch.from_numpy(image_np_float)[None,]
return (image_tensor,)
+11 -14
View File
@@ -17,7 +17,9 @@ class FL_ImageRandomizer:
}
}
RETURN_TYPES = ("IMAGE", "PATH")
RETURN_TYPES = ("IMAGE", "PATH", "IMAGE")
RETURN_NAMES = ("image_batch", "selected_path", "image_list")
OUTPUT_IS_LIST = (False, False, True)
FUNCTION = "select_media"
CATEGORY = "🏵️Fill Nodes/Image"
@@ -26,11 +28,13 @@ class FL_ImageRandomizer:
raise ValueError("Directory path is not provided.")
if mode == "Image":
return self.select_image(directory_path, seed, search_subdirectories)
image_tensor, selected_path = self.select_image_data(directory_path, seed, search_subdirectories)
return (image_tensor, selected_path, [image_tensor])
else: # Video mode
return self.select_video_frames(directory_path, seed, search_subdirectories)
frames_tensor, selected_path = self.select_video_data(directory_path, seed, search_subdirectories)
return (frames_tensor, selected_path, [frames_tensor]) # Video frames are already a batch, but we wrap in list for consistency
def select_image(self, directory_path, seed, search_subdirectories=False):
def select_image_data(self, directory_path, seed, search_subdirectories=False):
images = self.load_files(directory_path, search_subdirectories, file_type="image")
if not images:
raise ValueError("No images found in the specified directory.")
@@ -46,9 +50,9 @@ class FL_ImageRandomizer:
image_np = np.array(image).astype(np.float32) / 255.0
image_tensor = torch.from_numpy(image_np)[None,]
return (image_tensor, selected_image_path)
return image_tensor, selected_image_path
def select_video_frames(self, directory_path, seed, search_subdirectories=False):
def select_video_data(self, directory_path, seed, search_subdirectories=False):
videos = self.load_files(directory_path, search_subdirectories, file_type="video")
if not videos:
raise ValueError("No videos found in the specified directory.")
@@ -58,27 +62,21 @@ class FL_ImageRandomizer:
selected_video_path = videos[selected_index]
# Open the video file
cap = cv2.VideoCapture(selected_video_path)
if not cap.isOpened():
raise ValueError(f"Could not open video file: {selected_video_path}")
# Get video properties
frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
if frame_count <= 0:
raise ValueError(f"No frames found in video: {selected_video_path}")
# Extract all frames from the video
frames = []
success = True
while success:
success, frame = cap.read()
if success:
# Convert BGR to RGB (OpenCV uses BGR by default)
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# Normalize
frame_np = np.array(frame).astype(np.float32) / 255.0
frames.append(frame_np)
@@ -87,10 +85,9 @@ class FL_ImageRandomizer:
if not frames:
raise ValueError(f"Failed to extract frames from video: {selected_video_path}")
# Convert list of frames to tensor with batch dimension
frames_tensor = torch.from_numpy(np.stack(frames))
return (frames_tensor, selected_video_path)
return frames_tensor, selected_video_path
def load_files(self, directory, search_subdirectories=False, file_type="image"):
if file_type == "image":
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "comfyui_fill-nodes"
description = "Fill-Nodes is a versatile collection of custom nodes for ComfyUI that extends functionality across multiple domains. Features include advanced image processing (pixelation, slicing, masking), visual effects generation (glitch, halftone, pixel art), comprehensive file handling (PDF creation/extraction, Google Drive integration), AI model interfaces (GPT, DALL-E, Hugging Face), utility nodes for workflow enhancement, and specialized tools for video processing, captioning, and batch operations. The pack provides both practical workflow solutions and creative tools within a unified node collection."
version = "1.5.4"
version = "1.5.5"
license = "LICENSE"
dependencies = ["diffusers", "librosa", "sounddevice", "glitch_this", "PyOpenGL", "glfw", "scipy>=1.13.1", "requests", "aiohttp", "moviepy", "matplotlib", "reportlab", "openai", "PyPDF2", "pdf2image", "PyMuPDF", "reportlab", "PyPDF2", "ollama", "kornia", "opencv-python", "gdown", "open_clip_torch", "google-genai"]