mirror of
https://github.com/justUmen/Bjornulf_custom_nodes.git
synced 2026-03-21 20:52:11 -03:00
0.34
This commit is contained in:
38
README.md
38
README.md
@@ -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.
|
||||

|
||||
|
||||
**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
|
||||
|
||||

|
||||
|
||||
**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) :
|
||||

|
||||
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" :
|
||||

|
||||
|
||||
Here is another example of the same thing but excluding the save folder node :
|
||||

|
||||
|
||||
### 43 - 🖼🔍 Select an Image, Pick
|
||||
|
||||

|
||||
|
||||
**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 :
|
||||

|
||||
13
__init__.py
13
__init__.py
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
96
load_images_from_folder.py
Normal file
96
load_images_from_folder.py
Normal 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)
|
||||
@@ -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]
|
||||
|
||||
BIN
screenshots/bjornulf_save_character_group.png
Normal file
BIN
screenshots/bjornulf_save_character_group.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 56 KiB |
BIN
screenshots/bjornulf_save_character_group2.png
Normal file
BIN
screenshots/bjornulf_save_character_group2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
BIN
screenshots/character_save.png
Normal file
BIN
screenshots/character_save.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 117 KiB |
BIN
screenshots/load_images_folder.png
Normal file
BIN
screenshots/load_images_folder.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 553 KiB |
BIN
screenshots/select_image.png
Normal file
BIN
screenshots/select_image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 375 KiB |
BIN
screenshots/select_image_group.png
Normal file
BIN
screenshots/select_image_group.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 481 KiB |
32
select_image_from_list.py
Normal file
32
select_image_from_list.py
Normal 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,)
|
||||
Reference in New Issue
Block a user