This commit is contained in:
justumen
2024-09-16 10:36:11 +02:00
parent a3f930d8c6
commit 5f47d20f75
12 changed files with 176 additions and 9 deletions

View File

@@ -1,4 +1,4 @@
# 🔗 Comfyui : Bjornulf_custom_nodes v0.32 🔗
# 🔗 Comfyui : Bjornulf_custom_nodes v0.34 🔗
# ❤️ Coffee : ☕☕☕☕☕ 5/5
@@ -81,6 +81,7 @@ wget --content-disposition -P /workspace/ComfyUI/models/checkpoints "https://civ
- **v0.31**: ❗Sorry, Breaking changes for Write/Show text nodes, cleaner system : 1 simple write text and the other is 1 advanced with console and special syntax. Also Show can now manage INT, FLOAT, TEXT.
- **v0.32**: Quick rename to avoid breaking loop_text node.
- **v0.33**: Control random on paused nodes, fix pydub sound bug permissions on Windows.
- **v0.34**: Two new nodes : Load Images from output folder and Select an Image, Pick.
# 📝 Nodes descriptions
@@ -440,4 +441,37 @@ Just take a trio at random from a load checkpoint node.
![pick input](screenshots/loop_checkpoint.png)
**Description:**
Loop over all the trios from several checkpoint node.
Loop over all the trios from several checkpoint node.
### 42 - 📂🖼 Load Images from output folder
![pick input](screenshots/load_images_folder.png)
**Description:**
Quickly select all images from a folder inside the output folder. (Not recursively.)
So... As you can see from the screenshot the images are split based on their resolution.
It is not a choice I made, it is something that is part of the comfyui environment.
It's also not possible to edit dynamically the number of outputs, so I just picked a number : 4.
The node will separate the images based on their resolution, so with this node you can have 4 different resolutions per folder. (If you have more than that, maybe you should have another folder...)
To avoid error or crash if you have less than 4 resolutions in a folder, the node will just output white tensors. (white square image.)
So this node is a little hacky for now, but i can select my different characters in less than a second.
If you want to know how i personnaly save my images for a specific character, here is part of my workflow (Notice that i personnaly use / for folders because I'm on linux) :
![pick input](screenshots/character_save.png)
In this example I put "character/" as a string and then combine with "nothing". But it's the same if you do "character" and then combine with "/". (I just like having a / at the end of my folder's name...)
If you are satisfied with this logic, you can then select all these nodes, right click and `Convert to Group Node`, you can then have you own customized "save character node" :
![pick input](screenshots/bjornulf_save_character_group.png)
Here is another example of the same thing but excluding the save folder node :
![pick input](screenshots/bjornulf_save_character_group2.png)
### 43 - 🖼🔍 Select an Image, Pick
![pick input](screenshots/select_image.png)
**Description:**
Select an image from a list of images.
Useful in combination with my Load images from folder and preview image nodes.
You can also of course make a group node, like this one, which is the same as the screenshot above :
![pick input](screenshots/select_image_group.png)

View File

@@ -47,8 +47,9 @@ from .random_image import RandomImage
from .loop_model_clip_vae import LoopModelClipVae
from .write_text_advanced import WriteTextAdvanced
from .loop_write_text import LoopWriteText
# from .load_images_from_folder import LoadImagesFromSelectedFolder
from .load_images_from_folder import LoadImagesFromSelectedFolder
# from .show import ShowWhatever
from .select_image_from_list import SelectImageFromList
# from .pass_preview_image import PassPreviewImage
# from .check_black_image import CheckBlackImage
@@ -59,8 +60,9 @@ from .loop_write_text import LoopWriteText
NODE_CLASS_MAPPINGS = {
# "Bjornulf_CustomStringType": CustomStringType,
"Bjornulf_ollamaLoader": ollamaLoader,
"Bjornulf_SelectImageFromList": SelectImageFromList,
"Bjornulf_WriteText": WriteText,
# "Bjornulf_LoadImagesFromSelectedFolder": LoadImagesFromSelectedFolder,
"Bjornulf_LoadImagesFromSelectedFolder": LoadImagesFromSelectedFolder,
# "Bjornulf_ShowWhatever": ShowWhatever,
"Bjornulf_LoopModelClipVae": LoopModelClipVae,
# "Bjoenulf_RandomCheckpoint": RandomCheckpoint,
@@ -162,9 +164,10 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"Bjornulf_ollamaLoader": "🦙 Ollama (Description)",
"Bjornulf_FreeVRAM": "🧹 Free VRAM hack",
"Bjornulf_TextToSpeech": "🔊 TTS - Text to Speech",
"Bjornulf_PickInput": "⏸️🔍 Paused. Select input, Pick one",
"Bjornulf_PauseResume": "⏸️ Paused. Resume or Stop ?",
# "Bjornulf_LoadImagesFromSelectedFolder": "📂🖼 Load Images from folder",
"Bjornulf_PickInput": "⏸️ Paused. Select input, Pick 👇",
"Bjornulf_PauseResume": "⏸️ Paused. Resume or Stop, Pick 👇",
"Bjornulf_LoadImagesFromSelectedFolder": "📂🖼 Load Images from output folder",
"Bjornulf_SelectImageFromList": "🖼🔍 Select an Image, Pick",
}
WEB_DIRECTORY = "./web"

