mirror of
https://github.com/justUmen/Bjornulf_custom_nodes.git
synced 2026-03-21 12:42:11 -03:00
0.25
This commit is contained in:
19
README.md
19
README.md
@@ -68,6 +68,7 @@ wget --content-disposition -P /workspace/ComfyUI/models/checkpoints "https://civ
|
||||
- **v0.22**: Allow write text node to use random selection like this {hood|helmet} will randomly choose between hood or helmet.
|
||||
- **v0.23**: Add a new node: Pause, resume or stop workflow.
|
||||
- **v0.24**: Add a new node: Pause, select input, pick one.
|
||||
- **v0.25**: Two new nodes: Loop Images and Random image.
|
||||
|
||||
# 📝 Nodes descriptions
|
||||
|
||||
@@ -348,4 +349,20 @@ You can connect any type of node to the pause node, above is an example with tex
|
||||
**Description:**
|
||||
Automatically pause the workflow, and rings a bell when it does. (play the audio `bell.m4a` file provided)
|
||||
You can then manually select the input you want to use, and resume the workflow with it.
|
||||
You can connect this node to anything you want, above is an example with IMAGE. But you can pick whatever you want, in the node `input = output`.
|
||||
You can connect this node to anything you want, above is an example with IMAGE. But you can pick whatever you want, in the node `input = output`.
|
||||
|
||||
### 37 - 🎲🖼 Random Image
|
||||
|
||||

|
||||
|
||||
**Description:**
|
||||
Just take a random image from a list of images.
|
||||
|
||||
### 38 - ♻🖼 Loop (Images)
|
||||
|
||||

