20 Commits
Author SHA1 Message Date
taches-ai d1b774d7ce Push version number to Comfy registry 2024-10-30 13:54:36 +01:00
taches-ai b4c965b1a5 fix: inputs_list build method don't overwrite lists anymore (stupid extra indent) 2024-10-30 12:13:16 +01:00
taches-ai b94ba237a1 fix: alter action seed to add more randomness 2024-10-27 18:49:26 +01:00
taches-ai a7d498a081 fix: change old probability character 2024-10-27 17:44:47 +01:00
taches-ai 8fe532f2f7 fix: change '?' with '[]' to define distribution in config files (avoid comfyui bad behavior) 2024-10-27 17:38:58 +01:00
taches-ai 4d67a6b4cc fix: correctly update scene's seed 2024-10-27 17:06:42 +01:00
taches-ai 1ac972f912 docs: move wiki-related explanations and update LICENSE 2024-10-27 15:17:08 +01:00
taches-ai aa26f4aae2 Add workflows 2024-10-27 13:36:00 +01:00
taches-ai b78501d4e2 change: set nsfw default value to false 2024-10-27 13:24:40 +01:00
taches-ai acdcb0032b fix: clothes use select_tag to handle random attribution 2024-10-27 13:11:41 +01:00
taches-ai ed31d7dd82 Remove main.py file for testing purpose 2024-10-27 12:57:01 +01:00
taches-ai 4ab36b4470 fix: remove prefix replacement in act list to let the back-end target it 2024-10-27 12:53:30 +01:00
taches-ai a22a2e9023 refactor: rename "src" to "py" 2024-10-26 17:48:33 +02:00
Taches 489c526404 Merge pull request #3 from ComfyNodePRs/pyproject
Add pyproject.toml for Custom Node Registry
2024-10-26 16:54:42 +02:00
Taches c51e71aaf4 Merge branch 'main' into pyproject 2024-10-26 16:54:30 +02:00
taches-ai bd2cc15447 Update pyproject infos 2024-10-26 16:52:55 +02:00
snomiao d71cb04f88 chore(pyproject): Add pyproject.toml for Custom Node Registry 2024-10-26 16:52:55 +02:00
Taches 035b2c3003 Merge pull request #4 from ComfyNodePRs/publish
Add Github Action for Publishing to Comfy Registry
2024-10-26 16:49:28 +02:00
snomiao dc9bb0ad48 chore(pyproject): Add pyproject.toml for Custom Node Registry 2024-10-19 13:59:45 +00:00
snomiao 5feda0fbab chore(publish): Add Github Action for Publishing to Comfy Registry 2024-10-19 13:59:45 +00:00
24 changed files with 879 additions and 167 deletions
+24
View File
@@ -0,0 +1,24 @@
name: Publish to Comfy registry
on:
workflow_dispatch:
push:
branches:
- main
- master
paths:
- "pyproject.toml"
jobs:
publish-node:
name: Publish Custom Node to registry
runs-on: ubuntu-latest
# if this is a forked repository. Skipping the workflow.
if: github.event.repository.fork == false
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Publish Custom Node
uses: Comfy-Org/publish-node-action@main
with:
## Add your own personal access token to your Github Repository secrets and reference it here.
personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }}
+1
View File
@@ -83,6 +83,7 @@ coverage.xml
.hypothesis/
.pytest_cache/
cover/
test.py
# Translations
*.mo
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2024 taches-ai
Copyright (c) 2024 Taches
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+34 -91
View File
@@ -1,108 +1,51 @@
# ComfyUI Scene Composer
A collection of [ComfyUI](https://github.com/comfyanonymous/ComfyUI) nodes to create scenes with random procedural generation.
![Scene Composer workflow](workflow.png)
## Installation
Clone this repo in `custom_nodes`, in your ComfyUI folder.
### Via ComfyUI
If you have [ComfyUI Manager](https://github.com/ltdrdata/ComfyUI-Manager) installed, you can simply import [`workflow.json`](workflow.json) or [`workflow.png`](workflow.png) in ComfyUI and click on "Install Missing Nodes".
Alternatively, you can install it via the [Comfy Registry](https://registry.comfy.org/publishers/taches/nodes/comfyui-scene-composer) using the [CLI](https://docs.comfy.org/comfy-cli/getting-started#install-cli).
```shell
$ comfy node registry-install comfyui-scene-composer
```
### Manually
Clone this repo in `custom_nodes` situated in your ComfyUI install folder.
```shell
$ git clone https://github.com/taches/comfyui-scene-composer
$ pip install -r requirements.txt
```
## Nodes
### Scene
The Scene node generates a procedural random prompt as a string. The prompt follows the following structure:
| Component | Description |
| --------------- | ------------------------------------------------------------- |
| **C**omposition | The layout of the scene, cameras, number of protagonists, etc |
| **A**ctions | Activites done by the character/protagonist(s) |
| **C**haracter | Main character, focusing on body type and faces, etc |
| **C**lothes | Include casual clothes, uniforms, swimsuits, underwear, etc |
| **E**nvironment | The setting, including background, weather, time of day, etc |
You can define some aspects of the scene directly in the node. If you want to have more control over what's generated, you can override these components by passing a string as input. This work particularly well with the node components, which are explained just below.
### Components
Node components also generate a procedural random prompt as a string. It can be used to have better control over the generation of each concept. You can then pass it to the Scene node or even us it as standalone.
### Seed
Each node has its own seed. This way, you can alter the generation of each component independently without changing the whole scene. The scene seed is just there as a fallback if a component is not set in the optional input.
## Config files
You can configure components and other related concepts using the TOML files situated in the `config` folder.
Strings will output the chain of tags as it is. Lists will randomly take one tag
```toml
tags = "foo, bar" # -> foo, bar
tags = ["foo", "bar"] # -> bar
```
### Weighted lists
You can change the distribution by assigning a `?weight` next to the tag. By default, every tag has a weight of 1.
```toml
tags = ["foo?2", "bar", "baz"]
```
In this example, `foo` has a 50% chance to be chosen over `bar` or `baz`. This is because its weight is 2x the sum of all weights:
$\cfrac{\text{foo}}{\text{foo+bar+baz}} = \cfrac{2}{2+1+1}=0.5$
You can of course add fractions to the weights to make it _less_ likely to be chosen.
```toml
tags = ["foo?0.5", "bar", "baz"]
```
In this example, `foo` has a 20% chance to be chosen over `bar` or `baz`.
### Probabilities and repetitions
Some notations inside config files allow to alter the generation:
- `probability` define how likely the group of tags will be retained
- `repeat` define how many tags should be taken from a list with the key
You can do so by moving the string/list into a dictionary with the `tags` key.
```toml
# Before
[group]
items = ["foo", "bar", "baz"]
# After
[group.items]
probability = 0.75
repeat = 2
tags = ["foo", "bar", "baz"]
```
In this example, there is a 25% chance to ignore the tags. If it's not the case, 2 tags will be chosen from the list.
## Usage
> [!NOTE]
> You can also determine a range of tags to take from the list.<br>
> For example: `repeat = [2,4]` will take between 2 and 4 tags
> This is a basic guide to get you started. For more detailed information, please refer to the [wiki](https://github.com/mousse-taches/comfyui-scene-composer/wiki)
### Nested lists
Add the `🎞️ Scene` node to your workflow. The output will be a prompt formatted as a string.
You can directly pass it to a conditioning node and connect it to your sampler.
You can define a `list` that includes the name of nested elements. This way, you can set what concept is randomly chosen and how likely.
The other nodes are scene components. You can use it as an optional input for the `🎞️ Scene` node, or directly as standalone.
```toml
[group]
list = ["foo:2", "bar"]
foo = ["tag1", "tag2", "tag3"]
bar = ["tag4", "tag5", "tag6"]
baz = ["tag7", "tag8", "tag9"]
```
| Component nodes | Description |
| --------------- | ------------------------------------------------------------- |
| 📹 Composition | The layout of the scene, cameras, number of protagonists, etc |
| 🎬 Action | Activities done by the character/protagonist(s) |
| 👤 Character | Main character, focusing on body type and faces, etc |
| 👕 Clothes | Include casual clothes, uniforms, swimsuits, underwear, etc |
| ⛅️ Environment | The setting, including background, weather, time of day, etc |
In this example, `foo` tags are more likely to be chosen than `bar` tags. `baz` is not included in the `list` and will never be chosen.
### Config files
You can customize the prompt generation for components by editing the corresponding TOML file in the `config` folder. Keep it mind that the config will be replace by the default when there's an update, so you might want to keep a backup.
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
+6 -6
View File
@@ -1,9 +1,9 @@
from .src.scene import Scene
from .src.components.composition import Composition
from .src.components.action import Action
from .src.components.character.character import Character
from .src.components.clothes import Clothes
from .src.components.environment import Environment
from .py.scene import Scene
from .py.components.composition import Composition
from .py.components.action import Action
from .py.components.character.character import Character
from .py.components.clothes import Clothes
from .py.components.environment import Environment
NODE_CLASS_MAPPINGS = {
"🎞️ Scene": Scene,
+2 -2
View File
@@ -9,8 +9,8 @@ insertions = ["vaginal", "anal"]
# Male
hide_male = false
penis_sizes = ["small?0.5", "", "large", "huge?0.5"]
testicles_sizes = ["small?0.5", "", "large", "huge?0.5"]
penis_sizes = ["small[0.5]", "", "large", "huge[0.5]"]
testicles_sizes = ["small[0.5]", "", "large", "huge[0.5]"]
# Intensity (float number)
sweat = 1.0
+8 -8
View File
@@ -1,8 +1,8 @@
# BODY
[body.colors]
probability = 0.4
list = ["standard_colors", "special_colors?0.5"]
standard_colors = ["dark?1.5", "pale"]
list = ["standard_colors", "special_colors[0.5]"]
standard_colors = ["dark[1.5]", "pale"]
special_colors = ["black", "red", "green", "blue", "purple", "pink"]
[body.types]
@@ -15,8 +15,8 @@ tags = [
"muscular",
"curvy",
"plump",
"chubby?0.2",
"fat?0.1",
"chubby[0.2]",
"fat[0.1]",
]
[body.breasts]
@@ -25,7 +25,7 @@ sizes = [
"small breasts",
"medium breasts",
"large breasts",
"huge breasts?0.5",
"huge breasts[0.5]",
]
[body.breasts.nipples]
@@ -36,7 +36,7 @@ tags = ["dark", "puffy"]
[body.extras]
probability = 0.75
repeat = [1, 3]
tags = ["freckles?2", "tattoos", "piercings", "scars?0.2"]
tags = ["freckles[2]", "tattoos", "piercings", "scars[0.2]"]
# HAIR
[hair]
@@ -49,7 +49,7 @@ styles = ["ponytail", "bun", "braid", "pigtails"]
appearances = ["curly", "straight", "wavy"]
[hair.colors]
list = ["standard_colors", "special_colors?0.5"]
list = ["standard_colors", "special_colors[0.5]"]
standard_colors = ["black", "brown", "blonde", "ginger"]
special_colors = [
"red",
@@ -65,7 +65,7 @@ special_colors = [
# EYES & EYEWEAR
[eyes.colors]
list = ["standard_colors", "special_colors?0.8"]
list = ["standard_colors", "special_colors[0.8]"]
standard_colors = ["black", "brown", "blue", "green"]
special_colors = ["red", "yellow", "orange", "pink", "purple", "grey", "white"]
+1 -1
View File
@@ -1,5 +1,5 @@
# State: how clothed the character is
states = ["clothed", "underwear?0.3", "nude?0.1"]
states = ["clothed", "underwear[0.3]", "nude[0.1]"]
# Types: if clothed, what type of clothing
types = ["casual", "dress", "swimsuit", "uniform"]
+3 -3
View File
@@ -102,9 +102,9 @@ function filterInputList(node, actWidget, actTypeWidget) {
toggleWidget(node, actWidget, actTypeWidget.value !== "random");
// Filter the act list based on the selected act_type
actWidget.options.values = actWidget.options.values
.filter(act => act.startsWith(`${actTypeWidget.value}_`) || act == "random")
.map(act => act.replace(`${actTypeWidget.value}_`, ""));
actWidget.options.values = actWidget.options.values.filter(
act => act.startsWith(`${actTypeWidget.value}_`) || act == "random"
);
// Set the value to the first act in the list
actWidget.value = actWidget.options.values[0];
-38
View File
@@ -1,38 +0,0 @@
import argparse
import random
from src.scene import Scene
def main(seed, nsfw):
if not seed:
seed = random.randint(0, 0xffffffffffffffff)
scene = Scene()
scene.components["action"]["default"].INPUT_TYPES()
output = scene.build_prompt(seed)
print(f"SEED: {seed}")
print(f"TAGS: {output}")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Simulate a scene")
parser.add_argument(
"--seed", "-s",
type=int,
default=0,
help="Seed for the node"
)
parser.add_argument(
"--nsfw", "-n",
type=bool,
default=False,
help="Whether the scene is NSFW"
)
args = parser.parse_args()
main(args.seed, args.nsfw)
@@ -69,7 +69,8 @@ class Action(Node):
gesture = self.select_tags(
tags=data["gestures"],
selected=gesture
selected=gesture,
seed=self.seed+1
)
prompt = f"{position}, {gesture}"
@@ -87,7 +88,8 @@ class Action(Node):
act = self.select_tags(
tags=data["acts"][act_type],
selected=act
selected=act,
seed=self.seed+1
)
settings = self.apply_settings(data["settings"])
@@ -4,19 +4,16 @@ from ..node import Node
class Clothes(Node):
def __init__(self, seed=0, state="random", type="random"):
super().__init__(seed, data_file="clothes.toml")
self.state = state
self.type = type
super().__init__(seed, data_file="clothes.toml")
def build_prompt(self, seed):
self.seed = seed
prompt = ""
if self.state == "random":
self.state = self.select_tags(self.data["states"])
if self.type == "random":
self.type = self.select_tags(self.data["types"])
self.state = self.select_tags(self.data["states"], selected=self.state)
self.type = self.select_tags(self.data["types"], selected=self.type)
match self.state:
case "clothed":
+17 -7
View File
@@ -37,13 +37,16 @@ class Node:
def build_prompt(self):
pass
def select_tags(self, tags, p=1, n=1, selected="random", recursive=True):
def select_tags(self, tags, p=1, n=1,
selected="random", recursive=True, seed=None):
"""Return n tags from a string, list or dict
p: probability of selecting the tags
n: number of tags to select
selected: fallback tag to select if not random
recursive: go deeper randomly to select last children tags"""
rng = np.random.default_rng(self.seed)
if seed is None:
seed = self.seed
rng = np.random.default_rng(seed)
if selected != "random":
return selected
@@ -99,12 +102,14 @@ class Node:
def parse_tag_distribution(tags):
"""Choose tags randomly according to defined weights
If none are defined, tag weight is 1
Example: "foo?1.5, bar?0.5" -> 75% chance of foo, 25% chance of bar"""
Example: "foo[1.5], bar[0.5]" -> 75% chance of foo, 25% chance of bar
"""
tag_names = []
weights = []
for tag in tags:
if '?' in tag:
tag_name, weight = tag.split('?')
if '[' in tag and ']' in tag:
tag_name, weight = tag.split('[')
weight = weight.rstrip(']')
tag_names.append(tag_name)
weights.append(float(weight))
else:
@@ -127,8 +132,13 @@ class Node:
@staticmethod
def build_inputs_list(data):
"""Build a list with a random option"""
data = [tag.split('?')[0] for tag in data]
inputs_list = ["random"] + data
inputs_list = ["random"]
for tag in data:
if '[' in tag and ']' in tag:
tag = tag.split('[')[0]
inputs_list.append(tag)
return inputs_list
+2 -2
View File
@@ -1,6 +1,5 @@
from .node import Node
from .components.composition import Composition
# from .components.action.action import Action
from .components.action import Action
from .components.character.character import Character
from .components.clothes import Clothes
@@ -22,7 +21,7 @@ class Scene(Node):
},
"action": {
"default": Action(),
"nsfw": True,
"nsfw": False,
"position": "random",
"gesture": "random",
"act_type": "random",
@@ -63,6 +62,7 @@ class Scene(Node):
return inputs
def build_prompt(self, seed, **kwargs):
self.seed = seed
components = self.components
prompt = []
View File
+15
View File
@@ -0,0 +1,15 @@
[project]
name = "comfyui-scene-composer"
description = "A collection of nodes to facilitate the creation of scenes in ComfyUI."
version = "0.1.1"
license = { file = "LICENSE" }
dependencies = ["numpy==2.1.2", "PyYAML==6.0.2", "toml==0.10.2"]
[project.urls]
Repository = "https://github.com/taches-ai/comfyui-scene-composer"
# Used by Comfy Registry https://comfyregistry.org
[tool.comfy]
PublisherId = "taches"
DisplayName = "Scene Composer"
Icon = ""
+758
View File
@@ -0,0 +1,758 @@
{
"last_node_id": 21,
"last_link_id": 17,
"nodes": [
{
"id": 12,
"type": "📹 Composition",
"pos": {
"0": 26,
"1": 66
},
"size": {
"0": 263,
"1": 196
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "STRING",
"type": "STRING",
"links": [
10
],
"shape": 3
}
],
"properties": {
"Node name for S&R": "📹 Composition"
},
"widgets_values": [
"score_9, score_8_up, score_7_up, source_anime",
"1girl",
"random",
"random",
0,
"fixed"
]
},
{
"id": 19,
"type": "🎬 Action",
"pos": {
"0": 30,
"1": 314
},
"size": {
"0": 261,
"1": 154
},
"flags": {},
"order": 1,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "STRING",
"type": "STRING",
"links": [
17
],
"shape": 3
}
],
"properties": {
"Node name for S&R": "🎬 Action"
},
"widgets_values": [
false,
"random",
"random",
"intercourses",
"random",
0,
"fixed"
]
},
{
"id": 13,
"type": "👕 Clothes",
"pos": {
"0": 30,
"1": 650
},
"size": {
"0": 263,
"1": 82
},
"flags": {},
"order": 2,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "STRING",
"type": "STRING",
"links": [
13
],
"slot_index": 0,
"shape": 3
}
],
"properties": {
"Node name for S&R": "👕 Clothes"
},
"widgets_values": [
0,
"fixed"
]
},
{
"id": 10,
"type": "⛅️ Environment",
"pos": {
"0": 30,
"1": 780
},
"size": {
"0": 261,
"1": 82
},
"flags": {},
"order": 3,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "STRING",
"type": "STRING",
"links": [
14
],
"slot_index": 0,
"shape": 3
}
],
"properties": {
"Node name for S&R": "⛅️ Environment"
},
"widgets_values": [
0,
"fixed"
]
},
{
"id": 3,
"type": "KSampler",
"pos": {
"0": 931,
"1": 418
},
"size": {
"0": 267,
"1": 262
},
"flags": {},
"order": 12,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": 1
},
{
"name": "positive",
"type": "CONDITIONING",
"link": 4
},
{
"name": "negative",
"type": "CONDITIONING",
"link": 6
},
{
"name": "latent_image",
"type": "LATENT",
"link": 2
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
7
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "KSampler"
},
"widgets_values": [
0,
"fixed",
25,
7,
"euler",
"normal",
0.9500000000000001
],
"color": "#223",
"bgcolor": "#335"
},
{
"id": 8,
"type": "VAEDecode",
"pos": {
"0": 1211,
"1": 360
},
"size": {
"0": 210,
"1": 46
},
"flags": {
"collapsed": true
},
"order": 13,
"mode": 0,
"inputs": [
{
"name": "samples",
"type": "LATENT",
"link": 7
},
{
"name": "vae",
"type": "VAE",
"link": 8
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
9
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "VAEDecode"
},
"color": "#233",
"bgcolor": "#355"
},
{
"id": 20,
"type": "Note",
"pos": {
"0": 366,
"1": 62
},
"size": [
366,
205
],
"flags": {},
"order": 4,
"mode": 0,
"inputs": [],
"outputs": [],
"title": "📖 README",
"properties": {},
"widgets_values": [
"Here's a simple workflow to test the Scene Composer custom nodes.\n\nJust queue a prompt: the 🎞️ Scene will be able to randomly generate an image for you!\n\nHere, optional components are connected to the 🎞️ Scene. That let you have more control over the generation of the scene.\n\nFor example, just change the seed of the 👤 Character. The whole scene stay the same, but the character has changed."
],
"color": "#432",
"bgcolor": "#653"
},
{
"id": 21,
"type": "Note",
"pos": {
"0": 763,
"1": 61
},
"size": [
458,
208
],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [],
"outputs": [],
"title": "🔗 Links",
"properties": {},
"widgets_values": [
"For more info, check out these links:\n\nSource code: https://github.com/mus-taches/comfyui-scene-composer\nWiki: https://github.com/mus-taches/comfyui-scene-composer/wiki\nRoadmap: https://github.com/users/mousse-taches/projects/2/"
],
"color": "#222",
"bgcolor": "#000"
},
{
"id": 5,
"type": "EmptyLatentImage",
"pos": {
"0": 681,
"1": 699
},
"size": {
"0": 210,
"1": 106
},
"flags": {},
"order": 6,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [
2
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "EmptyLatentImage"
},
"widgets_values": [
1024,
1024,
1
],
"color": "#323",
"bgcolor": "#535"
},
{
"id": 7,
"type": "CLIPTextEncode",
"pos": {
"0": 685,
"1": 533
},
"size": {
"0": 210,
"1": 117
},
"flags": {},
"order": 9,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 5
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
6
],
"slot_index": 0
}
],
"title": "Negative",
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"score_6, score_5, score_4"
],
"color": "#322",
"bgcolor": "#533"
},
{
"id": 6,
"type": "CLIPTextEncode",
"pos": {
"0": 791,
"1": 490
},
"size": {
"0": 210,
"1": 54
},
"flags": {
"collapsed": true
},
"order": 11,
"mode": 0,
"inputs": [
{
"name": "clip",
"type": "CLIP",
"link": 3
},
{
"name": "text",
"type": "STRING",
"link": 15,
"widget": {
"name": "text"
}
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [
4
],
"slot_index": 0
}
],
"title": "Positive",
"properties": {
"Node name for S&R": "CLIPTextEncode"
},
"widgets_values": [
"beautiful scenery nature glass bottle landscape, , purple galaxy bottle,"
],
"color": "#232",
"bgcolor": "#353"
},
{
"id": 4,
"type": "CheckpointLoaderSimple",
"pos": {
"0": 365,
"1": 313
},
"size": [
312,
98
],
"flags": {},
"order": 7,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"links": [
1
],
"slot_index": 0
},
{
"name": "CLIP",
"type": "CLIP",
"links": [
3,
5
],
"slot_index": 1
},
{
"name": "VAE",
"type": "VAE",
"links": [
8
],
"slot_index": 2
}
],
"properties": {
"Node name for S&R": "CheckpointLoaderSimple"
},
"widgets_values": [
"tponyai.safetensors"
],
"color": "#332922",
"bgcolor": "#593930"
},
{
"id": 11,
"type": "🎞️ Scene",
"pos": {
"0": 363,
"1": 462
},
"size": [
274,
162
],
"flags": {},
"order": 10,
"mode": 0,
"inputs": [
{
"name": "composition",
"type": "STRING",
"link": 10,
"widget": {
"name": "composition"
}
},
{
"name": "action",
"type": "STRING",
"link": 17,
"widget": {
"name": "action"
}
},
{
"name": "character",
"type": "STRING",
"link": 12,
"widget": {
"name": "character"
}
},
{
"name": "clothes",
"type": "STRING",
"link": 13,
"widget": {
"name": "clothes"
}
},
{
"name": "environment",
"type": "STRING",
"link": 14,
"widget": {
"name": "environment"
}
}
],
"outputs": [
{
"name": "STRING",
"type": "STRING",
"links": [
15
],
"slot_index": 0,
"shape": 3
}
],
"properties": {
"Node name for S&R": "🎞️ Scene"
},
"widgets_values": [
1,
"fixed",
"",
"",
"",
"",
""
]
},
{
"id": 9,
"type": "SaveImage",
"pos": {
"0": 1231,
"1": 408
},
"size": {
"0": 428.97259521484375,
"1": 496.5888366699219
},
"flags": {},
"order": 14,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 9
}
],
"outputs": [],
"properties": {},
"widgets_values": [
"ComfyUI"
],
"color": "#233",
"bgcolor": "#355"
},
{
"id": 15,
"type": "👤 Character",
"pos": {
"0": 30,
"1": 520
},
"size": {
"0": 262,
"1": 82
},
"flags": {},
"order": 8,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "STRING",
"type": "STRING",
"links": [
12
],
"slot_index": 0,
"shape": 3
}
],
"properties": {
"Node name for S&R": "👤 Character"
},
"widgets_values": [
0,
"fixed"
]
}
],
"links": [
[
1,
4,
0,
3,
0,
"MODEL"
],
[
2,
5,
0,
3,
3,
"LATENT"
],
[
3,
4,
1,
6,
0,
"CLIP"
],
[
4,
6,
0,
3,
1,
"CONDITIONING"
],
[
5,
4,
1,
7,
0,
"CLIP"
],
[
6,
7,
0,
3,
2,
"CONDITIONING"
],
[
7,
3,
0,
8,
0,
"LATENT"
],
[
8,
4,
2,
8,
1,
"VAE"
],
[
9,
8,
0,
9,
0,
"IMAGE"
],
[
10,
12,
0,
11,
0,
"STRING"
],
[
12,
15,
0,
11,
2,
"STRING"
],
[
13,
13,
0,
11,
3,
"STRING"
],
[
14,
10,
0,
11,
4,
"STRING"
],
[
15,
11,
0,
6,
1,
"STRING"
],
[
17,
19,
0,
11,
1,
"STRING"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
0,
1
]
}
},
"version": 0.4
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 322 KiB