View File

@@ -4,7 +4,7 @@ class CombineTexts:
return {
"required": {
"number_of_inputs": ("INT", {"default": 2, "min": 2, "max": 50, "step": 1}),
"delimiter": (["newline", "comma", "space", "slash"], {"default": "newline"}),
"delimiter": (["newline", "comma", "space", "slash", "nothing"], {"default": "newline"}),
"text_1": ("STRING", {"forceInput": True}),
"text_2": ("STRING", {"forceInput": True}),
},
@@ -44,5 +44,7 @@ class CombineTexts:
return " "
elif delimiter == "slash":
return "/"
elif delimiter == "nothing":
return ""
else:
return "\n"

View File

@@ -0,0 +1,96 @@
import os
import numpy as np
from PIL import Image, ImageSequence, ImageOps
import torch
class LoadImagesFromSelectedFolder:
@classmethod
def INPUT_TYPES(cls):
# Get the directory where this script is located
script_dir = os.path.dirname(os.path.abspath(__file__))
comfyui_root = os.path.abspath(os.path.join(script_dir, '..', '..'))
output_dir = os.path.join(comfyui_root, 'output')
def count_images(folder_path):
# Count the number of image files in the folder
return len([f for f in os.listdir(folder_path) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.bmp'))])
folders = []
for root, dirs, files in os.walk(output_dir):
rel_path = os.path.relpath(root, output_dir)
if rel_path == '.':
continue
image_count = count_images(root)
if image_count > 0:
folder_name = f"{rel_path} ({image_count} images)"
folders.append((folder_name, rel_path))
# Sort folders alphabetically, case-insensitive
folders.sort(key=lambda x: x[0].lower())
return {
"required": {
"selected_folder": ([folder[0] for folder in folders],),
}
}
RETURN_TYPES = ("IMAGE", "IMAGE", "IMAGE", "IMAGE")
RETURN_NAMES = ("Images resolution 1", "Images resolution 2", "Images resolution 3", "Images resolution 4")
FUNCTION = "load_images_from_selected_folder"
CATEGORY = "Bjornulf"
def load_images_from_selected_folder(self, selected_folder):
script_dir = os.path.dirname(os.path.abspath(__file__))
comfyui_root = os.path.abspath(os.path.join(script_dir, '..', '..'))
output_dir = os.path.join(comfyui_root, 'output')
folder_path = os.path.join(output_dir, selected_folder.split(" (")[0])
images_by_resolution = {}
# Check if the folder exists and contains images
if not os.path.exists(folder_path):
print(f"Folder {folder_path} does not exist.")
return (None, None, None)
image_files = [f for f in os.listdir(folder_path) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.bmp'))]
if not image_files:
print(f"No images found in folder {folder_path}.")
return (None, None, None)
for image_file in image_files:
image_path = os.path.join(folder_path, image_file)
img = Image.open(image_path)
for i in ImageSequence.Iterator(img):
i = ImageOps.exif_transpose(i)
if i.mode == 'I':
i = i.point(lambda i: i * (1 / 255))
image = i.convert("RGB")
resolution = image.size
image = np.array(image).astype(np.float32) / 255.0
image = torch.from_numpy(image)[None,]
if resolution not in images_by_resolution:
images_by_resolution[resolution] = []
images_by_resolution[resolution].append(image)
# Sort resolutions by total pixel count (width * height)
sorted_resolutions = sorted(images_by_resolution.keys(), key=lambda r: r[0] * r[1], reverse=True)
outputs = []
for i in range(4): # Return up to 4 different resolutions
if i < len(sorted_resolutions):
resolution = sorted_resolutions[i]
output_image = torch.cat(images_by_resolution[resolution], dim=0)
outputs.append(output_image)
else:
# Create a placeholder tensor filled with 11111111111111111111111
H, W, C = 64, 64, 3
placeholder_image = torch.ones((1, H, W, C), dtype=torch.float32)
outputs.append(placeholder_image)
return tuple(outputs)

View File

@@ -1,7 +1,7 @@
[project]
name = "bjornulf_custom_nodes"
description = "Nodes: Ollama, Text to Speech, Combine Texts, Random Texts, Save image for Bjornulf LobeChat, Text with random Seed, Random line from input, Combine images, Image to grayscale (black & white), Remove image Transparency (alpha), Resize Image, ..."
version = "0.33"
version = "0.34"
license = {file = "LICENSE"}
[project.urls]

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 553 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 481 KiB

32
select_image_from_list.py Normal file
View File

@@ -0,0 +1,32 @@
import torch
class Everything(str):
def __ne__(self, __value: object) -> bool:
return False
class SelectImageFromList:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"all_images": ("IMAGE", {}),
"selection": ("INT", {"default": 1, "min": 1, "max": 999, "step": 1}),
}
}
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("selected_image",)
FUNCTION = "select_an_image"
CATEGORY = "Bjornulf"
def select_an_image(self, all_images, selection):
# Ensure the selection is within bounds
selection = max(1, min(selection, all_images.shape[0]))
# Adjust selection to 0-based index
index = selection - 1
# Select the image at the specified index
selected_image = all_images[index].unsqueeze(0)
return (selected_image,)