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.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.23**: Add a new node: Pause, resume or stop workflow.
|
||||||
- **v0.24**: Add a new node: Pause, select input, pick one.
|
- **v0.24**: Add a new node: Pause, select input, pick one.
|
||||||
|
- **v0.25**: Two new nodes: Loop Images and Random image.
|
||||||
|
|
||||||
# 📝 Nodes descriptions
|
# 📝 Nodes descriptions
|
||||||
|
|
||||||
@@ -348,4 +349,20 @@ You can connect any type of node to the pause node, above is an example with tex
|
|||||||
**Description:**
|
**Description:**
|
||||||
Automatically pause the workflow, and rings a bell when it does. (play the audio `bell.m4a` file provided)
|
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 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 .free_vram_hack import FreeVRAM
|
||||||
from .pause_resume_stop import PauseResume
|
from .pause_resume_stop import PauseResume
|
||||||
from .pick_input import PickInput
|
from .pick_input import PickInput
|
||||||
|
from .loop_images import LoopImages
|
||||||
|
from .random_image import RandomImage
|
||||||
|
|
||||||
# from .pass_preview_image import PassPreviewImage
|
# from .pass_preview_image import PassPreviewImage
|
||||||
# from .check_black_image import CheckBlackImage
|
# from .check_black_image import CheckBlackImage
|
||||||
# from .clear_vram import ClearVRAM
|
# from .clear_vram import ClearVRAM
|
||||||
@@ -51,6 +54,8 @@ from .pick_input import PickInput
|
|||||||
NODE_CLASS_MAPPINGS = {
|
NODE_CLASS_MAPPINGS = {
|
||||||
# "Bjornulf_CustomStringType": CustomStringType,
|
# "Bjornulf_CustomStringType": CustomStringType,
|
||||||
"Bjornulf_ollamaLoader": ollamaLoader,
|
"Bjornulf_ollamaLoader": ollamaLoader,
|
||||||
|
"Bjornulf_LoopImages": LoopImages,
|
||||||
|
"Bjornulf_RandomImage": RandomImage,
|
||||||
# "Bjornulf_PassPreviewImage": PassPreviewImage,
|
# "Bjornulf_PassPreviewImage": PassPreviewImage,
|
||||||
"Bjornulf_PickInput": PickInput,
|
"Bjornulf_PickInput": PickInput,
|
||||||
"Bjornulf_PauseResume": PauseResume,
|
"Bjornulf_PauseResume": PauseResume,
|
||||||
@@ -102,6 +107,8 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||||
# "Bjornulf_CustomStringType": "!!! CUSTOM STRING TYPE !!!",
|
# "Bjornulf_CustomStringType": "!!! CUSTOM STRING TYPE !!!",
|
||||||
"Bjornulf_ollamaLoader": "🦙 Ollama (Description)",
|
"Bjornulf_ollamaLoader": "🦙 Ollama (Description)",
|
||||||
|
"Bjornulf_LoopImages": "♻🖼 Loop (Images)",
|
||||||
|
"Bjornulf_RandomImage": "🎲🖼 Random Image",
|
||||||
# "Bjornulf_PassPreviewImage": "🖼⮕ Pass Preview Image",
|
# "Bjornulf_PassPreviewImage": "🖼⮕ Pass Preview Image",
|
||||||
"Bjornulf_PickInput": "⏸️🔍 Paused. Select input, Pick one",
|
"Bjornulf_PickInput": "⏸️🔍 Paused. Select input, Pick one",
|
||||||
"Bjornulf_PauseResume": "⏸️ Paused. Resume or Stop ?",
|
"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]
|
[project]
|
||||||
name = "bjornulf_custom_nodes"
|
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, ..."
|
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"}
|
license = {file = "LICENSE"}
|
||||||
|
|
||||||
[project.urls]
|
[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