|
||||
|
||||
**Description:**
|
||||
Loop over a list of images.
|
||||
Usage example : You have a list of images, and you want to apply the same process to all of them.
|
||||
Above is an example of the loop images node sending them to an Ipadapter style transfer workflow. (Same seed of course.)
|
||||
@@ -42,6 +42,9 @@ from .loop_combine_texts_by_lines import CombineTextsByLines
|
||||
from .free_vram_hack import FreeVRAM
|
||||
from .pause_resume_stop import PauseResume
|
||||
from .pick_input import PickInput
|
||||
from .loop_images import LoopImages
|
||||
from .random_image import RandomImage
|
||||
|
||||
# from .pass_preview_image import PassPreviewImage
|
||||
# from .check_black_image import CheckBlackImage
|
||||
# from .clear_vram import ClearVRAM
|
||||
@@ -51,6 +54,8 @@ from .pick_input import PickInput
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
# "Bjornulf_CustomStringType": CustomStringType,
|
||||
"Bjornulf_ollamaLoader": ollamaLoader,
|
||||
"Bjornulf_LoopImages": LoopImages,
|
||||
"Bjornulf_RandomImage": RandomImage,
|
||||
# "Bjornulf_PassPreviewImage": PassPreviewImage,
|
||||
"Bjornulf_PickInput": PickInput,
|
||||
"Bjornulf_PauseResume": PauseResume,
|
||||
@@ -102,6 +107,8 @@ NODE_CLASS_MAPPINGS = {
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
# "Bjornulf_CustomStringType": "!!! CUSTOM STRING TYPE !!!",
|
||||
"Bjornulf_ollamaLoader": "🦙 Ollama (Description)",
|
||||
"Bjornulf_LoopImages": "♻🖼 Loop (Images)",
|
||||
"Bjornulf_RandomImage": "🎲🖼 Random Image",
|
||||
# "Bjornulf_PassPreviewImage": "🖼⮕ Pass Preview Image",
|
||||
"Bjornulf_PickInput": "⏸️🔍 Paused. Select input, Pick one",
|
||||
"Bjornulf_PauseResume": "⏸️ Paused. Resume or Stop ?",
|
||||
|
||||
26
loop_images.py
Normal file
26
loop_images.py
Normal file
@@ -0,0 +1,26 @@
|
||||
class LoopImages:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"number_of_images": ("INT", {"default": 2, "min": 1, "max": 10, "step": 1}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
FUNCTION = "loop_images"
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
CATEGORY = "Bjornulf"
|
||||
|
||||
def loop_images(self, number_of_images, **kwargs):
|
||||
image_list = []
|
||||
for i in range(1, number_of_images + 1):
|
||||
image_key = f"image_{i}"
|
||||
if image_key in kwargs and kwargs[image_key] is not None:
|
||||
image_list.append(kwargs[image_key])
|
||||
return (image_list,)
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, number_of_images, ** kwargs):
|
||||
return float("nan") # This will force the node to always update
|
||||
|
||||
@@ -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.24"
|
||||
version = "0.25"
|
||||
license = {file = "LICENSE"}
|
||||
|
||||
[project.urls]
|
||||
|
||||
32
random_image.py
Normal file
32
random_image.py
Normal file
@@ -0,0 +1,32 @@
|
||||
import random
|
||||
|
||||
class RandomImage:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"number_of_images": ("INT", {"default": 2, "min": 1, "max": 10, "step": 1}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
FUNCTION = "random_image"
|
||||
CATEGORY = "Bjornulf"
|
||||
|
||||
def random_image(self, number_of_images, **kwargs):
|
||||
valid_images = []
|
||||
for i in range(1, number_of_images + 1):
|
||||
image_key = f"image_{i}"
|
||||
if image_key in kwargs and kwargs[image_key] is not None:
|
||||
valid_images.append(kwargs[image_key])
|
||||
|
||||
if not valid_images:
|
||||
raise ValueError("No valid images provided")
|
||||
|
||||
random_image = random.choice(valid_images)
|
||||
return (random_image,)
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, number_of_images, ** kwargs):
|
||||
return float("nan") # This will force the node to always update
|
||||
|
||||
BIN
screenshots/loop_images.png
Normal file
BIN
screenshots/loop_images.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 940 KiB |
BIN
screenshots/random_image.png
Normal file
BIN
screenshots/random_image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 224 KiB |
52
web/js/loop_images.js
Normal file
52
web/js/loop_images.js
Normal file
@@ -0,0 +1,52 @@
|
||||
import { app } from "../../../scripts/app.js";
|
||||
|
||||
app.registerExtension({
|
||||
name: "Bjornulf.LoopImages",
|
||||
async nodeCreated(node) {
|
||||
if (node.comfyClass === "Bjornulf_LoopImages") {
|
||||
const updateInputs = () => {
|
||||
const numInputsWidget = node.widgets.find(w => w.name === "number_of_images");
|
||||
if (!numInputsWidget) return;
|
||||
|
||||
const numInputs = numInputsWidget.value;
|
||||
|
||||
// Initialize node.inputs if it doesn't exist
|
||||
if (!node.inputs) {
|
||||
node.inputs = [];
|
||||
}
|
||||
|
||||
// Filter existing image inputs
|
||||
const existingInputs = node.inputs.filter(input => input.name.startsWith('image_'));
|
||||
|
||||
// Determine if we need to add or remove inputs
|
||||
if (existingInputs.length < numInputs) {
|
||||
// Add new image inputs if not enough existing
|
||||
for (let i = existingInputs.length + 1; i <= numInputs; i++) {
|
||||
const inputName = `image_${i}`;
|
||||
if (!node.inputs.find(input => input.name === inputName)) {
|
||||
node.addInput(inputName, "IMAGE");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Remove excess image inputs if too many
|
||||
node.inputs = node.inputs.filter(input => !input.name.startsWith('image_') || parseInt(input.name.split('_')[1]) <= numInputs);
|
||||
}
|
||||
|
||||
node.setSize(node.computeSize());
|
||||
};
|
||||
|
||||
// Move number_of_images to the top initially
|
||||
const numInputsWidget = node.widgets.find(w => w.name === "number_of_images");
|
||||
if (numInputsWidget) {
|
||||
node.widgets = [numInputsWidget, ...node.widgets.filter(w => w !== numInputsWidget)];
|
||||
numInputsWidget.callback = () => {
|
||||
updateInputs();
|
||||
app.graph.setDirtyCanvas(true);
|
||||
};
|
||||
}
|
||||
|
||||
// Delay the initial update to ensure node is fully initialized
|
||||
setTimeout(updateInputs, 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
58
web/js/random_image.js
Normal file
58
web/js/random_image.js
Normal file
@@ -0,0 +1,58 @@
|
||||
import { app } from "../../../scripts/app.js";
|
||||
|
||||
app.registerExtension({
|
||||
name: "Bjornulf.RandomImage",
|
||||
async nodeCreated(node) {
|
||||
if (node.comfyClass === "Bjornulf_RandomImage") {
|
||||
const updateInputs = () => {
|
||||
const numInputsWidget = node.widgets.find(w => w.name === "number_of_images");
|
||||
if (!numInputsWidget) return;
|
||||
|
||||
const numInputs = numInputsWidget.value;
|
||||
|
||||
// Initialize node.inputs if it doesn't exist
|
||||
if (!node.inputs) {
|
||||
node.inputs = [];
|
||||
}
|
||||
|
||||
// Filter existing image inputs
|
||||
const existingInputs = node.inputs.filter(input => input.name.startsWith('image_'));
|
||||
|
||||
// Determine if we need to add or remove inputs
|
||||
if (existingInputs.length < numInputs) {
|
||||
// Add new image inputs if not enough existing
|
||||
for (let i = existingInputs.length + 1; i <= numInputs; i++) {
|
||||
const inputName = `image_${i}`;
|
||||
if (!node.inputs.find(input => input.name === inputName)) {
|
||||
node.addInput(inputName, "IMAGE");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Remove excess image inputs if too many
|
||||
node.inputs = node.inputs.filter(input => !input.name.startsWith('image_') || parseInt(input.name.split('_')[1]) <= numInputs);
|
||||
}
|
||||
|
||||
node.setSize(node.computeSize());
|
||||
};
|
||||
|
||||
// Set seed widget to hidden input
|
||||
const seedWidget = node.widgets.find(w => w.name === "seed");
|
||||
if (seedWidget) {
|
||||
seedWidget.type = "HIDDEN";
|
||||
}
|
||||
|
||||
// Move number_of_images to the top initially
|
||||
const numInputsWidget = node.widgets.find(w => w.name === "number_of_images");
|
||||
if (numInputsWidget) {
|
||||
node.widgets = [numInputsWidget, ...node.widgets.filter(w => w !== numInputsWidget)];
|
||||
numInputsWidget.callback = () => {
|
||||
updateInputs();
|
||||
app.graph.setDirtyCanvas(true);
|
||||
};
|
||||
}
|
||||
|
||||
// Delay the initial update to ensure node is fully initialized
|
||||
setTimeout(updateInputs, 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user