mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-01-20 03:18:05 -05:00
Compare commits
47 Commits
feat/queue
...
test
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad786130ff | ||
|
|
77a444f3bc | ||
|
|
24209b60a4 | ||
|
|
cf2b847e33 | ||
|
|
5f35ad078d | ||
|
|
43266b18c7 | ||
|
|
d521145c36 | ||
|
|
bf359bd91f | ||
|
|
25ad74922e | ||
|
|
d8c31e9ed5 | ||
|
|
fc958217eb | ||
|
|
5010412341 | ||
|
|
0e93c4e856 | ||
|
|
98e6c62214 | ||
|
|
e1d2d382cf | ||
|
|
d349e00965 | ||
|
|
bbe1097c05 | ||
|
|
a10acde5eb | ||
|
|
171532ec44 | ||
|
|
54cbadeffa | ||
|
|
a76e58017c | ||
|
|
17be3e1234 | ||
|
|
73ba6b03ab | ||
|
|
6f37fbdee4 | ||
|
|
1928d1af29 | ||
|
|
f6127a1b6b | ||
|
|
7f457ca03d | ||
|
|
2b972cda6c | ||
|
|
47b0e1d7b4 | ||
|
|
fe0a16c846 | ||
|
|
f19c6069a9 | ||
|
|
6f45931711 | ||
|
|
278392d52c | ||
|
|
b2f942d714 | ||
|
|
6bc2253894 | ||
|
|
97d6f207d8 | ||
|
|
dc9a9d7bec | ||
|
|
15a3e49a40 | ||
|
|
7ccfc499dc | ||
|
|
56d0d80a39 | ||
|
|
2d64ee7f9e | ||
|
|
10ada84404 | ||
|
|
7744e01e2c | ||
|
|
ce8e5f9adf | ||
|
|
fc1021b6be | ||
|
|
fadfe1dfe9 | ||
|
|
2716ae353b |
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -2,4 +2,3 @@
|
|||||||
# Only affects text files and ignores other file types.
|
# Only affects text files and ignores other file types.
|
||||||
# For more info see: https://www.aleksandrhovhannisyan.com/blog/crlf-vs-lf-normalizing-line-endings-in-git/
|
# For more info see: https://www.aleksandrhovhannisyan.com/blog/crlf-vs-lf-normalizing-line-endings-in-git/
|
||||||
* text=auto
|
* text=auto
|
||||||
docker/** text eol=lf
|
|
||||||
20
.github/workflows/pyflakes.yml
vendored
Normal file
20
.github/workflows/pyflakes.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- development
|
||||||
|
- 'release-candidate-*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
pyflakes:
|
||||||
|
name: runner / pyflakes
|
||||||
|
if: github.event.pull_request.draft == false
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: pyflakes
|
||||||
|
uses: reviewdog/action-pyflakes@v1
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
reporter: github-pr-review
|
||||||
9
.github/workflows/style-checks.yml
vendored
9
.github/workflows/style-checks.yml
vendored
@@ -6,7 +6,7 @@ on:
|
|||||||
branches: main
|
branches: main
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ruff:
|
black:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
@@ -18,7 +18,8 @@ jobs:
|
|||||||
|
|
||||||
- name: Install dependencies with pip
|
- name: Install dependencies with pip
|
||||||
run: |
|
run: |
|
||||||
pip install ruff
|
pip install black flake8 Flake8-pyproject isort
|
||||||
|
|
||||||
- run: ruff check --output-format=github .
|
- run: isort --check-only .
|
||||||
- run: ruff format --check .
|
- run: black --check .
|
||||||
|
- run: flake8
|
||||||
|
|||||||
12
.gitignore
vendored
12
.gitignore
vendored
@@ -1,5 +1,8 @@
|
|||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
|
# ignore the Anaconda/Miniconda installer used while building Docker image
|
||||||
|
anaconda.sh
|
||||||
|
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
@@ -133,10 +136,12 @@ celerybeat.pid
|
|||||||
|
|
||||||
# Environments
|
# Environments
|
||||||
.env
|
.env
|
||||||
.venv*
|
.venv
|
||||||
env/
|
env/
|
||||||
venv/
|
venv/
|
||||||
ENV/
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
# Spyder project settings
|
# Spyder project settings
|
||||||
.spyderproject
|
.spyderproject
|
||||||
@@ -181,6 +186,11 @@ cython_debug/
|
|||||||
.scratch/
|
.scratch/
|
||||||
.vscode/
|
.vscode/
|
||||||
|
|
||||||
|
# ignore environment.yml and requirements.txt
|
||||||
|
# these are links to the real files in environments-and-requirements
|
||||||
|
environment.yml
|
||||||
|
requirements.txt
|
||||||
|
|
||||||
# source installer files
|
# source installer files
|
||||||
installer/*zip
|
installer/*zip
|
||||||
installer/install.bat
|
installer/install.bat
|
||||||
|
|||||||
21
Makefile
21
Makefile
@@ -1,21 +0,0 @@
|
|||||||
# simple Makefile with scripts that are otherwise hard to remember
|
|
||||||
# to use, run from the repo root `make <command>`
|
|
||||||
|
|
||||||
# Runs ruff, fixing any safely-fixable errors and formatting
|
|
||||||
ruff:
|
|
||||||
ruff check . --fix
|
|
||||||
ruff format .
|
|
||||||
|
|
||||||
# Runs ruff, fixing all errors it can fix and formatting
|
|
||||||
ruff-unsafe:
|
|
||||||
ruff check . --fix --unsafe-fixes
|
|
||||||
ruff format .
|
|
||||||
|
|
||||||
# Runs mypy, using the config in pyproject.toml
|
|
||||||
mypy:
|
|
||||||
mypy scripts/invokeai-web.py
|
|
||||||
|
|
||||||
# Runs mypy, ignoring the config in pyproject.toml but still ignoring missing (untyped) imports
|
|
||||||
# (many files are ignored by the config, so this is useful for checking all files)
|
|
||||||
mypy-all:
|
|
||||||
mypy scripts/invokeai-web.py --config-file= --ignore-missing-imports
|
|
||||||
@@ -123,7 +123,7 @@ and go to http://localhost:9090.
|
|||||||
|
|
||||||
### Command-Line Installation (for developers and users familiar with Terminals)
|
### Command-Line Installation (for developers and users familiar with Terminals)
|
||||||
|
|
||||||
You must have Python 3.10 through 3.11 installed on your machine. Earlier or
|
You must have Python 3.9 through 3.11 installed on your machine. Earlier or
|
||||||
later versions are not supported.
|
later versions are not supported.
|
||||||
Node.js also needs to be installed along with yarn (can be installed with
|
Node.js also needs to be installed along with yarn (can be installed with
|
||||||
the command `npm install -g yarn` if needed)
|
the command `npm install -g yarn` if needed)
|
||||||
@@ -161,7 +161,7 @@ the command `npm install -g yarn` if needed)
|
|||||||
_For Windows/Linux with an NVIDIA GPU:_
|
_For Windows/Linux with an NVIDIA GPU:_
|
||||||
|
|
||||||
```terminal
|
```terminal
|
||||||
pip install "InvokeAI[xformers]" --use-pep517 --extra-index-url https://download.pytorch.org/whl/cu121
|
pip install "InvokeAI[xformers]" --use-pep517 --extra-index-url https://download.pytorch.org/whl/cu118
|
||||||
```
|
```
|
||||||
|
|
||||||
_For Linux with an AMD GPU:_
|
_For Linux with an AMD GPU:_
|
||||||
@@ -175,7 +175,7 @@ the command `npm install -g yarn` if needed)
|
|||||||
pip install InvokeAI --use-pep517 --extra-index-url https://download.pytorch.org/whl/cpu
|
pip install InvokeAI --use-pep517 --extra-index-url https://download.pytorch.org/whl/cpu
|
||||||
```
|
```
|
||||||
|
|
||||||
_For Macintoshes, either Intel or M1/M2/M3:_
|
_For Macintoshes, either Intel or M1/M2:_
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
pip install InvokeAI --use-pep517
|
pip install InvokeAI --use-pep517
|
||||||
@@ -395,7 +395,7 @@ Notes](https://github.com/invoke-ai/InvokeAI/releases) and the
|
|||||||
|
|
||||||
### Troubleshooting
|
### Troubleshooting
|
||||||
|
|
||||||
Please check out our **[Troubleshooting Guide](https://invoke-ai.github.io/InvokeAI/installation/010_INSTALL_AUTOMATED/#troubleshooting)** to get solutions for common installation
|
Please check out our **[Q&A](https://invoke-ai.github.io/InvokeAI/help/TROUBLESHOOT/#faq)** to get solutions for common installation
|
||||||
problems and other issues. For more help, please join our [Discord][discord link]
|
problems and other issues. For more help, please join our [Discord][discord link]
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
## Make a copy of this file named `.env` and fill in the values below.
|
## Make a copy of this file named `.env` and fill in the values below.
|
||||||
## Any environment variables supported by InvokeAI can be specified here,
|
## Any environment variables supported by InvokeAI can be specified here.
|
||||||
## in addition to the examples below.
|
|
||||||
|
|
||||||
# INVOKEAI_ROOT is the path to a path on the local filesystem where InvokeAI will store data.
|
# INVOKEAI_ROOT is the path to a path on the local filesystem where InvokeAI will store data.
|
||||||
# Outputs will also be stored here by default.
|
# Outputs will also be stored here by default.
|
||||||
# This **must** be an absolute path.
|
# This **must** be an absolute path.
|
||||||
INVOKEAI_ROOT=
|
INVOKEAI_ROOT=
|
||||||
|
|
||||||
# Get this value from your HuggingFace account settings page.
|
HUGGINGFACE_TOKEN=
|
||||||
# HUGGING_FACE_HUB_TOKEN=
|
|
||||||
|
|
||||||
## optional variables specific to the docker setup.
|
## optional variables specific to the docker setup
|
||||||
# GPU_DRIVER=cuda # or rocm
|
# GPU_DRIVER=cuda
|
||||||
# CONTAINER_UID=1000
|
# CONTAINER_UID=1000
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Builder stage
|
## Builder stage
|
||||||
|
|
||||||
FROM library/ubuntu:23.04 AS builder
|
FROM library/ubuntu:22.04 AS builder
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
RUN rm -f /etc/apt/apt.conf.d/docker-clean; echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache
|
RUN rm -f /etc/apt/apt.conf.d/docker-clean; echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache
|
||||||
@@ -10,7 +10,7 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
|||||||
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||||
apt update && apt-get install -y \
|
apt update && apt-get install -y \
|
||||||
git \
|
git \
|
||||||
python3-venv \
|
python3.10-venv \
|
||||||
python3-pip \
|
python3-pip \
|
||||||
build-essential
|
build-essential
|
||||||
|
|
||||||
@@ -18,8 +18,8 @@ ENV INVOKEAI_SRC=/opt/invokeai
|
|||||||
ENV VIRTUAL_ENV=/opt/venv/invokeai
|
ENV VIRTUAL_ENV=/opt/venv/invokeai
|
||||||
|
|
||||||
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
|
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
|
||||||
ARG TORCH_VERSION=2.1.0
|
ARG TORCH_VERSION=2.0.1
|
||||||
ARG TORCHVISION_VERSION=0.16
|
ARG TORCHVISION_VERSION=0.15.2
|
||||||
ARG GPU_DRIVER=cuda
|
ARG GPU_DRIVER=cuda
|
||||||
ARG TARGETPLATFORM="linux/amd64"
|
ARG TARGETPLATFORM="linux/amd64"
|
||||||
# unused but available
|
# unused but available
|
||||||
@@ -35,9 +35,9 @@ RUN --mount=type=cache,target=/root/.cache/pip \
|
|||||||
if [ "$TARGETPLATFORM" = "linux/arm64" ] || [ "$GPU_DRIVER" = "cpu" ]; then \
|
if [ "$TARGETPLATFORM" = "linux/arm64" ] || [ "$GPU_DRIVER" = "cpu" ]; then \
|
||||||
extra_index_url_arg="--extra-index-url https://download.pytorch.org/whl/cpu"; \
|
extra_index_url_arg="--extra-index-url https://download.pytorch.org/whl/cpu"; \
|
||||||
elif [ "$GPU_DRIVER" = "rocm" ]; then \
|
elif [ "$GPU_DRIVER" = "rocm" ]; then \
|
||||||
extra_index_url_arg="--index-url https://download.pytorch.org/whl/rocm5.6"; \
|
extra_index_url_arg="--extra-index-url https://download.pytorch.org/whl/rocm5.4.2"; \
|
||||||
else \
|
else \
|
||||||
extra_index_url_arg="--extra-index-url https://download.pytorch.org/whl/cu121"; \
|
extra_index_url_arg="--extra-index-url https://download.pytorch.org/whl/cu118"; \
|
||||||
fi &&\
|
fi &&\
|
||||||
pip install $extra_index_url_arg \
|
pip install $extra_index_url_arg \
|
||||||
torch==$TORCH_VERSION \
|
torch==$TORCH_VERSION \
|
||||||
@@ -70,7 +70,7 @@ RUN --mount=type=cache,target=/usr/lib/node_modules \
|
|||||||
|
|
||||||
#### Runtime stage ---------------------------------------
|
#### Runtime stage ---------------------------------------
|
||||||
|
|
||||||
FROM library/ubuntu:23.04 AS runtime
|
FROM library/ubuntu:22.04 AS runtime
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
ENV PYTHONUNBUFFERED=1
|
ENV PYTHONUNBUFFERED=1
|
||||||
@@ -85,7 +85,6 @@ RUN apt update && apt install -y --no-install-recommends \
|
|||||||
iotop \
|
iotop \
|
||||||
bzip2 \
|
bzip2 \
|
||||||
gosu \
|
gosu \
|
||||||
magic-wormhole \
|
|
||||||
libglib2.0-0 \
|
libglib2.0-0 \
|
||||||
libgl1-mesa-glx \
|
libgl1-mesa-glx \
|
||||||
python3-venv \
|
python3-venv \
|
||||||
@@ -95,6 +94,10 @@ RUN apt update && apt install -y --no-install-recommends \
|
|||||||
libstdc++-10-dev &&\
|
libstdc++-10-dev &&\
|
||||||
apt-get clean && apt-get autoclean
|
apt-get clean && apt-get autoclean
|
||||||
|
|
||||||
|
# globally add magic-wormhole
|
||||||
|
# for ease of transferring data to and from the container
|
||||||
|
# when running in sandboxed cloud environments; e.g. Runpod etc.
|
||||||
|
RUN pip install magic-wormhole
|
||||||
|
|
||||||
ENV INVOKEAI_SRC=/opt/invokeai
|
ENV INVOKEAI_SRC=/opt/invokeai
|
||||||
ENV VIRTUAL_ENV=/opt/venv/invokeai
|
ENV VIRTUAL_ENV=/opt/venv/invokeai
|
||||||
@@ -117,7 +120,9 @@ WORKDIR ${INVOKEAI_SRC}
|
|||||||
RUN cd /usr/lib/$(uname -p)-linux-gnu/pkgconfig/ && ln -sf opencv4.pc opencv.pc
|
RUN cd /usr/lib/$(uname -p)-linux-gnu/pkgconfig/ && ln -sf opencv4.pc opencv.pc
|
||||||
RUN python3 -c "from patchmatch import patch_match"
|
RUN python3 -c "from patchmatch import patch_match"
|
||||||
|
|
||||||
RUN mkdir -p ${INVOKEAI_ROOT} && chown -R 1000:1000 ${INVOKEAI_ROOT}
|
# Create unprivileged user and make the local dir
|
||||||
|
RUN useradd --create-home --shell /bin/bash -u 1000 --comment "container local user" invoke
|
||||||
|
RUN mkdir -p ${INVOKEAI_ROOT} && chown -R invoke:invoke ${INVOKEAI_ROOT}
|
||||||
|
|
||||||
COPY docker/docker-entrypoint.sh ./
|
COPY docker/docker-entrypoint.sh ./
|
||||||
ENTRYPOINT ["/opt/invokeai/docker-entrypoint.sh"]
|
ENTRYPOINT ["/opt/invokeai/docker-entrypoint.sh"]
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ All commands are to be run from the `docker` directory: `cd docker`
|
|||||||
#### Linux
|
#### Linux
|
||||||
|
|
||||||
1. Ensure builkit is enabled in the Docker daemon settings (`/etc/docker/daemon.json`)
|
1. Ensure builkit is enabled in the Docker daemon settings (`/etc/docker/daemon.json`)
|
||||||
2. Install the `docker compose` plugin using your package manager, or follow a [tutorial](https://docs.docker.com/compose/install/linux/#install-using-the-repository).
|
2. Install the `docker compose` plugin using your package manager, or follow a [tutorial](https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-compose-on-ubuntu-22-04).
|
||||||
- The deprecated `docker-compose` (hyphenated) CLI continues to work for now.
|
- The deprecated `docker-compose` (hyphenated) CLI continues to work for now.
|
||||||
3. Ensure docker daemon is able to access the GPU.
|
3. Ensure docker daemon is able to access the GPU.
|
||||||
- You may need to install [nvidia-container-toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html)
|
- You may need to install [nvidia-container-toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html)
|
||||||
@@ -20,6 +20,7 @@ This is done via Docker Desktop preferences
|
|||||||
|
|
||||||
## Quickstart
|
## Quickstart
|
||||||
|
|
||||||
|
|
||||||
1. Make a copy of `env.sample` and name it `.env` (`cp env.sample .env` (Mac/Linux) or `copy example.env .env` (Windows)). Make changes as necessary. Set `INVOKEAI_ROOT` to an absolute path to:
|
1. Make a copy of `env.sample` and name it `.env` (`cp env.sample .env` (Mac/Linux) or `copy example.env .env` (Windows)). Make changes as necessary. Set `INVOKEAI_ROOT` to an absolute path to:
|
||||||
a. the desired location of the InvokeAI runtime directory, or
|
a. the desired location of the InvokeAI runtime directory, or
|
||||||
b. an existing, v3.0.0 compatible runtime directory.
|
b. an existing, v3.0.0 compatible runtime directory.
|
||||||
@@ -41,22 +42,20 @@ The Docker daemon on the system must be already set up to use the GPU. In case o
|
|||||||
|
|
||||||
Check the `.env.sample` file. It contains some environment variables for running in Docker. Copy it, name it `.env`, and fill it in with your own values. Next time you run `docker compose up`, your custom values will be used.
|
Check the `.env.sample` file. It contains some environment variables for running in Docker. Copy it, name it `.env`, and fill it in with your own values. Next time you run `docker compose up`, your custom values will be used.
|
||||||
|
|
||||||
You can also set these values in `docker-compose.yml` directly, but `.env` will help avoid conflicts when code is updated.
|
You can also set these values in `docker compose.yml` directly, but `.env` will help avoid conflicts when code is updated.
|
||||||
|
|
||||||
Example (values are optional, but setting `INVOKEAI_ROOT` is highly recommended):
|
Example (most values are optional):
|
||||||
|
|
||||||
```bash
|
```
|
||||||
INVOKEAI_ROOT=/Volumes/WorkDrive/invokeai
|
INVOKEAI_ROOT=/Volumes/WorkDrive/invokeai
|
||||||
HUGGINGFACE_TOKEN=the_actual_token
|
HUGGINGFACE_TOKEN=the_actual_token
|
||||||
CONTAINER_UID=1000
|
CONTAINER_UID=1000
|
||||||
GPU_DRIVER=cuda
|
GPU_DRIVER=cuda
|
||||||
```
|
```
|
||||||
|
|
||||||
Any environment variables supported by InvokeAI can be set here - please see the [Configuration docs](https://invoke-ai.github.io/InvokeAI/features/CONFIGURATION/) for further detail.
|
|
||||||
|
|
||||||
## Even Moar Customizing!
|
## Even Moar Customizing!
|
||||||
|
|
||||||
See the `docker-compose.yml` file. The `command` instruction can be uncommented and used to run arbitrary startup commands. Some examples below.
|
See the `docker compose.yaml` file. The `command` instruction can be uncommented and used to run arbitrary startup commands. Some examples below.
|
||||||
|
|
||||||
### Reconfigure the runtime directory
|
### Reconfigure the runtime directory
|
||||||
|
|
||||||
@@ -64,7 +63,7 @@ Can be used to download additional models from the supported model list
|
|||||||
|
|
||||||
In conjunction with `INVOKEAI_ROOT` can be also used to initialize a runtime directory
|
In conjunction with `INVOKEAI_ROOT` can be also used to initialize a runtime directory
|
||||||
|
|
||||||
```yaml
|
```
|
||||||
command:
|
command:
|
||||||
- invokeai-configure
|
- invokeai-configure
|
||||||
- --yes
|
- --yes
|
||||||
@@ -72,7 +71,7 @@ command:
|
|||||||
|
|
||||||
Or install models:
|
Or install models:
|
||||||
|
|
||||||
```yaml
|
```
|
||||||
command:
|
command:
|
||||||
- invokeai-model-install
|
- invokeai-model-install
|
||||||
```
|
```
|
||||||
@@ -5,7 +5,7 @@ build_args=""
|
|||||||
|
|
||||||
[[ -f ".env" ]] && build_args=$(awk '$1 ~ /\=[^$]/ {print "--build-arg " $0 " "}' .env)
|
[[ -f ".env" ]] && build_args=$(awk '$1 ~ /\=[^$]/ {print "--build-arg " $0 " "}' .env)
|
||||||
|
|
||||||
echo "docker compose build args:"
|
echo "docker-compose build args:"
|
||||||
echo $build_args
|
echo $build_args
|
||||||
|
|
||||||
docker compose build $build_args
|
docker-compose build $build_args
|
||||||
|
|||||||
@@ -15,10 +15,6 @@ services:
|
|||||||
- driver: nvidia
|
- driver: nvidia
|
||||||
count: 1
|
count: 1
|
||||||
capabilities: [gpu]
|
capabilities: [gpu]
|
||||||
# For AMD support, comment out the deploy section above and uncomment the devices section below:
|
|
||||||
#devices:
|
|
||||||
# - /dev/kfd:/dev/kfd
|
|
||||||
# - /dev/dri:/dev/dri
|
|
||||||
build:
|
build:
|
||||||
context: ..
|
context: ..
|
||||||
dockerfile: docker/Dockerfile
|
dockerfile: docker/Dockerfile
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ set -e -o pipefail
|
|||||||
# Default UID: 1000 chosen due to popularity on Linux systems. Possibly 501 on MacOS.
|
# Default UID: 1000 chosen due to popularity on Linux systems. Possibly 501 on MacOS.
|
||||||
|
|
||||||
USER_ID=${CONTAINER_UID:-1000}
|
USER_ID=${CONTAINER_UID:-1000}
|
||||||
USER=ubuntu
|
USER=invoke
|
||||||
usermod -u ${USER_ID} ${USER} 1>/dev/null
|
usermod -u ${USER_ID} ${USER} 1>/dev/null
|
||||||
|
|
||||||
configure() {
|
configure() {
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# This script is provided for backwards compatibility with the old docker setup.
|
|
||||||
# it doesn't do much aside from wrapping the usual docker compose CLI.
|
|
||||||
|
|
||||||
SCRIPTDIR=$(dirname "${BASH_SOURCE[0]}")
|
SCRIPTDIR=$(dirname "${BASH_SOURCE[0]}")
|
||||||
cd "$SCRIPTDIR" || exit 1
|
cd "$SCRIPTDIR" || exit 1
|
||||||
|
|
||||||
docker compose up -d
|
docker-compose up --build -d
|
||||||
docker compose logs -f
|
docker-compose logs -f
|
||||||
|
|||||||
@@ -488,7 +488,7 @@ sections describe what's new for InvokeAI.
|
|||||||
|
|
||||||
- A choice of installer scripts that automate installation and configuration.
|
- A choice of installer scripts that automate installation and configuration.
|
||||||
See
|
See
|
||||||
[Installation](installation/INSTALLATION.md).
|
[Installation](installation/index.md).
|
||||||
- A streamlined manual installation process that works for both Conda and
|
- A streamlined manual installation process that works for both Conda and
|
||||||
PIP-only installs. See
|
PIP-only installs. See
|
||||||
[Manual Installation](installation/020_INSTALL_MANUAL.md).
|
[Manual Installation](installation/020_INSTALL_MANUAL.md).
|
||||||
@@ -657,7 +657,7 @@ sections describe what's new for InvokeAI.
|
|||||||
|
|
||||||
## v1.13 <small>(3 September 2022)</small>
|
## v1.13 <small>(3 September 2022)</small>
|
||||||
|
|
||||||
- Support image variations (see [VARIATIONS](deprecated/VARIATIONS.md)
|
- Support image variations (see [VARIATIONS](features/VARIATIONS.md)
|
||||||
([Kevin Gibbons](https://github.com/bakkot) and many contributors and
|
([Kevin Gibbons](https://github.com/bakkot) and many contributors and
|
||||||
reviewers)
|
reviewers)
|
||||||
- Supports a Google Colab notebook for a standalone server running on Google
|
- Supports a Google Colab notebook for a standalone server running on Google
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Nodes
|
# Invocations
|
||||||
|
|
||||||
Features in InvokeAI are added in the form of modular nodes systems called
|
Features in InvokeAI are added in the form of modular node-like systems called
|
||||||
**Invocations**.
|
**Invocations**.
|
||||||
|
|
||||||
An Invocation is simply a single operation that takes in some inputs and gives
|
An Invocation is simply a single operation that takes in some inputs and gives
|
||||||
@@ -9,34 +9,13 @@ complex functionality.
|
|||||||
|
|
||||||
## Invocations Directory
|
## Invocations Directory
|
||||||
|
|
||||||
InvokeAI Nodes can be found in the `invokeai/app/invocations` directory. These can be used as examples to create your own nodes.
|
InvokeAI Invocations can be found in the `invokeai/app/invocations` directory.
|
||||||
|
|
||||||
New nodes should be added to a subfolder in `nodes` direction found at the root level of the InvokeAI installation location. Nodes added to this folder will be able to be used upon application startup.
|
You can add your new functionality to one of the existing Invocations in this
|
||||||
|
directory or create a new file in this directory as per your needs.
|
||||||
Example `nodes` subfolder structure:
|
|
||||||
```py
|
|
||||||
├── __init__.py # Invoke-managed custom node loader
|
|
||||||
│
|
|
||||||
├── cool_node
|
|
||||||
│ ├── __init__.py # see example below
|
|
||||||
│ └── cool_node.py
|
|
||||||
│
|
|
||||||
└── my_node_pack
|
|
||||||
├── __init__.py # see example below
|
|
||||||
├── tasty_node.py
|
|
||||||
├── bodacious_node.py
|
|
||||||
├── utils.py
|
|
||||||
└── extra_nodes
|
|
||||||
└── fancy_node.py
|
|
||||||
```
|
|
||||||
|
|
||||||
Each node folder must have an `__init__.py` file that imports its nodes. Only nodes imported in the `__init__.py` file are loaded.
|
|
||||||
See the README in the nodes folder for more examples:
|
|
||||||
|
|
||||||
```py
|
|
||||||
from .cool_node import CoolInvocation
|
|
||||||
```
|
|
||||||
|
|
||||||
|
**Note:** _All Invocations must be inside this directory for InvokeAI to
|
||||||
|
recognize them as valid Invocations._
|
||||||
|
|
||||||
## Creating A New Invocation
|
## Creating A New Invocation
|
||||||
|
|
||||||
@@ -65,7 +44,7 @@ The first set of things we need to do when creating a new Invocation are -
|
|||||||
So let us do that.
|
So let us do that.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from invokeai.app.invocations.baseinvocation import BaseInvocation, invocation
|
from .baseinvocation import BaseInvocation, invocation
|
||||||
|
|
||||||
@invocation('resize')
|
@invocation('resize')
|
||||||
class ResizeInvocation(BaseInvocation):
|
class ResizeInvocation(BaseInvocation):
|
||||||
@@ -99,8 +78,8 @@ create your own custom field types later in this guide. For now, let's go ahead
|
|||||||
and use it.
|
and use it.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from invokeai.app.invocations.baseinvocation import BaseInvocation, InputField, invocation
|
from .baseinvocation import BaseInvocation, InputField, invocation
|
||||||
from invokeai.app.invocations.primitives import ImageField
|
from .primitives import ImageField
|
||||||
|
|
||||||
@invocation('resize')
|
@invocation('resize')
|
||||||
class ResizeInvocation(BaseInvocation):
|
class ResizeInvocation(BaseInvocation):
|
||||||
@@ -124,8 +103,8 @@ image: ImageField = InputField(description="The input image")
|
|||||||
Great. Now let us create our other inputs for `width` and `height`
|
Great. Now let us create our other inputs for `width` and `height`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from invokeai.app.invocations.baseinvocation import BaseInvocation, InputField, invocation
|
from .baseinvocation import BaseInvocation, InputField, invocation
|
||||||
from invokeai.app.invocations.primitives import ImageField
|
from .primitives import ImageField
|
||||||
|
|
||||||
@invocation('resize')
|
@invocation('resize')
|
||||||
class ResizeInvocation(BaseInvocation):
|
class ResizeInvocation(BaseInvocation):
|
||||||
@@ -160,8 +139,8 @@ that are provided by it by InvokeAI.
|
|||||||
Let us create this function first.
|
Let us create this function first.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from invokeai.app.invocations.baseinvocation import BaseInvocation, InputField, invocation, InvocationContext
|
from .baseinvocation import BaseInvocation, InputField, invocation
|
||||||
from invokeai.app.invocations.primitives import ImageField
|
from .primitives import ImageField
|
||||||
|
|
||||||
@invocation('resize')
|
@invocation('resize')
|
||||||
class ResizeInvocation(BaseInvocation):
|
class ResizeInvocation(BaseInvocation):
|
||||||
@@ -189,9 +168,9 @@ all the necessary info related to image outputs. So let us use that.
|
|||||||
We will cover how to create your own output types later in this guide.
|
We will cover how to create your own output types later in this guide.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from invokeai.app.invocations.baseinvocation import BaseInvocation, InputField, invocation, InvocationContext
|
from .baseinvocation import BaseInvocation, InputField, invocation
|
||||||
from invokeai.app.invocations.primitives import ImageField
|
from .primitives import ImageField
|
||||||
from invokeai.app.invocations.image import ImageOutput
|
from .image import ImageOutput
|
||||||
|
|
||||||
@invocation('resize')
|
@invocation('resize')
|
||||||
class ResizeInvocation(BaseInvocation):
|
class ResizeInvocation(BaseInvocation):
|
||||||
@@ -216,9 +195,9 @@ Perfect. Now that we have our Invocation setup, let us do what we want to do.
|
|||||||
So let's do that.
|
So let's do that.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from invokeai.app.invocations.baseinvocation import BaseInvocation, InputField, invocation, InvocationContext
|
from .baseinvocation import BaseInvocation, InputField, invocation
|
||||||
from invokeai.app.invocations.primitives import ImageField
|
from .primitives import ImageField
|
||||||
from invokeai.app.invocations.image import ImageOutput, ResourceOrigin, ImageCategory
|
from .image import ImageOutput
|
||||||
|
|
||||||
@invocation("resize")
|
@invocation("resize")
|
||||||
class ResizeInvocation(BaseInvocation):
|
class ResizeInvocation(BaseInvocation):
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -45,5 +45,5 @@ For backend related work, please reach out to **@blessedcoolant**, **@lstein**,
|
|||||||
|
|
||||||
## **What does the Code of Conduct mean for me?**
|
## **What does the Code of Conduct mean for me?**
|
||||||
|
|
||||||
Our [Code of Conduct](../../CODE_OF_CONDUCT.md) means that you are responsible for treating everyone on the project with respect and courtesy regardless of their identity. If you are the victim of any inappropriate behavior or comments as described in our Code of Conduct, we are here for you and will do the best to ensure that the abuser is reprimanded appropriately, per our code.
|
Our [Code of Conduct](CODE_OF_CONDUCT.md) means that you are responsible for treating everyone on the project with respect and courtesy regardless of their identity. If you are the victim of any inappropriate behavior or comments as described in our Code of Conduct, we are here for you and will do the best to ensure that the abuser is reprimanded appropriately, per our code.
|
||||||
|
|
||||||
|
|||||||
@@ -211,8 +211,8 @@ Here are the invoke> command that apply to txt2img:
|
|||||||
| `--facetool <name>` | `-ft <name>` | `-ft gfpgan` | Select face restoration algorithm to use: gfpgan, codeformer |
|
| `--facetool <name>` | `-ft <name>` | `-ft gfpgan` | Select face restoration algorithm to use: gfpgan, codeformer |
|
||||||
| `--codeformer_fidelity` | `-cf <float>` | `0.75` | Used along with CodeFormer. Takes values between 0 and 1. 0 produces high quality but low accuracy. 1 produces high accuracy but low quality |
|
| `--codeformer_fidelity` | `-cf <float>` | `0.75` | Used along with CodeFormer. Takes values between 0 and 1. 0 produces high quality but low accuracy. 1 produces high accuracy but low quality |
|
||||||
| `--save_original` | `-save_orig` | `False` | When upscaling or fixing faces, this will cause the original image to be saved rather than replaced. |
|
| `--save_original` | `-save_orig` | `False` | When upscaling or fixing faces, this will cause the original image to be saved rather than replaced. |
|
||||||
| `--variation <float>` | `-v<float>` | `0.0` | Add a bit of noise (0.0=none, 1.0=high) to the image in order to generate a series of variations. Usually used in combination with `-S<seed>` and `-n<int>` to generate a series a riffs on a starting image. See [Variations](VARIATIONS.md). |
|
| `--variation <float>` | `-v<float>` | `0.0` | Add a bit of noise (0.0=none, 1.0=high) to the image in order to generate a series of variations. Usually used in combination with `-S<seed>` and `-n<int>` to generate a series a riffs on a starting image. See [Variations](../features/VARIATIONS.md). |
|
||||||
| `--with_variations <pattern>` | | `None` | Combine two or more variations. See [Variations](VARIATIONS.md) for now to use this. |
|
| `--with_variations <pattern>` | | `None` | Combine two or more variations. See [Variations](../features/VARIATIONS.md) for now to use this. |
|
||||||
| `--save_intermediates <n>` | | `None` | Save the image from every nth step into an "intermediates" folder inside the output directory |
|
| `--save_intermediates <n>` | | `None` | Save the image from every nth step into an "intermediates" folder inside the output directory |
|
||||||
| `--h_symmetry_time_pct <float>` | | `None` | Create symmetry along the X axis at the desired percent complete of the generation process. (Must be between 0.0 and 1.0; set to a very small number like 0.0001 for just after the first step of generation.) |
|
| `--h_symmetry_time_pct <float>` | | `None` | Create symmetry along the X axis at the desired percent complete of the generation process. (Must be between 0.0 and 1.0; set to a very small number like 0.0001 for just after the first step of generation.) |
|
||||||
| `--v_symmetry_time_pct <float>` | | `None` | Create symmetry along the Y axis at the desired percent complete of the generation process. (Must be between 0.0 and 1.0; set to a very small number like 0.0001 for just after the first step of generation.) |
|
| `--v_symmetry_time_pct <float>` | | `None` | Create symmetry along the Y axis at the desired percent complete of the generation process. (Must be between 0.0 and 1.0; set to a very small number like 0.0001 for just after the first step of generation.) |
|
||||||
|
|||||||
@@ -1,3 +1,12 @@
|
|||||||
|
---
|
||||||
|
title: Textual Inversion Embeddings and LoRAs
|
||||||
|
---
|
||||||
|
|
||||||
|
# :material-library-shelves: Textual Inversions and LoRAs
|
||||||
|
|
||||||
|
With the advances in research, many new capabilities are available to customize the knowledge and understanding of novel concepts not originally contained in the base model.
|
||||||
|
|
||||||
|
|
||||||
## Using Textual Inversion Files
|
## Using Textual Inversion Files
|
||||||
|
|
||||||
Textual inversion (TI) files are small models that customize the output of
|
Textual inversion (TI) files are small models that customize the output of
|
||||||
@@ -19,9 +28,8 @@ by placing them in the designated directory for the compatible model type
|
|||||||
|
|
||||||
### An Example
|
### An Example
|
||||||
|
|
||||||
Here are a few examples to illustrate how it works. All these images
|
Here are a few examples to illustrate how it works. All these images were
|
||||||
were generated using the legacy command-line client and the Stable
|
generated using the command-line client and the Stable Diffusion 1.5 model:
|
||||||
Diffusion 1.5 model:
|
|
||||||
|
|
||||||
| Japanese gardener | Japanese gardener <ghibli-face> | Japanese gardener <hoi4-leaders> | Japanese gardener <cartoona-animals> |
|
| Japanese gardener | Japanese gardener <ghibli-face> | Japanese gardener <hoi4-leaders> | Japanese gardener <cartoona-animals> |
|
||||||
| :--------------------------------: | :-----------------------------------: | :------------------------------------: | :----------------------------------------: |
|
| :--------------------------------: | :-----------------------------------: | :------------------------------------: | :----------------------------------------: |
|
||||||
@@ -52,4 +60,29 @@ files it finds there for compatible models. At startup you will see a message si
|
|||||||
>> Current embedding manager terms: <HOI4-Leader>, <princess-knight>
|
>> Current embedding manager terms: <HOI4-Leader>, <princess-knight>
|
||||||
```
|
```
|
||||||
To use these when generating, simply type the `<` key in your prompt to open the Textual Inversion WebUI and
|
To use these when generating, simply type the `<` key in your prompt to open the Textual Inversion WebUI and
|
||||||
select the embedding you'd like to use. This UI has type-ahead support, so you can easily find supported embeddings.
|
select the embedding you'd like to use. This UI has type-ahead support, so you can easily find supported embeddings.
|
||||||
|
|
||||||
|
## Using LoRAs
|
||||||
|
|
||||||
|
LoRA files are models that customize the output of Stable Diffusion
|
||||||
|
image generation. Larger than embeddings, but much smaller than full
|
||||||
|
models, they augment SD with improved understanding of subjects and
|
||||||
|
artistic styles.
|
||||||
|
|
||||||
|
Unlike TI files, LoRAs do not introduce novel vocabulary into the
|
||||||
|
model's known tokens. Instead, LoRAs augment the model's weights that
|
||||||
|
are applied to generate imagery. LoRAs may be supplied with a
|
||||||
|
"trigger" word that they have been explicitly trained on, or may
|
||||||
|
simply apply their effect without being triggered.
|
||||||
|
|
||||||
|
LoRAs are typically stored in .safetensors files, which are the most
|
||||||
|
secure way to store and transmit these types of weights. You may
|
||||||
|
install any number of `.safetensors` LoRA files simply by copying them
|
||||||
|
into the `autoimport/lora` directory of the corresponding InvokeAI models
|
||||||
|
directory (usually `invokeai` in your home directory).
|
||||||
|
|
||||||
|
To use these when generating, open the LoRA menu item in the options
|
||||||
|
panel, select the LoRAs you want to apply and ensure that they have
|
||||||
|
the appropriate weight recommended by the model provider. Typically,
|
||||||
|
most LoRAs perform best at a weight of .75-1.
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ format of YAML files can be found
|
|||||||
[here](https://circleci.com/blog/what-is-yaml-a-beginner-s-guide/).
|
[here](https://circleci.com/blog/what-is-yaml-a-beginner-s-guide/).
|
||||||
|
|
||||||
You can fix a broken `invokeai.yaml` by deleting it and running the
|
You can fix a broken `invokeai.yaml` by deleting it and running the
|
||||||
configuration script again -- option [6] in the launcher, "Re-run the
|
configuration script again -- option [7] in the launcher, "Re-run the
|
||||||
configure script".
|
configure script".
|
||||||
|
|
||||||
#### Reading Environment Variables
|
#### Reading Environment Variables
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ image generation, providing you with a way to direct the network
|
|||||||
towards generating images that better fit your desired style or
|
towards generating images that better fit your desired style or
|
||||||
outcome.
|
outcome.
|
||||||
|
|
||||||
|
|
||||||
|
#### How it works
|
||||||
|
|
||||||
ControlNet works by analyzing an input image, pre-processing that
|
ControlNet works by analyzing an input image, pre-processing that
|
||||||
image to identify relevant information that can be interpreted by each
|
image to identify relevant information that can be interpreted by each
|
||||||
specific ControlNet model, and then inserting that control information
|
specific ControlNet model, and then inserting that control information
|
||||||
@@ -24,21 +27,35 @@ into the generation process. This can be used to adjust the style,
|
|||||||
composition, or other aspects of the image to better achieve a
|
composition, or other aspects of the image to better achieve a
|
||||||
specific result.
|
specific result.
|
||||||
|
|
||||||
#### Installation
|
|
||||||
|
#### Models
|
||||||
|
|
||||||
InvokeAI provides access to a series of ControlNet models that provide
|
InvokeAI provides access to a series of ControlNet models that provide
|
||||||
different effects or styles in your generated images.
|
different effects or styles in your generated images. Currently
|
||||||
|
InvokeAI only supports "diffuser" style ControlNet models. These are
|
||||||
|
folders that contain the files `config.json` and/or
|
||||||
|
`diffusion_pytorch_model.safetensors` and
|
||||||
|
`diffusion_pytorch_model.fp16.safetensors`. The name of the folder is
|
||||||
|
the name of the model.
|
||||||
|
|
||||||
To install ControlNet Models:
|
***InvokeAI does not currently support checkpoint-format
|
||||||
|
ControlNets. These come in the form of a single file with the
|
||||||
|
extension `.safetensors`.***
|
||||||
|
|
||||||
1. The easiest way to install them is
|
Diffuser-style ControlNet models are available at HuggingFace
|
||||||
|
(http://huggingface.co) and accessed via their repo IDs (identifiers
|
||||||
|
in the format "author/modelname"). The easiest way to install them is
|
||||||
to use the InvokeAI model installer application. Use the
|
to use the InvokeAI model installer application. Use the
|
||||||
`invoke.sh`/`invoke.bat` launcher to select item [4] and then navigate
|
`invoke.sh`/`invoke.bat` launcher to select item [5] and then navigate
|
||||||
to the CONTROLNETS section. Select the models you wish to install and
|
to the CONTROLNETS section. Select the models you wish to install and
|
||||||
press "APPLY CHANGES". You may also enter additional HuggingFace
|
press "APPLY CHANGES". You may also enter additional HuggingFace
|
||||||
repo_ids in the "Additional models" textbox.
|
repo_ids in the "Additional models" textbox:
|
||||||
2. Using the "Add Model" function of the model manager, enter the HuggingFace Repo ID of the ControlNet. The ID is in the format "author/repoName"
|
|
||||||
|
|
||||||
|
{:width="640px"}
|
||||||
|
|
||||||
|
Command-line users can launch the model installer using the command
|
||||||
|
`invokeai-model-install`.
|
||||||
|
|
||||||
_Be aware that some ControlNet models require additional code
|
_Be aware that some ControlNet models require additional code
|
||||||
functionality in order to work properly, so just installing a
|
functionality in order to work properly, so just installing a
|
||||||
@@ -46,17 +63,6 @@ third-party ControlNet model may not have the desired effect._ Please
|
|||||||
read and follow the documentation for installing a third party model
|
read and follow the documentation for installing a third party model
|
||||||
not currently included among InvokeAI's default list.
|
not currently included among InvokeAI's default list.
|
||||||
|
|
||||||
Currently InvokeAI **only** supports 🤗 Diffusers-format ControlNet models. These are
|
|
||||||
folders that contain the files `config.json` and/or
|
|
||||||
`diffusion_pytorch_model.safetensors` and
|
|
||||||
`diffusion_pytorch_model.fp16.safetensors`. The name of the folder is
|
|
||||||
the name of the model.
|
|
||||||
|
|
||||||
🤗 Diffusers-format ControlNet models are available at HuggingFace
|
|
||||||
(http://huggingface.co) and accessed via their repo IDs (identifiers
|
|
||||||
in the format "author/modelname").
|
|
||||||
|
|
||||||
#### ControlNet Models
|
|
||||||
The models currently supported include:
|
The models currently supported include:
|
||||||
|
|
||||||
**Canny**:
|
**Canny**:
|
||||||
@@ -127,29 +133,6 @@ Start/End - 0 represents the start of the generation, 1 represents the end. The
|
|||||||
|
|
||||||
Additionally, each ControlNet section can be expanded in order to manipulate settings for the image pre-processor that adjusts your uploaded image before using it in when you Invoke.
|
Additionally, each ControlNet section can be expanded in order to manipulate settings for the image pre-processor that adjusts your uploaded image before using it in when you Invoke.
|
||||||
|
|
||||||
## T2I-Adapter
|
|
||||||
[T2I-Adapter](https://github.com/TencentARC/T2I-Adapter) is a tool similar to ControlNet that allows for control over the generation process by providing control information during the generation process. T2I-Adapter models tend to be smaller and more efficient than ControlNets.
|
|
||||||
|
|
||||||
##### Installation
|
|
||||||
To install T2I-Adapter Models:
|
|
||||||
|
|
||||||
1. The easiest way to install models is
|
|
||||||
to use the InvokeAI model installer application. Use the
|
|
||||||
`invoke.sh`/`invoke.bat` launcher to select item [5] and then navigate
|
|
||||||
to the T2I-Adapters section. Select the models you wish to install and
|
|
||||||
press "APPLY CHANGES". You may also enter additional HuggingFace
|
|
||||||
repo_ids in the "Additional models" textbox.
|
|
||||||
2. Using the "Add Model" function of the model manager, enter the HuggingFace Repo ID of the T2I-Adapter. The ID is in the format "author/repoName"
|
|
||||||
|
|
||||||
#### Usage
|
|
||||||
Each T2I Adapter has two settings that are applied.
|
|
||||||
|
|
||||||
Weight - Strength of the model applied to the generation for the section, defined by start/end.
|
|
||||||
|
|
||||||
Start/End - 0 represents the start of the generation, 1 represents the end. The Start/end setting controls what steps during the generation process have the ControlNet applied.
|
|
||||||
|
|
||||||
Additionally, each section can be expanded with the "Show Advanced" button in order to manipulate settings for the image pre-processor that adjusts your uploaded image before using it in during the generation process.
|
|
||||||
|
|
||||||
|
|
||||||
## IP-Adapter
|
## IP-Adapter
|
||||||
|
|
||||||
@@ -157,13 +140,13 @@ Additionally, each section can be expanded with the "Show Advanced" button in o
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
#### Installation
|
#### Installation
|
||||||
There are several ways to install IP-Adapter models with an existing InvokeAI installation:
|
There are several ways to install IP-Adapter models with an existing InvokeAI installation:
|
||||||
|
|
||||||
1. Through the command line interface launched from the invoke.sh / invoke.bat scripts, option [4] to download models.
|
1. Through the command line interface launched from the invoke.sh / invoke.bat scripts, option [5] to download models.
|
||||||
2. Through the Model Manager UI with models from the *Tools* section of [www.models.invoke.ai](https://www.models.invoke.ai). To do this, copy the repo ID from the desired model page, and paste it in the Add Model field of the model manager. **Note** Both the IP-Adapter and the Image Encoder must be installed for IP-Adapter to work. For example, the [SD 1.5 IP-Adapter](https://models.invoke.ai/InvokeAI/ip_adapter_plus_sd15) and [SD1.5 Image Encoder](https://models.invoke.ai/InvokeAI/ip_adapter_sd_image_encoder) must be installed to use IP-Adapter with SD1.5 based models.
|
2. Through the Model Manager UI with models from the *Tools* section of [www.models.invoke.ai](www.models.invoke.ai). To do this, copy the repo ID from the desired model page, and paste it in the Add Model field of the model manager. **Note** Both the IP-Adapter and the Image Encoder must be installed for IP-Adapter to work. For example, the [SD 1.5 IP-Adapter](https://models.invoke.ai/InvokeAI/ip_adapter_plus_sd15) and [SD1.5 Image Encoder](https://models.invoke.ai/InvokeAI/ip_adapter_sd_image_encoder) must be installed to use IP-Adapter with SD1.5 based models.
|
||||||
3. **Advanced -- Not recommended ** Manually downloading the IP-Adapter and Image Encoder files - Image Encoder folders shouid be placed in the `models\any\clip_vision` folders. IP Adapter Model folders should be placed in the relevant `ip-adapter` folder of relevant base model folder of Invoke root directory. For example, for the SDXL IP-Adapter, files should be added to the `model/sdxl/ip_adapter/` folder.
|
3. **Advanced -- Not recommended ** Manually downloading the IP-Adapter and Image Encoder files - Image Encoder folders shouid be placed in the `models\any\clip_vision` folders. IP Adapter Model folders should be placed in the relevant `ip-adapter` folder of relevant base model folder of Invoke root directory. For example, for the SDXL IP-Adapter, files should be added to the `model/sdxl/ip_adapter/` folder.
|
||||||
|
|
||||||
#### Using IP-Adapter
|
#### Using IP-Adapter
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
---
|
|
||||||
title: LoRAs & LCM-LoRAs
|
|
||||||
---
|
|
||||||
|
|
||||||
# :material-library-shelves: LoRAs & LCM-LoRAs
|
|
||||||
|
|
||||||
With the advances in research, many new capabilities are available to customize the knowledge and understanding of novel concepts not originally contained in the base model.
|
|
||||||
|
|
||||||
## LoRAs
|
|
||||||
|
|
||||||
Low-Rank Adaptation (LoRA) files are models that customize the output of Stable Diffusion
|
|
||||||
image generation. Larger than embeddings, but much smaller than full
|
|
||||||
models, they augment SD with improved understanding of subjects and
|
|
||||||
artistic styles.
|
|
||||||
|
|
||||||
Unlike TI files, LoRAs do not introduce novel vocabulary into the
|
|
||||||
model's known tokens. Instead, LoRAs augment the model's weights that
|
|
||||||
are applied to generate imagery. LoRAs may be supplied with a
|
|
||||||
"trigger" word that they have been explicitly trained on, or may
|
|
||||||
simply apply their effect without being triggered.
|
|
||||||
|
|
||||||
LoRAs are typically stored in .safetensors files, which are the most
|
|
||||||
secure way to store and transmit these types of weights. You may
|
|
||||||
install any number of `.safetensors` LoRA files simply by copying them
|
|
||||||
into the `autoimport/lora` directory of the corresponding InvokeAI models
|
|
||||||
directory (usually `invokeai` in your home directory).
|
|
||||||
|
|
||||||
To use these when generating, open the LoRA menu item in the options
|
|
||||||
panel, select the LoRAs you want to apply and ensure that they have
|
|
||||||
the appropriate weight recommended by the model provider. Typically,
|
|
||||||
most LoRAs perform best at a weight of .75-1.
|
|
||||||
|
|
||||||
|
|
||||||
## LCM-LoRAs
|
|
||||||
Latent Consistency Models (LCMs) allowed a reduced number of steps to be used to generate images with Stable Diffusion. These are created by distilling base models, creating models that only require a small number of steps to generate images. However, LCMs require that any fine-tune of a base model be distilled to be used as an LCM.
|
|
||||||
|
|
||||||
LCM-LoRAs are models that provide the benefit of LCMs but are able to be used as LoRAs and applied to any fine tune of a base model. LCM-LoRAs are created by training a small number of adapters, rather than distilling the entire fine-tuned base model. The resulting LoRA can be used the same way as a standard LoRA, but with a greatly reduced step count. This enables SDXL images to be generated up to 10x faster than without the use of LCM-LoRAs.
|
|
||||||
|
|
||||||
|
|
||||||
**Using LCM-LoRAs**
|
|
||||||
|
|
||||||
LCM-LoRAs are natively supported in InvokeAI throughout the application. To get started, install any diffusers format LCM-LoRAs using the model manager and select it in the LoRA field.
|
|
||||||
|
|
||||||
There are a number parameter differences when using LCM-LoRAs and standard generation:
|
|
||||||
|
|
||||||
- When using LCM-LoRAs, the LoRA strength should be lower than if using a standard LoRA, with 0.35 recommended as a starting point.
|
|
||||||
- The LCM scheduler should be used for generation
|
|
||||||
- CFG-Scale should be reduced to ~1
|
|
||||||
- Steps should be reduced in the range of 4-8
|
|
||||||
|
|
||||||
Standard LoRAs can also be used alongside LCM-LoRAs, but will also require a lower strength, with 0.45 being recommended as a starting point.
|
|
||||||
|
|
||||||
More information can be found here: https://huggingface.co/blog/lcm_lora#fast-inference-with-sdxl-lcm-loras
|
|
||||||
@@ -16,10 +16,9 @@ Model Merging can be be done by navigating to the Model Manager and clicking the
|
|||||||
display all the diffusers-style models that InvokeAI knows about.
|
display all the diffusers-style models that InvokeAI knows about.
|
||||||
If you do not see the model you are looking for, then it is probably
|
If you do not see the model you are looking for, then it is probably
|
||||||
a legacy checkpoint model and needs to be converted using the
|
a legacy checkpoint model and needs to be converted using the
|
||||||
"Convert" option in the Web-based Model Manager tab.
|
`invoke` command-line client and its `!optimize` command. You
|
||||||
|
must select at least two models to merge. The third can be left at
|
||||||
You must select at least two models to merge. The third can be left
|
"None" if you desire.
|
||||||
at "None" if you desire.
|
|
||||||
|
|
||||||
* Alpha: This is the ratio to use when combining models. It ranges
|
* Alpha: This is the ratio to use when combining models. It ranges
|
||||||
from 0 to 1. The higher the value, the more weight is given to the
|
from 0 to 1. The higher the value, the more weight is given to the
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ title: Command-line Utilities
|
|||||||
|
|
||||||
InvokeAI comes with several scripts that are accessible via the
|
InvokeAI comes with several scripts that are accessible via the
|
||||||
command line. To access these commands, start the "developer's
|
command line. To access these commands, start the "developer's
|
||||||
console" from the launcher (`invoke.bat` menu item [7]). Users who are
|
console" from the launcher (`invoke.bat` menu item [8]). Users who are
|
||||||
familiar with Python can alternatively activate InvokeAI's virtual
|
familiar with Python can alternatively activate InvokeAI's virtual
|
||||||
environment (typically, but not necessarily `invokeai/.venv`).
|
environment (typically, but not necessarily `invokeai/.venv`).
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ invokeai-web --ram 7
|
|||||||
|
|
||||||
## **invokeai-merge**
|
## **invokeai-merge**
|
||||||
|
|
||||||
This is the model merge script, the same as launcher option [3]. Call
|
This is the model merge script, the same as launcher option [4]. Call
|
||||||
it with the `--gui` command-line argument to start the interactive
|
it with the `--gui` command-line argument to start the interactive
|
||||||
console-based GUI. Alternatively, you can run it non-interactively
|
console-based GUI. Alternatively, you can run it non-interactively
|
||||||
using command-line arguments as illustrated in the example below which
|
using command-line arguments as illustrated in the example below which
|
||||||
@@ -48,7 +48,7 @@ invokeai-merge --force --base-model sd-1 --models stable-diffusion-1.5 inkdiffus
|
|||||||
## **invokeai-ti**
|
## **invokeai-ti**
|
||||||
|
|
||||||
This is the textual inversion training script that is run by launcher
|
This is the textual inversion training script that is run by launcher
|
||||||
option [2]. Call it with `--gui` to run the interactive console-based
|
option [3]. Call it with `--gui` to run the interactive console-based
|
||||||
front end. It can also be run non-interactively. It has about a
|
front end. It can also be run non-interactively. It has about a
|
||||||
zillion arguments, but a typical training session can be launched
|
zillion arguments, but a typical training session can be launched
|
||||||
with:
|
with:
|
||||||
@@ -68,7 +68,7 @@ in Windows).
|
|||||||
## **invokeai-install**
|
## **invokeai-install**
|
||||||
|
|
||||||
This is the console-based model install script that is run by launcher
|
This is the console-based model install script that is run by launcher
|
||||||
option [4]. If called without arguments, it will launch the
|
option [5]. If called without arguments, it will launch the
|
||||||
interactive console-based interface. It can also be used
|
interactive console-based interface. It can also be used
|
||||||
non-interactively to list, add and remove models as shown by these
|
non-interactively to list, add and remove models as shown by these
|
||||||
examples:
|
examples:
|
||||||
@@ -148,7 +148,7 @@ launch the web server against it with `invokeai-web --root InvokeAI-New`.
|
|||||||
## **invokeai-update**
|
## **invokeai-update**
|
||||||
|
|
||||||
This is the interactive console-based script that is run by launcher
|
This is the interactive console-based script that is run by launcher
|
||||||
menu item [8] to update to a new version of InvokeAI. It takes no
|
menu item [9] to update to a new version of InvokeAI. It takes no
|
||||||
command-line arguments.
|
command-line arguments.
|
||||||
|
|
||||||
## **invokeai-metadata**
|
## **invokeai-metadata**
|
||||||
|
|||||||
@@ -126,6 +126,6 @@ amounts of image-to-image variation even when the seed is fixed and the
|
|||||||
`-v` argument is very low. Others are more deterministic. Feel free to
|
`-v` argument is very low. Others are more deterministic. Feel free to
|
||||||
experiment until you find the combination that you like.
|
experiment until you find the combination that you like.
|
||||||
|
|
||||||
Also be aware of the [Perlin Noise](../features/OTHER.md#thresholding-and-perlin-noise-initialization-options)
|
Also be aware of the [Perlin Noise](OTHER.md#thresholding-and-perlin-noise-initialization-options)
|
||||||
feature, which provides another way of introducing variability into your
|
feature, which provides another way of introducing variability into your
|
||||||
image generation requests.
|
image generation requests.
|
||||||
@@ -20,7 +20,7 @@ a single convenient digital artist-optimized user interface.
|
|||||||
### * [Prompt Engineering](PROMPTS.md)
|
### * [Prompt Engineering](PROMPTS.md)
|
||||||
Get the images you want with the InvokeAI prompt engineering language.
|
Get the images you want with the InvokeAI prompt engineering language.
|
||||||
|
|
||||||
### * The [LoRA, LyCORIS, LCM-LoRA Models](CONCEPTS.md)
|
### * The [LoRA, LyCORIS and Textual Inversion Models](CONCEPTS.md)
|
||||||
Add custom subjects and styles using a variety of fine-tuned models.
|
Add custom subjects and styles using a variety of fine-tuned models.
|
||||||
|
|
||||||
### * [ControlNet](CONTROLNET.md)
|
### * [ControlNet](CONTROLNET.md)
|
||||||
@@ -28,7 +28,7 @@ Learn how to install and use ControlNet models for fine control over
|
|||||||
image output.
|
image output.
|
||||||
|
|
||||||
### * [Image-to-Image Guide](IMG2IMG.md)
|
### * [Image-to-Image Guide](IMG2IMG.md)
|
||||||
Use a seed image to build new creations.
|
Use a seed image to build new creations in the CLI.
|
||||||
|
|
||||||
## Model Management
|
## Model Management
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ guide also covers optimizing models to load quickly.
|
|||||||
Teach an old model new tricks. Merge 2-3 models together to create a
|
Teach an old model new tricks. Merge 2-3 models together to create a
|
||||||
new model that combines characteristics of the originals.
|
new model that combines characteristics of the originals.
|
||||||
|
|
||||||
### * [Textual Inversion](TEXTUAL_INVERSIONS.md)
|
### * [Textual Inversion](TRAINING.md)
|
||||||
Personalize models by adding your own style or subjects.
|
Personalize models by adding your own style or subjects.
|
||||||
|
|
||||||
## Other Features
|
## Other Features
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
# FAQs
|
|
||||||
|
|
||||||
**Where do I get started? How can I install Invoke?**
|
|
||||||
|
|
||||||
- You can download the latest installers [here](https://github.com/invoke-ai/InvokeAI/releases) - Note that any releases marked as *pre-release* are in a beta state. You may experience some issues, but we appreciate your help testing those! For stable/reliable installations, please install the **[Latest Release](https://github.com/invoke-ai/InvokeAI/releases/latest)**
|
|
||||||
|
|
||||||
**How can I download models? Can I use models I already have downloaded?**
|
|
||||||
|
|
||||||
- Models can be downloaded through the model manager, or through option [4] in the invoke.bat/invoke.sh launcher script. To download a model through the Model Manager, use the HuggingFace Repo ID by pressing the “Copy” button next to the repository name. Alternatively, to download a model from CivitAi, use the download link in the Model Manager.
|
|
||||||
- Models that are already downloaded can be used by creating a symlink to the model location in the `autoimport` folder or by using the Model Manger’s “Scan for Models” function.
|
|
||||||
|
|
||||||
**My images are taking a long time to generate. How can I speed up generation?**
|
|
||||||
|
|
||||||
- A common solution is to reduce the size of your RAM & VRAM cache to 0.25. This ensures your system has enough memory to generate images.
|
|
||||||
- Additionally, check the [hardware requirements](https://invoke-ai.github.io/InvokeAI/#hardware-requirements) to ensure that your system is capable of generating images.
|
|
||||||
- Lastly, double check your generations are happening on your GPU (if you have one). InvokeAI will log what is being used for generation upon startup.
|
|
||||||
|
|
||||||
**I’ve installed Python on Windows but the installer says it can’t find it?**
|
|
||||||
|
|
||||||
- Then ensure that you checked **'Add python.exe to PATH'** when installing Python. This can be found at the bottom of the Python Installer window. If you already have Python installed, this can be done with the modify / repair feature of the installer.
|
|
||||||
|
|
||||||
**I’ve installed everything successfully but I still get an error about Triton when starting Invoke?**
|
|
||||||
|
|
||||||
- This can be safely ignored. InvokeAI doesn't use Triton, but if you are on Linux and wish to dismiss the error, you can install Triton.
|
|
||||||
|
|
||||||
**I updated to 3.4.0 and now xFormers can’t load C++/CUDA?**
|
|
||||||
|
|
||||||
- An issue occurred with your PyTorch update. Follow these steps to fix :
|
|
||||||
1. Launch your invoke.bat / invoke.sh and select the option to open the developer console
|
|
||||||
2. Run:`pip install ".[xformers]" --upgrade --force-reinstall --extra-index-url https://download.pytorch.org/whl/cu121`
|
|
||||||
- If you run into an error with `typing_extensions`, re-open the developer console and run: `pip install -U typing-extensions`
|
|
||||||
|
|
||||||
**It says my pip is out of date - is that why my install isn't working?**
|
|
||||||
- An out of date won't cause an installation to fail. The cause of the error can likely be found above the message that says pip is out of date.
|
|
||||||
- If you saw that warning but the install went well, don't worry about it (but you can update pip afterwards if you'd like).
|
|
||||||
|
|
||||||
**How can I generate the exact same that I found on the internet?**
|
|
||||||
Most example images with prompts that you'll find on the internet have been generated using different software, so you can't expect to get identical results. In order to reproduce an image, you need to replicate the exact settings and processing steps, including (but not limited to) the model, the positive and negative prompts, the seed, the sampler, the exact image size, any upscaling steps, etc.
|
|
||||||
|
|
||||||
|
|
||||||
**Where can I get more help?**
|
|
||||||
|
|
||||||
- Create an issue on [GitHub](https://github.com/invoke-ai/InvokeAI/issues) or post in the [#help channel](https://discord.com/channels/1020123559063990373/1149510134058471514) of the InvokeAI Discord
|
|
||||||
@@ -57,9 +57,7 @@ Prompts provide the models directions on what to generate. As a general rule of
|
|||||||
|
|
||||||
Models are the magic that power InvokeAI. These files represent the output of training a machine on understanding massive amounts of images - providing them with the capability to generate new images using just a text description of what you’d like to see. (Like Stable Diffusion!)
|
Models are the magic that power InvokeAI. These files represent the output of training a machine on understanding massive amounts of images - providing them with the capability to generate new images using just a text description of what you’d like to see. (Like Stable Diffusion!)
|
||||||
|
|
||||||
Invoke offers a simple way to download several different models upon installation, but many more can be discovered online, including at https://models.invoke.ai
|
Invoke offers a simple way to download several different models upon installation, but many more can be discovered online, including at ****. Each model can produce a unique style of output, based on the images it was trained on - Try out different models to see which best fits your creative vision!
|
||||||
|
|
||||||
Each model can produce a unique style of output, based on the images it was trained on - Try out different models to see which best fits your creative vision!
|
|
||||||
|
|
||||||
- *Models that contain “inpainting” in the name are designed for use with the inpainting feature of the Unified Canvas*
|
- *Models that contain “inpainting” in the name are designed for use with the inpainting feature of the Unified Canvas*
|
||||||
|
|
||||||
|
|||||||
@@ -101,13 +101,16 @@ Mac and Linux machines, and runs on GPU cards with as little as 4 GB of RAM.
|
|||||||
|
|
||||||
<div align="center"><img src="assets/invoke-web-server-1.png" width=640></div>
|
<div align="center"><img src="assets/invoke-web-server-1.png" width=640></div>
|
||||||
|
|
||||||
|
!!! Note
|
||||||
|
|
||||||
|
This project is rapidly evolving. Please use the [Issues tab](https://github.com/invoke-ai/InvokeAI/issues) to report bugs and make feature requests. Be sure to use the provided templates as it will help aid response time.
|
||||||
|
|
||||||
## :octicons-link-24: Quick Links
|
## :octicons-link-24: Quick Links
|
||||||
|
|
||||||
<div class="button-container">
|
<div class="button-container">
|
||||||
<a href="installation/INSTALLATION"> <button class="button">Installation</button> </a>
|
<a href="installation/INSTALLATION"> <button class="button">Installation</button> </a>
|
||||||
<a href="features/"> <button class="button">Features</button> </a>
|
<a href="features/"> <button class="button">Features</button> </a>
|
||||||
<a href="help/gettingStartedWithAI/"> <button class="button">Getting Started</button> </a>
|
<a href="help/gettingStartedWithAI/"> <button class="button">Getting Started</button> </a>
|
||||||
<a href="help/FAQ/"> <button class="button">FAQ</button> </a>
|
|
||||||
<a href="contributing/CONTRIBUTING/"> <button class="button">Contributing</button> </a>
|
<a href="contributing/CONTRIBUTING/"> <button class="button">Contributing</button> </a>
|
||||||
<a href="https://github.com/invoke-ai/InvokeAI/"> <button class="button">Code and Downloads</button> </a>
|
<a href="https://github.com/invoke-ai/InvokeAI/"> <button class="button">Code and Downloads</button> </a>
|
||||||
<a href="https://github.com/invoke-ai/InvokeAI/issues"> <button class="button">Bug Reports </button> </a>
|
<a href="https://github.com/invoke-ai/InvokeAI/issues"> <button class="button">Bug Reports </button> </a>
|
||||||
@@ -140,6 +143,7 @@ Mac and Linux machines, and runs on GPU cards with as little as 4 GB of RAM.
|
|||||||
<!-- seperator -->
|
<!-- seperator -->
|
||||||
### Prompt Engineering
|
### Prompt Engineering
|
||||||
- [Prompt Syntax](features/PROMPTS.md)
|
- [Prompt Syntax](features/PROMPTS.md)
|
||||||
|
- [Generating Variations](features/VARIATIONS.md)
|
||||||
|
|
||||||
### InvokeAI Configuration
|
### InvokeAI Configuration
|
||||||
- [Guide to InvokeAI Runtime Settings](features/CONFIGURATION.md)
|
- [Guide to InvokeAI Runtime Settings](features/CONFIGURATION.md)
|
||||||
@@ -162,8 +166,10 @@ still a work in progress, but coming soon.
|
|||||||
|
|
||||||
### Command-Line Interface Retired
|
### Command-Line Interface Retired
|
||||||
|
|
||||||
All "invokeai" command-line interfaces have been retired as of version
|
The original "invokeai" command-line interface has been retired. The
|
||||||
3.4.
|
`invokeai` command will now launch a new command-line client that can
|
||||||
|
be used by developers to create and test nodes. It is not intended to
|
||||||
|
be used for routine image generation or manipulation.
|
||||||
|
|
||||||
To launch the Web GUI from the command-line, use the command
|
To launch the Web GUI from the command-line, use the command
|
||||||
`invokeai-web` rather than the traditional `invokeai --web`.
|
`invokeai-web` rather than the traditional `invokeai --web`.
|
||||||
@@ -195,7 +201,6 @@ The list of schedulers has been completely revamped and brought up to date:
|
|||||||
| **dpmpp_2m** | DPMSolverMultistepScheduler | original noise scnedule |
|
| **dpmpp_2m** | DPMSolverMultistepScheduler | original noise scnedule |
|
||||||
| **dpmpp_2m_k** | DPMSolverMultistepScheduler | using karras noise schedule |
|
| **dpmpp_2m_k** | DPMSolverMultistepScheduler | using karras noise schedule |
|
||||||
| **unipc** | UniPCMultistepScheduler | CPU only |
|
| **unipc** | UniPCMultistepScheduler | CPU only |
|
||||||
| **lcm** | LCMScheduler | |
|
|
||||||
|
|
||||||
Please see [3.0.0 Release Notes](https://github.com/invoke-ai/InvokeAI/releases/tag/v3.0.0) for further details.
|
Please see [3.0.0 Release Notes](https://github.com/invoke-ai/InvokeAI/releases/tag/v3.0.0) for further details.
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ experimental versions later.
|
|||||||
this, open up a command-line window ("Terminal" on Linux and
|
this, open up a command-line window ("Terminal" on Linux and
|
||||||
Macintosh, "Command" or "Powershell" on Windows) and type `python
|
Macintosh, "Command" or "Powershell" on Windows) and type `python
|
||||||
--version`. If Python is installed, it will print out the version
|
--version`. If Python is installed, it will print out the version
|
||||||
number. If it is version `3.10.*` or `3.11.*` you meet
|
number. If it is version `3.9.*`, `3.10.*` or `3.11.*` you meet
|
||||||
requirements.
|
requirements.
|
||||||
|
|
||||||
!!! warning "What to do if you have an unsupported version"
|
!!! warning "What to do if you have an unsupported version"
|
||||||
@@ -48,7 +48,7 @@ experimental versions later.
|
|||||||
Go to [Python Downloads](https://www.python.org/downloads/)
|
Go to [Python Downloads](https://www.python.org/downloads/)
|
||||||
and download the appropriate installer package for your
|
and download the appropriate installer package for your
|
||||||
platform. We recommend [Version
|
platform. We recommend [Version
|
||||||
3.10.12](https://www.python.org/downloads/release/python-3109/),
|
3.10.9](https://www.python.org/downloads/release/python-3109/),
|
||||||
which has been extensively tested with InvokeAI.
|
which has been extensively tested with InvokeAI.
|
||||||
|
|
||||||
_Please select your platform in the section below for platform-specific
|
_Please select your platform in the section below for platform-specific
|
||||||
@@ -179,7 +179,7 @@ experimental versions later.
|
|||||||
you will have the choice of CUDA (NVidia cards), ROCm (AMD cards),
|
you will have the choice of CUDA (NVidia cards), ROCm (AMD cards),
|
||||||
or CPU (no graphics acceleration). On Windows, you'll have the
|
or CPU (no graphics acceleration). On Windows, you'll have the
|
||||||
choice of CUDA vs CPU, and on Macs you'll be offered CPU only. When
|
choice of CUDA vs CPU, and on Macs you'll be offered CPU only. When
|
||||||
you select CPU on M1/M2/M3 Macintoshes, you will get MPS-based
|
you select CPU on M1 or M2 Macintoshes, you will get MPS-based
|
||||||
graphics acceleration without installing additional drivers. If you
|
graphics acceleration without installing additional drivers. If you
|
||||||
are unsure what GPU you are using, you can ask the installer to
|
are unsure what GPU you are using, you can ask the installer to
|
||||||
guess.
|
guess.
|
||||||
@@ -471,7 +471,7 @@ Then type the following commands:
|
|||||||
|
|
||||||
=== "NVIDIA System"
|
=== "NVIDIA System"
|
||||||
```bash
|
```bash
|
||||||
pip install torch torchvision --force-reinstall --extra-index-url https://download.pytorch.org/whl/cu121
|
pip install torch torchvision --force-reinstall --extra-index-url https://download.pytorch.org/whl/cu118
|
||||||
pip install xformers
|
pip install xformers
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ gaming):
|
|||||||
|
|
||||||
* **Python**
|
* **Python**
|
||||||
|
|
||||||
version 3.10 through 3.11
|
version 3.9 through 3.11
|
||||||
|
|
||||||
* **CUDA Tools**
|
* **CUDA Tools**
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ gaming):
|
|||||||
To install InvokeAI with virtual environments and the PIP package
|
To install InvokeAI with virtual environments and the PIP package
|
||||||
manager, please follow these steps:
|
manager, please follow these steps:
|
||||||
|
|
||||||
1. Please make sure you are using Python 3.10 through 3.11. The rest of the install
|
1. Please make sure you are using Python 3.9 through 3.11. The rest of the install
|
||||||
procedure depends on this and will not work with other versions:
|
procedure depends on this and will not work with other versions:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -148,7 +148,7 @@ manager, please follow these steps:
|
|||||||
=== "CUDA (NVidia)"
|
=== "CUDA (NVidia)"
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install "InvokeAI[xformers]" --use-pep517 --extra-index-url https://download.pytorch.org/whl/cu121
|
pip install "InvokeAI[xformers]" --use-pep517 --extra-index-url https://download.pytorch.org/whl/cu118
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "ROCm (AMD)"
|
=== "ROCm (AMD)"
|
||||||
@@ -327,7 +327,7 @@ installation protocol (important!)
|
|||||||
|
|
||||||
=== "CUDA (NVidia)"
|
=== "CUDA (NVidia)"
|
||||||
```bash
|
```bash
|
||||||
pip install -e .[xformers] --use-pep517 --extra-index-url https://download.pytorch.org/whl/cu121
|
pip install -e .[xformers] --use-pep517 --extra-index-url https://download.pytorch.org/whl/cu118
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "ROCm (AMD)"
|
=== "ROCm (AMD)"
|
||||||
@@ -375,7 +375,7 @@ you can do so using this unsupported recipe:
|
|||||||
mkdir ~/invokeai
|
mkdir ~/invokeai
|
||||||
conda create -n invokeai python=3.10
|
conda create -n invokeai python=3.10
|
||||||
conda activate invokeai
|
conda activate invokeai
|
||||||
pip install InvokeAI[xformers] --use-pep517 --extra-index-url https://download.pytorch.org/whl/cu121
|
pip install InvokeAI[xformers] --use-pep517 --extra-index-url https://download.pytorch.org/whl/cu118
|
||||||
invokeai-configure --root ~/invokeai
|
invokeai-configure --root ~/invokeai
|
||||||
invokeai --root ~/invokeai --web
|
invokeai --root ~/invokeai --web
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ You can find which version you should download from [this link](https://docs.nvi
|
|||||||
|
|
||||||
When installing torch and torchvision manually with `pip`, remember to provide
|
When installing torch and torchvision manually with `pip`, remember to provide
|
||||||
the argument `--extra-index-url
|
the argument `--extra-index-url
|
||||||
https://download.pytorch.org/whl/cu121` as described in the [Manual
|
https://download.pytorch.org/whl/cu118` as described in the [Manual
|
||||||
Installation Guide](020_INSTALL_MANUAL.md).
|
Installation Guide](020_INSTALL_MANUAL.md).
|
||||||
|
|
||||||
## :simple-amd: ROCm
|
## :simple-amd: ROCm
|
||||||
|
|||||||
@@ -4,49 +4,38 @@ title: Installing with Docker
|
|||||||
|
|
||||||
# :fontawesome-brands-docker: Docker
|
# :fontawesome-brands-docker: Docker
|
||||||
|
|
||||||
!!! warning "macOS and AMD GPU Users"
|
!!! warning "For most users"
|
||||||
|
|
||||||
We highly recommend to Install InvokeAI locally using [these instructions](INSTALLATION.md),
|
We highly recommend to Install InvokeAI locally using [these instructions](INSTALLATION.md)
|
||||||
because Docker containers can not access the GPU on macOS.
|
|
||||||
|
|
||||||
!!! warning "AMD GPU Users"
|
!!! tip "For developers"
|
||||||
|
|
||||||
Container support for AMD GPUs has been reported to work by the community, but has not received
|
For container-related development tasks or for enabling easy
|
||||||
extensive testing. Please make sure to set the `GPU_DRIVER=rocm` environment variable (see below), and
|
deployment to other environments (on-premises or cloud), follow these
|
||||||
use the `build.sh` script to build the image for this to take effect at build time.
|
instructions.
|
||||||
|
|
||||||
!!! tip "Linux and Windows Users"
|
For general use, install locally to leverage your machine's GPU.
|
||||||
|
|
||||||
For optimal performance, configure your Docker daemon to access your machine's GPU.
|
|
||||||
Docker Desktop on Windows [includes GPU support](https://www.docker.com/blog/wsl-2-gpu-support-for-docker-desktop-on-nvidia-gpus/).
|
|
||||||
Linux users should install and configure the [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html)
|
|
||||||
|
|
||||||
## Why containers?
|
## Why containers?
|
||||||
|
|
||||||
They provide a flexible, reliable way to build and deploy InvokeAI.
|
They provide a flexible, reliable way to build and deploy InvokeAI. You'll also
|
||||||
See [Processes](https://12factor.net/processes) under the Twelve-Factor App
|
use a Docker volume to store the largest model files and image outputs as a
|
||||||
methodology for details on why running applications in such a stateless fashion is important.
|
first step in decoupling storage and compute. Future enhancements can do this
|
||||||
|
for other assets. See [Processes](https://12factor.net/processes) under the
|
||||||
|
Twelve-Factor App methodology for details on why running applications in such a
|
||||||
|
stateless fashion is important.
|
||||||
|
|
||||||
The container is configured for CUDA by default, but can be built to support AMD GPUs
|
You can specify the target platform when building the image and running the
|
||||||
by setting the `GPU_DRIVER=rocm` environment variable at Docker image build time.
|
container. You'll also need to specify the InvokeAI requirements file that
|
||||||
|
matches the container's OS and the architecture it will run on.
|
||||||
|
|
||||||
Developers on Apple silicon (M1/M2/M3): You
|
Developers on Apple silicon (M1/M2): You
|
||||||
[can't access your GPU cores from Docker containers](https://github.com/pytorch/pytorch/issues/81224)
|
[can't access your GPU cores from Docker containers](https://github.com/pytorch/pytorch/issues/81224)
|
||||||
and performance is reduced compared with running it directly on macOS but for
|
and performance is reduced compared with running it directly on macOS but for
|
||||||
development purposes it's fine. Once you're done with development tasks on your
|
development purposes it's fine. Once you're done with development tasks on your
|
||||||
laptop you can build for the target platform and architecture and deploy to
|
laptop you can build for the target platform and architecture and deploy to
|
||||||
another environment with NVIDIA GPUs on-premises or in the cloud.
|
another environment with NVIDIA GPUs on-premises or in the cloud.
|
||||||
|
|
||||||
## TL;DR
|
|
||||||
|
|
||||||
This assumes properly configured Docker on Linux or Windows/WSL2. Read on for detailed customization options.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# docker compose commands should be run from the `docker` directory
|
|
||||||
cd docker
|
|
||||||
docker compose up
|
|
||||||
```
|
|
||||||
|
|
||||||
## Installation in a Linux container (desktop)
|
## Installation in a Linux container (desktop)
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
@@ -69,44 +58,222 @@ a token and copy it, since you will need in for the next step.
|
|||||||
|
|
||||||
### Setup
|
### Setup
|
||||||
|
|
||||||
Set up your environmnent variables. In the `docker` directory, make a copy of `env.sample` and name it `.env`. Make changes as necessary.
|
Set the fork you want to use and other variables.
|
||||||
|
|
||||||
Any environment variables supported by InvokeAI can be set here - please see the [CONFIGURATION](../features/CONFIGURATION.md) for further detail.
|
!!! tip
|
||||||
|
|
||||||
At a minimum, you might want to set the `INVOKEAI_ROOT` environment variable
|
I preffer to save my env vars
|
||||||
to point to the location where you wish to store your InvokeAI models, configuration, and outputs.
|
in the repository root in a `.env` (or `.envrc`) file to automatically re-apply
|
||||||
|
them when I come back.
|
||||||
|
|
||||||
|
The build- and run- scripts contain default values for almost everything,
|
||||||
|
besides the [Hugging Face Token](https://huggingface.co/settings/tokens) you
|
||||||
|
created in the last step.
|
||||||
|
|
||||||
|
Some Suggestions of variables you may want to change besides the Token:
|
||||||
|
|
||||||
<figure markdown>
|
<figure markdown>
|
||||||
|
|
||||||
| Environment-Variable <img width="220" align="right"/> | Default value <img width="360" align="right"/> | Description |
|
| Environment-Variable <img width="220" align="right"/> | Default value <img width="360" align="right"/> | Description |
|
||||||
| ----------------------------------------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ----------------------------------------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `INVOKEAI_ROOT` | `~/invokeai` | **Required** - the location of your InvokeAI root directory. It will be created if it does not exist.
|
| `HUGGING_FACE_HUB_TOKEN` | No default, but **required**! | This is the only **required** variable, without it you can't download the huggingface models |
|
||||||
| `HUGGING_FACE_HUB_TOKEN` | | InvokeAI will work without it, but some of the integrations with HuggingFace (like downloading from models from private repositories) may not work|
|
| `REPOSITORY_NAME` | The Basename of the Repo folder | This name will used as the container repository/image name |
|
||||||
| `GPU_DRIVER` | `cuda` | Optionally change this to `rocm` to build the image for AMD GPUs. NOTE: Use the `build.sh` script to build the image for this to take effect.
|
| `VOLUMENAME` | `${REPOSITORY_NAME,,}_data` | Name of the Docker Volume where model files will be stored |
|
||||||
|
| `ARCH` | arch of the build machine | Can be changed if you want to build the image for another arch |
|
||||||
|
| `CONTAINER_REGISTRY` | ghcr.io | Name of the Container Registry to use for the full tag |
|
||||||
|
| `CONTAINER_REPOSITORY` | `$(whoami)/${REPOSITORY_NAME}` | Name of the Container Repository |
|
||||||
|
| `CONTAINER_FLAVOR` | `cuda` | The flavor of the image to built, available options are `cuda`, `rocm` and `cpu`. If you choose `rocm` or `cpu`, the extra-index-url will be selected automatically, unless you set one yourself. |
|
||||||
|
| `CONTAINER_TAG` | `${INVOKEAI_BRANCH##*/}-${CONTAINER_FLAVOR}` | The Container Repository / Tag which will be used |
|
||||||
|
| `INVOKE_DOCKERFILE` | `Dockerfile` | The Dockerfile which should be built, handy for development |
|
||||||
|
| `PIP_EXTRA_INDEX_URL` | | If you want to use a custom pip-extra-index-url |
|
||||||
|
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
#### Build the Image
|
#### Build the Image
|
||||||
|
|
||||||
Use the standard `docker compose build` command from within the `docker` directory.
|
I provided a build script, which is located next to the Dockerfile in
|
||||||
|
`docker/build.sh`. It can be executed from repository root like this:
|
||||||
|
|
||||||
If using an AMD GPU:
|
```bash
|
||||||
a: set the `GPU_DRIVER=rocm` environment variable in `docker-compose.yml` and continue using `docker compose build` as usual, or
|
./docker/build.sh
|
||||||
b: set `GPU_DRIVER=rocm` in the `.env` file and use the `build.sh` script, provided for convenience
|
```
|
||||||
|
|
||||||
|
The build Script not only builds the container, but also creates the docker
|
||||||
|
volume if not existing yet.
|
||||||
|
|
||||||
#### Run the Container
|
#### Run the Container
|
||||||
|
|
||||||
Use the standard `docker compose up` command, and generally the `docker compose` [CLI](https://docs.docker.com/compose/reference/) as usual.
|
After the build process is done, you can run the container via the provided
|
||||||
|
`docker/run.sh` script
|
||||||
|
|
||||||
Once the container starts up (and configures the InvokeAI root directory if this is a new installation), you can access InvokeAI at [http://localhost:9090](http://localhost:9090)
|
```bash
|
||||||
|
./docker/run.sh
|
||||||
|
```
|
||||||
|
|
||||||
## Troubleshooting / FAQ
|
When used without arguments, the container will start the webserver and provide
|
||||||
|
you the link to open it. But if you want to use some other parameters you can
|
||||||
|
also do so.
|
||||||
|
|
||||||
- Q: I am running on Windows under WSL2, and am seeing a "no such file or directory" error.
|
!!! example "run script example"
|
||||||
- A: Your `docker-entrypoint.sh` file likely has Windows (CRLF) as opposed to Unix (LF) line endings,
|
|
||||||
and you may have cloned this repository before the issue was fixed. To solve this, please change
|
```bash
|
||||||
the line endings in the `docker-entrypoint.sh` file to `LF`. You can do this in VSCode
|
./docker/run.sh "banana sushi" -Ak_lms -S42 -s10
|
||||||
(`Ctrl+P` and search for "line endings"), or by using the `dos2unix` utility in WSL.
|
```
|
||||||
Finally, you may delete `docker-entrypoint.sh` followed by `git pull; git checkout docker/docker-entrypoint.sh`
|
|
||||||
to reset the file to its most recent version.
|
This would generate the legendary "banana sushi" with Seed 42, k_lms Sampler and 10 steps.
|
||||||
For more information on this issue, please see the [Docker Desktop documentation](https://docs.docker.com/desktop/troubleshoot/topics/#avoid-unexpected-syntax-errors-use-unix-style-line-endings-for-files-in-containers)
|
|
||||||
|
Find out more about available CLI-Parameters at [features/CLI.md](../../features/CLI/#arguments)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Running the container on your GPU
|
||||||
|
|
||||||
|
If you have an Nvidia GPU, you can enable InvokeAI to run on the GPU by running
|
||||||
|
the container with an extra environment variable to enable GPU usage and have
|
||||||
|
the process run much faster:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
GPU_FLAGS=all ./docker/run.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This passes the `--gpus all` to docker and uses the GPU.
|
||||||
|
|
||||||
|
If you don't have a GPU (or your host is not yet setup to use it) you will see a
|
||||||
|
message like this:
|
||||||
|
|
||||||
|
`docker: Error response from daemon: could not select device driver "" with capabilities: [[gpu]].`
|
||||||
|
|
||||||
|
You can use the full set of GPU combinations documented here:
|
||||||
|
|
||||||
|
https://docs.docker.com/config/containers/resource_constraints/#gpu
|
||||||
|
|
||||||
|
For example, use `GPU_FLAGS=device=GPU-3a23c669-1f69-c64e-cf85-44e9b07e7a2a` to
|
||||||
|
choose a specific device identified by a UUID.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
!!! warning "Deprecated"
|
||||||
|
|
||||||
|
From here on you will find the the previous Docker-Docs, which will still
|
||||||
|
provide some usefull informations.
|
||||||
|
|
||||||
|
## Usage (time to have fun)
|
||||||
|
|
||||||
|
### Startup
|
||||||
|
|
||||||
|
If you're on a **Linux container** the `invoke` script is **automatically
|
||||||
|
started** and the output dir set to the Docker volume you created earlier.
|
||||||
|
|
||||||
|
If you're **directly on macOS follow these startup instructions**. With the
|
||||||
|
Conda environment activated (`conda activate ldm`), run the interactive
|
||||||
|
interface that combines the functionality of the original scripts `txt2img` and
|
||||||
|
`img2img`: Use the more accurate but VRAM-intensive full precision math because
|
||||||
|
half-precision requires autocast and won't work. By default the images are saved
|
||||||
|
in `outputs/img-samples/`.
|
||||||
|
|
||||||
|
```Shell
|
||||||
|
python3 scripts/invoke.py --full_precision
|
||||||
|
```
|
||||||
|
|
||||||
|
You'll get the script's prompt. You can see available options or quit.
|
||||||
|
|
||||||
|
```Shell
|
||||||
|
invoke> -h
|
||||||
|
invoke> q
|
||||||
|
```
|
||||||
|
|
||||||
|
### Text to Image
|
||||||
|
|
||||||
|
For quick (but bad) image results test with 5 steps (default 50) and 1 sample
|
||||||
|
image. This will let you know that everything is set up correctly. Then increase
|
||||||
|
steps to 100 or more for good (but slower) results. The prompt can be in quotes
|
||||||
|
or not.
|
||||||
|
|
||||||
|
```Shell
|
||||||
|
invoke> The hulk fighting with sheldon cooper -s5 -n1
|
||||||
|
invoke> "woman closeup highly detailed" -s 150
|
||||||
|
# Reuse previous seed and apply face restoration
|
||||||
|
invoke> "woman closeup highly detailed" --steps 150 --seed -1 -G 0.75
|
||||||
|
```
|
||||||
|
|
||||||
|
You'll need to experiment to see if face restoration is making it better or
|
||||||
|
worse for your specific prompt.
|
||||||
|
|
||||||
|
If you're on a container the output is set to the Docker volume. You can copy it
|
||||||
|
wherever you want. You can download it from the Docker Desktop app, Volumes,
|
||||||
|
my-vol, data. Or you can copy it from your Mac terminal. Keep in mind
|
||||||
|
`docker cp` can't expand `*.png` so you'll need to specify the image file name.
|
||||||
|
|
||||||
|
On your host Mac (you can use the name of any container that mounted the
|
||||||
|
volume):
|
||||||
|
|
||||||
|
```Shell
|
||||||
|
docker cp dummy:/data/000001.928403745.png /Users/<your-user>/Pictures
|
||||||
|
```
|
||||||
|
|
||||||
|
### Image to Image
|
||||||
|
|
||||||
|
You can also do text-guided image-to-image translation. For example, turning a
|
||||||
|
sketch into a detailed drawing.
|
||||||
|
|
||||||
|
`strength` is a value between 0.0 and 1.0 that controls the amount of noise that
|
||||||
|
is added to the input image. Values that approach 1.0 allow for lots of
|
||||||
|
variations but will also produce images that are not semantically consistent
|
||||||
|
with the input. 0.0 preserves image exactly, 1.0 replaces it completely.
|
||||||
|
|
||||||
|
Make sure your input image size dimensions are multiples of 64 e.g. 512x512.
|
||||||
|
Otherwise you'll get `Error: product of dimension sizes > 2**31'`. If you still
|
||||||
|
get the error
|
||||||
|
[try a different size](https://support.apple.com/guide/preview/resize-rotate-or-flip-an-image-prvw2015/mac#:~:text=image's%20file%20size-,In%20the%20Preview%20app%20on%20your%20Mac%2C%20open%20the%20file,is%20shown%20at%20the%20bottom.)
|
||||||
|
like 512x256.
|
||||||
|
|
||||||
|
If you're on a Docker container, copy your input image into the Docker volume
|
||||||
|
|
||||||
|
```Shell
|
||||||
|
docker cp /Users/<your-user>/Pictures/sketch-mountains-input.jpg dummy:/data/
|
||||||
|
```
|
||||||
|
|
||||||
|
Try it out generating an image (or more). The `invoke` script needs absolute
|
||||||
|
paths to find the image so don't use `~`.
|
||||||
|
|
||||||
|
If you're on your Mac
|
||||||
|
|
||||||
|
```Shell
|
||||||
|
invoke> "A fantasy landscape, trending on artstation" -I /Users/<your-user>/Pictures/sketch-mountains-input.jpg --strength 0.75 --steps 100 -n4
|
||||||
|
```
|
||||||
|
|
||||||
|
If you're on a Linux container on your Mac
|
||||||
|
|
||||||
|
```Shell
|
||||||
|
invoke> "A fantasy landscape, trending on artstation" -I /data/sketch-mountains-input.jpg --strength 0.75 --steps 50 -n1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Web Interface
|
||||||
|
|
||||||
|
You can use the `invoke` script with a graphical web interface. Start the web
|
||||||
|
server with:
|
||||||
|
|
||||||
|
```Shell
|
||||||
|
python3 scripts/invoke.py --full_precision --web
|
||||||
|
```
|
||||||
|
|
||||||
|
If it's running on your Mac point your Mac web browser to
|
||||||
|
<http://127.0.0.1:9090>
|
||||||
|
|
||||||
|
Press Control-C at the command line to stop the web server.
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
Some text you can add at the end of the prompt to make it very pretty:
|
||||||
|
|
||||||
|
```Shell
|
||||||
|
cinematic photo, highly detailed, cinematic lighting, ultra-detailed, ultrarealistic, photorealism, Octane Rendering, cyberpunk lights, Hyper Detail, 8K, HD, Unreal Engine, V-Ray, full hd, cyberpunk, abstract, 3d octane render + 4k UHD + immense detail + dramatic lighting + well lit + black, purple, blue, pink, cerulean, teal, metallic colours, + fine details, ultra photoreal, photographic, concept art, cinematic composition, rule of thirds, mysterious, eerie, photorealism, breathtaking detailed, painting art deco pattern, by hsiao, ron cheng, john james audubon, bizarre compositions, exquisite detail, extremely moody lighting, painted by greg rutkowski makoto shinkai takashi takeuchi studio ghibli, akihiko yoshida
|
||||||
|
```
|
||||||
|
|
||||||
|
The original scripts should work as well.
|
||||||
|
|
||||||
|
```Shell
|
||||||
|
python3 scripts/orig_scripts/txt2img.py --help
|
||||||
|
python3 scripts/orig_scripts/txt2img.py --ddim_steps 100 --n_iter 1 --n_samples 1 --plms --prompt "new born baby kitten. Hyper Detail, Octane Rendering, Unreal Engine, V-Ray"
|
||||||
|
python3 scripts/orig_scripts/txt2img.py --ddim_steps 5 --n_iter 1 --n_samples 1 --plms --prompt "ocean" # or --klms
|
||||||
|
```
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ InvokeAI root directory's `autoimport` folder.
|
|||||||
|
|
||||||
### Installation via `invokeai-model-install`
|
### Installation via `invokeai-model-install`
|
||||||
|
|
||||||
From the `invoke` launcher, choose option [4] "Download and install
|
From the `invoke` launcher, choose option [5] "Download and install
|
||||||
models." This will launch the same script that prompted you to select
|
models." This will launch the same script that prompted you to select
|
||||||
models at install time. You can use this to add models that you
|
models at install time. You can use this to add models that you
|
||||||
skipped the first time around. It is all right to specify a model that
|
skipped the first time around. It is all right to specify a model that
|
||||||
|
|||||||
@@ -59,7 +59,8 @@ Prior to installing PyPatchMatch, you need to take the following steps:
|
|||||||
`from patchmatch import patch_match`: It should look like the following:
|
`from patchmatch import patch_match`: It should look like the following:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
Python 3.10.12 (main, Jun 11 2023, 05:26:28) [GCC 11.4.0] on linux
|
Python 3.9.5 (default, Nov 23 2021, 15:27:38)
|
||||||
|
[GCC 9.3.0] on linux
|
||||||
Type "help", "copyright", "credits" or "license" for more information.
|
Type "help", "copyright", "credits" or "license" for more information.
|
||||||
>>> from patchmatch import patch_match
|
>>> from patchmatch import patch_match
|
||||||
Compiling and loading c extensions from "/home/lstein/Projects/InvokeAI/.invokeai-env/src/pypatchmatch/patchmatch".
|
Compiling and loading c extensions from "/home/lstein/Projects/InvokeAI/.invokeai-env/src/pypatchmatch/patchmatch".
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ command line, then just be sure to activate it's virtual environment.
|
|||||||
Then run the following three commands:
|
Then run the following three commands:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
pip install xformers~=0.0.22
|
pip install xformers~=0.0.19
|
||||||
pip install triton # WON'T WORK ON WINDOWS
|
pip install triton # WON'T WORK ON WINDOWS
|
||||||
python -m xformers.info output
|
python -m xformers.info output
|
||||||
```
|
```
|
||||||
@@ -42,7 +42,7 @@ If all goes well, you'll see a report like the
|
|||||||
following:
|
following:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
xFormers 0.0.22
|
xFormers 0.0.20
|
||||||
memory_efficient_attention.cutlassF: available
|
memory_efficient_attention.cutlassF: available
|
||||||
memory_efficient_attention.cutlassB: available
|
memory_efficient_attention.cutlassB: available
|
||||||
memory_efficient_attention.flshattF: available
|
memory_efficient_attention.flshattF: available
|
||||||
@@ -59,14 +59,14 @@ swiglu.gemm_fused_operand_sum: available
|
|||||||
swiglu.fused.p.cpp: available
|
swiglu.fused.p.cpp: available
|
||||||
is_triton_available: True
|
is_triton_available: True
|
||||||
is_functorch_available: False
|
is_functorch_available: False
|
||||||
pytorch.version: 2.1.0+cu121
|
pytorch.version: 2.0.1+cu118
|
||||||
pytorch.cuda: available
|
pytorch.cuda: available
|
||||||
gpu.compute_capability: 8.9
|
gpu.compute_capability: 8.9
|
||||||
gpu.name: NVIDIA GeForce RTX 4070
|
gpu.name: NVIDIA GeForce RTX 4070
|
||||||
build.info: available
|
build.info: available
|
||||||
build.cuda_version: 1108
|
build.cuda_version: 1108
|
||||||
build.python_version: 3.10.11
|
build.python_version: 3.10.11
|
||||||
build.torch_version: 2.1.0+cu121
|
build.torch_version: 2.0.1+cu118
|
||||||
build.env.TORCH_CUDA_ARCH_LIST: 5.0+PTX 6.0 6.1 7.0 7.5 8.0 8.6
|
build.env.TORCH_CUDA_ARCH_LIST: 5.0+PTX 6.0 6.1 7.0 7.5 8.0 8.6
|
||||||
build.env.XFORMERS_BUILD_TYPE: Release
|
build.env.XFORMERS_BUILD_TYPE: Release
|
||||||
build.env.XFORMERS_ENABLE_DEBUG_ASSERTIONS: None
|
build.env.XFORMERS_ENABLE_DEBUG_ASSERTIONS: None
|
||||||
@@ -92,22 +92,33 @@ installed from source. These instructions were written for a system
|
|||||||
running Ubuntu 22.04, but other Linux distributions should be able to
|
running Ubuntu 22.04, but other Linux distributions should be able to
|
||||||
adapt this recipe.
|
adapt this recipe.
|
||||||
|
|
||||||
#### 1. Install CUDA Toolkit 12.1
|
#### 1. Install CUDA Toolkit 11.8
|
||||||
|
|
||||||
You will need the CUDA developer's toolkit in order to compile and
|
You will need the CUDA developer's toolkit in order to compile and
|
||||||
install xFormers. **Do not try to install Ubuntu's nvidia-cuda-toolkit
|
install xFormers. **Do not try to install Ubuntu's nvidia-cuda-toolkit
|
||||||
package.** It is out of date and will cause conflicts among the NVIDIA
|
package.** It is out of date and will cause conflicts among the NVIDIA
|
||||||
driver and binaries. Instead install the CUDA Toolkit package provided
|
driver and binaries. Instead install the CUDA Toolkit package provided
|
||||||
by NVIDIA itself. Go to [CUDA Toolkit 12.1
|
by NVIDIA itself. Go to [CUDA Toolkit 11.8
|
||||||
Downloads](https://developer.nvidia.com/cuda-12-1-0-download-archive)
|
Downloads](https://developer.nvidia.com/cuda-11-8-0-download-archive)
|
||||||
and use the target selection wizard to choose your platform and Linux
|
and use the target selection wizard to choose your platform and Linux
|
||||||
distribution. Select an installer type of "runfile (local)" at the
|
distribution. Select an installer type of "runfile (local)" at the
|
||||||
last step.
|
last step.
|
||||||
|
|
||||||
This will provide you with a recipe for downloading and running a
|
This will provide you with a recipe for downloading and running a
|
||||||
install shell script that will install the toolkit and drivers.
|
install shell script that will install the toolkit and drivers. For
|
||||||
|
example, the install script recipe for Ubuntu 22.04 running on a
|
||||||
|
x86_64 system is:
|
||||||
|
|
||||||
#### 2. Confirm/Install pyTorch 2.1.0 with CUDA 12.1 support
|
```
|
||||||
|
wget https://developer.download.nvidia.com/compute/cuda/11.8.0/local_installers/cuda_11.8.0_520.61.05_linux.run
|
||||||
|
sudo sh cuda_11.8.0_520.61.05_linux.run
|
||||||
|
```
|
||||||
|
|
||||||
|
Rather than cut-and-paste this example, We recommend that you walk
|
||||||
|
through the toolkit wizard in order to get the most up to date
|
||||||
|
installer for your system.
|
||||||
|
|
||||||
|
#### 2. Confirm/Install pyTorch 2.01 with CUDA 11.8 support
|
||||||
|
|
||||||
If you are using InvokeAI 3.0.2 or higher, these will already be
|
If you are using InvokeAI 3.0.2 or higher, these will already be
|
||||||
installed. If not, you can check whether you have the needed libraries
|
installed. If not, you can check whether you have the needed libraries
|
||||||
@@ -122,7 +133,7 @@ Then run the command:
|
|||||||
python -c 'exec("import torch\nprint(torch.__version__)")'
|
python -c 'exec("import torch\nprint(torch.__version__)")'
|
||||||
```
|
```
|
||||||
|
|
||||||
If it prints __2.1.0+cu121__ you're good. If not, you can install the
|
If it prints __1.13.1+cu118__ you're good. If not, you can install the
|
||||||
most up to date libraries with this command:
|
most up to date libraries with this command:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ title: Manual Installation, Linux
|
|||||||
and obtaining an access token for downloading. It will then download and
|
and obtaining an access token for downloading. It will then download and
|
||||||
install the weights files for you.
|
install the weights files for you.
|
||||||
|
|
||||||
Please look [here](../020_INSTALL_MANUAL.md) for a manual process for doing
|
Please look [here](../INSTALL_MANUAL.md) for a manual process for doing
|
||||||
the same thing.
|
the same thing.
|
||||||
|
|
||||||
7. Start generating images!
|
7. Start generating images!
|
||||||
@@ -112,7 +112,7 @@ title: Manual Installation, Linux
|
|||||||
To use an alternative model you may invoke the `!switch` command in
|
To use an alternative model you may invoke the `!switch` command in
|
||||||
the CLI, or pass `--model <model_name>` during `invoke.py` launch for
|
the CLI, or pass `--model <model_name>` during `invoke.py` launch for
|
||||||
either the CLI or the Web UI. See [Command Line
|
either the CLI or the Web UI. See [Command Line
|
||||||
Client](../../deprecated/CLI.md#model-selection-and-importation). The
|
Client](../../features/CLI.md#model-selection-and-importation). The
|
||||||
model names are defined in `configs/models.yaml`.
|
model names are defined in `configs/models.yaml`.
|
||||||
|
|
||||||
8. Subsequently, to relaunch the script, be sure to run "conda activate
|
8. Subsequently, to relaunch the script, be sure to run "conda activate
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ will do our best to help.
|
|||||||
To use an alternative model you may invoke the `!switch` command in
|
To use an alternative model you may invoke the `!switch` command in
|
||||||
the CLI, or pass `--model <model_name>` during `invoke.py` launch for
|
the CLI, or pass `--model <model_name>` during `invoke.py` launch for
|
||||||
either the CLI or the Web UI. See [Command Line
|
either the CLI or the Web UI. See [Command Line
|
||||||
Client](../../deprecated/CLI.md#model-selection-and-importation). The
|
Client](../../features/CLI.md#model-selection-and-importation). The
|
||||||
model names are defined in `configs/models.yaml`.
|
model names are defined in `configs/models.yaml`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ python scripts/invoke.py --web --max_load_models=3 \
|
|||||||
```
|
```
|
||||||
|
|
||||||
These options are described in detail in the
|
These options are described in detail in the
|
||||||
[Command-Line Interface](../../deprecated/CLI.md) documentation.
|
[Command-Line Interface](../../features/CLI.md) documentation.
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ Note that you will need NVIDIA drivers, Python 3.10, and Git installed beforehan
|
|||||||
obtaining an access token for downloading. It will then download and install the
|
obtaining an access token for downloading. It will then download and install the
|
||||||
weights files for you.
|
weights files for you.
|
||||||
|
|
||||||
Please look [here](../020_INSTALL_MANUAL.md) for a manual process for doing the
|
Please look [here](../INSTALL_MANUAL.md) for a manual process for doing the
|
||||||
same thing.
|
same thing.
|
||||||
|
|
||||||
8. Start generating images!
|
8. Start generating images!
|
||||||
@@ -108,7 +108,7 @@ Note that you will need NVIDIA drivers, Python 3.10, and Git installed beforehan
|
|||||||
To use an alternative model you may invoke the `!switch` command in
|
To use an alternative model you may invoke the `!switch` command in
|
||||||
the CLI, or pass `--model <model_name>` during `invoke.py` launch for
|
the CLI, or pass `--model <model_name>` during `invoke.py` launch for
|
||||||
either the CLI or the Web UI. See [Command Line
|
either the CLI or the Web UI. See [Command Line
|
||||||
Client](../../deprecated/CLI.md#model-selection-and-importation). The
|
Client](../../features/CLI.md#model-selection-and-importation). The
|
||||||
model names are defined in `configs/models.yaml`.
|
model names are defined in `configs/models.yaml`.
|
||||||
|
|
||||||
9. Subsequently, to relaunch the script, first activate the Anaconda
|
9. Subsequently, to relaunch the script, first activate the Anaconda
|
||||||
|
|||||||
@@ -4,16 +4,11 @@ These are nodes that have been developed by the community, for the community. If
|
|||||||
|
|
||||||
If you'd like to submit a node for the community, please refer to the [node creation overview](contributingNodes.md).
|
If you'd like to submit a node for the community, please refer to the [node creation overview](contributingNodes.md).
|
||||||
|
|
||||||
To use a node, add the node to the `nodes` folder found in your InvokeAI install location.
|
To download a node, simply download the `.py` node file from the link and add it to the `invokeai/app/invocations` folder in your Invoke AI install location. If you used the automated installation, this can be found inside the `.venv` folder. Along with the node, an example node graph should be provided to help you get started with the node.
|
||||||
|
|
||||||
The suggested method is to use `git clone` to clone the repository the node is found in. This allows for easy updates of the node in the future.
|
|
||||||
|
|
||||||
If you'd prefer, you can also just download the whole node folder from the linked repository and add it to the `nodes` folder.
|
|
||||||
|
|
||||||
To use a community workflow, download the the `.json` node graph file and load it into Invoke AI via the **Load Workflow** button in the Workflow Editor.
|
To use a community workflow, download the the `.json` node graph file and load it into Invoke AI via the **Load Workflow** button in the Workflow Editor.
|
||||||
|
|
||||||
- Community Nodes
|
- Community Nodes
|
||||||
+ [Average Images](#average-images)
|
|
||||||
+ [Depth Map from Wavefront OBJ](#depth-map-from-wavefront-obj)
|
+ [Depth Map from Wavefront OBJ](#depth-map-from-wavefront-obj)
|
||||||
+ [Film Grain](#film-grain)
|
+ [Film Grain](#film-grain)
|
||||||
+ [Generative Grammar-Based Prompt Nodes](#generative-grammar-based-prompt-nodes)
|
+ [Generative Grammar-Based Prompt Nodes](#generative-grammar-based-prompt-nodes)
|
||||||
@@ -26,28 +21,18 @@ To use a community workflow, download the the `.json` node graph file and load i
|
|||||||
+ [Image Picker](#image-picker)
|
+ [Image Picker](#image-picker)
|
||||||
+ [Load Video Frame](#load-video-frame)
|
+ [Load Video Frame](#load-video-frame)
|
||||||
+ [Make 3D](#make-3d)
|
+ [Make 3D](#make-3d)
|
||||||
+ [Match Histogram](#match-histogram)
|
|
||||||
+ [Oobabooga](#oobabooga)
|
+ [Oobabooga](#oobabooga)
|
||||||
+ [Prompt Tools](#prompt-tools)
|
+ [Prompt Tools](#prompt-tools)
|
||||||
+ [Remote Image](#remote-image)
|
|
||||||
+ [Retroize](#retroize)
|
+ [Retroize](#retroize)
|
||||||
+ [Size Stepper Nodes](#size-stepper-nodes)
|
+ [Size Stepper Nodes](#size-stepper-nodes)
|
||||||
+ [Text font to Image](#text-font-to-image)
|
+ [Text font to Image](#text-font-to-image)
|
||||||
+ [Thresholding](#thresholding)
|
+ [Thresholding](#thresholding)
|
||||||
+ [Unsharp Mask](#unsharp-mask)
|
|
||||||
+ [XY Image to Grid and Images to Grids nodes](#xy-image-to-grid-and-images-to-grids-nodes)
|
+ [XY Image to Grid and Images to Grids nodes](#xy-image-to-grid-and-images-to-grids-nodes)
|
||||||
- [Example Node Template](#example-node-template)
|
- [Example Node Template](#example-node-template)
|
||||||
- [Disclaimer](#disclaimer)
|
- [Disclaimer](#disclaimer)
|
||||||
- [Help](#help)
|
- [Help](#help)
|
||||||
|
|
||||||
|
|
||||||
--------------------------------
|
|
||||||
### Average Images
|
|
||||||
|
|
||||||
**Description:** This node takes in a collection of images of the same size and averages them as output. It converts everything to RGB mode first.
|
|
||||||
|
|
||||||
**Node Link:** https://github.com/JPPhoto/average-images-node
|
|
||||||
|
|
||||||
--------------------------------
|
--------------------------------
|
||||||
### Depth Map from Wavefront OBJ
|
### Depth Map from Wavefront OBJ
|
||||||
|
|
||||||
@@ -192,8 +177,12 @@ This includes 15 Nodes:
|
|||||||
|
|
||||||
**Node Link:** https://github.com/helix4u/load_video_frame
|
**Node Link:** https://github.com/helix4u/load_video_frame
|
||||||
|
|
||||||
|
**Example Node Graph:** https://github.com/helix4u/load_video_frame/blob/main/Example_Workflow.json
|
||||||
|
|
||||||
**Output Example:**
|
**Output Example:**
|
||||||
<img src="https://raw.githubusercontent.com/helix4u/load_video_frame/main/_git_assets/testmp4_embed_converted.gif" width="500" />
|
|
||||||
|
<img src="https://github.com/helix4u/load_video_frame/blob/main/testmp4_embed_converted.gif" width="500" />
|
||||||
|
[Full mp4 of Example Output test.mp4](https://github.com/helix4u/load_video_frame/blob/main/test.mp4)
|
||||||
|
|
||||||
--------------------------------
|
--------------------------------
|
||||||
### Make 3D
|
### Make 3D
|
||||||
@@ -209,23 +198,6 @@ This includes 15 Nodes:
|
|||||||
<img src="https://gitlab.com/srcrr/shift3d/-/raw/main/example-1.png" width="300" />
|
<img src="https://gitlab.com/srcrr/shift3d/-/raw/main/example-1.png" width="300" />
|
||||||
<img src="https://gitlab.com/srcrr/shift3d/-/raw/main/example-2.png" width="300" />
|
<img src="https://gitlab.com/srcrr/shift3d/-/raw/main/example-2.png" width="300" />
|
||||||
|
|
||||||
--------------------------------
|
|
||||||
### Match Histogram
|
|
||||||
|
|
||||||
**Description:** An InvokeAI node to match a histogram from one image to another. This is a bit like the `color correct` node in the main InvokeAI but this works in the YCbCr colourspace and can handle images of different sizes. Also does not require a mask input.
|
|
||||||
- Option to only transfer luminance channel.
|
|
||||||
- Option to save output as grayscale
|
|
||||||
|
|
||||||
A good use case for this node is to normalize the colors of an image that has been through the tiled scaling workflow of my XYGrid Nodes.
|
|
||||||
|
|
||||||
See full docs here: https://github.com/skunkworxdark/Prompt-tools-nodes/edit/main/README.md
|
|
||||||
|
|
||||||
**Node Link:** https://github.com/skunkworxdark/match_histogram
|
|
||||||
|
|
||||||
**Output Examples**
|
|
||||||
|
|
||||||
<img src="https://github.com/skunkworxdark/match_histogram/assets/21961335/ed12f329-a0ef-444a-9bae-129ed60d6097" width="300" />
|
|
||||||
|
|
||||||
--------------------------------
|
--------------------------------
|
||||||
### Oobabooga
|
### Oobabooga
|
||||||
|
|
||||||
@@ -255,41 +227,22 @@ This node works best with SDXL models, especially as the style can be described
|
|||||||
--------------------------------
|
--------------------------------
|
||||||
### Prompt Tools
|
### Prompt Tools
|
||||||
|
|
||||||
**Description:** A set of InvokeAI nodes that add general prompt (string) manipulation tools. Designed to accompany the `Prompts From File` node and other prompt generation nodes.
|
**Description:** A set of InvokeAI nodes that add general prompt manipulation tools. These were written to accompany the PromptsFromFile node and other prompt generation nodes.
|
||||||
|
|
||||||
1. `Prompt To File` - saves a prompt or collection of prompts to a file. one per line. There is an append/overwrite option.
|
|
||||||
2. `PTFields Collect` - Converts image generation fields into a Json format string that can be passed to Prompt to file.
|
|
||||||
3. `PTFields Expand` - Takes Json string and converts it to individual generation parameters. This can be fed from the Prompts to file node.
|
|
||||||
4. `Prompt Strength` - Formats prompt with strength like the weighted format of compel
|
|
||||||
5. `Prompt Strength Combine` - Combines weighted prompts for .and()/.blend()
|
|
||||||
6. `CSV To Index String` - Gets a string from a CSV by index. Includes a Random index option
|
|
||||||
|
|
||||||
The following Nodes are now included in v3.2 of Invoke and are nolonger in this set of tools.<br>
|
|
||||||
- `Prompt Join` -> `String Join`
|
|
||||||
- `Prompt Join Three` -> `String Join Three`
|
|
||||||
- `Prompt Replace` -> `String Replace`
|
|
||||||
- `Prompt Split Neg` -> `String Split Neg`
|
|
||||||
|
|
||||||
|
1. PromptJoin - Joins to prompts into one.
|
||||||
|
2. PromptReplace - performs a search and replace on a prompt. With the option of using regex.
|
||||||
|
3. PromptSplitNeg - splits a prompt into positive and negative using the old V2 method of [] for negative.
|
||||||
|
4. PromptToFile - saves a prompt or collection of prompts to a file. one per line. There is an append/overwrite option.
|
||||||
|
5. PTFieldsCollect - Converts image generation fields into a Json format string that can be passed to Prompt to file.
|
||||||
|
6. PTFieldsExpand - Takes Json string and converts it to individual generation parameters This can be fed from the Prompts to file node.
|
||||||
|
7. PromptJoinThree - Joins 3 prompt together.
|
||||||
|
8. PromptStrength - This take a string and float and outputs another string in the format of (string)strength like the weighted format of compel.
|
||||||
|
9. PromptStrengthCombine - This takes a collection of prompt strength strings and outputs a string in the .and() or .blend() format that can be fed into a proper prompt node.
|
||||||
|
|
||||||
See full docs here: https://github.com/skunkworxdark/Prompt-tools-nodes/edit/main/README.md
|
See full docs here: https://github.com/skunkworxdark/Prompt-tools-nodes/edit/main/README.md
|
||||||
|
|
||||||
**Node Link:** https://github.com/skunkworxdark/Prompt-tools-nodes
|
**Node Link:** https://github.com/skunkworxdark/Prompt-tools-nodes
|
||||||
|
|
||||||
**Workflow Examples**
|
|
||||||
|
|
||||||
<img src="https://github.com/skunkworxdark/prompt-tools/blob/main/images/CSVToIndexStringNode.png" width="300" />
|
|
||||||
|
|
||||||
--------------------------------
|
|
||||||
### Remote Image
|
|
||||||
|
|
||||||
**Description:** This is a pack of nodes to interoperate with other services, be they public websites or bespoke local servers. The pack consists of these nodes:
|
|
||||||
|
|
||||||
- *Load Remote Image* - Lets you load remote images such as a realtime webcam image, an image of the day, or dynamically created images.
|
|
||||||
- *Post Image to Remote Server* - Lets you upload an image to a remote server using an HTTP POST request, eg for storage, display or further processing.
|
|
||||||
|
|
||||||
**Node Link:** https://github.com/fieldOfView/InvokeAI-remote_image
|
|
||||||
|
|
||||||
|
|
||||||
--------------------------------
|
--------------------------------
|
||||||
### Retroize
|
### Retroize
|
||||||
|
|
||||||
@@ -355,45 +308,26 @@ Highlights/Midtones/Shadows (with LUT blur enabled):
|
|||||||
<img src="https://github.com/invoke-ai/InvokeAI/assets/34005131/0a440e43-697f-4d17-82ee-f287467df0a5" width="300" />
|
<img src="https://github.com/invoke-ai/InvokeAI/assets/34005131/0a440e43-697f-4d17-82ee-f287467df0a5" width="300" />
|
||||||
<img src="https://github.com/invoke-ai/InvokeAI/assets/34005131/0701fd0f-2ca7-4fe2-8613-2b52547bafce" width="300" />
|
<img src="https://github.com/invoke-ai/InvokeAI/assets/34005131/0701fd0f-2ca7-4fe2-8613-2b52547bafce" width="300" />
|
||||||
|
|
||||||
--------------------------------
|
|
||||||
### Unsharp Mask
|
|
||||||
|
|
||||||
**Description:** Applies an unsharp mask filter to an image, preserving its alpha channel in the process.
|
|
||||||
|
|
||||||
**Node Link:** https://github.com/JPPhoto/unsharp-mask-node
|
|
||||||
|
|
||||||
--------------------------------
|
--------------------------------
|
||||||
### XY Image to Grid and Images to Grids nodes
|
### XY Image to Grid and Images to Grids nodes
|
||||||
|
|
||||||
**Description:** These nodes add the following to InvokeAI:
|
**Description:** Image to grid nodes and supporting tools.
|
||||||
- Generate grids of images from multiple input images
|
|
||||||
- Create XY grid images with labels from parameters
|
|
||||||
- Split images into overlapping tiles for processing (for super-resolution workflows)
|
|
||||||
- Recombine image tiles into a single output image blending the seams
|
|
||||||
|
|
||||||
The nodes include:
|
1. "Images To Grids" node - Takes a collection of images and creates a grid(s) of images. If there are more images than the size of a single grid then multiple grids will be created until it runs out of images.
|
||||||
1. `Images To Grids` - Combine multiple images into a grid of images
|
2. "XYImage To Grid" node - Converts a collection of XYImages into a labeled Grid of images. The XYImages collection has to be built using the supporting nodes. See example node setups for more details.
|
||||||
2. `XYImage To Grid` - Take X & Y params and creates a labeled image grid.
|
|
||||||
3. `XYImage Tiles` - Super-resolution (embiggen) style tiled resizing
|
|
||||||
4. `Image Tot XYImages` - Takes an image and cuts it up into a number of columns and rows.
|
|
||||||
5. Multiple supporting nodes - Helper nodes for data wrangling and building `XYImage` collections
|
|
||||||
|
|
||||||
See full docs here: https://github.com/skunkworxdark/XYGrid_nodes/edit/main/README.md
|
See full docs here: https://github.com/skunkworxdark/XYGrid_nodes/edit/main/README.md
|
||||||
|
|
||||||
**Node Link:** https://github.com/skunkworxdark/XYGrid_nodes
|
**Node Link:** https://github.com/skunkworxdark/XYGrid_nodes
|
||||||
|
|
||||||
**Output Examples**
|
|
||||||
|
|
||||||
<img src="https://github.com/skunkworxdark/XYGrid_nodes/blob/main/images/collage.png" width="300" />
|
|
||||||
|
|
||||||
--------------------------------
|
--------------------------------
|
||||||
### Example Node Template
|
### Example Node Template
|
||||||
|
|
||||||
**Description:** This node allows you to do super cool things with InvokeAI.
|
**Description:** This node allows you to do super cool things with InvokeAI.
|
||||||
|
|
||||||
**Node Link:** https://github.com/invoke-ai/InvokeAI/blob/main/invokeai/app/invocations/prompt.py
|
**Node Link:** https://github.com/invoke-ai/InvokeAI/fake_node.py
|
||||||
|
|
||||||
**Example Workflow:** https://github.com/invoke-ai/InvokeAI/blob/docs/main/docs/workflows/Prompt_from_File.json
|
**Example Node Graph:** https://github.com/invoke-ai/InvokeAI/fake_node_graph.json
|
||||||
|
|
||||||
**Output Examples**
|
**Output Examples**
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ To learn about the specifics of creating a new node, please visit our [Node crea
|
|||||||
|
|
||||||
Once you’ve created a node and confirmed that it behaves as expected locally, follow these steps:
|
Once you’ve created a node and confirmed that it behaves as expected locally, follow these steps:
|
||||||
|
|
||||||
- Make sure the node is contained in a new Python (.py) file. Preferably, the node is in a repo with a README detailing the nodes usage & examples to help others more easily use your node. Including the tag "invokeai-node" in your repository's README can also help other users find it more easily.
|
- Make sure the node is contained in a new Python (.py) file. Preferrably, the node is in a repo with a README detaling the nodes usage & examples to help others more easily use your node.
|
||||||
- Submit a pull request with a link to your node(s) repo in GitHub against the `main` branch to add the node to the [Community Nodes](communityNodes.md) list
|
- Submit a pull request with a link to your node(s) repo in GitHub against the `main` branch to add the node to the [Community Nodes](communityNodes.md) list
|
||||||
- Make sure you are following the template below and have provided all relevant details about the node and what it does. Example output images and workflows are very helpful for other users looking to use your node.
|
- Make sure you are following the template below and have provided all relevant details about the node and what it does. Example output images and workflows are very helpful for other users looking to use your node.
|
||||||
- A maintainer will review the pull request and node. If the node is aligned with the direction of the project, you may be asked for permission to include it in the core project.
|
- A maintainer will review the pull request and node. If the node is aligned with the direction of the project, you may be asked for permission to include it in the core project.
|
||||||
|
|||||||
@@ -1,106 +1,104 @@
|
|||||||
# List of Default Nodes
|
# List of Default Nodes
|
||||||
|
|
||||||
The table below contains a list of the default nodes shipped with InvokeAI and
|
The table below contains a list of the default nodes shipped with InvokeAI and their descriptions.
|
||||||
their descriptions.
|
|
||||||
|
|
||||||
| Node <img width=160 align="right"> | Function |
|
| Node <img width=160 align="right"> | Function |
|
||||||
| :------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|: ---------------------------------- | :--------------------------------------------------------------------------------------|
|
||||||
| Add Integers | Adds two numbers |
|
|Add Integers | Adds two numbers|
|
||||||
| Boolean Primitive Collection | A collection of boolean primitive values |
|
|Boolean Primitive Collection | A collection of boolean primitive values|
|
||||||
| Boolean Primitive | A boolean primitive value |
|
|Boolean Primitive | A boolean primitive value|
|
||||||
| Canny Processor | Canny edge detection for ControlNet |
|
|Canny Processor | Canny edge detection for ControlNet|
|
||||||
| CenterPadCrop | Pad or crop an image's sides from the center by specified pixels. Positive values are outside of the image. |
|
|CLIP Skip | Skip layers in clip text_encoder model.|
|
||||||
| CLIP Skip | Skip layers in clip text_encoder model. |
|
|Collect | Collects values into a collection|
|
||||||
| Collect | Collects values into a collection |
|
|Color Correct | Shifts the colors of a target image to match the reference image, optionally using a mask to only color-correct certain regions of the target image.|
|
||||||
| Color Correct | Shifts the colors of a target image to match the reference image, optionally using a mask to only color-correct certain regions of the target image. |
|
|Color Primitive | A color primitive value|
|
||||||
| Color Primitive | A color primitive value |
|
|Compel Prompt | Parse prompt using compel package to conditioning.|
|
||||||
| Compel Prompt | Parse prompt using compel package to conditioning. |
|
|Conditioning Primitive Collection | A collection of conditioning tensor primitive values|
|
||||||
| Conditioning Primitive Collection | A collection of conditioning tensor primitive values |
|
|Conditioning Primitive | A conditioning tensor primitive value|
|
||||||
| Conditioning Primitive | A conditioning tensor primitive value |
|
|Content Shuffle Processor | Applies content shuffle processing to image|
|
||||||
| Content Shuffle Processor | Applies content shuffle processing to image |
|
|ControlNet | Collects ControlNet info to pass to other nodes|
|
||||||
| ControlNet | Collects ControlNet info to pass to other nodes |
|
|Denoise Latents | Denoises noisy latents to decodable images|
|
||||||
| Denoise Latents | Denoises noisy latents to decodable images |
|
|Divide Integers | Divides two numbers|
|
||||||
| Divide Integers | Divides two numbers |
|
|Dynamic Prompt | Parses a prompt using adieyal/dynamicprompts' random or combinatorial generator|
|
||||||
| Dynamic Prompt | Parses a prompt using adieyal/dynamicprompts' random or combinatorial generator |
|
|[FaceMask](./detailedNodes/faceTools.md#facemask) | Generates masks for faces in an image to use with Inpainting|
|
||||||
| [FaceMask](./detailedNodes/faceTools.md#facemask) | Generates masks for faces in an image to use with Inpainting |
|
|[FaceIdentifier](./detailedNodes/faceTools.md#faceidentifier) | Identifies and labels faces in an image|
|
||||||
| [FaceIdentifier](./detailedNodes/faceTools.md#faceidentifier) | Identifies and labels faces in an image |
|
|[FaceOff](./detailedNodes/faceTools.md#faceoff) | Creates a new image that is a scaled bounding box with a mask on the face for Inpainting|
|
||||||
| [FaceOff](./detailedNodes/faceTools.md#faceoff) | Creates a new image that is a scaled bounding box with a mask on the face for Inpainting |
|
|Float Math | Perform basic math operations on two floats|
|
||||||
| Float Math | Perform basic math operations on two floats |
|
|Float Primitive Collection | A collection of float primitive values|
|
||||||
| Float Primitive Collection | A collection of float primitive values |
|
|Float Primitive | A float primitive value|
|
||||||
| Float Primitive | A float primitive value |
|
|Float Range | Creates a range|
|
||||||
| Float Range | Creates a range |
|
|HED (softedge) Processor | Applies HED edge detection to image|
|
||||||
| HED (softedge) Processor | Applies HED edge detection to image |
|
|Blur Image | Blurs an image|
|
||||||
| Blur Image | Blurs an image |
|
|Extract Image Channel | Gets a channel from an image.|
|
||||||
| Extract Image Channel | Gets a channel from an image. |
|
|Image Primitive Collection | A collection of image primitive values|
|
||||||
| Image Primitive Collection | A collection of image primitive values |
|
|Integer Math | Perform basic math operations on two integers|
|
||||||
| Integer Math | Perform basic math operations on two integers |
|
|Convert Image Mode | Converts an image to a different mode.|
|
||||||
| Convert Image Mode | Converts an image to a different mode. |
|
|Crop Image | Crops an image to a specified box. The box can be outside of the image.|
|
||||||
| Crop Image | Crops an image to a specified box. The box can be outside of the image. |
|
|Image Hue Adjustment | Adjusts the Hue of an image.|
|
||||||
| Image Hue Adjustment | Adjusts the Hue of an image. |
|
|Inverse Lerp Image | Inverse linear interpolation of all pixels of an image|
|
||||||
| Inverse Lerp Image | Inverse linear interpolation of all pixels of an image |
|
|Image Primitive | An image primitive value|
|
||||||
| Image Primitive | An image primitive value |
|
|Lerp Image | Linear interpolation of all pixels of an image|
|
||||||
| Lerp Image | Linear interpolation of all pixels of an image |
|
|Offset Image Channel | Add to or subtract from an image color channel by a uniform value.|
|
||||||
| Offset Image Channel | Add to or subtract from an image color channel by a uniform value. |
|
|Multiply Image Channel | Multiply or Invert an image color channel by a scalar value.|
|
||||||
| Multiply Image Channel | Multiply or Invert an image color channel by a scalar value. |
|
|Multiply Images | Multiplies two images together using `PIL.ImageChops.multiply()`.|
|
||||||
| Multiply Images | Multiplies two images together using `PIL.ImageChops.multiply()`. |
|
|Blur NSFW Image | Add blur to NSFW-flagged images|
|
||||||
| Blur NSFW Image | Add blur to NSFW-flagged images |
|
|Paste Image | Pastes an image into another image.|
|
||||||
| Paste Image | Pastes an image into another image. |
|
|ImageProcessor | Base class for invocations that preprocess images for ControlNet|
|
||||||
| ImageProcessor | Base class for invocations that preprocess images for ControlNet |
|
|Resize Image | Resizes an image to specific dimensions|
|
||||||
| Resize Image | Resizes an image to specific dimensions |
|
|Round Float | Rounds a float to a specified number of decimal places|
|
||||||
| Round Float | Rounds a float to a specified number of decimal places |
|
|Float to Integer | Converts a float to an integer. Optionally rounds to an even multiple of a input number.|
|
||||||
| Float to Integer | Converts a float to an integer. Optionally rounds to an even multiple of a input number. |
|
|Scale Image | Scales an image by a factor|
|
||||||
| Scale Image | Scales an image by a factor |
|
|Image to Latents | Encodes an image into latents.|
|
||||||
| Image to Latents | Encodes an image into latents. |
|
|Add Invisible Watermark | Add an invisible watermark to an image|
|
||||||
| Add Invisible Watermark | Add an invisible watermark to an image |
|
|Solid Color Infill | Infills transparent areas of an image with a solid color|
|
||||||
| Solid Color Infill | Infills transparent areas of an image with a solid color |
|
|PatchMatch Infill | Infills transparent areas of an image using the PatchMatch algorithm|
|
||||||
| PatchMatch Infill | Infills transparent areas of an image using the PatchMatch algorithm |
|
|Tile Infill | Infills transparent areas of an image with tiles of the image|
|
||||||
| Tile Infill | Infills transparent areas of an image with tiles of the image |
|
|Integer Primitive Collection | A collection of integer primitive values|
|
||||||
| Integer Primitive Collection | A collection of integer primitive values |
|
|Integer Primitive | An integer primitive value|
|
||||||
| Integer Primitive | An integer primitive value |
|
|Iterate | Iterates over a list of items|
|
||||||
| Iterate | Iterates over a list of items |
|
|Latents Primitive Collection | A collection of latents tensor primitive values|
|
||||||
| Latents Primitive Collection | A collection of latents tensor primitive values |
|
|Latents Primitive | A latents tensor primitive value|
|
||||||
| Latents Primitive | A latents tensor primitive value |
|
|Latents to Image | Generates an image from latents.|
|
||||||
| Latents to Image | Generates an image from latents. |
|
|Leres (Depth) Processor | Applies leres processing to image|
|
||||||
| Leres (Depth) Processor | Applies leres processing to image |
|
|Lineart Anime Processor | Applies line art anime processing to image|
|
||||||
| Lineart Anime Processor | Applies line art anime processing to image |
|
|Lineart Processor | Applies line art processing to image|
|
||||||
| Lineart Processor | Applies line art processing to image |
|
|LoRA Loader | Apply selected lora to unet and text_encoder.|
|
||||||
| LoRA Loader | Apply selected lora to unet and text_encoder. |
|
|Main Model Loader | Loads a main model, outputting its submodels.|
|
||||||
| Main Model Loader | Loads a main model, outputting its submodels. |
|
|Combine Mask | Combine two masks together by multiplying them using `PIL.ImageChops.multiply()`.|
|
||||||
| Combine Mask | Combine two masks together by multiplying them using `PIL.ImageChops.multiply()`. |
|
|Mask Edge | Applies an edge mask to an image|
|
||||||
| Mask Edge | Applies an edge mask to an image |
|
|Mask from Alpha | Extracts the alpha channel of an image as a mask.|
|
||||||
| Mask from Alpha | Extracts the alpha channel of an image as a mask. |
|
|Mediapipe Face Processor | Applies mediapipe face processing to image|
|
||||||
| Mediapipe Face Processor | Applies mediapipe face processing to image |
|
|Midas (Depth) Processor | Applies Midas depth processing to image|
|
||||||
| Midas (Depth) Processor | Applies Midas depth processing to image |
|
|MLSD Processor | Applies MLSD processing to image|
|
||||||
| MLSD Processor | Applies MLSD processing to image |
|
|Multiply Integers | Multiplies two numbers|
|
||||||
| Multiply Integers | Multiplies two numbers |
|
|Noise | Generates latent noise.|
|
||||||
| Noise | Generates latent noise. |
|
|Normal BAE Processor | Applies NormalBae processing to image|
|
||||||
| Normal BAE Processor | Applies NormalBae processing to image |
|
|ONNX Latents to Image | Generates an image from latents.|
|
||||||
| ONNX Latents to Image | Generates an image from latents. |
|
|ONNX Prompt (Raw) | A node to process inputs and produce outputs. May use dependency injection in __init__ to receive providers.|
|
||||||
| ONNX Prompt (Raw) | A node to process inputs and produce outputs. May use dependency injection in **init** to receive providers. |
|
|ONNX Text to Latents | Generates latents from conditionings.|
|
||||||
| ONNX Text to Latents | Generates latents from conditionings. |
|
|ONNX Model Loader | Loads a main model, outputting its submodels.|
|
||||||
| ONNX Model Loader | Loads a main model, outputting its submodels. |
|
|OpenCV Inpaint | Simple inpaint using opencv.|
|
||||||
| OpenCV Inpaint | Simple inpaint using opencv. |
|
|Openpose Processor | Applies Openpose processing to image|
|
||||||
| Openpose Processor | Applies Openpose processing to image |
|
|PIDI Processor | Applies PIDI processing to image|
|
||||||
| PIDI Processor | Applies PIDI processing to image |
|
|Prompts from File | Loads prompts from a text file|
|
||||||
| Prompts from File | Loads prompts from a text file |
|
|Random Integer | Outputs a single random integer.|
|
||||||
| Random Integer | Outputs a single random integer. |
|
|Random Range | Creates a collection of random numbers|
|
||||||
| Random Range | Creates a collection of random numbers |
|
|Integer Range | Creates a range of numbers from start to stop with step|
|
||||||
| Integer Range | Creates a range of numbers from start to stop with step |
|
|Integer Range of Size | Creates a range from start to start + size with step|
|
||||||
| Integer Range of Size | Creates a range from start to start + size with step |
|
|Resize Latents | Resizes latents to explicit width/height (in pixels). Provided dimensions are floor-divided by 8.|
|
||||||
| Resize Latents | Resizes latents to explicit width/height (in pixels). Provided dimensions are floor-divided by 8. |
|
|SDXL Compel Prompt | Parse prompt using compel package to conditioning.|
|
||||||
| SDXL Compel Prompt | Parse prompt using compel package to conditioning. |
|
|SDXL LoRA Loader | Apply selected lora to unet and text_encoder.|
|
||||||
| SDXL LoRA Loader | Apply selected lora to unet and text_encoder. |
|
|SDXL Main Model Loader | Loads an sdxl base model, outputting its submodels.|
|
||||||
| SDXL Main Model Loader | Loads an sdxl base model, outputting its submodels. |
|
|SDXL Refiner Compel Prompt | Parse prompt using compel package to conditioning.|
|
||||||
| SDXL Refiner Compel Prompt | Parse prompt using compel package to conditioning. |
|
|SDXL Refiner Model Loader | Loads an sdxl refiner model, outputting its submodels.|
|
||||||
| SDXL Refiner Model Loader | Loads an sdxl refiner model, outputting its submodels. |
|
|Scale Latents | Scales latents by a given factor.|
|
||||||
| Scale Latents | Scales latents by a given factor. |
|
|Segment Anything Processor | Applies segment anything processing to image|
|
||||||
| Segment Anything Processor | Applies segment anything processing to image |
|
|Show Image | Displays a provided image, and passes it forward in the pipeline.|
|
||||||
| Show Image | Displays a provided image, and passes it forward in the pipeline. |
|
|Step Param Easing | Experimental per-step parameter easing for denoising steps|
|
||||||
| Step Param Easing | Experimental per-step parameter easing for denoising steps |
|
|String Primitive Collection | A collection of string primitive values|
|
||||||
| String Primitive Collection | A collection of string primitive values |
|
|String Primitive | A string primitive value|
|
||||||
| String Primitive | A string primitive value |
|
|Subtract Integers | Subtracts two numbers|
|
||||||
| Subtract Integers | Subtracts two numbers |
|
|Tile Resample Processor | Tile resampler processor|
|
||||||
| Tile Resample Processor | Tile resampler processor |
|
|Upscale (RealESRGAN) | Upscales an image using RealESRGAN.|
|
||||||
| Upscale (RealESRGAN) | Upscales an image using RealESRGAN. |
|
|VAE Loader | Loads a VAE model, outputting a VaeLoaderOutput|
|
||||||
| VAE Loader | Loads a VAE model, outputting a VaeLoaderOutput |
|
|Zoe (Depth) Processor | Applies Zoe depth processing to image|
|
||||||
| Zoe (Depth) Processor | Applies Zoe depth processing to image |
|
|
||||||
@@ -2,17 +2,13 @@
|
|||||||
|
|
||||||
We've curated some example workflows for you to get started with Workflows in InvokeAI
|
We've curated some example workflows for you to get started with Workflows in InvokeAI
|
||||||
|
|
||||||
To use them, right click on your desired workflow, follow the link to GitHub and click the "⬇" button to download the raw file. You can then use the "Load Workflow" functionality in InvokeAI to load the workflow and start generating images!
|
To use them, right click on your desired workflow, press "Download Linked File". You can then use the "Load Workflow" functionality in InvokeAI to load the workflow and start generating images!
|
||||||
|
|
||||||
If you're interested in finding more workflows, checkout the [#share-your-workflows](https://discord.com/channels/1020123559063990373/1130291608097661000) channel in the InvokeAI Discord.
|
If you're interested in finding more workflows, checkout the [#share-your-workflows](https://discord.com/channels/1020123559063990373/1130291608097661000) channel in the InvokeAI Discord.
|
||||||
|
|
||||||
* [SD1.5 / SD2 Text to Image](https://github.com/invoke-ai/InvokeAI/blob/main/docs/workflows/Text_to_Image.json)
|
* [SD1.5 / SD2 Text to Image](https://github.com/invoke-ai/InvokeAI/blob/main/docs/workflows/Text_to_Image.json)
|
||||||
* [SDXL Text to Image](https://github.com/invoke-ai/InvokeAI/blob/main/docs/workflows/SDXL_Text_to_Image.json)
|
* [SDXL Text to Image](https://github.com/invoke-ai/InvokeAI/blob/main/docs/workflows/SDXL_Text_to_Image.json)
|
||||||
* [SDXL Text to Image with Refiner](https://github.com/invoke-ai/InvokeAI/blob/main/docs/workflows/SDXL_w_Refiner_Text_to_Image.json)
|
* [SDXL (with Refiner) Text to Image](https://github.com/invoke-ai/InvokeAI/blob/main/docs/workflows/SDXL_Text_to_Image.json)
|
||||||
* [Multi ControlNet (Canny & Depth)](https://github.com/invoke-ai/InvokeAI/blob/main/docs/workflows/Multi_ControlNet_Canny_and_Depth.json)
|
* [Tiled Upscaling with ControlNet](https://github.com/invoke-ai/InvokeAI/blob/main/docs/workflows/ESRGAN_img2img_upscale w_Canny_ControlNet.json)
|
||||||
* [Tiled Upscaling with ControlNet](https://github.com/invoke-ai/InvokeAI/blob/main/docs/workflows/ESRGAN_img2img_upscale_w_Canny_ControlNet.json)
|
|
||||||
* [Prompt From File](https://github.com/invoke-ai/InvokeAI/blob/main/docs/workflows/Prompt_from_File.json)
|
|
||||||
* [Face Detailer with IP-Adapter & ControlNet](https://github.com/invoke-ai/InvokeAI/blob/main/docs/workflows/Face_Detailer_with_IP-Adapter_and_Canny.json)
|
|
||||||
* [FaceMask](https://github.com/invoke-ai/InvokeAI/blob/main/docs/workflows/FaceMask.json)
|
* [FaceMask](https://github.com/invoke-ai/InvokeAI/blob/main/docs/workflows/FaceMask.json)
|
||||||
* [FaceOff with 2x Face Scaling](https://github.com/invoke-ai/InvokeAI/blob/main/docs/workflows/FaceOff_FaceScale2x.json)
|
* [FaceOff with 2x Face Scaling](https://github.com/invoke-ai/InvokeAI/blob/main/docs/workflows/FaceOff_FaceScale2x.json)
|
||||||
* [QR Code Monster](https://github.com/invoke-ai/InvokeAI/blob/main/docs/workflows/QR_Code_Monster.json)
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,985 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Multi ControlNet (Canny & Depth)",
|
|
||||||
"author": "Millu",
|
|
||||||
"description": "A sample workflow using canny & depth ControlNets to guide the generation process. ",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"contact": "millun@invoke.ai",
|
|
||||||
"tags": "ControlNet, canny, depth",
|
|
||||||
"notes": "",
|
|
||||||
"exposedFields": [
|
|
||||||
{
|
|
||||||
"nodeId": "54486974-835b-4d81-8f82-05f9f32ce9e9",
|
|
||||||
"fieldName": "model"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nodeId": "7ce68934-3419-42d4-ac70-82cfc9397306",
|
|
||||||
"fieldName": "prompt"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nodeId": "273e3f96-49ea-4dc5-9d5b-9660390f14e1",
|
|
||||||
"fieldName": "prompt"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nodeId": "c4b23e64-7986-40c4-9cad-46327b12e204",
|
|
||||||
"fieldName": "image"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nodeId": "8e860e51-5045-456e-bf04-9a62a2a5c49e",
|
|
||||||
"fieldName": "image"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"meta": {
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"nodes": [
|
|
||||||
{
|
|
||||||
"id": "8e860e51-5045-456e-bf04-9a62a2a5c49e",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "8e860e51-5045-456e-bf04-9a62a2a5c49e",
|
|
||||||
"type": "image",
|
|
||||||
"inputs": {
|
|
||||||
"image": {
|
|
||||||
"id": "189c8adf-68cc-4774-a729-49da89f6fdf1",
|
|
||||||
"name": "image",
|
|
||||||
"type": "ImageField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "Depth Input Image"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"image": {
|
|
||||||
"id": "1a31cacd-9d19-4f32-b558-c5e4aa39ce73",
|
|
||||||
"name": "image",
|
|
||||||
"type": "ImageField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"width": {
|
|
||||||
"id": "12f298fd-1d11-4cca-9426-01240f7ec7cf",
|
|
||||||
"name": "width",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"height": {
|
|
||||||
"id": "c47dabcb-44e8-40c9-992d-81dca59f598e",
|
|
||||||
"name": "height",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "",
|
|
||||||
"isOpen": true,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": true,
|
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 225,
|
|
||||||
"position": {
|
|
||||||
"x": 3617.163483500202,
|
|
||||||
"y": 40.5529847930888
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "a33199c2-8340-401e-b8a2-42ffa875fc1c",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "a33199c2-8340-401e-b8a2-42ffa875fc1c",
|
|
||||||
"type": "controlnet",
|
|
||||||
"inputs": {
|
|
||||||
"image": {
|
|
||||||
"id": "4e0a3172-d3c2-4005-a84c-fa12a404f8a0",
|
|
||||||
"name": "image",
|
|
||||||
"type": "ImageField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"control_model": {
|
|
||||||
"id": "8cb2d998-4086-430a-8b13-94cbc81e3ca3",
|
|
||||||
"name": "control_model",
|
|
||||||
"type": "ControlNetModelField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": {
|
|
||||||
"model_name": "sd-controlnet-depth",
|
|
||||||
"base_model": "sd-1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"control_weight": {
|
|
||||||
"id": "5e32bd8a-9dc8-42d8-9bcc-c2b0460c0b0f",
|
|
||||||
"name": "control_weight",
|
|
||||||
"type": "FloatPolymorphic",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 1
|
|
||||||
},
|
|
||||||
"begin_step_percent": {
|
|
||||||
"id": "c258a276-352a-416c-8358-152f11005c0c",
|
|
||||||
"name": "begin_step_percent",
|
|
||||||
"type": "float",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 0
|
|
||||||
},
|
|
||||||
"end_step_percent": {
|
|
||||||
"id": "43001125-0d70-4f87-8e79-da6603ad6c33",
|
|
||||||
"name": "end_step_percent",
|
|
||||||
"type": "float",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 1
|
|
||||||
},
|
|
||||||
"control_mode": {
|
|
||||||
"id": "d2f14561-9443-4374-9270-e2f05007944e",
|
|
||||||
"name": "control_mode",
|
|
||||||
"type": "enum",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": "balanced"
|
|
||||||
},
|
|
||||||
"resize_mode": {
|
|
||||||
"id": "727ee7d3-8bf6-4c7d-8b8a-43546b3b59cd",
|
|
||||||
"name": "resize_mode",
|
|
||||||
"type": "enum",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": "just_resize"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"control": {
|
|
||||||
"id": "b034aa0f-4d0d-46e4-b5e3-e25a9588d087",
|
|
||||||
"name": "control",
|
|
||||||
"type": "ControlField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "",
|
|
||||||
"isOpen": true,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": true,
|
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 508,
|
|
||||||
"position": {
|
|
||||||
"x": 4477.604342844504,
|
|
||||||
"y": -49.39005411272677
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "273e3f96-49ea-4dc5-9d5b-9660390f14e1",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "273e3f96-49ea-4dc5-9d5b-9660390f14e1",
|
|
||||||
"type": "compel",
|
|
||||||
"inputs": {
|
|
||||||
"prompt": {
|
|
||||||
"id": "7c2c4771-2161-4d77-aced-ff8c4b3f1c15",
|
|
||||||
"name": "prompt",
|
|
||||||
"type": "string",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "Negative Prompt",
|
|
||||||
"value": ""
|
|
||||||
},
|
|
||||||
"clip": {
|
|
||||||
"id": "06d59e91-9cca-411d-bf05-86b099b3e8f7",
|
|
||||||
"name": "clip",
|
|
||||||
"type": "ClipField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"conditioning": {
|
|
||||||
"id": "858bc33c-134c-4bf6-8855-f943e1d26f14",
|
|
||||||
"name": "conditioning",
|
|
||||||
"type": "ConditioningField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "",
|
|
||||||
"isOpen": true,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": true,
|
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 261,
|
|
||||||
"position": {
|
|
||||||
"x": 4444.706437017514,
|
|
||||||
"y": -924.0715320874991
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "54486974-835b-4d81-8f82-05f9f32ce9e9",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "54486974-835b-4d81-8f82-05f9f32ce9e9",
|
|
||||||
"type": "main_model_loader",
|
|
||||||
"inputs": {
|
|
||||||
"model": {
|
|
||||||
"id": "f4a915a5-593e-4b6d-9198-c78eb5cefaed",
|
|
||||||
"name": "model",
|
|
||||||
"type": "MainModelField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": {
|
|
||||||
"model_name": "stable-diffusion-v1-5",
|
|
||||||
"base_model": "sd-1",
|
|
||||||
"model_type": "main"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"unet": {
|
|
||||||
"id": "ee24fb16-da38-4c66-9fbc-e8f296ed40d2",
|
|
||||||
"name": "unet",
|
|
||||||
"type": "UNetField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"clip": {
|
|
||||||
"id": "f3fb0524-8803-41c1-86db-a61a13ee6a33",
|
|
||||||
"name": "clip",
|
|
||||||
"type": "ClipField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"vae": {
|
|
||||||
"id": "5c4878a8-b40f-44ab-b146-1c1f42c860b3",
|
|
||||||
"name": "vae",
|
|
||||||
"type": "VaeField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "",
|
|
||||||
"isOpen": true,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": true,
|
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 226,
|
|
||||||
"position": {
|
|
||||||
"x": 3837.096149678291,
|
|
||||||
"y": -1050.015351148365
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "7ce68934-3419-42d4-ac70-82cfc9397306",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "7ce68934-3419-42d4-ac70-82cfc9397306",
|
|
||||||
"type": "compel",
|
|
||||||
"inputs": {
|
|
||||||
"prompt": {
|
|
||||||
"id": "7c2c4771-2161-4d77-aced-ff8c4b3f1c15",
|
|
||||||
"name": "prompt",
|
|
||||||
"type": "string",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "Positive Prompt",
|
|
||||||
"value": ""
|
|
||||||
},
|
|
||||||
"clip": {
|
|
||||||
"id": "06d59e91-9cca-411d-bf05-86b099b3e8f7",
|
|
||||||
"name": "clip",
|
|
||||||
"type": "ClipField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"conditioning": {
|
|
||||||
"id": "858bc33c-134c-4bf6-8855-f943e1d26f14",
|
|
||||||
"name": "conditioning",
|
|
||||||
"type": "ConditioningField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "",
|
|
||||||
"isOpen": true,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": true,
|
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 261,
|
|
||||||
"position": {
|
|
||||||
"x": 4449.356038911986,
|
|
||||||
"y": -1201.659695420063
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "d204d184-f209-4fae-a0a1-d152800844e1",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "d204d184-f209-4fae-a0a1-d152800844e1",
|
|
||||||
"type": "controlnet",
|
|
||||||
"inputs": {
|
|
||||||
"image": {
|
|
||||||
"id": "4e0a3172-d3c2-4005-a84c-fa12a404f8a0",
|
|
||||||
"name": "image",
|
|
||||||
"type": "ImageField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"control_model": {
|
|
||||||
"id": "8cb2d998-4086-430a-8b13-94cbc81e3ca3",
|
|
||||||
"name": "control_model",
|
|
||||||
"type": "ControlNetModelField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": {
|
|
||||||
"model_name": "sd-controlnet-canny",
|
|
||||||
"base_model": "sd-1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"control_weight": {
|
|
||||||
"id": "5e32bd8a-9dc8-42d8-9bcc-c2b0460c0b0f",
|
|
||||||
"name": "control_weight",
|
|
||||||
"type": "FloatPolymorphic",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 1
|
|
||||||
},
|
|
||||||
"begin_step_percent": {
|
|
||||||
"id": "c258a276-352a-416c-8358-152f11005c0c",
|
|
||||||
"name": "begin_step_percent",
|
|
||||||
"type": "float",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 0
|
|
||||||
},
|
|
||||||
"end_step_percent": {
|
|
||||||
"id": "43001125-0d70-4f87-8e79-da6603ad6c33",
|
|
||||||
"name": "end_step_percent",
|
|
||||||
"type": "float",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 1
|
|
||||||
},
|
|
||||||
"control_mode": {
|
|
||||||
"id": "d2f14561-9443-4374-9270-e2f05007944e",
|
|
||||||
"name": "control_mode",
|
|
||||||
"type": "enum",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": "balanced"
|
|
||||||
},
|
|
||||||
"resize_mode": {
|
|
||||||
"id": "727ee7d3-8bf6-4c7d-8b8a-43546b3b59cd",
|
|
||||||
"name": "resize_mode",
|
|
||||||
"type": "enum",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": "just_resize"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"control": {
|
|
||||||
"id": "b034aa0f-4d0d-46e4-b5e3-e25a9588d087",
|
|
||||||
"name": "control",
|
|
||||||
"type": "ControlField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "",
|
|
||||||
"isOpen": true,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": true,
|
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 508,
|
|
||||||
"position": {
|
|
||||||
"x": 4479.68542130465,
|
|
||||||
"y": -618.4221638099414
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "c4b23e64-7986-40c4-9cad-46327b12e204",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "c4b23e64-7986-40c4-9cad-46327b12e204",
|
|
||||||
"type": "image",
|
|
||||||
"inputs": {
|
|
||||||
"image": {
|
|
||||||
"id": "189c8adf-68cc-4774-a729-49da89f6fdf1",
|
|
||||||
"name": "image",
|
|
||||||
"type": "ImageField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "Canny Input Image"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"image": {
|
|
||||||
"id": "1a31cacd-9d19-4f32-b558-c5e4aa39ce73",
|
|
||||||
"name": "image",
|
|
||||||
"type": "ImageField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"width": {
|
|
||||||
"id": "12f298fd-1d11-4cca-9426-01240f7ec7cf",
|
|
||||||
"name": "width",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"height": {
|
|
||||||
"id": "c47dabcb-44e8-40c9-992d-81dca59f598e",
|
|
||||||
"name": "height",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "",
|
|
||||||
"isOpen": true,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": true,
|
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 225,
|
|
||||||
"position": {
|
|
||||||
"x": 3593.7474460420153,
|
|
||||||
"y": -538.1200472386865
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "ca4d5059-8bfb-447f-b415-da0faba5a143",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "ca4d5059-8bfb-447f-b415-da0faba5a143",
|
|
||||||
"type": "collect",
|
|
||||||
"inputs": {
|
|
||||||
"item": {
|
|
||||||
"id": "b16ae602-8708-4b1b-8d4f-9e0808d429ab",
|
|
||||||
"name": "item",
|
|
||||||
"type": "CollectionItem",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"collection": {
|
|
||||||
"id": "d8987dd8-dec8-4d94-816a-3e356af29884",
|
|
||||||
"name": "collection",
|
|
||||||
"type": "Collection",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "ControlNet Collection",
|
|
||||||
"isOpen": true,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": true,
|
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 104,
|
|
||||||
"position": {
|
|
||||||
"x": 4866.191497139488,
|
|
||||||
"y": -299.0538619537037
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "018b1214-c2af-43a7-9910-fb687c6726d7",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "018b1214-c2af-43a7-9910-fb687c6726d7",
|
|
||||||
"type": "midas_depth_image_processor",
|
|
||||||
"inputs": {
|
|
||||||
"metadata": {
|
|
||||||
"id": "77f91980-c696-4a18-a9ea-6e2fc329a747",
|
|
||||||
"name": "metadata",
|
|
||||||
"type": "MetadataField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"image": {
|
|
||||||
"id": "50710a20-2af5-424d-9d17-aa08167829c6",
|
|
||||||
"name": "image",
|
|
||||||
"type": "ImageField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"a_mult": {
|
|
||||||
"id": "f3b26f9d-2498-415e-9c01-197a8d06c0a5",
|
|
||||||
"name": "a_mult",
|
|
||||||
"type": "float",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 2
|
|
||||||
},
|
|
||||||
"bg_th": {
|
|
||||||
"id": "4b1eb3ae-9d4a-47d6-b0ed-da62501e007f",
|
|
||||||
"name": "bg_th",
|
|
||||||
"type": "float",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 0.1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"image": {
|
|
||||||
"id": "b4ed637c-c4a0-4fdd-a24e-36d6412e4ccf",
|
|
||||||
"name": "image",
|
|
||||||
"type": "ImageField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"width": {
|
|
||||||
"id": "6bf9b609-d72c-4239-99bd-390a73cc3a9c",
|
|
||||||
"name": "width",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"height": {
|
|
||||||
"id": "3e8aef09-cf44-4e3e-a490-d3c9e7b23119",
|
|
||||||
"name": "height",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "",
|
|
||||||
"isOpen": true,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": true,
|
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 339,
|
|
||||||
"position": {
|
|
||||||
"x": 4054.229311491893,
|
|
||||||
"y": -31.611411056365725
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "c826ba5e-9676-4475-b260-07b85e88753c",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "c826ba5e-9676-4475-b260-07b85e88753c",
|
|
||||||
"type": "canny_image_processor",
|
|
||||||
"inputs": {
|
|
||||||
"metadata": {
|
|
||||||
"id": "08331ea6-99df-4e61-a919-204d9bfa8fb2",
|
|
||||||
"name": "metadata",
|
|
||||||
"type": "MetadataField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"image": {
|
|
||||||
"id": "33a37284-06ac-459c-ba93-1655e4f69b2d",
|
|
||||||
"name": "image",
|
|
||||||
"type": "ImageField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"low_threshold": {
|
|
||||||
"id": "21ec18a3-50c5-4ba1-9642-f921744d594f",
|
|
||||||
"name": "low_threshold",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 100
|
|
||||||
},
|
|
||||||
"high_threshold": {
|
|
||||||
"id": "ebeab271-a5ff-4c88-acfd-1d0271ab6ed4",
|
|
||||||
"name": "high_threshold",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 200
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"image": {
|
|
||||||
"id": "c0caadbf-883f-4cb4-a62d-626b9c81fc4e",
|
|
||||||
"name": "image",
|
|
||||||
"type": "ImageField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"width": {
|
|
||||||
"id": "df225843-8098-49c0-99d1-3b0b6600559f",
|
|
||||||
"name": "width",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"height": {
|
|
||||||
"id": "e4abe0de-aa16-41f3-9cd7-968b49db5da3",
|
|
||||||
"name": "height",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "",
|
|
||||||
"isOpen": true,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": true,
|
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 339,
|
|
||||||
"position": {
|
|
||||||
"x": 4095.757337055795,
|
|
||||||
"y": -455.63440891935863
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "9db25398-c869-4a63-8815-c6559341ef12",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "9db25398-c869-4a63-8815-c6559341ef12",
|
|
||||||
"type": "l2i",
|
|
||||||
"inputs": {
|
|
||||||
"metadata": {
|
|
||||||
"id": "2f269793-72e5-4ff3-b76c-fab4f93e983f",
|
|
||||||
"name": "metadata",
|
|
||||||
"type": "MetadataField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"latents": {
|
|
||||||
"id": "4aaedd3b-cc77-420c-806e-c7fa74ec4cdf",
|
|
||||||
"name": "latents",
|
|
||||||
"type": "LatentsField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"vae": {
|
|
||||||
"id": "432b066a-2462-4d18-83d9-64620b72df45",
|
|
||||||
"name": "vae",
|
|
||||||
"type": "VaeField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"tiled": {
|
|
||||||
"id": "61f86e0f-7c46-40f8-b3f5-fe2f693595ca",
|
|
||||||
"name": "tiled",
|
|
||||||
"type": "boolean",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": false
|
|
||||||
},
|
|
||||||
"fp32": {
|
|
||||||
"id": "39b6c89a-37ef-4a7e-9509-daeca49d5092",
|
|
||||||
"name": "fp32",
|
|
||||||
"type": "boolean",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"image": {
|
|
||||||
"id": "6204e9b0-61dd-4250-b685-2092ba0e28e6",
|
|
||||||
"name": "image",
|
|
||||||
"type": "ImageField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"width": {
|
|
||||||
"id": "b4140649-8d5d-4d2d-bfa6-09e389ede5f9",
|
|
||||||
"name": "width",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"height": {
|
|
||||||
"id": "f3a0c0c8-fc24-4646-8be1-ed8cdd140828",
|
|
||||||
"name": "height",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "",
|
|
||||||
"isOpen": true,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": false,
|
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 267,
|
|
||||||
"position": {
|
|
||||||
"x": 5678.726701377887,
|
|
||||||
"y": -351.6792416734579
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "ac481b7f-08bf-4a9d-9e0c-3a82ea5243ce",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "ac481b7f-08bf-4a9d-9e0c-3a82ea5243ce",
|
|
||||||
"type": "denoise_latents",
|
|
||||||
"inputs": {
|
|
||||||
"positive_conditioning": {
|
|
||||||
"id": "869cd309-c238-444b-a1a0-5021f99785ba",
|
|
||||||
"name": "positive_conditioning",
|
|
||||||
"type": "ConditioningField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"negative_conditioning": {
|
|
||||||
"id": "343447b4-1e37-4e9e-8ac7-4d04864066af",
|
|
||||||
"name": "negative_conditioning",
|
|
||||||
"type": "ConditioningField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"noise": {
|
|
||||||
"id": "b556571e-0cf9-4e03-8cfc-5caad937d957",
|
|
||||||
"name": "noise",
|
|
||||||
"type": "LatentsField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"steps": {
|
|
||||||
"id": "a3b3d2de-9308-423e-b00d-c209c3e6e808",
|
|
||||||
"name": "steps",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 10
|
|
||||||
},
|
|
||||||
"cfg_scale": {
|
|
||||||
"id": "b13c50a4-ec7e-4579-b0ef-2fe5df2605ea",
|
|
||||||
"name": "cfg_scale",
|
|
||||||
"type": "FloatPolymorphic",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 7.5
|
|
||||||
},
|
|
||||||
"denoising_start": {
|
|
||||||
"id": "57d5d755-f58f-4347-b991-f0bca4a0ab29",
|
|
||||||
"name": "denoising_start",
|
|
||||||
"type": "float",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 0
|
|
||||||
},
|
|
||||||
"denoising_end": {
|
|
||||||
"id": "323e78a6-880a-4d73-a62c-70faff965aa6",
|
|
||||||
"name": "denoising_end",
|
|
||||||
"type": "float",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 1
|
|
||||||
},
|
|
||||||
"scheduler": {
|
|
||||||
"id": "c25fdc17-a089-43ac-953e-067c45d5c76b",
|
|
||||||
"name": "scheduler",
|
|
||||||
"type": "Scheduler",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": "euler"
|
|
||||||
},
|
|
||||||
"unet": {
|
|
||||||
"id": "6cde662b-e633-4569-b6b4-ec87c52c9c11",
|
|
||||||
"name": "unet",
|
|
||||||
"type": "UNetField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"control": {
|
|
||||||
"id": "276a4df9-bb26-4505-a4d3-a94e18c7b541",
|
|
||||||
"name": "control",
|
|
||||||
"type": "ControlPolymorphic",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"ip_adapter": {
|
|
||||||
"id": "48d40c51-b5e2-4457-a428-eef0696695e8",
|
|
||||||
"name": "ip_adapter",
|
|
||||||
"type": "IPAdapterPolymorphic",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"t2i_adapter": {
|
|
||||||
"id": "75dd8af2-e7d7-48b4-a574-edd9f6e686ad",
|
|
||||||
"name": "t2i_adapter",
|
|
||||||
"type": "T2IAdapterPolymorphic",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"latents": {
|
|
||||||
"id": "9223d67b-1dd7-4b34-a45f-ed0a725d9702",
|
|
||||||
"name": "latents",
|
|
||||||
"type": "LatentsField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"denoise_mask": {
|
|
||||||
"id": "4ee99177-6923-4b7f-8fe0-d721dd7cb05b",
|
|
||||||
"name": "denoise_mask",
|
|
||||||
"type": "DenoiseMaskField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"latents": {
|
|
||||||
"id": "7fb4e326-a974-43e8-9ee7-2e3ab235819d",
|
|
||||||
"name": "latents",
|
|
||||||
"type": "LatentsField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"width": {
|
|
||||||
"id": "6bb8acd0-8973-4195-a095-e376385dc705",
|
|
||||||
"name": "width",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"height": {
|
|
||||||
"id": "795dea52-1c7d-4e64-99f7-2f60ec6e3ab9",
|
|
||||||
"name": "height",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "",
|
|
||||||
"isOpen": true,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": true,
|
|
||||||
"useCache": true,
|
|
||||||
"version": "1.4.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 646,
|
|
||||||
"position": {
|
|
||||||
"x": 5274.672987098195,
|
|
||||||
"y": -823.0752416664332
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"edges": [
|
|
||||||
{
|
|
||||||
"source": "54486974-835b-4d81-8f82-05f9f32ce9e9",
|
|
||||||
"sourceHandle": "clip",
|
|
||||||
"target": "7ce68934-3419-42d4-ac70-82cfc9397306",
|
|
||||||
"targetHandle": "clip",
|
|
||||||
"id": "reactflow__edge-54486974-835b-4d81-8f82-05f9f32ce9e9clip-7ce68934-3419-42d4-ac70-82cfc9397306clip",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "54486974-835b-4d81-8f82-05f9f32ce9e9",
|
|
||||||
"sourceHandle": "clip",
|
|
||||||
"target": "273e3f96-49ea-4dc5-9d5b-9660390f14e1",
|
|
||||||
"targetHandle": "clip",
|
|
||||||
"id": "reactflow__edge-54486974-835b-4d81-8f82-05f9f32ce9e9clip-273e3f96-49ea-4dc5-9d5b-9660390f14e1clip",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "a33199c2-8340-401e-b8a2-42ffa875fc1c",
|
|
||||||
"sourceHandle": "control",
|
|
||||||
"target": "ca4d5059-8bfb-447f-b415-da0faba5a143",
|
|
||||||
"targetHandle": "item",
|
|
||||||
"id": "reactflow__edge-a33199c2-8340-401e-b8a2-42ffa875fc1ccontrol-ca4d5059-8bfb-447f-b415-da0faba5a143item",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "d204d184-f209-4fae-a0a1-d152800844e1",
|
|
||||||
"sourceHandle": "control",
|
|
||||||
"target": "ca4d5059-8bfb-447f-b415-da0faba5a143",
|
|
||||||
"targetHandle": "item",
|
|
||||||
"id": "reactflow__edge-d204d184-f209-4fae-a0a1-d152800844e1control-ca4d5059-8bfb-447f-b415-da0faba5a143item",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "8e860e51-5045-456e-bf04-9a62a2a5c49e",
|
|
||||||
"sourceHandle": "image",
|
|
||||||
"target": "018b1214-c2af-43a7-9910-fb687c6726d7",
|
|
||||||
"targetHandle": "image",
|
|
||||||
"id": "reactflow__edge-8e860e51-5045-456e-bf04-9a62a2a5c49eimage-018b1214-c2af-43a7-9910-fb687c6726d7image",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "018b1214-c2af-43a7-9910-fb687c6726d7",
|
|
||||||
"sourceHandle": "image",
|
|
||||||
"target": "a33199c2-8340-401e-b8a2-42ffa875fc1c",
|
|
||||||
"targetHandle": "image",
|
|
||||||
"id": "reactflow__edge-018b1214-c2af-43a7-9910-fb687c6726d7image-a33199c2-8340-401e-b8a2-42ffa875fc1cimage",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "c4b23e64-7986-40c4-9cad-46327b12e204",
|
|
||||||
"sourceHandle": "image",
|
|
||||||
"target": "c826ba5e-9676-4475-b260-07b85e88753c",
|
|
||||||
"targetHandle": "image",
|
|
||||||
"id": "reactflow__edge-c4b23e64-7986-40c4-9cad-46327b12e204image-c826ba5e-9676-4475-b260-07b85e88753cimage",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "c826ba5e-9676-4475-b260-07b85e88753c",
|
|
||||||
"sourceHandle": "image",
|
|
||||||
"target": "d204d184-f209-4fae-a0a1-d152800844e1",
|
|
||||||
"targetHandle": "image",
|
|
||||||
"id": "reactflow__edge-c826ba5e-9676-4475-b260-07b85e88753cimage-d204d184-f209-4fae-a0a1-d152800844e1image",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "54486974-835b-4d81-8f82-05f9f32ce9e9",
|
|
||||||
"sourceHandle": "vae",
|
|
||||||
"target": "9db25398-c869-4a63-8815-c6559341ef12",
|
|
||||||
"targetHandle": "vae",
|
|
||||||
"id": "reactflow__edge-54486974-835b-4d81-8f82-05f9f32ce9e9vae-9db25398-c869-4a63-8815-c6559341ef12vae",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "ac481b7f-08bf-4a9d-9e0c-3a82ea5243ce",
|
|
||||||
"sourceHandle": "latents",
|
|
||||||
"target": "9db25398-c869-4a63-8815-c6559341ef12",
|
|
||||||
"targetHandle": "latents",
|
|
||||||
"id": "reactflow__edge-ac481b7f-08bf-4a9d-9e0c-3a82ea5243celatents-9db25398-c869-4a63-8815-c6559341ef12latents",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "ca4d5059-8bfb-447f-b415-da0faba5a143",
|
|
||||||
"sourceHandle": "collection",
|
|
||||||
"target": "ac481b7f-08bf-4a9d-9e0c-3a82ea5243ce",
|
|
||||||
"targetHandle": "control",
|
|
||||||
"id": "reactflow__edge-ca4d5059-8bfb-447f-b415-da0faba5a143collection-ac481b7f-08bf-4a9d-9e0c-3a82ea5243cecontrol",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "54486974-835b-4d81-8f82-05f9f32ce9e9",
|
|
||||||
"sourceHandle": "unet",
|
|
||||||
"target": "ac481b7f-08bf-4a9d-9e0c-3a82ea5243ce",
|
|
||||||
"targetHandle": "unet",
|
|
||||||
"id": "reactflow__edge-54486974-835b-4d81-8f82-05f9f32ce9e9unet-ac481b7f-08bf-4a9d-9e0c-3a82ea5243ceunet",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "273e3f96-49ea-4dc5-9d5b-9660390f14e1",
|
|
||||||
"sourceHandle": "conditioning",
|
|
||||||
"target": "ac481b7f-08bf-4a9d-9e0c-3a82ea5243ce",
|
|
||||||
"targetHandle": "negative_conditioning",
|
|
||||||
"id": "reactflow__edge-273e3f96-49ea-4dc5-9d5b-9660390f14e1conditioning-ac481b7f-08bf-4a9d-9e0c-3a82ea5243cenegative_conditioning",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "7ce68934-3419-42d4-ac70-82cfc9397306",
|
|
||||||
"sourceHandle": "conditioning",
|
|
||||||
"target": "ac481b7f-08bf-4a9d-9e0c-3a82ea5243ce",
|
|
||||||
"targetHandle": "positive_conditioning",
|
|
||||||
"id": "reactflow__edge-7ce68934-3419-42d4-ac70-82cfc9397306conditioning-ac481b7f-08bf-4a9d-9e0c-3a82ea5243cepositive_conditioning",
|
|
||||||
"type": "default"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,719 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Prompt from File",
|
|
||||||
"author": "InvokeAI",
|
|
||||||
"description": "Sample workflow using prompt from file capabilities of InvokeAI ",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"contact": "millun@invoke.ai",
|
|
||||||
"tags": "text2image, prompt from file, default",
|
|
||||||
"notes": "",
|
|
||||||
"exposedFields": [
|
|
||||||
{
|
|
||||||
"nodeId": "d6353b7f-b447-4e17-8f2e-80a88c91d426",
|
|
||||||
"fieldName": "model"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nodeId": "1b7e0df8-8589-4915-a4ea-c0088f15d642",
|
|
||||||
"fieldName": "file_path"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"meta": {
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"nodes": [
|
|
||||||
{
|
|
||||||
"id": "c2eaf1ba-5708-4679-9e15-945b8b432692",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "c2eaf1ba-5708-4679-9e15-945b8b432692",
|
|
||||||
"type": "compel",
|
|
||||||
"inputs": {
|
|
||||||
"prompt": {
|
|
||||||
"id": "dcdf3f6d-9b96-4bcd-9b8d-f992fefe4f62",
|
|
||||||
"name": "prompt",
|
|
||||||
"type": "string",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": ""
|
|
||||||
},
|
|
||||||
"clip": {
|
|
||||||
"id": "3f1981c9-d8a9-42eb-a739-4f120eb80745",
|
|
||||||
"name": "clip",
|
|
||||||
"type": "ClipField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"conditioning": {
|
|
||||||
"id": "46205e6c-c5e2-44cb-9c82-1cd20b95674a",
|
|
||||||
"name": "conditioning",
|
|
||||||
"type": "ConditioningField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "",
|
|
||||||
"isOpen": true,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": true,
|
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 261,
|
|
||||||
"position": {
|
|
||||||
"x": 1177.3417789657444,
|
|
||||||
"y": -102.0924766641035
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1b7e0df8-8589-4915-a4ea-c0088f15d642",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "1b7e0df8-8589-4915-a4ea-c0088f15d642",
|
|
||||||
"type": "prompt_from_file",
|
|
||||||
"inputs": {
|
|
||||||
"file_path": {
|
|
||||||
"id": "37e37684-4f30-4ec8-beae-b333e550f904",
|
|
||||||
"name": "file_path",
|
|
||||||
"type": "string",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "Prompts File Path",
|
|
||||||
"value": ""
|
|
||||||
},
|
|
||||||
"pre_prompt": {
|
|
||||||
"id": "7de02feb-819a-4992-bad3-72a30920ddea",
|
|
||||||
"name": "pre_prompt",
|
|
||||||
"type": "string",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": ""
|
|
||||||
},
|
|
||||||
"post_prompt": {
|
|
||||||
"id": "95f191d8-a282-428e-bd65-de8cb9b7513a",
|
|
||||||
"name": "post_prompt",
|
|
||||||
"type": "string",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": ""
|
|
||||||
},
|
|
||||||
"start_line": {
|
|
||||||
"id": "efee9a48-05ab-4829-8429-becfa64a0782",
|
|
||||||
"name": "start_line",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 1
|
|
||||||
},
|
|
||||||
"max_prompts": {
|
|
||||||
"id": "abebb428-3d3d-49fd-a482-4e96a16fff08",
|
|
||||||
"name": "max_prompts",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"collection": {
|
|
||||||
"id": "77d5d7f1-9877-4ab1-9a8c-33e9ffa9abf3",
|
|
||||||
"name": "collection",
|
|
||||||
"type": "StringCollection",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "Prompts from File",
|
|
||||||
"isOpen": true,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": true,
|
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 589,
|
|
||||||
"position": {
|
|
||||||
"x": 394.181884547075,
|
|
||||||
"y": -423.5345157864633
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1b89067c-3f6b-42c8-991f-e3055789b251",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "1b89067c-3f6b-42c8-991f-e3055789b251",
|
|
||||||
"type": "iterate",
|
|
||||||
"inputs": {
|
|
||||||
"collection": {
|
|
||||||
"id": "4c564bf8-5ed6-441e-ad2c-dda265d5785f",
|
|
||||||
"name": "collection",
|
|
||||||
"type": "Collection",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"item": {
|
|
||||||
"id": "36340f9a-e7a5-4afa-b4b5-313f4e292380",
|
|
||||||
"name": "item",
|
|
||||||
"type": "CollectionItem",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "",
|
|
||||||
"isOpen": true,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": true,
|
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 104,
|
|
||||||
"position": {
|
|
||||||
"x": 792.8735298060233,
|
|
||||||
"y": -432.6964953027252
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "d6353b7f-b447-4e17-8f2e-80a88c91d426",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "d6353b7f-b447-4e17-8f2e-80a88c91d426",
|
|
||||||
"type": "main_model_loader",
|
|
||||||
"inputs": {
|
|
||||||
"model": {
|
|
||||||
"id": "3f264259-3418-47d5-b90d-b6600e36ae46",
|
|
||||||
"name": "model",
|
|
||||||
"type": "MainModelField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": {
|
|
||||||
"model_name": "stable-diffusion-v1-5",
|
|
||||||
"base_model": "sd-1",
|
|
||||||
"model_type": "main"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"unet": {
|
|
||||||
"id": "8e182ea2-9d0a-4c02-9407-27819288d4b5",
|
|
||||||
"name": "unet",
|
|
||||||
"type": "UNetField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"clip": {
|
|
||||||
"id": "d67d9d30-058c-46d5-bded-3d09d6d1aa39",
|
|
||||||
"name": "clip",
|
|
||||||
"type": "ClipField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"vae": {
|
|
||||||
"id": "89641601-0429-4448-98d5-190822d920d8",
|
|
||||||
"name": "vae",
|
|
||||||
"type": "VaeField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "",
|
|
||||||
"isOpen": true,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": true,
|
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 226,
|
|
||||||
"position": {
|
|
||||||
"x": -47.66201354137797,
|
|
||||||
"y": -299.218193067033
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "fc9d0e35-a6de-4a19-84e1-c72497c823f6",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "fc9d0e35-a6de-4a19-84e1-c72497c823f6",
|
|
||||||
"type": "compel",
|
|
||||||
"inputs": {
|
|
||||||
"prompt": {
|
|
||||||
"id": "dcdf3f6d-9b96-4bcd-9b8d-f992fefe4f62",
|
|
||||||
"name": "prompt",
|
|
||||||
"type": "string",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": ""
|
|
||||||
},
|
|
||||||
"clip": {
|
|
||||||
"id": "3f1981c9-d8a9-42eb-a739-4f120eb80745",
|
|
||||||
"name": "clip",
|
|
||||||
"type": "ClipField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"conditioning": {
|
|
||||||
"id": "46205e6c-c5e2-44cb-9c82-1cd20b95674a",
|
|
||||||
"name": "conditioning",
|
|
||||||
"type": "ConditioningField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "",
|
|
||||||
"isOpen": true,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": true,
|
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 261,
|
|
||||||
"position": {
|
|
||||||
"x": 1175.0187896425462,
|
|
||||||
"y": -420.64289413577114
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "0eb5f3f5-1b91-49eb-9ef0-41d67c7eae77",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "0eb5f3f5-1b91-49eb-9ef0-41d67c7eae77",
|
|
||||||
"type": "noise",
|
|
||||||
"inputs": {
|
|
||||||
"seed": {
|
|
||||||
"id": "b722d84a-eeee-484f-bef2-0250c027cb67",
|
|
||||||
"name": "seed",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 0
|
|
||||||
},
|
|
||||||
"width": {
|
|
||||||
"id": "d5f8ce11-0502-4bfc-9a30-5757dddf1f94",
|
|
||||||
"name": "width",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 512
|
|
||||||
},
|
|
||||||
"height": {
|
|
||||||
"id": "f187d5ff-38a5-4c3f-b780-fc5801ef34af",
|
|
||||||
"name": "height",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 512
|
|
||||||
},
|
|
||||||
"use_cpu": {
|
|
||||||
"id": "12f112b8-8b76-4816-b79e-662edc9f9aa5",
|
|
||||||
"name": "use_cpu",
|
|
||||||
"type": "boolean",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"noise": {
|
|
||||||
"id": "08576ad1-96d9-42d2-96ef-6f5c1961933f",
|
|
||||||
"name": "noise",
|
|
||||||
"type": "LatentsField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"width": {
|
|
||||||
"id": "f3e1f94a-258d-41ff-9789-bd999bd9f40d",
|
|
||||||
"name": "width",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"height": {
|
|
||||||
"id": "6cefc357-4339-415e-a951-49b9c2be32f4",
|
|
||||||
"name": "height",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "",
|
|
||||||
"isOpen": true,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": true,
|
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 389,
|
|
||||||
"position": {
|
|
||||||
"x": 809.1964864135837,
|
|
||||||
"y": 183.2735123359796
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "dfc20e07-7aef-4fc0-a3a1-7bf68ec6a4e5",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "dfc20e07-7aef-4fc0-a3a1-7bf68ec6a4e5",
|
|
||||||
"type": "rand_int",
|
|
||||||
"inputs": {
|
|
||||||
"low": {
|
|
||||||
"id": "b9fc6cf1-469c-4037-9bf0-04836965826f",
|
|
||||||
"name": "low",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 0
|
|
||||||
},
|
|
||||||
"high": {
|
|
||||||
"id": "06eac725-0f60-4ba2-b8cd-7ad9f757488c",
|
|
||||||
"name": "high",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 2147483647
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"value": {
|
|
||||||
"id": "df08c84e-7346-4e92-9042-9e5cb773aaff",
|
|
||||||
"name": "value",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "",
|
|
||||||
"isOpen": true,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": true,
|
|
||||||
"useCache": false,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 218,
|
|
||||||
"position": {
|
|
||||||
"x": 354.19913145404166,
|
|
||||||
"y": 301.86324846905165
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "491ec988-3c77-4c37-af8a-39a0c4e7a2a1",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "491ec988-3c77-4c37-af8a-39a0c4e7a2a1",
|
|
||||||
"type": "l2i",
|
|
||||||
"inputs": {
|
|
||||||
"metadata": {
|
|
||||||
"id": "022e4b33-562b-438d-b7df-41c3fd931f40",
|
|
||||||
"name": "metadata",
|
|
||||||
"type": "MetadataField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"latents": {
|
|
||||||
"id": "67cb6c77-a394-4a66-a6a9-a0a7dcca69ec",
|
|
||||||
"name": "latents",
|
|
||||||
"type": "LatentsField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"vae": {
|
|
||||||
"id": "7b3fd9ad-a4ef-4e04-89fa-3832a9902dbd",
|
|
||||||
"name": "vae",
|
|
||||||
"type": "VaeField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"tiled": {
|
|
||||||
"id": "5ac5680d-3add-4115-8ec0-9ef5bb87493b",
|
|
||||||
"name": "tiled",
|
|
||||||
"type": "boolean",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": false
|
|
||||||
},
|
|
||||||
"fp32": {
|
|
||||||
"id": "db8297f5-55f8-452f-98cf-6572c2582152",
|
|
||||||
"name": "fp32",
|
|
||||||
"type": "boolean",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"image": {
|
|
||||||
"id": "d8778d0c-592a-4960-9280-4e77e00a7f33",
|
|
||||||
"name": "image",
|
|
||||||
"type": "ImageField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"width": {
|
|
||||||
"id": "c8b0a75a-f5de-4ff2-9227-f25bb2b97bec",
|
|
||||||
"name": "width",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"height": {
|
|
||||||
"id": "83c05fbf-76b9-49ab-93c4-fa4b10e793e4",
|
|
||||||
"name": "height",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "",
|
|
||||||
"isOpen": true,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": true,
|
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 267,
|
|
||||||
"position": {
|
|
||||||
"x": 2037.861329274915,
|
|
||||||
"y": -329.8393457509562
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2fb1577f-0a56-4f12-8711-8afcaaaf1d5e",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "2fb1577f-0a56-4f12-8711-8afcaaaf1d5e",
|
|
||||||
"type": "denoise_latents",
|
|
||||||
"inputs": {
|
|
||||||
"positive_conditioning": {
|
|
||||||
"id": "751fb35b-3f23-45ce-af1c-053e74251337",
|
|
||||||
"name": "positive_conditioning",
|
|
||||||
"type": "ConditioningField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"negative_conditioning": {
|
|
||||||
"id": "b9dc06b6-7481-4db1-a8c2-39d22a5eacff",
|
|
||||||
"name": "negative_conditioning",
|
|
||||||
"type": "ConditioningField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"noise": {
|
|
||||||
"id": "6e15e439-3390-48a4-8031-01e0e19f0e1d",
|
|
||||||
"name": "noise",
|
|
||||||
"type": "LatentsField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"steps": {
|
|
||||||
"id": "bfdfb3df-760b-4d51-b17b-0abb38b976c2",
|
|
||||||
"name": "steps",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 10
|
|
||||||
},
|
|
||||||
"cfg_scale": {
|
|
||||||
"id": "47770858-322e-41af-8494-d8b63ed735f3",
|
|
||||||
"name": "cfg_scale",
|
|
||||||
"type": "FloatPolymorphic",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 7.5
|
|
||||||
},
|
|
||||||
"denoising_start": {
|
|
||||||
"id": "2ba78720-ee02-4130-a348-7bc3531f790b",
|
|
||||||
"name": "denoising_start",
|
|
||||||
"type": "float",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 0
|
|
||||||
},
|
|
||||||
"denoising_end": {
|
|
||||||
"id": "a874dffb-d433-4d1a-9f59-af4367bb05e4",
|
|
||||||
"name": "denoising_end",
|
|
||||||
"type": "float",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 1
|
|
||||||
},
|
|
||||||
"scheduler": {
|
|
||||||
"id": "36e021ad-b762-4fe4-ad4d-17f0291c40b2",
|
|
||||||
"name": "scheduler",
|
|
||||||
"type": "Scheduler",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": "euler"
|
|
||||||
},
|
|
||||||
"unet": {
|
|
||||||
"id": "98d3282d-f9f6-4b5e-b9e8-58658f1cac78",
|
|
||||||
"name": "unet",
|
|
||||||
"type": "UNetField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"control": {
|
|
||||||
"id": "f2ea3216-43d5-42b4-887f-36e8f7166d53",
|
|
||||||
"name": "control",
|
|
||||||
"type": "ControlPolymorphic",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"ip_adapter": {
|
|
||||||
"id": "d0780610-a298-47c8-a54e-70e769e0dfe2",
|
|
||||||
"name": "ip_adapter",
|
|
||||||
"type": "IPAdapterPolymorphic",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"t2i_adapter": {
|
|
||||||
"id": "fdb40970-185e-4ea8-8bb5-88f06f91f46a",
|
|
||||||
"name": "t2i_adapter",
|
|
||||||
"type": "T2IAdapterPolymorphic",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"latents": {
|
|
||||||
"id": "e05b538a-1b5a-4aa5-84b1-fd2361289a81",
|
|
||||||
"name": "latents",
|
|
||||||
"type": "LatentsField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"denoise_mask": {
|
|
||||||
"id": "463a419e-df30-4382-8ffb-b25b25abe425",
|
|
||||||
"name": "denoise_mask",
|
|
||||||
"type": "DenoiseMaskField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"latents": {
|
|
||||||
"id": "559ee688-66cf-4139-8b82-3d3aa69995ce",
|
|
||||||
"name": "latents",
|
|
||||||
"type": "LatentsField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"width": {
|
|
||||||
"id": "0b4285c2-e8b9-48e5-98f6-0a49d3f98fd2",
|
|
||||||
"name": "width",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"height": {
|
|
||||||
"id": "8b0881b9-45e5-47d5-b526-24b6661de0ee",
|
|
||||||
"name": "height",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "",
|
|
||||||
"isOpen": true,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": true,
|
|
||||||
"useCache": true,
|
|
||||||
"version": "1.4.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 646,
|
|
||||||
"position": {
|
|
||||||
"x": 1570.9941088179146,
|
|
||||||
"y": -407.6505491604564
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"edges": [
|
|
||||||
{
|
|
||||||
"source": "1b7e0df8-8589-4915-a4ea-c0088f15d642",
|
|
||||||
"sourceHandle": "collection",
|
|
||||||
"target": "1b89067c-3f6b-42c8-991f-e3055789b251",
|
|
||||||
"targetHandle": "collection",
|
|
||||||
"id": "reactflow__edge-1b7e0df8-8589-4915-a4ea-c0088f15d642collection-1b89067c-3f6b-42c8-991f-e3055789b251collection",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "d6353b7f-b447-4e17-8f2e-80a88c91d426",
|
|
||||||
"sourceHandle": "clip",
|
|
||||||
"target": "fc9d0e35-a6de-4a19-84e1-c72497c823f6",
|
|
||||||
"targetHandle": "clip",
|
|
||||||
"id": "reactflow__edge-d6353b7f-b447-4e17-8f2e-80a88c91d426clip-fc9d0e35-a6de-4a19-84e1-c72497c823f6clip",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "1b89067c-3f6b-42c8-991f-e3055789b251",
|
|
||||||
"sourceHandle": "item",
|
|
||||||
"target": "fc9d0e35-a6de-4a19-84e1-c72497c823f6",
|
|
||||||
"targetHandle": "prompt",
|
|
||||||
"id": "reactflow__edge-1b89067c-3f6b-42c8-991f-e3055789b251item-fc9d0e35-a6de-4a19-84e1-c72497c823f6prompt",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "d6353b7f-b447-4e17-8f2e-80a88c91d426",
|
|
||||||
"sourceHandle": "clip",
|
|
||||||
"target": "c2eaf1ba-5708-4679-9e15-945b8b432692",
|
|
||||||
"targetHandle": "clip",
|
|
||||||
"id": "reactflow__edge-d6353b7f-b447-4e17-8f2e-80a88c91d426clip-c2eaf1ba-5708-4679-9e15-945b8b432692clip",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "dfc20e07-7aef-4fc0-a3a1-7bf68ec6a4e5",
|
|
||||||
"sourceHandle": "value",
|
|
||||||
"target": "0eb5f3f5-1b91-49eb-9ef0-41d67c7eae77",
|
|
||||||
"targetHandle": "seed",
|
|
||||||
"id": "reactflow__edge-dfc20e07-7aef-4fc0-a3a1-7bf68ec6a4e5value-0eb5f3f5-1b91-49eb-9ef0-41d67c7eae77seed",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "fc9d0e35-a6de-4a19-84e1-c72497c823f6",
|
|
||||||
"sourceHandle": "conditioning",
|
|
||||||
"target": "2fb1577f-0a56-4f12-8711-8afcaaaf1d5e",
|
|
||||||
"targetHandle": "positive_conditioning",
|
|
||||||
"id": "reactflow__edge-fc9d0e35-a6de-4a19-84e1-c72497c823f6conditioning-2fb1577f-0a56-4f12-8711-8afcaaaf1d5epositive_conditioning",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "c2eaf1ba-5708-4679-9e15-945b8b432692",
|
|
||||||
"sourceHandle": "conditioning",
|
|
||||||
"target": "2fb1577f-0a56-4f12-8711-8afcaaaf1d5e",
|
|
||||||
"targetHandle": "negative_conditioning",
|
|
||||||
"id": "reactflow__edge-c2eaf1ba-5708-4679-9e15-945b8b432692conditioning-2fb1577f-0a56-4f12-8711-8afcaaaf1d5enegative_conditioning",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "0eb5f3f5-1b91-49eb-9ef0-41d67c7eae77",
|
|
||||||
"sourceHandle": "noise",
|
|
||||||
"target": "2fb1577f-0a56-4f12-8711-8afcaaaf1d5e",
|
|
||||||
"targetHandle": "noise",
|
|
||||||
"id": "reactflow__edge-0eb5f3f5-1b91-49eb-9ef0-41d67c7eae77noise-2fb1577f-0a56-4f12-8711-8afcaaaf1d5enoise",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "d6353b7f-b447-4e17-8f2e-80a88c91d426",
|
|
||||||
"sourceHandle": "unet",
|
|
||||||
"target": "2fb1577f-0a56-4f12-8711-8afcaaaf1d5e",
|
|
||||||
"targetHandle": "unet",
|
|
||||||
"id": "reactflow__edge-d6353b7f-b447-4e17-8f2e-80a88c91d426unet-2fb1577f-0a56-4f12-8711-8afcaaaf1d5eunet",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "2fb1577f-0a56-4f12-8711-8afcaaaf1d5e",
|
|
||||||
"sourceHandle": "latents",
|
|
||||||
"target": "491ec988-3c77-4c37-af8a-39a0c4e7a2a1",
|
|
||||||
"targetHandle": "latents",
|
|
||||||
"id": "reactflow__edge-2fb1577f-0a56-4f12-8711-8afcaaaf1d5elatents-491ec988-3c77-4c37-af8a-39a0c4e7a2a1latents",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "d6353b7f-b447-4e17-8f2e-80a88c91d426",
|
|
||||||
"sourceHandle": "vae",
|
|
||||||
"target": "491ec988-3c77-4c37-af8a-39a0c4e7a2a1",
|
|
||||||
"targetHandle": "vae",
|
|
||||||
"id": "reactflow__edge-d6353b7f-b447-4e17-8f2e-80a88c91d426vae-491ec988-3c77-4c37-af8a-39a0c4e7a2a1vae",
|
|
||||||
"type": "default"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,758 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "QR Code Monster",
|
|
||||||
"author": "InvokeAI",
|
|
||||||
"description": "Sample workflow for create images with QR code Monster ControlNet",
|
|
||||||
"version": "1.0.1",
|
|
||||||
"contact": "invoke@invoke.ai",
|
|
||||||
"tags": "qrcode, controlnet, default",
|
|
||||||
"notes": "",
|
|
||||||
"exposedFields": [
|
|
||||||
{
|
|
||||||
"nodeId": "a6cc0986-f928-4a7e-8d44-ba2d4b36f54a",
|
|
||||||
"fieldName": "image"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nodeId": "aca3b054-bfba-4392-bd20-6476f59504df",
|
|
||||||
"fieldName": "prompt"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nodeId": "3db7cee0-31e2-4a3d-94a1-268cb16177dd",
|
|
||||||
"fieldName": "prompt"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"meta": {
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"nodes": [
|
|
||||||
{
|
|
||||||
"id": "3db7cee0-31e2-4a3d-94a1-268cb16177dd",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "3db7cee0-31e2-4a3d-94a1-268cb16177dd",
|
|
||||||
"type": "compel",
|
|
||||||
"inputs": {
|
|
||||||
"prompt": {
|
|
||||||
"id": "6a1fe244-5656-4f8c-91d1-1fb474e28807",
|
|
||||||
"name": "prompt",
|
|
||||||
"type": "string",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "Negative Prompt",
|
|
||||||
"value": ""
|
|
||||||
},
|
|
||||||
"clip": {
|
|
||||||
"id": "f24688f3-29b8-4a2d-8603-046e5a5c7250",
|
|
||||||
"name": "clip",
|
|
||||||
"type": "ClipField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"conditioning": {
|
|
||||||
"id": "700528eb-3f8b-4745-b540-34f919b5b228",
|
|
||||||
"name": "conditioning",
|
|
||||||
"type": "ConditioningField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "Prompt",
|
|
||||||
"isOpen": true,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": true,
|
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 261,
|
|
||||||
"position": {
|
|
||||||
"x": 773.0502679628016,
|
|
||||||
"y": 1622.4836086770556
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "610384f1-6f0c-4847-a9a2-37ce7f456ed1",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "610384f1-6f0c-4847-a9a2-37ce7f456ed1",
|
|
||||||
"type": "main_model_loader",
|
|
||||||
"inputs": {
|
|
||||||
"model": {
|
|
||||||
"id": "cb36b6d3-6c1f-4911-a200-646745b0ff74",
|
|
||||||
"name": "model",
|
|
||||||
"type": "MainModelField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": {
|
|
||||||
"model_name": "stable-diffusion-v1-5",
|
|
||||||
"base_model": "sd-1",
|
|
||||||
"model_type": "main"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"unet": {
|
|
||||||
"id": "7246895b-b252-49bc-b952-8d801b4672f7",
|
|
||||||
"name": "unet",
|
|
||||||
"type": "UNetField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"clip": {
|
|
||||||
"id": "3c2aedb8-30d5-4d4b-99df-d06a0d7bedc6",
|
|
||||||
"name": "clip",
|
|
||||||
"type": "ClipField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"vae": {
|
|
||||||
"id": "b9743815-5501-4bbb-8bde-8bd6ba298a4e",
|
|
||||||
"name": "vae",
|
|
||||||
"type": "VaeField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "",
|
|
||||||
"isOpen": true,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": true,
|
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 226,
|
|
||||||
"position": {
|
|
||||||
"x": 211.58866462619744,
|
|
||||||
"y": 1376.0542388105248
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "aca3b054-bfba-4392-bd20-6476f59504df",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "aca3b054-bfba-4392-bd20-6476f59504df",
|
|
||||||
"type": "compel",
|
|
||||||
"inputs": {
|
|
||||||
"prompt": {
|
|
||||||
"id": "6a1fe244-5656-4f8c-91d1-1fb474e28807",
|
|
||||||
"name": "prompt",
|
|
||||||
"type": "string",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "Positive Prompt",
|
|
||||||
"value": ""
|
|
||||||
},
|
|
||||||
"clip": {
|
|
||||||
"id": "f24688f3-29b8-4a2d-8603-046e5a5c7250",
|
|
||||||
"name": "clip",
|
|
||||||
"type": "ClipField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"conditioning": {
|
|
||||||
"id": "700528eb-3f8b-4745-b540-34f919b5b228",
|
|
||||||
"name": "conditioning",
|
|
||||||
"type": "ConditioningField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "",
|
|
||||||
"isOpen": true,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": true,
|
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 261,
|
|
||||||
"position": {
|
|
||||||
"x": 770.6491131680111,
|
|
||||||
"y": 1316.379247112241
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "a6cc0986-f928-4a7e-8d44-ba2d4b36f54a",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "a6cc0986-f928-4a7e-8d44-ba2d4b36f54a",
|
|
||||||
"type": "image",
|
|
||||||
"inputs": {
|
|
||||||
"image": {
|
|
||||||
"id": "89ba5d58-28c9-4e04-a5df-79fb7a6f3531",
|
|
||||||
"name": "image",
|
|
||||||
"type": "ImageField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "QR Code / Hidden Image"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"image": {
|
|
||||||
"id": "54335653-0e17-42da-b9e8-83c5fb5af670",
|
|
||||||
"name": "image",
|
|
||||||
"type": "ImageField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"width": {
|
|
||||||
"id": "a3c65953-39ea-4d97-8858-d65154ff9d11",
|
|
||||||
"name": "width",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"height": {
|
|
||||||
"id": "2c7db511-ebc9-4286-a46b-bc11e0fd779f",
|
|
||||||
"name": "height",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "",
|
|
||||||
"isOpen": true,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": true,
|
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 225,
|
|
||||||
"position": {
|
|
||||||
"x": 700.5034176864369,
|
|
||||||
"y": 1981.749600549388
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "280fd8a7-3b0c-49fe-8be4-6246e08b6c9a",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "280fd8a7-3b0c-49fe-8be4-6246e08b6c9a",
|
|
||||||
"type": "noise",
|
|
||||||
"inputs": {
|
|
||||||
"seed": {
|
|
||||||
"id": "7c6c76dd-127b-4829-b1ec-430790cb7ed7",
|
|
||||||
"name": "seed",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 0
|
|
||||||
},
|
|
||||||
"width": {
|
|
||||||
"id": "8ec6a525-a421-40d8-a17e-39e7b6836438",
|
|
||||||
"name": "width",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 512
|
|
||||||
},
|
|
||||||
"height": {
|
|
||||||
"id": "6af1e58a-e2ee-4ec4-9f06-d8d0412922ca",
|
|
||||||
"name": "height",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 512
|
|
||||||
},
|
|
||||||
"use_cpu": {
|
|
||||||
"id": "26662e99-5720-43a6-a5d8-06c9dab0e261",
|
|
||||||
"name": "use_cpu",
|
|
||||||
"type": "boolean",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"noise": {
|
|
||||||
"id": "cb4c4dfc-a744-49eb-af4f-677448e28407",
|
|
||||||
"name": "noise",
|
|
||||||
"type": "LatentsField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"width": {
|
|
||||||
"id": "97e87be6-e81f-40a3-a522-28ebe4aad0ac",
|
|
||||||
"name": "width",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"height": {
|
|
||||||
"id": "80784420-f1e1-47b0-bd1d-1d381a15e22d",
|
|
||||||
"name": "height",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "",
|
|
||||||
"isOpen": false,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": true,
|
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 32,
|
|
||||||
"position": {
|
|
||||||
"x": 1182.460291960481,
|
|
||||||
"y": 1759.592972960265
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2ac03cf6-0326-454a-bed0-d8baef2bf30d",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "2ac03cf6-0326-454a-bed0-d8baef2bf30d",
|
|
||||||
"type": "controlnet",
|
|
||||||
"inputs": {
|
|
||||||
"image": {
|
|
||||||
"id": "1f683889-9f14-40c8-af29-4b991b211a3a",
|
|
||||||
"name": "image",
|
|
||||||
"type": "ImageField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"control_model": {
|
|
||||||
"id": "a933b21d-22c1-4e06-818f-15416b971282",
|
|
||||||
"name": "control_model",
|
|
||||||
"type": "ControlNetModelField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": {
|
|
||||||
"model_name": "qrcode_monster",
|
|
||||||
"base_model": "sd-1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"control_weight": {
|
|
||||||
"id": "198a0825-e55e-4496-bc54-c3d7b02f3d75",
|
|
||||||
"name": "control_weight",
|
|
||||||
"type": "FloatPolymorphic",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 1.4
|
|
||||||
},
|
|
||||||
"begin_step_percent": {
|
|
||||||
"id": "c85ce42f-22af-42a0-8993-676002fb275e",
|
|
||||||
"name": "begin_step_percent",
|
|
||||||
"type": "float",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 0
|
|
||||||
},
|
|
||||||
"end_step_percent": {
|
|
||||||
"id": "a61a65c4-9e6f-4fe2-96a5-1294d17ec6e4",
|
|
||||||
"name": "end_step_percent",
|
|
||||||
"type": "float",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 1
|
|
||||||
},
|
|
||||||
"control_mode": {
|
|
||||||
"id": "1aa45cfa-0249-46b7-bf24-3e38e92f5fa0",
|
|
||||||
"name": "control_mode",
|
|
||||||
"type": "enum",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": "balanced"
|
|
||||||
},
|
|
||||||
"resize_mode": {
|
|
||||||
"id": "a89d3cb9-a141-4cea-bb49-977bf267377b",
|
|
||||||
"name": "resize_mode",
|
|
||||||
"type": "enum",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": "just_resize"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"control": {
|
|
||||||
"id": "c9a1fc7e-cb25-45a9-adff-1a97c9ff04d6",
|
|
||||||
"name": "control",
|
|
||||||
"type": "ControlField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "",
|
|
||||||
"isOpen": true,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": true,
|
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 508,
|
|
||||||
"position": {
|
|
||||||
"x": 1165.434407461108,
|
|
||||||
"y": 1862.916856351665
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "28542b66-5a00-4780-a318-0a036d2df914",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "28542b66-5a00-4780-a318-0a036d2df914",
|
|
||||||
"type": "l2i",
|
|
||||||
"inputs": {
|
|
||||||
"metadata": {
|
|
||||||
"id": "a38e8f55-7f2c-4fcc-a71f-d51e2eb0374a",
|
|
||||||
"name": "metadata",
|
|
||||||
"type": "MetadataField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"latents": {
|
|
||||||
"id": "80e97bc8-e716-4175-9115-5b58495aa30c",
|
|
||||||
"name": "latents",
|
|
||||||
"type": "LatentsField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"vae": {
|
|
||||||
"id": "5641bce6-ac2b-47eb-bb32-2f290026b7e1",
|
|
||||||
"name": "vae",
|
|
||||||
"type": "VaeField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"tiled": {
|
|
||||||
"id": "9e75eb16-ae48-47ed-b180-e0409d377436",
|
|
||||||
"name": "tiled",
|
|
||||||
"type": "boolean",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": false
|
|
||||||
},
|
|
||||||
"fp32": {
|
|
||||||
"id": "0518b0ce-ee37-437b-8437-cc2976a3279f",
|
|
||||||
"name": "fp32",
|
|
||||||
"type": "boolean",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"image": {
|
|
||||||
"id": "ec2ff985-a7eb-401f-92c4-1217cddad6a2",
|
|
||||||
"name": "image",
|
|
||||||
"type": "ImageField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"width": {
|
|
||||||
"id": "ba1d1720-6d67-4eca-9e9d-b97d08636774",
|
|
||||||
"name": "width",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"height": {
|
|
||||||
"id": "10bcf8f4-6394-422f-b0c0-51680f3bfb25",
|
|
||||||
"name": "height",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "",
|
|
||||||
"isOpen": true,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": true,
|
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 267,
|
|
||||||
"position": {
|
|
||||||
"x": 2110.8415693683014,
|
|
||||||
"y": 1487.253341116115
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "9755ae4c-ef30-4db3-80f6-a31f98979a11",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "9755ae4c-ef30-4db3-80f6-a31f98979a11",
|
|
||||||
"type": "denoise_latents",
|
|
||||||
"inputs": {
|
|
||||||
"positive_conditioning": {
|
|
||||||
"id": "8e6aceaa-a986-4ab2-9c04-5b1027b3daf6",
|
|
||||||
"name": "positive_conditioning",
|
|
||||||
"type": "ConditioningField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"negative_conditioning": {
|
|
||||||
"id": "fbbaa712-ca1a-420b-9016-763f2a29d68c",
|
|
||||||
"name": "negative_conditioning",
|
|
||||||
"type": "ConditioningField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"noise": {
|
|
||||||
"id": "a3b3d5d2-c0f9-4b89-a9b3-8de9418f7bb5",
|
|
||||||
"name": "noise",
|
|
||||||
"type": "LatentsField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"steps": {
|
|
||||||
"id": "e491e664-2f8c-4f49-b3e4-57b051fbb9c5",
|
|
||||||
"name": "steps",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 10
|
|
||||||
},
|
|
||||||
"cfg_scale": {
|
|
||||||
"id": "f0318abd-ed65-4cad-86a7-48d1c19a6d14",
|
|
||||||
"name": "cfg_scale",
|
|
||||||
"type": "FloatPolymorphic",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 7.5
|
|
||||||
},
|
|
||||||
"denoising_start": {
|
|
||||||
"id": "f7c24c51-496f-44c4-836a-c734e529fec0",
|
|
||||||
"name": "denoising_start",
|
|
||||||
"type": "float",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 0
|
|
||||||
},
|
|
||||||
"denoising_end": {
|
|
||||||
"id": "54f7656a-fb0d-4d9e-a459-f700f7dccd2e",
|
|
||||||
"name": "denoising_end",
|
|
||||||
"type": "float",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 1
|
|
||||||
},
|
|
||||||
"scheduler": {
|
|
||||||
"id": "363ee440-040d-499b-bf84-bf5391b08681",
|
|
||||||
"name": "scheduler",
|
|
||||||
"type": "Scheduler",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": "euler"
|
|
||||||
},
|
|
||||||
"unet": {
|
|
||||||
"id": "5c93d4e5-1064-4700-ab1d-d12e1e9b5ba7",
|
|
||||||
"name": "unet",
|
|
||||||
"type": "UNetField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"control": {
|
|
||||||
"id": "e1948eb3-7407-43b0-93e3-139470f186b7",
|
|
||||||
"name": "control",
|
|
||||||
"type": "ControlPolymorphic",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"ip_adapter": {
|
|
||||||
"id": "5675b2c3-adfb-49ee-b33c-26bdbfab1fed",
|
|
||||||
"name": "ip_adapter",
|
|
||||||
"type": "IPAdapterPolymorphic",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"t2i_adapter": {
|
|
||||||
"id": "89cd4ab3-3bfc-4063-9de5-91d42305c651",
|
|
||||||
"name": "t2i_adapter",
|
|
||||||
"type": "T2IAdapterPolymorphic",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"latents": {
|
|
||||||
"id": "ec01df90-5042-418d-b6d6-86b251c13770",
|
|
||||||
"name": "latents",
|
|
||||||
"type": "LatentsField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"denoise_mask": {
|
|
||||||
"id": "561cde00-cb20-42ae-9bd3-4f477f73fbe1",
|
|
||||||
"name": "denoise_mask",
|
|
||||||
"type": "DenoiseMaskField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"latents": {
|
|
||||||
"id": "f9addefe-efcc-4e01-8945-6ebbc934b002",
|
|
||||||
"name": "latents",
|
|
||||||
"type": "LatentsField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"width": {
|
|
||||||
"id": "6d48f78b-d681-422a-8677-0111bd0625f1",
|
|
||||||
"name": "width",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"height": {
|
|
||||||
"id": "f25997b8-6316-44ce-b696-b82e4ed51ae5",
|
|
||||||
"name": "height",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "",
|
|
||||||
"isOpen": true,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": true,
|
|
||||||
"useCache": true,
|
|
||||||
"version": "1.4.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 646,
|
|
||||||
"position": {
|
|
||||||
"x": 1597.9598293300219,
|
|
||||||
"y": 1420.4637727891632
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "59349822-af20-4e0e-a53f-3ba135d00c3f",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "59349822-af20-4e0e-a53f-3ba135d00c3f",
|
|
||||||
"type": "rand_int",
|
|
||||||
"inputs": {
|
|
||||||
"low": {
|
|
||||||
"id": "051f22f9-2d4f-414f-bc51-84af2d626efa",
|
|
||||||
"name": "low",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 0
|
|
||||||
},
|
|
||||||
"high": {
|
|
||||||
"id": "77206186-f264-4224-9589-f925cf903dc9",
|
|
||||||
"name": "high",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": 2147483647
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"value": {
|
|
||||||
"id": "a7ed9387-3a24-4d34-b7c5-f713bd544ab1",
|
|
||||||
"name": "value",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "",
|
|
||||||
"isOpen": false,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": true,
|
|
||||||
"useCache": false,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 32,
|
|
||||||
"position": {
|
|
||||||
"x": 1178.16746986153,
|
|
||||||
"y": 1663.9433412808876
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"edges": [
|
|
||||||
{
|
|
||||||
"source": "59349822-af20-4e0e-a53f-3ba135d00c3f",
|
|
||||||
"target": "280fd8a7-3b0c-49fe-8be4-6246e08b6c9a",
|
|
||||||
"id": "59349822-af20-4e0e-a53f-3ba135d00c3f-280fd8a7-3b0c-49fe-8be4-6246e08b6c9a-collapsed",
|
|
||||||
"type": "collapsed"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "610384f1-6f0c-4847-a9a2-37ce7f456ed1",
|
|
||||||
"sourceHandle": "clip",
|
|
||||||
"target": "aca3b054-bfba-4392-bd20-6476f59504df",
|
|
||||||
"targetHandle": "clip",
|
|
||||||
"id": "reactflow__edge-610384f1-6f0c-4847-a9a2-37ce7f456ed1clip-aca3b054-bfba-4392-bd20-6476f59504dfclip",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "610384f1-6f0c-4847-a9a2-37ce7f456ed1",
|
|
||||||
"sourceHandle": "clip",
|
|
||||||
"target": "3db7cee0-31e2-4a3d-94a1-268cb16177dd",
|
|
||||||
"targetHandle": "clip",
|
|
||||||
"id": "reactflow__edge-610384f1-6f0c-4847-a9a2-37ce7f456ed1clip-3db7cee0-31e2-4a3d-94a1-268cb16177ddclip",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "a6cc0986-f928-4a7e-8d44-ba2d4b36f54a",
|
|
||||||
"sourceHandle": "image",
|
|
||||||
"target": "2ac03cf6-0326-454a-bed0-d8baef2bf30d",
|
|
||||||
"targetHandle": "image",
|
|
||||||
"id": "reactflow__edge-a6cc0986-f928-4a7e-8d44-ba2d4b36f54aimage-2ac03cf6-0326-454a-bed0-d8baef2bf30dimage",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "610384f1-6f0c-4847-a9a2-37ce7f456ed1",
|
|
||||||
"sourceHandle": "vae",
|
|
||||||
"target": "28542b66-5a00-4780-a318-0a036d2df914",
|
|
||||||
"targetHandle": "vae",
|
|
||||||
"id": "reactflow__edge-610384f1-6f0c-4847-a9a2-37ce7f456ed1vae-28542b66-5a00-4780-a318-0a036d2df914vae",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "280fd8a7-3b0c-49fe-8be4-6246e08b6c9a",
|
|
||||||
"sourceHandle": "noise",
|
|
||||||
"target": "9755ae4c-ef30-4db3-80f6-a31f98979a11",
|
|
||||||
"targetHandle": "noise",
|
|
||||||
"id": "reactflow__edge-280fd8a7-3b0c-49fe-8be4-6246e08b6c9anoise-9755ae4c-ef30-4db3-80f6-a31f98979a11noise",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "3db7cee0-31e2-4a3d-94a1-268cb16177dd",
|
|
||||||
"sourceHandle": "conditioning",
|
|
||||||
"target": "9755ae4c-ef30-4db3-80f6-a31f98979a11",
|
|
||||||
"targetHandle": "negative_conditioning",
|
|
||||||
"id": "reactflow__edge-3db7cee0-31e2-4a3d-94a1-268cb16177ddconditioning-9755ae4c-ef30-4db3-80f6-a31f98979a11negative_conditioning",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "aca3b054-bfba-4392-bd20-6476f59504df",
|
|
||||||
"sourceHandle": "conditioning",
|
|
||||||
"target": "9755ae4c-ef30-4db3-80f6-a31f98979a11",
|
|
||||||
"targetHandle": "positive_conditioning",
|
|
||||||
"id": "reactflow__edge-aca3b054-bfba-4392-bd20-6476f59504dfconditioning-9755ae4c-ef30-4db3-80f6-a31f98979a11positive_conditioning",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "610384f1-6f0c-4847-a9a2-37ce7f456ed1",
|
|
||||||
"sourceHandle": "unet",
|
|
||||||
"target": "9755ae4c-ef30-4db3-80f6-a31f98979a11",
|
|
||||||
"targetHandle": "unet",
|
|
||||||
"id": "reactflow__edge-610384f1-6f0c-4847-a9a2-37ce7f456ed1unet-9755ae4c-ef30-4db3-80f6-a31f98979a11unet",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "2ac03cf6-0326-454a-bed0-d8baef2bf30d",
|
|
||||||
"sourceHandle": "control",
|
|
||||||
"target": "9755ae4c-ef30-4db3-80f6-a31f98979a11",
|
|
||||||
"targetHandle": "control",
|
|
||||||
"id": "reactflow__edge-2ac03cf6-0326-454a-bed0-d8baef2bf30dcontrol-9755ae4c-ef30-4db3-80f6-a31f98979a11control",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "9755ae4c-ef30-4db3-80f6-a31f98979a11",
|
|
||||||
"sourceHandle": "latents",
|
|
||||||
"target": "28542b66-5a00-4780-a318-0a036d2df914",
|
|
||||||
"targetHandle": "latents",
|
|
||||||
"id": "reactflow__edge-9755ae4c-ef30-4db3-80f6-a31f98979a11latents-28542b66-5a00-4780-a318-0a036d2df914latents",
|
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "59349822-af20-4e0e-a53f-3ba135d00c3f",
|
|
||||||
"sourceHandle": "value",
|
|
||||||
"target": "280fd8a7-3b0c-49fe-8be4-6246e08b6c9a",
|
|
||||||
"targetHandle": "seed",
|
|
||||||
"id": "reactflow__edge-59349822-af20-4e0e-a53f-3ba135d00c3fvalue-280fd8a7-3b0c-49fe-8be4-6246e08b6c9aseed",
|
|
||||||
"type": "default"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -26,6 +26,10 @@
|
|||||||
{
|
{
|
||||||
"nodeId": "3193ad09-a7c2-4bf4-a3a9-1c61cc33a204",
|
"nodeId": "3193ad09-a7c2-4bf4-a3a9-1c61cc33a204",
|
||||||
"fieldName": "style"
|
"fieldName": "style"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nodeId": "87ee6243-fb0d-4f77-ad5f-56591659339e",
|
||||||
|
"fieldName": "steps"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"meta": {
|
"meta": {
|
||||||
@@ -36,6 +40,7 @@
|
|||||||
"id": "3193ad09-a7c2-4bf4-a3a9-1c61cc33a204",
|
"id": "3193ad09-a7c2-4bf4-a3a9-1c61cc33a204",
|
||||||
"type": "invocation",
|
"type": "invocation",
|
||||||
"data": {
|
"data": {
|
||||||
|
"version": "1.0.0",
|
||||||
"id": "3193ad09-a7c2-4bf4-a3a9-1c61cc33a204",
|
"id": "3193ad09-a7c2-4bf4-a3a9-1c61cc33a204",
|
||||||
"type": "sdxl_compel_prompt",
|
"type": "sdxl_compel_prompt",
|
||||||
"inputs": {
|
"inputs": {
|
||||||
@@ -130,12 +135,10 @@
|
|||||||
"isOpen": true,
|
"isOpen": true,
|
||||||
"notes": "",
|
"notes": "",
|
||||||
"embedWorkflow": false,
|
"embedWorkflow": false,
|
||||||
"isIntermediate": true,
|
"isIntermediate": true
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
},
|
||||||
"width": 320,
|
"width": 320,
|
||||||
"height": 793,
|
"height": 764,
|
||||||
"position": {
|
"position": {
|
||||||
"x": 1275,
|
"x": 1275,
|
||||||
"y": -350
|
"y": -350
|
||||||
@@ -145,6 +148,7 @@
|
|||||||
"id": "55705012-79b9-4aac-9f26-c0b10309785b",
|
"id": "55705012-79b9-4aac-9f26-c0b10309785b",
|
||||||
"type": "invocation",
|
"type": "invocation",
|
||||||
"data": {
|
"data": {
|
||||||
|
"version": "1.0.0",
|
||||||
"id": "55705012-79b9-4aac-9f26-c0b10309785b",
|
"id": "55705012-79b9-4aac-9f26-c0b10309785b",
|
||||||
"type": "noise",
|
"type": "noise",
|
||||||
"inputs": {
|
"inputs": {
|
||||||
@@ -205,9 +209,7 @@
|
|||||||
"isOpen": false,
|
"isOpen": false,
|
||||||
"notes": "",
|
"notes": "",
|
||||||
"embedWorkflow": false,
|
"embedWorkflow": false,
|
||||||
"isIntermediate": true,
|
"isIntermediate": true
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
},
|
||||||
"width": 320,
|
"width": 320,
|
||||||
"height": 32,
|
"height": 32,
|
||||||
@@ -216,10 +218,83 @@
|
|||||||
"y": -300
|
"y": -300
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "dbcd2f98-d809-48c8-bf64-2635f88a2fe9",
|
||||||
|
"type": "invocation",
|
||||||
|
"data": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"id": "dbcd2f98-d809-48c8-bf64-2635f88a2fe9",
|
||||||
|
"type": "l2i",
|
||||||
|
"inputs": {
|
||||||
|
"tiled": {
|
||||||
|
"id": "24f5bc7b-f6a1-425d-8ab1-f50b4db5d0df",
|
||||||
|
"name": "tiled",
|
||||||
|
"type": "boolean",
|
||||||
|
"fieldKind": "input",
|
||||||
|
"label": "",
|
||||||
|
"value": false
|
||||||
|
},
|
||||||
|
"fp32": {
|
||||||
|
"id": "b146d873-ffb9-4767-986a-5360504841a2",
|
||||||
|
"name": "fp32",
|
||||||
|
"type": "boolean",
|
||||||
|
"fieldKind": "input",
|
||||||
|
"label": "",
|
||||||
|
"value": true
|
||||||
|
},
|
||||||
|
"latents": {
|
||||||
|
"id": "65441abd-7713-4b00-9d8d-3771404002e8",
|
||||||
|
"name": "latents",
|
||||||
|
"type": "LatentsField",
|
||||||
|
"fieldKind": "input",
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
|
"vae": {
|
||||||
|
"id": "a478b833-6e13-4611-9a10-842c89603c74",
|
||||||
|
"name": "vae",
|
||||||
|
"type": "VaeField",
|
||||||
|
"fieldKind": "input",
|
||||||
|
"label": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"outputs": {
|
||||||
|
"image": {
|
||||||
|
"id": "c87ae925-f858-417a-8940-8708ba9b4b53",
|
||||||
|
"name": "image",
|
||||||
|
"type": "ImageField",
|
||||||
|
"fieldKind": "output"
|
||||||
|
},
|
||||||
|
"width": {
|
||||||
|
"id": "4bcb8512-b5a1-45f1-9e52-6e92849f9d6c",
|
||||||
|
"name": "width",
|
||||||
|
"type": "integer",
|
||||||
|
"fieldKind": "output"
|
||||||
|
},
|
||||||
|
"height": {
|
||||||
|
"id": "23e41c00-a354-48e8-8f59-5875679c27ab",
|
||||||
|
"name": "height",
|
||||||
|
"type": "integer",
|
||||||
|
"fieldKind": "output"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"label": "",
|
||||||
|
"isOpen": true,
|
||||||
|
"notes": "",
|
||||||
|
"embedWorkflow": true,
|
||||||
|
"isIntermediate": false
|
||||||
|
},
|
||||||
|
"width": 320,
|
||||||
|
"height": 224,
|
||||||
|
"position": {
|
||||||
|
"x": 2025,
|
||||||
|
"y": -250
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "ea94bc37-d995-4a83-aa99-4af42479f2f2",
|
"id": "ea94bc37-d995-4a83-aa99-4af42479f2f2",
|
||||||
"type": "invocation",
|
"type": "invocation",
|
||||||
"data": {
|
"data": {
|
||||||
|
"version": "1.0.0",
|
||||||
"id": "ea94bc37-d995-4a83-aa99-4af42479f2f2",
|
"id": "ea94bc37-d995-4a83-aa99-4af42479f2f2",
|
||||||
"type": "rand_int",
|
"type": "rand_int",
|
||||||
"inputs": {
|
"inputs": {
|
||||||
@@ -252,9 +327,7 @@
|
|||||||
"isOpen": false,
|
"isOpen": false,
|
||||||
"notes": "",
|
"notes": "",
|
||||||
"embedWorkflow": false,
|
"embedWorkflow": false,
|
||||||
"isIntermediate": true,
|
"isIntermediate": true
|
||||||
"useCache": false,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
},
|
||||||
"width": 320,
|
"width": 320,
|
||||||
"height": 32,
|
"height": 32,
|
||||||
@@ -267,6 +340,7 @@
|
|||||||
"id": "30d3289c-773c-4152-a9d2-bd8a99c8fd22",
|
"id": "30d3289c-773c-4152-a9d2-bd8a99c8fd22",
|
||||||
"type": "invocation",
|
"type": "invocation",
|
||||||
"data": {
|
"data": {
|
||||||
|
"version": "1.0.0",
|
||||||
"id": "30d3289c-773c-4152-a9d2-bd8a99c8fd22",
|
"id": "30d3289c-773c-4152-a9d2-bd8a99c8fd22",
|
||||||
"type": "sdxl_model_loader",
|
"type": "sdxl_model_loader",
|
||||||
"inputs": {
|
"inputs": {
|
||||||
@@ -277,7 +351,7 @@
|
|||||||
"fieldKind": "input",
|
"fieldKind": "input",
|
||||||
"label": "",
|
"label": "",
|
||||||
"value": {
|
"value": {
|
||||||
"model_name": "stable-diffusion-xl-base-1-0",
|
"model_name": "stable-diffusion-xl-base-1.0",
|
||||||
"base_model": "sdxl",
|
"base_model": "sdxl",
|
||||||
"model_type": "main"
|
"model_type": "main"
|
||||||
}
|
}
|
||||||
@@ -313,12 +387,10 @@
|
|||||||
"isOpen": true,
|
"isOpen": true,
|
||||||
"notes": "",
|
"notes": "",
|
||||||
"embedWorkflow": false,
|
"embedWorkflow": false,
|
||||||
"isIntermediate": true,
|
"isIntermediate": true
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
},
|
||||||
"width": 320,
|
"width": 320,
|
||||||
"height": 258,
|
"height": 234,
|
||||||
"position": {
|
"position": {
|
||||||
"x": 475,
|
"x": 475,
|
||||||
"y": 25
|
"y": 25
|
||||||
@@ -328,6 +400,7 @@
|
|||||||
"id": "faf965a4-7530-427b-b1f3-4ba6505c2a08",
|
"id": "faf965a4-7530-427b-b1f3-4ba6505c2a08",
|
||||||
"type": "invocation",
|
"type": "invocation",
|
||||||
"data": {
|
"data": {
|
||||||
|
"version": "1.0.0",
|
||||||
"id": "faf965a4-7530-427b-b1f3-4ba6505c2a08",
|
"id": "faf965a4-7530-427b-b1f3-4ba6505c2a08",
|
||||||
"type": "sdxl_compel_prompt",
|
"type": "sdxl_compel_prompt",
|
||||||
"inputs": {
|
"inputs": {
|
||||||
@@ -422,143 +495,48 @@
|
|||||||
"isOpen": true,
|
"isOpen": true,
|
||||||
"notes": "",
|
"notes": "",
|
||||||
"embedWorkflow": false,
|
"embedWorkflow": false,
|
||||||
"isIntermediate": true,
|
"isIntermediate": true
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
},
|
||||||
"width": 320,
|
"width": 320,
|
||||||
"height": 793,
|
"height": 764,
|
||||||
"position": {
|
"position": {
|
||||||
"x": 900,
|
"x": 900,
|
||||||
"y": -350
|
"y": -350
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "63e91020-83b2-4f35-b174-ad9692aabb48",
|
"id": "87ee6243-fb0d-4f77-ad5f-56591659339e",
|
||||||
"type": "invocation",
|
"type": "invocation",
|
||||||
"data": {
|
"data": {
|
||||||
"id": "63e91020-83b2-4f35-b174-ad9692aabb48",
|
"version": "1.0.0",
|
||||||
"type": "l2i",
|
"id": "87ee6243-fb0d-4f77-ad5f-56591659339e",
|
||||||
"inputs": {
|
|
||||||
"metadata": {
|
|
||||||
"id": "88971324-3fdb-442d-b8b7-7612478a8622",
|
|
||||||
"name": "metadata",
|
|
||||||
"type": "MetadataField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"latents": {
|
|
||||||
"id": "da0e40cb-c49f-4fa5-9856-338b91a65f6b",
|
|
||||||
"name": "latents",
|
|
||||||
"type": "LatentsField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"vae": {
|
|
||||||
"id": "ae5164ce-1710-4ec5-a83a-6113a0d1b5c0",
|
|
||||||
"name": "vae",
|
|
||||||
"type": "VaeField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"tiled": {
|
|
||||||
"id": "2ccfd535-1a7b-4ecf-84db-9430a64fb3d7",
|
|
||||||
"name": "tiled",
|
|
||||||
"type": "boolean",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": false
|
|
||||||
},
|
|
||||||
"fp32": {
|
|
||||||
"id": "64f07d5a-54a2-429c-8c5b-0c2a3a8e5cd5",
|
|
||||||
"name": "fp32",
|
|
||||||
"type": "boolean",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"image": {
|
|
||||||
"id": "9b281eaa-6504-407d-a5ca-1e5e8020a4bf",
|
|
||||||
"name": "image",
|
|
||||||
"type": "ImageField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"width": {
|
|
||||||
"id": "98e545f3-b53b-490d-b94d-bed9418ccc75",
|
|
||||||
"name": "width",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"height": {
|
|
||||||
"id": "4a74bd43-d7f7-4c7f-bb3b-d09bb2992c46",
|
|
||||||
"name": "height",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "",
|
|
||||||
"isOpen": true,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": false,
|
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 267,
|
|
||||||
"position": {
|
|
||||||
"x": 2112.5626808057173,
|
|
||||||
"y": -174.24042139280238
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "50a36525-3c0a-4cc5-977c-e4bfc3fd6dfb",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "50a36525-3c0a-4cc5-977c-e4bfc3fd6dfb",
|
|
||||||
"type": "denoise_latents",
|
"type": "denoise_latents",
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"positive_conditioning": {
|
|
||||||
"id": "29b73dfa-a06e-4b4a-a844-515b9eb93a81",
|
|
||||||
"name": "positive_conditioning",
|
|
||||||
"type": "ConditioningField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"negative_conditioning": {
|
|
||||||
"id": "a81e6f5b-f4de-4919-b483-b6e2f067465a",
|
|
||||||
"name": "negative_conditioning",
|
|
||||||
"type": "ConditioningField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"noise": {
|
"noise": {
|
||||||
"id": "4ba06bb7-eb45-4fb9-9984-31001b545587",
|
"id": "4884a4b7-cc19-4fea-83c7-1f940e6edd24",
|
||||||
"name": "noise",
|
"name": "noise",
|
||||||
"type": "LatentsField",
|
"type": "LatentsField",
|
||||||
"fieldKind": "input",
|
"fieldKind": "input",
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"steps": {
|
"steps": {
|
||||||
"id": "36ee8a45-ca69-44bc-9bc3-aa881e6045c0",
|
"id": "4c61675c-b6b9-41ac-b187-b5c13b587039",
|
||||||
"name": "steps",
|
"name": "steps",
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"fieldKind": "input",
|
"fieldKind": "input",
|
||||||
"label": "",
|
"label": "",
|
||||||
"value": 10
|
"value": 36
|
||||||
},
|
},
|
||||||
"cfg_scale": {
|
"cfg_scale": {
|
||||||
"id": "2a2024e0-a736-46ec-933c-c1c1ebe96943",
|
"id": "f8213f35-4637-4a1a-83f4-1f8cfb9ccd2c",
|
||||||
"name": "cfg_scale",
|
"name": "cfg_scale",
|
||||||
"type": "FloatPolymorphic",
|
"type": "float",
|
||||||
"fieldKind": "input",
|
"fieldKind": "input",
|
||||||
"label": "",
|
"label": "",
|
||||||
"value": 7.5
|
"value": 7.5
|
||||||
},
|
},
|
||||||
"denoising_start": {
|
"denoising_start": {
|
||||||
"id": "be219d5e-41b7-430a-8fb5-bc21a31ad219",
|
"id": "01e2f30d-0acd-4e21-98b9-a9b8e24c6db2",
|
||||||
"name": "denoising_start",
|
"name": "denoising_start",
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"fieldKind": "input",
|
"fieldKind": "input",
|
||||||
@@ -566,7 +544,7 @@
|
|||||||
"value": 0
|
"value": 0
|
||||||
},
|
},
|
||||||
"denoising_end": {
|
"denoising_end": {
|
||||||
"id": "3adfb7ae-c9f7-4a40-b6e0-4c2050bd1a99",
|
"id": "3db95479-a73b-4c75-9b44-08daec16b224",
|
||||||
"name": "denoising_end",
|
"name": "denoising_end",
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"fieldKind": "input",
|
"fieldKind": "input",
|
||||||
@@ -574,71 +552,71 @@
|
|||||||
"value": 1
|
"value": 1
|
||||||
},
|
},
|
||||||
"scheduler": {
|
"scheduler": {
|
||||||
"id": "14423e0d-7215-4ee0-b065-f9e95eaa8d7d",
|
"id": "db8430a9-64c3-4c54-ae38-9f597cf7b6d5",
|
||||||
"name": "scheduler",
|
"name": "scheduler",
|
||||||
"type": "Scheduler",
|
"type": "Scheduler",
|
||||||
"fieldKind": "input",
|
"fieldKind": "input",
|
||||||
"label": "",
|
"label": "",
|
||||||
"value": "euler"
|
"value": "euler"
|
||||||
},
|
},
|
||||||
"unet": {
|
|
||||||
"id": "e73bbf98-6489-492b-b83c-faed215febac",
|
|
||||||
"name": "unet",
|
|
||||||
"type": "UNetField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"control": {
|
"control": {
|
||||||
"id": "dab351b3-0c86-4ea5-9782-4e8edbfb0607",
|
"id": "599b49e8-6435-4576-be41-a5155f3a17e3",
|
||||||
"name": "control",
|
"name": "control",
|
||||||
"type": "ControlPolymorphic",
|
"type": "ControlField",
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"ip_adapter": {
|
|
||||||
"id": "192daea0-a90a-43cc-a2ee-0114a8e90318",
|
|
||||||
"name": "ip_adapter",
|
|
||||||
"type": "IPAdapterPolymorphic",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"t2i_adapter": {
|
|
||||||
"id": "ee386a55-d4c7-48c1-ac57-7bc4e3aada7a",
|
|
||||||
"name": "t2i_adapter",
|
|
||||||
"type": "T2IAdapterPolymorphic",
|
|
||||||
"fieldKind": "input",
|
"fieldKind": "input",
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"latents": {
|
"latents": {
|
||||||
"id": "3a922c6a-3d8c-4c9e-b3ec-2f4d81cda077",
|
"id": "226f9e91-454e-4159-9fa6-019c0cf29277",
|
||||||
"name": "latents",
|
"name": "latents",
|
||||||
"type": "LatentsField",
|
"type": "LatentsField",
|
||||||
"fieldKind": "input",
|
"fieldKind": "input",
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"denoise_mask": {
|
"denoise_mask": {
|
||||||
"id": "cd7ce032-835f-495f-8b45-d57272f33132",
|
"id": "de019cb6-7fb5-45bf-a266-22e20889893f",
|
||||||
"name": "denoise_mask",
|
"name": "denoise_mask",
|
||||||
"type": "DenoiseMaskField",
|
"type": "DenoiseMaskField",
|
||||||
"fieldKind": "input",
|
"fieldKind": "input",
|
||||||
"label": ""
|
"label": ""
|
||||||
|
},
|
||||||
|
"positive_conditioning": {
|
||||||
|
"id": "02fc400a-110d-470e-8411-f404f966a949",
|
||||||
|
"name": "positive_conditioning",
|
||||||
|
"type": "ConditioningField",
|
||||||
|
"fieldKind": "input",
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
|
"negative_conditioning": {
|
||||||
|
"id": "4bd3bdfa-fcf4-42be-8e47-1e314255798f",
|
||||||
|
"name": "negative_conditioning",
|
||||||
|
"type": "ConditioningField",
|
||||||
|
"fieldKind": "input",
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
|
"unet": {
|
||||||
|
"id": "7c2d58a8-b5f1-4e63-8ffd-8ada52c35832",
|
||||||
|
"name": "unet",
|
||||||
|
"type": "UNetField",
|
||||||
|
"fieldKind": "input",
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"outputs": {
|
"outputs": {
|
||||||
"latents": {
|
"latents": {
|
||||||
"id": "6260b84f-8361-470a-98d8-5b22a45c2d8c",
|
"id": "6a6fa492-de26-4e95-b1d9-a322fe37eb13",
|
||||||
"name": "latents",
|
"name": "latents",
|
||||||
"type": "LatentsField",
|
"type": "LatentsField",
|
||||||
"fieldKind": "output"
|
"fieldKind": "output"
|
||||||
},
|
},
|
||||||
"width": {
|
"width": {
|
||||||
"id": "aede0ecf-25b6-46be-aa30-b77f79715deb",
|
"id": "a9790729-7d6c-4418-903d-4da961fccf56",
|
||||||
"name": "width",
|
"name": "width",
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"fieldKind": "output"
|
"fieldKind": "output"
|
||||||
},
|
},
|
||||||
"height": {
|
"height": {
|
||||||
"id": "519abf62-d475-48ef-ab8f-66136bc0e499",
|
"id": "fa74efe5-7330-4a3c-b256-c82a544585b4",
|
||||||
"name": "height",
|
"name": "height",
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"fieldKind": "output"
|
"fieldKind": "output"
|
||||||
@@ -648,15 +626,13 @@
|
|||||||
"isOpen": true,
|
"isOpen": true,
|
||||||
"notes": "",
|
"notes": "",
|
||||||
"embedWorkflow": false,
|
"embedWorkflow": false,
|
||||||
"isIntermediate": true,
|
"isIntermediate": true
|
||||||
"useCache": true,
|
|
||||||
"version": "1.4.0"
|
|
||||||
},
|
},
|
||||||
"width": 320,
|
"width": 320,
|
||||||
"height": 646,
|
"height": 558,
|
||||||
"position": {
|
"position": {
|
||||||
"x": 1642.955772577545,
|
"x": 1650,
|
||||||
"y": -230.2485847594651
|
"y": -250
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -710,42 +686,50 @@
|
|||||||
{
|
{
|
||||||
"source": "30d3289c-773c-4152-a9d2-bd8a99c8fd22",
|
"source": "30d3289c-773c-4152-a9d2-bd8a99c8fd22",
|
||||||
"sourceHandle": "vae",
|
"sourceHandle": "vae",
|
||||||
"target": "63e91020-83b2-4f35-b174-ad9692aabb48",
|
"target": "dbcd2f98-d809-48c8-bf64-2635f88a2fe9",
|
||||||
"targetHandle": "vae",
|
"targetHandle": "vae",
|
||||||
"id": "reactflow__edge-30d3289c-773c-4152-a9d2-bd8a99c8fd22vae-63e91020-83b2-4f35-b174-ad9692aabb48vae",
|
"id": "reactflow__edge-30d3289c-773c-4152-a9d2-bd8a99c8fd22vae-dbcd2f98-d809-48c8-bf64-2635f88a2fe9vae",
|
||||||
"type": "default"
|
"type": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"source": "30d3289c-773c-4152-a9d2-bd8a99c8fd22",
|
"source": "87ee6243-fb0d-4f77-ad5f-56591659339e",
|
||||||
"sourceHandle": "unet",
|
"sourceHandle": "latents",
|
||||||
"target": "50a36525-3c0a-4cc5-977c-e4bfc3fd6dfb",
|
"target": "dbcd2f98-d809-48c8-bf64-2635f88a2fe9",
|
||||||
"targetHandle": "unet",
|
"targetHandle": "latents",
|
||||||
"id": "reactflow__edge-30d3289c-773c-4152-a9d2-bd8a99c8fd22unet-50a36525-3c0a-4cc5-977c-e4bfc3fd6dfbunet",
|
"id": "reactflow__edge-87ee6243-fb0d-4f77-ad5f-56591659339elatents-dbcd2f98-d809-48c8-bf64-2635f88a2fe9latents",
|
||||||
"type": "default"
|
"type": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"source": "faf965a4-7530-427b-b1f3-4ba6505c2a08",
|
"source": "faf965a4-7530-427b-b1f3-4ba6505c2a08",
|
||||||
"sourceHandle": "conditioning",
|
"sourceHandle": "conditioning",
|
||||||
"target": "50a36525-3c0a-4cc5-977c-e4bfc3fd6dfb",
|
"target": "87ee6243-fb0d-4f77-ad5f-56591659339e",
|
||||||
"targetHandle": "positive_conditioning",
|
"targetHandle": "positive_conditioning",
|
||||||
"id": "reactflow__edge-faf965a4-7530-427b-b1f3-4ba6505c2a08conditioning-50a36525-3c0a-4cc5-977c-e4bfc3fd6dfbpositive_conditioning",
|
"id": "reactflow__edge-faf965a4-7530-427b-b1f3-4ba6505c2a08conditioning-87ee6243-fb0d-4f77-ad5f-56591659339epositive_conditioning",
|
||||||
"type": "default"
|
"type": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"source": "3193ad09-a7c2-4bf4-a3a9-1c61cc33a204",
|
"source": "3193ad09-a7c2-4bf4-a3a9-1c61cc33a204",
|
||||||
"sourceHandle": "conditioning",
|
"sourceHandle": "conditioning",
|
||||||
"target": "50a36525-3c0a-4cc5-977c-e4bfc3fd6dfb",
|
"target": "87ee6243-fb0d-4f77-ad5f-56591659339e",
|
||||||
"targetHandle": "negative_conditioning",
|
"targetHandle": "negative_conditioning",
|
||||||
"id": "reactflow__edge-3193ad09-a7c2-4bf4-a3a9-1c61cc33a204conditioning-50a36525-3c0a-4cc5-977c-e4bfc3fd6dfbnegative_conditioning",
|
"id": "reactflow__edge-3193ad09-a7c2-4bf4-a3a9-1c61cc33a204conditioning-87ee6243-fb0d-4f77-ad5f-56591659339enegative_conditioning",
|
||||||
|
"type": "default"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "30d3289c-773c-4152-a9d2-bd8a99c8fd22",
|
||||||
|
"sourceHandle": "unet",
|
||||||
|
"target": "87ee6243-fb0d-4f77-ad5f-56591659339e",
|
||||||
|
"targetHandle": "unet",
|
||||||
|
"id": "reactflow__edge-30d3289c-773c-4152-a9d2-bd8a99c8fd22unet-87ee6243-fb0d-4f77-ad5f-56591659339eunet",
|
||||||
"type": "default"
|
"type": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"source": "55705012-79b9-4aac-9f26-c0b10309785b",
|
"source": "55705012-79b9-4aac-9f26-c0b10309785b",
|
||||||
"sourceHandle": "noise",
|
"sourceHandle": "noise",
|
||||||
"target": "50a36525-3c0a-4cc5-977c-e4bfc3fd6dfb",
|
"target": "87ee6243-fb0d-4f77-ad5f-56591659339e",
|
||||||
"targetHandle": "noise",
|
"targetHandle": "noise",
|
||||||
"id": "reactflow__edge-55705012-79b9-4aac-9f26-c0b10309785bnoise-50a36525-3c0a-4cc5-977c-e4bfc3fd6dfbnoise",
|
"id": "reactflow__edge-55705012-79b9-4aac-9f26-c0b10309785bnoise-87ee6243-fb0d-4f77-ad5f-56591659339enoise",
|
||||||
"type": "default"
|
"type": "default"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,10 @@
|
|||||||
{
|
{
|
||||||
"nodeId": "93dc02a4-d05b-48ed-b99c-c9b616af3402",
|
"nodeId": "93dc02a4-d05b-48ed-b99c-c9b616af3402",
|
||||||
"fieldName": "prompt"
|
"fieldName": "prompt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nodeId": "75899702-fa44-46d2-b2d5-3e17f234c3e7",
|
||||||
|
"fieldName": "steps"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"meta": {
|
"meta": {
|
||||||
@@ -28,6 +32,7 @@
|
|||||||
"id": "93dc02a4-d05b-48ed-b99c-c9b616af3402",
|
"id": "93dc02a4-d05b-48ed-b99c-c9b616af3402",
|
||||||
"type": "invocation",
|
"type": "invocation",
|
||||||
"data": {
|
"data": {
|
||||||
|
"version": "1.0.0",
|
||||||
"id": "93dc02a4-d05b-48ed-b99c-c9b616af3402",
|
"id": "93dc02a4-d05b-48ed-b99c-c9b616af3402",
|
||||||
"type": "compel",
|
"type": "compel",
|
||||||
"inputs": {
|
"inputs": {
|
||||||
@@ -59,21 +64,20 @@
|
|||||||
"isOpen": true,
|
"isOpen": true,
|
||||||
"notes": "",
|
"notes": "",
|
||||||
"embedWorkflow": false,
|
"embedWorkflow": false,
|
||||||
"isIntermediate": true,
|
"isIntermediate": true
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
},
|
||||||
"width": 320,
|
"width": 320,
|
||||||
"height": 261,
|
"height": 235,
|
||||||
"position": {
|
"position": {
|
||||||
"x": 995.7263915923627,
|
"x": 1400,
|
||||||
"y": 239.67783573351227
|
"y": -75
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "55705012-79b9-4aac-9f26-c0b10309785b",
|
"id": "55705012-79b9-4aac-9f26-c0b10309785b",
|
||||||
"type": "invocation",
|
"type": "invocation",
|
||||||
"data": {
|
"data": {
|
||||||
|
"version": "1.0.0",
|
||||||
"id": "55705012-79b9-4aac-9f26-c0b10309785b",
|
"id": "55705012-79b9-4aac-9f26-c0b10309785b",
|
||||||
"type": "noise",
|
"type": "noise",
|
||||||
"inputs": {
|
"inputs": {
|
||||||
@@ -134,21 +138,92 @@
|
|||||||
"isOpen": true,
|
"isOpen": true,
|
||||||
"notes": "",
|
"notes": "",
|
||||||
"embedWorkflow": false,
|
"embedWorkflow": false,
|
||||||
"isIntermediate": true,
|
"isIntermediate": true
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
},
|
||||||
"width": 320,
|
"width": 320,
|
||||||
"height": 389,
|
"height": 364,
|
||||||
"position": {
|
"position": {
|
||||||
"x": 993.4442117555518,
|
"x": 1000,
|
||||||
"y": 605.6757415334787
|
"y": 350
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "dbcd2f98-d809-48c8-bf64-2635f88a2fe9",
|
||||||
|
"type": "invocation",
|
||||||
|
"data": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"id": "dbcd2f98-d809-48c8-bf64-2635f88a2fe9",
|
||||||
|
"type": "l2i",
|
||||||
|
"inputs": {
|
||||||
|
"tiled": {
|
||||||
|
"id": "24f5bc7b-f6a1-425d-8ab1-f50b4db5d0df",
|
||||||
|
"name": "tiled",
|
||||||
|
"type": "boolean",
|
||||||
|
"fieldKind": "input",
|
||||||
|
"label": "",
|
||||||
|
"value": false
|
||||||
|
},
|
||||||
|
"fp32": {
|
||||||
|
"id": "b146d873-ffb9-4767-986a-5360504841a2",
|
||||||
|
"name": "fp32",
|
||||||
|
"type": "boolean",
|
||||||
|
"fieldKind": "input",
|
||||||
|
"label": "",
|
||||||
|
"value": false
|
||||||
|
},
|
||||||
|
"latents": {
|
||||||
|
"id": "65441abd-7713-4b00-9d8d-3771404002e8",
|
||||||
|
"name": "latents",
|
||||||
|
"type": "LatentsField",
|
||||||
|
"fieldKind": "input",
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
|
"vae": {
|
||||||
|
"id": "a478b833-6e13-4611-9a10-842c89603c74",
|
||||||
|
"name": "vae",
|
||||||
|
"type": "VaeField",
|
||||||
|
"fieldKind": "input",
|
||||||
|
"label": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"outputs": {
|
||||||
|
"image": {
|
||||||
|
"id": "c87ae925-f858-417a-8940-8708ba9b4b53",
|
||||||
|
"name": "image",
|
||||||
|
"type": "ImageField",
|
||||||
|
"fieldKind": "output"
|
||||||
|
},
|
||||||
|
"width": {
|
||||||
|
"id": "4bcb8512-b5a1-45f1-9e52-6e92849f9d6c",
|
||||||
|
"name": "width",
|
||||||
|
"type": "integer",
|
||||||
|
"fieldKind": "output"
|
||||||
|
},
|
||||||
|
"height": {
|
||||||
|
"id": "23e41c00-a354-48e8-8f59-5875679c27ab",
|
||||||
|
"name": "height",
|
||||||
|
"type": "integer",
|
||||||
|
"fieldKind": "output"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"label": "",
|
||||||
|
"isOpen": true,
|
||||||
|
"notes": "",
|
||||||
|
"embedWorkflow": true,
|
||||||
|
"isIntermediate": false
|
||||||
|
},
|
||||||
|
"width": 320,
|
||||||
|
"height": 266,
|
||||||
|
"position": {
|
||||||
|
"x": 1800,
|
||||||
|
"y": 200
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "c8d55139-f380-4695-b7f2-8b3d1e1e3db8",
|
"id": "c8d55139-f380-4695-b7f2-8b3d1e1e3db8",
|
||||||
"type": "invocation",
|
"type": "invocation",
|
||||||
"data": {
|
"data": {
|
||||||
|
"version": "1.0.0",
|
||||||
"id": "c8d55139-f380-4695-b7f2-8b3d1e1e3db8",
|
"id": "c8d55139-f380-4695-b7f2-8b3d1e1e3db8",
|
||||||
"type": "main_model_loader",
|
"type": "main_model_loader",
|
||||||
"inputs": {
|
"inputs": {
|
||||||
@@ -186,24 +261,23 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"label": "",
|
"label": "",
|
||||||
"isOpen": true,
|
"isOpen": false,
|
||||||
"notes": "",
|
"notes": "",
|
||||||
"embedWorkflow": false,
|
"embedWorkflow": false,
|
||||||
"isIntermediate": true,
|
"isIntermediate": true
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
},
|
||||||
"width": 320,
|
"width": 320,
|
||||||
"height": 226,
|
"height": 32,
|
||||||
"position": {
|
"position": {
|
||||||
"x": 163.04436745878343,
|
"x": 1000,
|
||||||
"y": 254.63156870373479
|
"y": 200
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "7d8bf987-284f-413a-b2fd-d825445a5d6c",
|
"id": "7d8bf987-284f-413a-b2fd-d825445a5d6c",
|
||||||
"type": "invocation",
|
"type": "invocation",
|
||||||
"data": {
|
"data": {
|
||||||
|
"version": "1.0.0",
|
||||||
"id": "7d8bf987-284f-413a-b2fd-d825445a5d6c",
|
"id": "7d8bf987-284f-413a-b2fd-d825445a5d6c",
|
||||||
"type": "compel",
|
"type": "compel",
|
||||||
"inputs": {
|
"inputs": {
|
||||||
@@ -235,21 +309,20 @@
|
|||||||
"isOpen": true,
|
"isOpen": true,
|
||||||
"notes": "",
|
"notes": "",
|
||||||
"embedWorkflow": false,
|
"embedWorkflow": false,
|
||||||
"isIntermediate": true,
|
"isIntermediate": true
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
},
|
||||||
"width": 320,
|
"width": 320,
|
||||||
"height": 261,
|
"height": 235,
|
||||||
"position": {
|
"position": {
|
||||||
"x": 595.7263915923627,
|
"x": 1000,
|
||||||
"y": 239.67783573351227
|
"y": -75
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "ea94bc37-d995-4a83-aa99-4af42479f2f2",
|
"id": "ea94bc37-d995-4a83-aa99-4af42479f2f2",
|
||||||
"type": "invocation",
|
"type": "invocation",
|
||||||
"data": {
|
"data": {
|
||||||
|
"version": "1.0.0",
|
||||||
"id": "ea94bc37-d995-4a83-aa99-4af42479f2f2",
|
"id": "ea94bc37-d995-4a83-aa99-4af42479f2f2",
|
||||||
"type": "rand_int",
|
"type": "rand_int",
|
||||||
"inputs": {
|
"inputs": {
|
||||||
@@ -279,66 +352,51 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"label": "Random Seed",
|
"label": "Random Seed",
|
||||||
"isOpen": true,
|
"isOpen": false,
|
||||||
"notes": "",
|
"notes": "",
|
||||||
"embedWorkflow": false,
|
"embedWorkflow": false,
|
||||||
"isIntermediate": true,
|
"isIntermediate": true
|
||||||
"useCache": false,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
},
|
||||||
"width": 320,
|
"width": 320,
|
||||||
"height": 218,
|
"height": 32,
|
||||||
"position": {
|
"position": {
|
||||||
"x": 541.094822888628,
|
"x": 1000,
|
||||||
"y": 694.5704476446829
|
"y": 275
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "eea2702a-19fb-45b5-9d75-56b4211ec03c",
|
"id": "75899702-fa44-46d2-b2d5-3e17f234c3e7",
|
||||||
"type": "invocation",
|
"type": "invocation",
|
||||||
"data": {
|
"data": {
|
||||||
"id": "eea2702a-19fb-45b5-9d75-56b4211ec03c",
|
"version": "1.0.0",
|
||||||
|
"id": "75899702-fa44-46d2-b2d5-3e17f234c3e7",
|
||||||
"type": "denoise_latents",
|
"type": "denoise_latents",
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"positive_conditioning": {
|
|
||||||
"id": "90b7f4f8-ada7-4028-8100-d2e54f192052",
|
|
||||||
"name": "positive_conditioning",
|
|
||||||
"type": "ConditioningField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"negative_conditioning": {
|
|
||||||
"id": "9393779e-796c-4f64-b740-902a1177bf53",
|
|
||||||
"name": "negative_conditioning",
|
|
||||||
"type": "ConditioningField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"noise": {
|
"noise": {
|
||||||
"id": "8e17f1e5-4f98-40b1-b7f4-86aeeb4554c1",
|
"id": "8b18f3eb-40d2-45c1-9a9d-28d6af0dce2b",
|
||||||
"name": "noise",
|
"name": "noise",
|
||||||
"type": "LatentsField",
|
"type": "LatentsField",
|
||||||
"fieldKind": "input",
|
"fieldKind": "input",
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"steps": {
|
"steps": {
|
||||||
"id": "9b63302d-6bd2-42c9-ac13-9b1afb51af88",
|
"id": "0be4373c-46f3-441c-80a7-a4bb6ceb498c",
|
||||||
"name": "steps",
|
"name": "steps",
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"fieldKind": "input",
|
"fieldKind": "input",
|
||||||
"label": "",
|
"label": "",
|
||||||
"value": 10
|
"value": 36
|
||||||
},
|
},
|
||||||
"cfg_scale": {
|
"cfg_scale": {
|
||||||
"id": "87dd04d3-870e-49e1-98bf-af003a810109",
|
"id": "107267ce-4666-4cd7-94b3-7476b7973ae9",
|
||||||
"name": "cfg_scale",
|
"name": "cfg_scale",
|
||||||
"type": "FloatPolymorphic",
|
"type": "float",
|
||||||
"fieldKind": "input",
|
"fieldKind": "input",
|
||||||
"label": "",
|
"label": "",
|
||||||
"value": 7.5
|
"value": 7.5
|
||||||
},
|
},
|
||||||
"denoising_start": {
|
"denoising_start": {
|
||||||
"id": "f369d80f-4931-4740-9bcd-9f0620719fab",
|
"id": "d2ce9f0f-5fc2-48b2-b917-53442941e9a1",
|
||||||
"name": "denoising_start",
|
"name": "denoising_start",
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"fieldKind": "input",
|
"fieldKind": "input",
|
||||||
@@ -346,7 +404,7 @@
|
|||||||
"value": 0
|
"value": 0
|
||||||
},
|
},
|
||||||
"denoising_end": {
|
"denoising_end": {
|
||||||
"id": "747d10e5-6f02-445c-994c-0604d814de8c",
|
"id": "8ad51505-b8d0-422a-beb8-96fc6fc6b65f",
|
||||||
"name": "denoising_end",
|
"name": "denoising_end",
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"fieldKind": "input",
|
"fieldKind": "input",
|
||||||
@@ -354,71 +412,71 @@
|
|||||||
"value": 1
|
"value": 1
|
||||||
},
|
},
|
||||||
"scheduler": {
|
"scheduler": {
|
||||||
"id": "1de84a4e-3a24-4ec8-862b-16ce49633b9b",
|
"id": "53092874-a43b-4623-91a2-76e62fdb1f2e",
|
||||||
"name": "scheduler",
|
"name": "scheduler",
|
||||||
"type": "Scheduler",
|
"type": "Scheduler",
|
||||||
"fieldKind": "input",
|
"fieldKind": "input",
|
||||||
"label": "",
|
"label": "",
|
||||||
"value": "euler"
|
"value": "euler"
|
||||||
},
|
},
|
||||||
"unet": {
|
|
||||||
"id": "ffa6fef4-3ce2-4bdb-9296-9a834849489b",
|
|
||||||
"name": "unet",
|
|
||||||
"type": "UNetField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"control": {
|
"control": {
|
||||||
"id": "077b64cb-34be-4fcc-83f2-e399807a02bd",
|
"id": "7abe57cc-469d-437e-ad72-a18efa28215f",
|
||||||
"name": "control",
|
"name": "control",
|
||||||
"type": "ControlPolymorphic",
|
"type": "ControlField",
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"ip_adapter": {
|
|
||||||
"id": "1d6948f7-3a65-4a65-a20c-768b287251aa",
|
|
||||||
"name": "ip_adapter",
|
|
||||||
"type": "IPAdapterPolymorphic",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"t2i_adapter": {
|
|
||||||
"id": "75e67b09-952f-4083-aaf4-6b804d690412",
|
|
||||||
"name": "t2i_adapter",
|
|
||||||
"type": "T2IAdapterPolymorphic",
|
|
||||||
"fieldKind": "input",
|
"fieldKind": "input",
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"latents": {
|
"latents": {
|
||||||
"id": "334d4ba3-5a99-4195-82c5-86fb3f4f7d43",
|
"id": "add8bbe5-14d0-42d4-a867-9c65ab8dd129",
|
||||||
"name": "latents",
|
"name": "latents",
|
||||||
"type": "LatentsField",
|
"type": "LatentsField",
|
||||||
"fieldKind": "input",
|
"fieldKind": "input",
|
||||||
"label": ""
|
"label": ""
|
||||||
},
|
},
|
||||||
"denoise_mask": {
|
"denoise_mask": {
|
||||||
"id": "0d3dbdbf-b014-4e95-8b18-ff2ff9cb0bfa",
|
"id": "f373a190-0fc8-45b7-ae62-c4aa8e9687e1",
|
||||||
"name": "denoise_mask",
|
"name": "denoise_mask",
|
||||||
"type": "DenoiseMaskField",
|
"type": "DenoiseMaskField",
|
||||||
"fieldKind": "input",
|
"fieldKind": "input",
|
||||||
"label": ""
|
"label": ""
|
||||||
|
},
|
||||||
|
"positive_conditioning": {
|
||||||
|
"id": "c7160303-8a23-4f15-9197-855d48802a7f",
|
||||||
|
"name": "positive_conditioning",
|
||||||
|
"type": "ConditioningField",
|
||||||
|
"fieldKind": "input",
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
|
"negative_conditioning": {
|
||||||
|
"id": "fd750efa-1dfc-4d0b-accb-828e905ba320",
|
||||||
|
"name": "negative_conditioning",
|
||||||
|
"type": "ConditioningField",
|
||||||
|
"fieldKind": "input",
|
||||||
|
"label": ""
|
||||||
|
},
|
||||||
|
"unet": {
|
||||||
|
"id": "af1f41ba-ce2a-4314-8d7f-494bb5800381",
|
||||||
|
"name": "unet",
|
||||||
|
"type": "UNetField",
|
||||||
|
"fieldKind": "input",
|
||||||
|
"label": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"outputs": {
|
"outputs": {
|
||||||
"latents": {
|
"latents": {
|
||||||
"id": "70fa5bbc-0c38-41bb-861a-74d6d78d2f38",
|
"id": "8508d04d-f999-4a44-94d0-388ab1401d27",
|
||||||
"name": "latents",
|
"name": "latents",
|
||||||
"type": "LatentsField",
|
"type": "LatentsField",
|
||||||
"fieldKind": "output"
|
"fieldKind": "output"
|
||||||
},
|
},
|
||||||
"width": {
|
"width": {
|
||||||
"id": "98ee0e6c-82aa-4e8f-8be5-dc5f00ee47f0",
|
"id": "93dc8287-0a2a-4320-83a4-5e994b7ba23e",
|
||||||
"name": "width",
|
"name": "width",
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"fieldKind": "output"
|
"fieldKind": "output"
|
||||||
},
|
},
|
||||||
"height": {
|
"height": {
|
||||||
"id": "e8cb184a-5e1a-47c8-9695-4b8979564f5d",
|
"id": "d9862f5c-0ab5-46fa-8c29-5059bb581d96",
|
||||||
"name": "height",
|
"name": "height",
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"fieldKind": "output"
|
"fieldKind": "output"
|
||||||
@@ -428,95 +486,13 @@
|
|||||||
"isOpen": true,
|
"isOpen": true,
|
||||||
"notes": "",
|
"notes": "",
|
||||||
"embedWorkflow": false,
|
"embedWorkflow": false,
|
||||||
"isIntermediate": true,
|
"isIntermediate": true
|
||||||
"useCache": true,
|
|
||||||
"version": "1.4.0"
|
|
||||||
},
|
},
|
||||||
"width": 320,
|
"width": 320,
|
||||||
"height": 646,
|
"height": 558,
|
||||||
"position": {
|
"position": {
|
||||||
"x": 1476.5794704734735,
|
"x": 1400,
|
||||||
"y": 256.80174342731783
|
"y": 200
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "58c957f5-0d01-41fc-a803-b2bbf0413d4f",
|
|
||||||
"type": "invocation",
|
|
||||||
"data": {
|
|
||||||
"id": "58c957f5-0d01-41fc-a803-b2bbf0413d4f",
|
|
||||||
"type": "l2i",
|
|
||||||
"inputs": {
|
|
||||||
"metadata": {
|
|
||||||
"id": "ab375f12-0042-4410-9182-29e30db82c85",
|
|
||||||
"name": "metadata",
|
|
||||||
"type": "MetadataField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"latents": {
|
|
||||||
"id": "3a7e7efd-bff5-47d7-9d48-615127afee78",
|
|
||||||
"name": "latents",
|
|
||||||
"type": "LatentsField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"vae": {
|
|
||||||
"id": "a1f5f7a1-0795-4d58-b036-7820c0b0ef2b",
|
|
||||||
"name": "vae",
|
|
||||||
"type": "VaeField",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"tiled": {
|
|
||||||
"id": "da52059a-0cee-4668-942f-519aa794d739",
|
|
||||||
"name": "tiled",
|
|
||||||
"type": "boolean",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": false
|
|
||||||
},
|
|
||||||
"fp32": {
|
|
||||||
"id": "c4841df3-b24e-4140-be3b-ccd454c2522c",
|
|
||||||
"name": "fp32",
|
|
||||||
"type": "boolean",
|
|
||||||
"fieldKind": "input",
|
|
||||||
"label": "",
|
|
||||||
"value": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": {
|
|
||||||
"image": {
|
|
||||||
"id": "72d667d0-cf85-459d-abf2-28bd8b823fe7",
|
|
||||||
"name": "image",
|
|
||||||
"type": "ImageField",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"width": {
|
|
||||||
"id": "c8c907d8-1066-49d1-b9a6-83bdcd53addc",
|
|
||||||
"name": "width",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
},
|
|
||||||
"height": {
|
|
||||||
"id": "230f359c-b4ea-436c-b372-332d7dcdca85",
|
|
||||||
"name": "height",
|
|
||||||
"type": "integer",
|
|
||||||
"fieldKind": "output"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"label": "",
|
|
||||||
"isOpen": true,
|
|
||||||
"notes": "",
|
|
||||||
"embedWorkflow": false,
|
|
||||||
"isIntermediate": false,
|
|
||||||
"useCache": true,
|
|
||||||
"version": "1.0.0"
|
|
||||||
},
|
|
||||||
"width": 320,
|
|
||||||
"height": 267,
|
|
||||||
"position": {
|
|
||||||
"x": 2037.9648469717395,
|
|
||||||
"y": 426.10844427600136
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -546,52 +522,52 @@
|
|||||||
"type": "default"
|
"type": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"source": "55705012-79b9-4aac-9f26-c0b10309785b",
|
"source": "c8d55139-f380-4695-b7f2-8b3d1e1e3db8",
|
||||||
"sourceHandle": "noise",
|
"sourceHandle": "vae",
|
||||||
"target": "eea2702a-19fb-45b5-9d75-56b4211ec03c",
|
"target": "dbcd2f98-d809-48c8-bf64-2635f88a2fe9",
|
||||||
"targetHandle": "noise",
|
"targetHandle": "vae",
|
||||||
"id": "reactflow__edge-55705012-79b9-4aac-9f26-c0b10309785bnoise-eea2702a-19fb-45b5-9d75-56b4211ec03cnoise",
|
"id": "reactflow__edge-c8d55139-f380-4695-b7f2-8b3d1e1e3db8vae-dbcd2f98-d809-48c8-bf64-2635f88a2fe9vae",
|
||||||
|
"type": "default"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"source": "75899702-fa44-46d2-b2d5-3e17f234c3e7",
|
||||||
|
"sourceHandle": "latents",
|
||||||
|
"target": "dbcd2f98-d809-48c8-bf64-2635f88a2fe9",
|
||||||
|
"targetHandle": "latents",
|
||||||
|
"id": "reactflow__edge-75899702-fa44-46d2-b2d5-3e17f234c3e7latents-dbcd2f98-d809-48c8-bf64-2635f88a2fe9latents",
|
||||||
"type": "default"
|
"type": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"source": "7d8bf987-284f-413a-b2fd-d825445a5d6c",
|
"source": "7d8bf987-284f-413a-b2fd-d825445a5d6c",
|
||||||
"sourceHandle": "conditioning",
|
"sourceHandle": "conditioning",
|
||||||
"target": "eea2702a-19fb-45b5-9d75-56b4211ec03c",
|
"target": "75899702-fa44-46d2-b2d5-3e17f234c3e7",
|
||||||
"targetHandle": "positive_conditioning",
|
"targetHandle": "positive_conditioning",
|
||||||
"id": "reactflow__edge-7d8bf987-284f-413a-b2fd-d825445a5d6cconditioning-eea2702a-19fb-45b5-9d75-56b4211ec03cpositive_conditioning",
|
"id": "reactflow__edge-7d8bf987-284f-413a-b2fd-d825445a5d6cconditioning-75899702-fa44-46d2-b2d5-3e17f234c3e7positive_conditioning",
|
||||||
"type": "default"
|
"type": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"source": "93dc02a4-d05b-48ed-b99c-c9b616af3402",
|
"source": "93dc02a4-d05b-48ed-b99c-c9b616af3402",
|
||||||
"sourceHandle": "conditioning",
|
"sourceHandle": "conditioning",
|
||||||
"target": "eea2702a-19fb-45b5-9d75-56b4211ec03c",
|
"target": "75899702-fa44-46d2-b2d5-3e17f234c3e7",
|
||||||
"targetHandle": "negative_conditioning",
|
"targetHandle": "negative_conditioning",
|
||||||
"id": "reactflow__edge-93dc02a4-d05b-48ed-b99c-c9b616af3402conditioning-eea2702a-19fb-45b5-9d75-56b4211ec03cnegative_conditioning",
|
"id": "reactflow__edge-93dc02a4-d05b-48ed-b99c-c9b616af3402conditioning-75899702-fa44-46d2-b2d5-3e17f234c3e7negative_conditioning",
|
||||||
"type": "default"
|
"type": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"source": "c8d55139-f380-4695-b7f2-8b3d1e1e3db8",
|
"source": "c8d55139-f380-4695-b7f2-8b3d1e1e3db8",
|
||||||
"sourceHandle": "unet",
|
"sourceHandle": "unet",
|
||||||
"target": "eea2702a-19fb-45b5-9d75-56b4211ec03c",
|
"target": "75899702-fa44-46d2-b2d5-3e17f234c3e7",
|
||||||
"targetHandle": "unet",
|
"targetHandle": "unet",
|
||||||
"id": "reactflow__edge-c8d55139-f380-4695-b7f2-8b3d1e1e3db8unet-eea2702a-19fb-45b5-9d75-56b4211ec03cunet",
|
"id": "reactflow__edge-c8d55139-f380-4695-b7f2-8b3d1e1e3db8unet-75899702-fa44-46d2-b2d5-3e17f234c3e7unet",
|
||||||
"type": "default"
|
"type": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"source": "eea2702a-19fb-45b5-9d75-56b4211ec03c",
|
"source": "55705012-79b9-4aac-9f26-c0b10309785b",
|
||||||
"sourceHandle": "latents",
|
"sourceHandle": "noise",
|
||||||
"target": "58c957f5-0d01-41fc-a803-b2bbf0413d4f",
|
"target": "75899702-fa44-46d2-b2d5-3e17f234c3e7",
|
||||||
"targetHandle": "latents",
|
"targetHandle": "noise",
|
||||||
"id": "reactflow__edge-eea2702a-19fb-45b5-9d75-56b4211ec03clatents-58c957f5-0d01-41fc-a803-b2bbf0413d4flatents",
|
"id": "reactflow__edge-55705012-79b9-4aac-9f26-c0b10309785bnoise-75899702-fa44-46d2-b2d5-3e17f234c3e7noise",
|
||||||
"type": "default"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "c8d55139-f380-4695-b7f2-8b3d1e1e3db8",
|
|
||||||
"sourceHandle": "vae",
|
|
||||||
"target": "58c957f5-0d01-41fc-a803-b2bbf0413d4f",
|
|
||||||
"targetHandle": "vae",
|
|
||||||
"id": "reactflow__edge-c8d55139-f380-4695-b7f2-8b3d1e1e3db8vae-58c957f5-0d01-41fc-a803-b2bbf0413d4fvae",
|
|
||||||
"type": "default"
|
"type": "default"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
@echo off
|
@echo off
|
||||||
setlocal EnableExtensions EnableDelayedExpansion
|
setlocal EnableExtensions EnableDelayedExpansion
|
||||||
|
|
||||||
@rem This script requires the user to install Python 3.10 or higher. All other
|
@rem This script requires the user to install Python 3.9 or higher. All other
|
||||||
@rem requirements are downloaded as needed.
|
@rem requirements are downloaded as needed.
|
||||||
|
|
||||||
@rem change to the script's directory
|
@rem change to the script's directory
|
||||||
@@ -19,7 +19,7 @@ set INVOKEAI_VERSION=latest
|
|||||||
set INSTRUCTIONS=https://invoke-ai.github.io/InvokeAI/installation/INSTALL_AUTOMATED/
|
set INSTRUCTIONS=https://invoke-ai.github.io/InvokeAI/installation/INSTALL_AUTOMATED/
|
||||||
set TROUBLESHOOTING=https://invoke-ai.github.io/InvokeAI/installation/INSTALL_AUTOMATED/#troubleshooting
|
set TROUBLESHOOTING=https://invoke-ai.github.io/InvokeAI/installation/INSTALL_AUTOMATED/#troubleshooting
|
||||||
set PYTHON_URL=https://www.python.org/downloads/windows/
|
set PYTHON_URL=https://www.python.org/downloads/windows/
|
||||||
set MINIMUM_PYTHON_VERSION=3.10.0
|
set MINIMUM_PYTHON_VERSION=3.9.0
|
||||||
set PYTHON_URL=https://www.python.org/downloads/release/python-3109/
|
set PYTHON_URL=https://www.python.org/downloads/release/python-3109/
|
||||||
|
|
||||||
set err_msg=An error has occurred and the script could not continue.
|
set err_msg=An error has occurred and the script could not continue.
|
||||||
@@ -28,7 +28,8 @@ set err_msg=An error has occurred and the script could not continue.
|
|||||||
echo This script will install InvokeAI and its dependencies.
|
echo This script will install InvokeAI and its dependencies.
|
||||||
echo.
|
echo.
|
||||||
echo BEFORE YOU START PLEASE MAKE SURE TO DO THE FOLLOWING
|
echo BEFORE YOU START PLEASE MAKE SURE TO DO THE FOLLOWING
|
||||||
echo 1. Install python 3.10 or 3.11. Python version 3.9 is no longer supported.
|
echo 1. Install python 3.9 or 3.10. Python version 3.11 and above are
|
||||||
|
echo not supported at the moment.
|
||||||
echo 2. Double-click on the file WinLongPathsEnabled.reg in order to
|
echo 2. Double-click on the file WinLongPathsEnabled.reg in order to
|
||||||
echo enable long path support on your system.
|
echo enable long path support on your system.
|
||||||
echo 3. Install the Visual C++ core libraries.
|
echo 3. Install the Visual C++ core libraries.
|
||||||
@@ -45,19 +46,19 @@ echo ***** Checking and Updating Python *****
|
|||||||
|
|
||||||
call python --version >.tmp1 2>.tmp2
|
call python --version >.tmp1 2>.tmp2
|
||||||
if %errorlevel% == 1 (
|
if %errorlevel% == 1 (
|
||||||
set err_msg=Please install Python 3.10-11. See %INSTRUCTIONS% for details.
|
set err_msg=Please install Python 3.10. See %INSTRUCTIONS% for details.
|
||||||
goto err_exit
|
goto err_exit
|
||||||
)
|
)
|
||||||
|
|
||||||
for /f "tokens=2" %%i in (.tmp1) do set python_version=%%i
|
for /f "tokens=2" %%i in (.tmp1) do set python_version=%%i
|
||||||
if "%python_version%" == "" (
|
if "%python_version%" == "" (
|
||||||
set err_msg=No python was detected on your system. Please install Python version %MINIMUM_PYTHON_VERSION% or higher. We recommend Python 3.10.12 from %PYTHON_URL%
|
set err_msg=No python was detected on your system. Please install Python version %MINIMUM_PYTHON_VERSION% or higher. We recommend Python 3.10.9 from %PYTHON_URL%
|
||||||
goto err_exit
|
goto err_exit
|
||||||
)
|
)
|
||||||
|
|
||||||
call :compareVersions %MINIMUM_PYTHON_VERSION% %python_version%
|
call :compareVersions %MINIMUM_PYTHON_VERSION% %python_version%
|
||||||
if %errorlevel% == 1 (
|
if %errorlevel% == 1 (
|
||||||
set err_msg=Your version of Python is too low. You need at least %MINIMUM_PYTHON_VERSION% but you have %python_version%. We recommend Python 3.10.12 from %PYTHON_URL%
|
set err_msg=Your version of Python is too low. You need at least %MINIMUM_PYTHON_VERSION% but you have %python_version%. We recommend Python 3.10.9 from %PYTHON_URL%
|
||||||
goto err_exit
|
goto err_exit
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ cd $scriptdir
|
|||||||
|
|
||||||
function version { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'; }
|
function version { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'; }
|
||||||
|
|
||||||
MINIMUM_PYTHON_VERSION=3.10.0
|
MINIMUM_PYTHON_VERSION=3.9.0
|
||||||
MAXIMUM_PYTHON_VERSION=3.11.100
|
MAXIMUM_PYTHON_VERSION=3.11.100
|
||||||
PYTHON=""
|
PYTHON=""
|
||||||
for candidate in python3.11 python3.10 python3 python ; do
|
for candidate in python3.11 python3.10 python3.9 python3 python ; do
|
||||||
if ppath=`which $candidate`; then
|
if ppath=`which $candidate`; then
|
||||||
# when using `pyenv`, the executable for an inactive Python version will exist but will not be operational
|
# when using `pyenv`, the executable for an inactive Python version will exist but will not be operational
|
||||||
# we check that this found executable can actually run
|
# we check that this found executable can actually run
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from pathlib import Path
|
|||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
SUPPORTED_PYTHON = ">=3.10.0,<=3.11.100"
|
SUPPORTED_PYTHON = ">=3.9.0,<=3.11.100"
|
||||||
INSTALLER_REQS = ["rich", "semver", "requests", "plumbum", "prompt-toolkit"]
|
INSTALLER_REQS = ["rich", "semver", "requests", "plumbum", "prompt-toolkit"]
|
||||||
BOOTSTRAP_VENV_PREFIX = "invokeai-installer-tmp"
|
BOOTSTRAP_VENV_PREFIX = "invokeai-installer-tmp"
|
||||||
|
|
||||||
@@ -67,6 +67,7 @@ class Installer:
|
|||||||
# Cleaning up temporary directories on Windows results in a race condition
|
# Cleaning up temporary directories on Windows results in a race condition
|
||||||
# and a stack trace.
|
# and a stack trace.
|
||||||
# `ignore_cleanup_errors` was only added in Python 3.10
|
# `ignore_cleanup_errors` was only added in Python 3.10
|
||||||
|
# users of Python 3.9 will see a gnarly stack trace on installer exit
|
||||||
if OS == "Windows" and int(platform.python_version_tuple()[1]) >= 10:
|
if OS == "Windows" and int(platform.python_version_tuple()[1]) >= 10:
|
||||||
venv_dir = TemporaryDirectory(prefix=BOOTSTRAP_VENV_PREFIX, ignore_cleanup_errors=True)
|
venv_dir = TemporaryDirectory(prefix=BOOTSTRAP_VENV_PREFIX, ignore_cleanup_errors=True)
|
||||||
else:
|
else:
|
||||||
@@ -138,6 +139,13 @@ class Installer:
|
|||||||
except shutil.SameFileError:
|
except shutil.SameFileError:
|
||||||
venv.create(venv_dir, with_pip=True, symlinks=True)
|
venv.create(venv_dir, with_pip=True, symlinks=True)
|
||||||
|
|
||||||
|
# upgrade pip in Python 3.9 environments
|
||||||
|
if int(platform.python_version_tuple()[1]) == 9:
|
||||||
|
from plumbum import FG, local
|
||||||
|
|
||||||
|
pip = local[get_pip_from_venv(venv_dir)]
|
||||||
|
pip["install", "--upgrade", "pip"] & FG
|
||||||
|
|
||||||
return venv_dir
|
return venv_dir
|
||||||
|
|
||||||
def install(
|
def install(
|
||||||
@@ -244,7 +252,7 @@ class InvokeAiInstance:
|
|||||||
"numpy~=1.24.0", # choose versions that won't be uninstalled during phase 2
|
"numpy~=1.24.0", # choose versions that won't be uninstalled during phase 2
|
||||||
"urllib3~=1.26.0",
|
"urllib3~=1.26.0",
|
||||||
"requests~=2.28.0",
|
"requests~=2.28.0",
|
||||||
"torch==2.1.0",
|
"torch~=2.0.0",
|
||||||
"torchmetrics==0.11.4",
|
"torchmetrics==0.11.4",
|
||||||
"torchvision>=0.14.1",
|
"torchvision>=0.14.1",
|
||||||
"--force-reinstall",
|
"--force-reinstall",
|
||||||
@@ -460,10 +468,10 @@ def get_torch_source() -> (Union[str, None], str):
|
|||||||
url = "https://download.pytorch.org/whl/cpu"
|
url = "https://download.pytorch.org/whl/cpu"
|
||||||
|
|
||||||
if device == "cuda":
|
if device == "cuda":
|
||||||
url = "https://download.pytorch.org/whl/cu121"
|
url = "https://download.pytorch.org/whl/cu118"
|
||||||
optional_modules = "[xformers,onnx-cuda]"
|
optional_modules = "[xformers,onnx-cuda]"
|
||||||
if device == "cuda_and_dml":
|
if device == "cuda_and_dml":
|
||||||
url = "https://download.pytorch.org/whl/cu121"
|
url = "https://download.pytorch.org/whl/cu118"
|
||||||
optional_modules = "[xformers,onnx-directml]"
|
optional_modules = "[xformers,onnx-directml]"
|
||||||
|
|
||||||
# in all other cases, Torch wheels should be coming from PyPi as of Torch 1.13
|
# in all other cases, Torch wheels should be coming from PyPi as of Torch 1.13
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ def dest_path(dest=None) -> Path:
|
|||||||
path_completer = PathCompleter(
|
path_completer = PathCompleter(
|
||||||
only_directories=True,
|
only_directories=True,
|
||||||
expanduser=True,
|
expanduser=True,
|
||||||
get_paths=lambda: [browse_start], # noqa: B023
|
get_paths=lambda: [browse_start],
|
||||||
# get_paths=lambda: [".."].extend(list(browse_start.iterdir()))
|
# get_paths=lambda: [".."].extend(list(browse_start.iterdir()))
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -149,7 +149,7 @@ def dest_path(dest=None) -> Path:
|
|||||||
completer=path_completer,
|
completer=path_completer,
|
||||||
default=str(browse_start) + os.sep,
|
default=str(browse_start) + os.sep,
|
||||||
vi_mode=True,
|
vi_mode=True,
|
||||||
complete_while_typing=True,
|
complete_while_typing=True
|
||||||
# Test that this is not needed on Windows
|
# Test that this is not needed on Windows
|
||||||
# complete_style=CompleteStyle.READLINE_LIKE,
|
# complete_style=CompleteStyle.READLINE_LIKE,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Project homepage: https://github.com/invoke-ai/InvokeAI
|
|||||||
|
|
||||||
Preparations:
|
Preparations:
|
||||||
|
|
||||||
You will need to install Python 3.10 or higher for this installer
|
You will need to install Python 3.9 or higher for this installer
|
||||||
to work. Instructions are given here:
|
to work. Instructions are given here:
|
||||||
https://invoke-ai.github.io/InvokeAI/installation/INSTALL_AUTOMATED/
|
https://invoke-ai.github.io/InvokeAI/installation/INSTALL_AUTOMATED/
|
||||||
|
|
||||||
@@ -14,15 +14,15 @@ Preparations:
|
|||||||
python --version
|
python --version
|
||||||
|
|
||||||
If all is well, it will print "Python 3.X.X", where the version number
|
If all is well, it will print "Python 3.X.X", where the version number
|
||||||
is at least 3.10.*, and not higher than 3.11.*.
|
is at least 3.9.*, and not higher than 3.11.*.
|
||||||
|
|
||||||
If this works, check the version of the Python package manager, pip:
|
If this works, check the version of the Python package manager, pip:
|
||||||
|
|
||||||
pip --version
|
pip --version
|
||||||
|
|
||||||
You should get a message that indicates that the pip package
|
You should get a message that indicates that the pip package
|
||||||
installer was derived from Python 3.10 or 3.11. For example:
|
installer was derived from Python 3.9 or 3.10. For example:
|
||||||
"pip 22.0.1 from /usr/bin/pip (python 3.10)"
|
"pip 22.3.1 from /usr/bin/pip (python 3.9)"
|
||||||
|
|
||||||
Long Paths on Windows:
|
Long Paths on Windows:
|
||||||
|
|
||||||
|
|||||||
@@ -9,37 +9,41 @@ set INVOKEAI_ROOT=.
|
|||||||
:start
|
:start
|
||||||
echo Desired action:
|
echo Desired action:
|
||||||
echo 1. Generate images with the browser-based interface
|
echo 1. Generate images with the browser-based interface
|
||||||
echo 2. Run textual inversion training
|
echo 2. Explore InvokeAI nodes using a command-line interface
|
||||||
echo 3. Merge models (diffusers type only)
|
echo 3. Run textual inversion training
|
||||||
echo 4. Download and install models
|
echo 4. Merge models (diffusers type only)
|
||||||
echo 5. Change InvokeAI startup options
|
echo 5. Download and install models
|
||||||
echo 6. Re-run the configure script to fix a broken install or to complete a major upgrade
|
echo 6. Change InvokeAI startup options
|
||||||
echo 7. Open the developer console
|
echo 7. Re-run the configure script to fix a broken install or to complete a major upgrade
|
||||||
echo 8. Update InvokeAI
|
echo 8. Open the developer console
|
||||||
echo 9. Run the InvokeAI image database maintenance script
|
echo 9. Update InvokeAI
|
||||||
echo 10. Command-line help
|
echo 10. Run the InvokeAI image database maintenance script
|
||||||
|
echo 11. Command-line help
|
||||||
echo Q - Quit
|
echo Q - Quit
|
||||||
set /P choice="Please enter 1-10, Q: [1] "
|
set /P choice="Please enter 1-11, Q: [1] "
|
||||||
if not defined choice set choice=1
|
if not defined choice set choice=1
|
||||||
IF /I "%choice%" == "1" (
|
IF /I "%choice%" == "1" (
|
||||||
echo Starting the InvokeAI browser-based UI..
|
echo Starting the InvokeAI browser-based UI..
|
||||||
python .venv\Scripts\invokeai-web.exe %*
|
python .venv\Scripts\invokeai-web.exe %*
|
||||||
) ELSE IF /I "%choice%" == "2" (
|
) ELSE IF /I "%choice%" == "2" (
|
||||||
|
echo Starting the InvokeAI command-line..
|
||||||
|
python .venv\Scripts\invokeai.exe %*
|
||||||
|
) ELSE IF /I "%choice%" == "3" (
|
||||||
echo Starting textual inversion training..
|
echo Starting textual inversion training..
|
||||||
python .venv\Scripts\invokeai-ti.exe --gui
|
python .venv\Scripts\invokeai-ti.exe --gui
|
||||||
) ELSE IF /I "%choice%" == "3" (
|
) ELSE IF /I "%choice%" == "4" (
|
||||||
echo Starting model merging script..
|
echo Starting model merging script..
|
||||||
python .venv\Scripts\invokeai-merge.exe --gui
|
python .venv\Scripts\invokeai-merge.exe --gui
|
||||||
) ELSE IF /I "%choice%" == "4" (
|
) ELSE IF /I "%choice%" == "5" (
|
||||||
echo Running invokeai-model-install...
|
echo Running invokeai-model-install...
|
||||||
python .venv\Scripts\invokeai-model-install.exe
|
python .venv\Scripts\invokeai-model-install.exe
|
||||||
) ELSE IF /I "%choice%" == "5" (
|
|
||||||
echo Running invokeai-configure...
|
|
||||||
python .venv\Scripts\invokeai-configure.exe --skip-sd-weight --skip-support-models
|
|
||||||
) ELSE IF /I "%choice%" == "6" (
|
) ELSE IF /I "%choice%" == "6" (
|
||||||
echo Running invokeai-configure...
|
echo Running invokeai-configure...
|
||||||
python .venv\Scripts\invokeai-configure.exe --yes --skip-sd-weight
|
python .venv\Scripts\invokeai-configure.exe --skip-sd-weight --skip-support-models
|
||||||
) ELSE IF /I "%choice%" == "7" (
|
) ELSE IF /I "%choice%" == "7" (
|
||||||
|
echo Running invokeai-configure...
|
||||||
|
python .venv\Scripts\invokeai-configure.exe --yes --skip-sd-weight
|
||||||
|
) ELSE IF /I "%choice%" == "8" (
|
||||||
echo Developer Console
|
echo Developer Console
|
||||||
echo Python command is:
|
echo Python command is:
|
||||||
where python
|
where python
|
||||||
@@ -51,13 +55,13 @@ IF /I "%choice%" == "1" (
|
|||||||
echo *************************
|
echo *************************
|
||||||
echo *** Type `exit` to quit this shell and deactivate the Python virtual environment ***
|
echo *** Type `exit` to quit this shell and deactivate the Python virtual environment ***
|
||||||
call cmd /k
|
call cmd /k
|
||||||
) ELSE IF /I "%choice%" == "8" (
|
) ELSE IF /I "%choice%" == "9" (
|
||||||
echo Running invokeai-update...
|
echo Running invokeai-update...
|
||||||
python -m invokeai.frontend.install.invokeai_update
|
python -m invokeai.frontend.install.invokeai_update
|
||||||
) ELSE IF /I "%choice%" == "9" (
|
) ELSE IF /I "%choice%" == "10" (
|
||||||
echo Running the db maintenance script...
|
echo Running the db maintenance script...
|
||||||
python .venv\Scripts\invokeai-db-maintenance.exe
|
python .venv\Scripts\invokeai-db-maintenance.exe
|
||||||
) ELSE IF /I "%choice%" == "10" (
|
) ELSE IF /I "%choice%" == "11" (
|
||||||
echo Displaying command line help...
|
echo Displaying command line help...
|
||||||
python .venv\Scripts\invokeai-web.exe --help %*
|
python .venv\Scripts\invokeai-web.exe --help %*
|
||||||
pause
|
pause
|
||||||
|
|||||||
@@ -58,47 +58,52 @@ do_choice() {
|
|||||||
invokeai-web $PARAMS
|
invokeai-web $PARAMS
|
||||||
;;
|
;;
|
||||||
2)
|
2)
|
||||||
|
clear
|
||||||
|
printf "Explore InvokeAI nodes using a command-line interface\n"
|
||||||
|
invokeai $PARAMS
|
||||||
|
;;
|
||||||
|
3)
|
||||||
clear
|
clear
|
||||||
printf "Textual inversion training\n"
|
printf "Textual inversion training\n"
|
||||||
invokeai-ti --gui $PARAMS
|
invokeai-ti --gui $PARAMS
|
||||||
;;
|
;;
|
||||||
3)
|
4)
|
||||||
clear
|
clear
|
||||||
printf "Merge models (diffusers type only)\n"
|
printf "Merge models (diffusers type only)\n"
|
||||||
invokeai-merge --gui $PARAMS
|
invokeai-merge --gui $PARAMS
|
||||||
;;
|
;;
|
||||||
4)
|
5)
|
||||||
clear
|
clear
|
||||||
printf "Download and install models\n"
|
printf "Download and install models\n"
|
||||||
invokeai-model-install --root ${INVOKEAI_ROOT}
|
invokeai-model-install --root ${INVOKEAI_ROOT}
|
||||||
;;
|
;;
|
||||||
5)
|
6)
|
||||||
clear
|
clear
|
||||||
printf "Change InvokeAI startup options\n"
|
printf "Change InvokeAI startup options\n"
|
||||||
invokeai-configure --root ${INVOKEAI_ROOT} --skip-sd-weights --skip-support-models
|
invokeai-configure --root ${INVOKEAI_ROOT} --skip-sd-weights --skip-support-models
|
||||||
;;
|
;;
|
||||||
6)
|
7)
|
||||||
clear
|
clear
|
||||||
printf "Re-run the configure script to fix a broken install or to complete a major upgrade\n"
|
printf "Re-run the configure script to fix a broken install or to complete a major upgrade\n"
|
||||||
invokeai-configure --root ${INVOKEAI_ROOT} --yes --default_only --skip-sd-weights
|
invokeai-configure --root ${INVOKEAI_ROOT} --yes --default_only --skip-sd-weights
|
||||||
;;
|
;;
|
||||||
7)
|
8)
|
||||||
clear
|
clear
|
||||||
printf "Open the developer console\n"
|
printf "Open the developer console\n"
|
||||||
file_name=$(basename "${BASH_SOURCE[0]}")
|
file_name=$(basename "${BASH_SOURCE[0]}")
|
||||||
bash --init-file "$file_name"
|
bash --init-file "$file_name"
|
||||||
;;
|
;;
|
||||||
8)
|
9)
|
||||||
clear
|
clear
|
||||||
printf "Update InvokeAI\n"
|
printf "Update InvokeAI\n"
|
||||||
python -m invokeai.frontend.install.invokeai_update
|
python -m invokeai.frontend.install.invokeai_update
|
||||||
;;
|
;;
|
||||||
9)
|
10)
|
||||||
clear
|
clear
|
||||||
printf "Running the db maintenance script\n"
|
printf "Running the db maintenance script\n"
|
||||||
invokeai-db-maintenance --root ${INVOKEAI_ROOT}
|
invokeai-db-maintenance --root ${INVOKEAI_ROOT}
|
||||||
;;
|
;;
|
||||||
10)
|
11)
|
||||||
clear
|
clear
|
||||||
printf "Command-line help\n"
|
printf "Command-line help\n"
|
||||||
invokeai-web --help
|
invokeai-web --help
|
||||||
@@ -116,15 +121,16 @@ do_choice() {
|
|||||||
do_dialog() {
|
do_dialog() {
|
||||||
options=(
|
options=(
|
||||||
1 "Generate images with a browser-based interface"
|
1 "Generate images with a browser-based interface"
|
||||||
2 "Textual inversion training"
|
2 "Explore InvokeAI nodes using a command-line interface"
|
||||||
3 "Merge models (diffusers type only)"
|
3 "Textual inversion training"
|
||||||
4 "Download and install models"
|
4 "Merge models (diffusers type only)"
|
||||||
5 "Change InvokeAI startup options"
|
5 "Download and install models"
|
||||||
6 "Re-run the configure script to fix a broken install or to complete a major upgrade"
|
6 "Change InvokeAI startup options"
|
||||||
7 "Open the developer console"
|
7 "Re-run the configure script to fix a broken install or to complete a major upgrade"
|
||||||
8 "Update InvokeAI"
|
8 "Open the developer console"
|
||||||
9 "Run the InvokeAI image database maintenance script"
|
9 "Update InvokeAI"
|
||||||
10 "Command-line help"
|
10 "Run the InvokeAI image database maintenance script"
|
||||||
|
11 "Command-line help"
|
||||||
)
|
)
|
||||||
|
|
||||||
choice=$(dialog --clear \
|
choice=$(dialog --clear \
|
||||||
@@ -149,17 +155,18 @@ do_line_input() {
|
|||||||
printf " ** For a more attractive experience, please install the 'dialog' utility using your package manager. **\n\n"
|
printf " ** For a more attractive experience, please install the 'dialog' utility using your package manager. **\n\n"
|
||||||
printf "What would you like to do?\n"
|
printf "What would you like to do?\n"
|
||||||
printf "1: Generate images using the browser-based interface\n"
|
printf "1: Generate images using the browser-based interface\n"
|
||||||
printf "2: Run textual inversion training\n"
|
printf "2: Explore InvokeAI nodes using the command-line interface\n"
|
||||||
printf "3: Merge models (diffusers type only)\n"
|
printf "3: Run textual inversion training\n"
|
||||||
printf "4: Download and install models\n"
|
printf "4: Merge models (diffusers type only)\n"
|
||||||
printf "5: Change InvokeAI startup options\n"
|
printf "5: Download and install models\n"
|
||||||
printf "6: Re-run the configure script to fix a broken install\n"
|
printf "6: Change InvokeAI startup options\n"
|
||||||
printf "7: Open the developer console\n"
|
printf "7: Re-run the configure script to fix a broken install\n"
|
||||||
printf "8: Update InvokeAI\n"
|
printf "8: Open the developer console\n"
|
||||||
printf "9: Run the InvokeAI image database maintenance script\n"
|
printf "9: Update InvokeAI\n"
|
||||||
printf "10: Command-line help\n"
|
printf "10: Run the InvokeAI image database maintenance script\n"
|
||||||
|
printf "11: Command-line help\n"
|
||||||
printf "Q: Quit\n\n"
|
printf "Q: Quit\n\n"
|
||||||
read -p "Please enter 1-10, Q: [1] " yn
|
read -p "Please enter 1-11, Q: [1] " yn
|
||||||
choice=${yn:='1'}
|
choice=${yn:='1'}
|
||||||
do_choice $choice
|
do_choice $choice
|
||||||
clear
|
clear
|
||||||
|
|||||||
@@ -1,38 +1,35 @@
|
|||||||
# Copyright (c) 2022 Kyle Schouviller (https://github.com/kyle0654)
|
# Copyright (c) 2022 Kyle Schouviller (https://github.com/kyle0654)
|
||||||
|
|
||||||
|
import sqlite3
|
||||||
from logging import Logger
|
from logging import Logger
|
||||||
|
|
||||||
from invokeai.app.services.workflow_image_records.workflow_image_records_sqlite import SqliteWorkflowImageRecordsStorage
|
from invokeai.app.services.board_image_record_storage import SqliteBoardImageRecordStorage
|
||||||
|
from invokeai.app.services.board_images import BoardImagesService, BoardImagesServiceDependencies
|
||||||
|
from invokeai.app.services.board_record_storage import SqliteBoardRecordStorage
|
||||||
|
from invokeai.app.services.boards import BoardService, BoardServiceDependencies
|
||||||
|
from invokeai.app.services.config import InvokeAIAppConfig
|
||||||
|
from invokeai.app.services.image_record_storage import SqliteImageRecordStorage
|
||||||
|
from invokeai.app.services.images import ImageService, ImageServiceDependencies
|
||||||
|
from invokeai.app.services.invocation_cache.invocation_cache_memory import MemoryInvocationCache
|
||||||
|
from invokeai.app.services.resource_name import SimpleNameService
|
||||||
|
from invokeai.app.services.session_processor.session_processor_default import DefaultSessionProcessor
|
||||||
|
from invokeai.app.services.session_queue.session_queue_sqlite import SqliteSessionQueue
|
||||||
|
from invokeai.app.services.urls import LocalUrlService
|
||||||
from invokeai.backend.util.logging import InvokeAILogger
|
from invokeai.backend.util.logging import InvokeAILogger
|
||||||
from invokeai.version.invokeai_version import __version__
|
from invokeai.version.invokeai_version import __version__
|
||||||
|
|
||||||
from ..services.board_image_records.board_image_records_sqlite import SqliteBoardImageRecordStorage
|
from ..services.default_graphs import create_system_graphs
|
||||||
from ..services.board_images.board_images_default import BoardImagesService
|
from ..services.graph import GraphExecutionState, LibraryGraph
|
||||||
from ..services.board_records.board_records_sqlite import SqliteBoardRecordStorage
|
from ..services.image_file_storage import DiskImageFileStorage
|
||||||
from ..services.boards.boards_default import BoardService
|
from ..services.invocation_queue import MemoryInvocationQueue
|
||||||
from ..services.config import InvokeAIAppConfig
|
|
||||||
from ..services.image_files.image_files_disk import DiskImageFileStorage
|
|
||||||
from ..services.image_records.image_records_sqlite import SqliteImageRecordStorage
|
|
||||||
from ..services.images.images_default import ImageService
|
|
||||||
from ..services.invocation_cache.invocation_cache_memory import MemoryInvocationCache
|
|
||||||
from ..services.invocation_processor.invocation_processor_default import DefaultInvocationProcessor
|
|
||||||
from ..services.invocation_queue.invocation_queue_memory import MemoryInvocationQueue
|
|
||||||
from ..services.invocation_services import InvocationServices
|
from ..services.invocation_services import InvocationServices
|
||||||
from ..services.invocation_stats.invocation_stats_default import InvocationStatsService
|
from ..services.invocation_stats import InvocationStatsService
|
||||||
from ..services.invoker import Invoker
|
from ..services.invoker import Invoker
|
||||||
from ..services.item_storage.item_storage_sqlite import SqliteItemStorage
|
from ..services.latent_storage import DiskLatentsStorage, ForwardCacheLatentsStorage
|
||||||
from ..services.latents_storage.latents_storage_disk import DiskLatentsStorage
|
from ..services.model_manager_service import ModelManagerService
|
||||||
from ..services.latents_storage.latents_storage_forward_cache import ForwardCacheLatentsStorage
|
from ..services.processor import DefaultInvocationProcessor
|
||||||
from ..services.model_manager.model_manager_default import ModelManagerService
|
from ..services.sqlite import SqliteItemStorage
|
||||||
from ..services.model_records import ModelRecordServiceSQL
|
from ..services.thread import lock
|
||||||
from ..services.names.names_default import SimpleNameService
|
|
||||||
from ..services.session_processor.session_processor_default import DefaultSessionProcessor
|
|
||||||
from ..services.session_queue.session_queue_sqlite import SqliteSessionQueue
|
|
||||||
from ..services.shared.default_graphs import create_system_graphs
|
|
||||||
from ..services.shared.graph import GraphExecutionState, LibraryGraph
|
|
||||||
from ..services.shared.sqlite import SqliteDatabase
|
|
||||||
from ..services.urls.urls_default import LocalUrlService
|
|
||||||
from ..services.workflow_records.workflow_records_sqlite import SqliteWorkflowRecordsStorage
|
|
||||||
from .events import FastAPIEventService
|
from .events import FastAPIEventService
|
||||||
|
|
||||||
|
|
||||||
@@ -66,70 +63,100 @@ class ApiDependencies:
|
|||||||
logger.info(f"Root directory = {str(config.root_path)}")
|
logger.info(f"Root directory = {str(config.root_path)}")
|
||||||
logger.debug(f"Internet connectivity is {config.internet_available}")
|
logger.debug(f"Internet connectivity is {config.internet_available}")
|
||||||
|
|
||||||
|
events = FastAPIEventService(event_handler_id)
|
||||||
|
|
||||||
output_folder = config.output_path
|
output_folder = config.output_path
|
||||||
|
|
||||||
db = SqliteDatabase(config, logger)
|
# TODO: build a file/path manager?
|
||||||
|
if config.use_memory_db:
|
||||||
|
db_location = ":memory:"
|
||||||
|
else:
|
||||||
|
db_path = config.db_path
|
||||||
|
db_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
db_location = str(db_path)
|
||||||
|
|
||||||
configuration = config
|
logger.info(f"Using database at {db_location}")
|
||||||
logger = logger
|
db_conn = sqlite3.connect(db_location, check_same_thread=False) # TODO: figure out a better threading solution
|
||||||
|
|
||||||
|
if config.log_sql:
|
||||||
|
db_conn.set_trace_callback(print)
|
||||||
|
db_conn.execute("PRAGMA foreign_keys = ON;")
|
||||||
|
|
||||||
|
graph_execution_manager = SqliteItemStorage[GraphExecutionState](
|
||||||
|
conn=db_conn, table_name="graph_executions", lock=lock
|
||||||
|
)
|
||||||
|
|
||||||
board_image_records = SqliteBoardImageRecordStorage(db=db)
|
|
||||||
board_images = BoardImagesService()
|
|
||||||
board_records = SqliteBoardRecordStorage(db=db)
|
|
||||||
boards = BoardService()
|
|
||||||
events = FastAPIEventService(event_handler_id)
|
|
||||||
graph_execution_manager = SqliteItemStorage[GraphExecutionState](db=db, table_name="graph_executions")
|
|
||||||
graph_library = SqliteItemStorage[LibraryGraph](db=db, table_name="graphs")
|
|
||||||
image_files = DiskImageFileStorage(f"{output_folder}/images")
|
|
||||||
image_records = SqliteImageRecordStorage(db=db)
|
|
||||||
images = ImageService()
|
|
||||||
invocation_cache = MemoryInvocationCache(max_cache_size=config.node_cache_size)
|
|
||||||
latents = ForwardCacheLatentsStorage(DiskLatentsStorage(f"{output_folder}/latents"))
|
|
||||||
model_manager = ModelManagerService(config, logger)
|
|
||||||
model_record_service = ModelRecordServiceSQL(db=db)
|
|
||||||
names = SimpleNameService()
|
|
||||||
performance_statistics = InvocationStatsService()
|
|
||||||
processor = DefaultInvocationProcessor()
|
|
||||||
queue = MemoryInvocationQueue()
|
|
||||||
session_processor = DefaultSessionProcessor()
|
|
||||||
session_queue = SqliteSessionQueue(db=db)
|
|
||||||
urls = LocalUrlService()
|
urls = LocalUrlService()
|
||||||
workflow_image_records = SqliteWorkflowImageRecordsStorage(db=db)
|
image_record_storage = SqliteImageRecordStorage(conn=db_conn, lock=lock)
|
||||||
workflow_records = SqliteWorkflowRecordsStorage(db=db)
|
image_file_storage = DiskImageFileStorage(f"{output_folder}/images")
|
||||||
|
names = SimpleNameService()
|
||||||
|
latents = ForwardCacheLatentsStorage(DiskLatentsStorage(f"{output_folder}/latents"))
|
||||||
|
|
||||||
|
board_record_storage = SqliteBoardRecordStorage(conn=db_conn, lock=lock)
|
||||||
|
board_image_record_storage = SqliteBoardImageRecordStorage(conn=db_conn, lock=lock)
|
||||||
|
|
||||||
|
boards = BoardService(
|
||||||
|
services=BoardServiceDependencies(
|
||||||
|
board_image_record_storage=board_image_record_storage,
|
||||||
|
board_record_storage=board_record_storage,
|
||||||
|
image_record_storage=image_record_storage,
|
||||||
|
url=urls,
|
||||||
|
logger=logger,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
board_images = BoardImagesService(
|
||||||
|
services=BoardImagesServiceDependencies(
|
||||||
|
board_image_record_storage=board_image_record_storage,
|
||||||
|
board_record_storage=board_record_storage,
|
||||||
|
image_record_storage=image_record_storage,
|
||||||
|
url=urls,
|
||||||
|
logger=logger,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
images = ImageService(
|
||||||
|
services=ImageServiceDependencies(
|
||||||
|
board_image_record_storage=board_image_record_storage,
|
||||||
|
image_record_storage=image_record_storage,
|
||||||
|
image_file_storage=image_file_storage,
|
||||||
|
url=urls,
|
||||||
|
logger=logger,
|
||||||
|
names=names,
|
||||||
|
graph_execution_manager=graph_execution_manager,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
services = InvocationServices(
|
services = InvocationServices(
|
||||||
board_image_records=board_image_records,
|
model_manager=ModelManagerService(config, logger),
|
||||||
board_images=board_images,
|
|
||||||
board_records=board_records,
|
|
||||||
boards=boards,
|
|
||||||
configuration=configuration,
|
|
||||||
events=events,
|
events=events,
|
||||||
graph_execution_manager=graph_execution_manager,
|
|
||||||
graph_library=graph_library,
|
|
||||||
image_files=image_files,
|
|
||||||
image_records=image_records,
|
|
||||||
images=images,
|
|
||||||
invocation_cache=invocation_cache,
|
|
||||||
latents=latents,
|
latents=latents,
|
||||||
|
images=images,
|
||||||
|
boards=boards,
|
||||||
|
board_images=board_images,
|
||||||
|
queue=MemoryInvocationQueue(),
|
||||||
|
graph_library=SqliteItemStorage[LibraryGraph](conn=db_conn, lock=lock, table_name="graphs"),
|
||||||
|
graph_execution_manager=graph_execution_manager,
|
||||||
|
processor=DefaultInvocationProcessor(),
|
||||||
|
configuration=config,
|
||||||
|
performance_statistics=InvocationStatsService(graph_execution_manager),
|
||||||
logger=logger,
|
logger=logger,
|
||||||
model_manager=model_manager,
|
session_queue=SqliteSessionQueue(conn=db_conn, lock=lock),
|
||||||
model_records=model_record_service,
|
session_processor=DefaultSessionProcessor(),
|
||||||
names=names,
|
invocation_cache=MemoryInvocationCache(max_cache_size=config.node_cache_size),
|
||||||
performance_statistics=performance_statistics,
|
|
||||||
processor=processor,
|
|
||||||
queue=queue,
|
|
||||||
session_processor=session_processor,
|
|
||||||
session_queue=session_queue,
|
|
||||||
urls=urls,
|
|
||||||
workflow_image_records=workflow_image_records,
|
|
||||||
workflow_records=workflow_records,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
create_system_graphs(services.graph_library)
|
create_system_graphs(services.graph_library)
|
||||||
|
|
||||||
ApiDependencies.invoker = Invoker(services)
|
ApiDependencies.invoker = Invoker(services)
|
||||||
|
|
||||||
db.clean()
|
try:
|
||||||
|
lock.acquire()
|
||||||
|
db_conn.execute("VACUUM;")
|
||||||
|
db_conn.commit()
|
||||||
|
logger.info("Cleaned database")
|
||||||
|
finally:
|
||||||
|
lock.release()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def shutdown():
|
def shutdown():
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from typing import Any
|
|||||||
|
|
||||||
from fastapi_events.dispatcher import dispatch
|
from fastapi_events.dispatcher import dispatch
|
||||||
|
|
||||||
from ..services.events.events_base import EventServiceBase
|
from ..services.events import EventServiceBase
|
||||||
|
|
||||||
|
|
||||||
class FastAPIEventService(EventServiceBase):
|
class FastAPIEventService(EventServiceBase):
|
||||||
@@ -28,7 +28,7 @@ class FastAPIEventService(EventServiceBase):
|
|||||||
self.__queue.put(None)
|
self.__queue.put(None)
|
||||||
|
|
||||||
def dispatch(self, event_name: str, payload: Any) -> None:
|
def dispatch(self, event_name: str, payload: Any) -> None:
|
||||||
self.__queue.put({"event_name": event_name, "payload": payload})
|
self.__queue.put(dict(event_name=event_name, payload=payload))
|
||||||
|
|
||||||
async def __dispatch_from_queue(self, stop_event: threading.Event):
|
async def __dispatch_from_queue(self, stop_event: threading.Event):
|
||||||
"""Get events on from the queue and dispatch them, from the correct thread"""
|
"""Get events on from the queue and dispatch them, from the correct thread"""
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ from fastapi import Body, HTTPException, Path, Query
|
|||||||
from fastapi.routing import APIRouter
|
from fastapi.routing import APIRouter
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
from invokeai.app.services.board_records.board_records_common import BoardChanges
|
from invokeai.app.services.board_record_storage import BoardChanges
|
||||||
from invokeai.app.services.boards.boards_common import BoardDTO
|
from invokeai.app.services.image_record_storage import OffsetPaginatedResults
|
||||||
from invokeai.app.services.shared.pagination import OffsetPaginatedResults
|
from invokeai.app.services.models.board_record import BoardDTO
|
||||||
|
|
||||||
from ..dependencies import ApiDependencies
|
from ..dependencies import ApiDependencies
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
import io
|
import io
|
||||||
import traceback
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from fastapi import Body, HTTPException, Path, Query, Request, Response, UploadFile
|
from fastapi import Body, HTTPException, Path, Query, Request, Response, UploadFile
|
||||||
from fastapi.responses import FileResponse
|
from fastapi.responses import FileResponse
|
||||||
from fastapi.routing import APIRouter
|
from fastapi.routing import APIRouter
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from pydantic import BaseModel, Field, ValidationError
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
from invokeai.app.invocations.baseinvocation import MetadataField, MetadataFieldValidator, WorkflowFieldValidator
|
from invokeai.app.invocations.metadata import ImageMetadata
|
||||||
from invokeai.app.services.image_records.image_records_common import ImageCategory, ImageRecordChanges, ResourceOrigin
|
from invokeai.app.models.image import ImageCategory, ResourceOrigin
|
||||||
from invokeai.app.services.images.images_common import ImageDTO, ImageUrlsDTO
|
from invokeai.app.services.image_record_storage import OffsetPaginatedResults
|
||||||
from invokeai.app.services.shared.pagination import OffsetPaginatedResults
|
from invokeai.app.services.models.image_record import ImageDTO, ImageRecordChanges, ImageUrlsDTO
|
||||||
|
|
||||||
from ..dependencies import ApiDependencies
|
from ..dependencies import ApiDependencies
|
||||||
|
|
||||||
@@ -43,41 +42,20 @@ async def upload_image(
|
|||||||
crop_visible: Optional[bool] = Query(default=False, description="Whether to crop the image"),
|
crop_visible: Optional[bool] = Query(default=False, description="Whether to crop the image"),
|
||||||
) -> ImageDTO:
|
) -> ImageDTO:
|
||||||
"""Uploads an image"""
|
"""Uploads an image"""
|
||||||
if not file.content_type or not file.content_type.startswith("image"):
|
if not file.content_type.startswith("image"):
|
||||||
raise HTTPException(status_code=415, detail="Not an image")
|
raise HTTPException(status_code=415, detail="Not an image")
|
||||||
|
|
||||||
metadata = None
|
|
||||||
workflow = None
|
|
||||||
|
|
||||||
contents = await file.read()
|
contents = await file.read()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
pil_image = Image.open(io.BytesIO(contents))
|
pil_image = Image.open(io.BytesIO(contents))
|
||||||
if crop_visible:
|
if crop_visible:
|
||||||
bbox = pil_image.getbbox()
|
bbox = pil_image.getbbox()
|
||||||
pil_image = pil_image.crop(bbox)
|
pil_image = pil_image.crop(bbox)
|
||||||
except Exception:
|
except Exception:
|
||||||
ApiDependencies.invoker.services.logger.error(traceback.format_exc())
|
# Error opening the image
|
||||||
raise HTTPException(status_code=415, detail="Failed to read image")
|
raise HTTPException(status_code=415, detail="Failed to read image")
|
||||||
|
|
||||||
# TODO: retain non-invokeai metadata on upload?
|
|
||||||
# attempt to parse metadata from image
|
|
||||||
metadata_raw = pil_image.info.get("invokeai_metadata", None)
|
|
||||||
if metadata_raw:
|
|
||||||
try:
|
|
||||||
metadata = MetadataFieldValidator.validate_json(metadata_raw)
|
|
||||||
except ValidationError:
|
|
||||||
ApiDependencies.invoker.services.logger.warn("Failed to parse metadata for uploaded image")
|
|
||||||
pass
|
|
||||||
|
|
||||||
# attempt to parse workflow from image
|
|
||||||
workflow_raw = pil_image.info.get("invokeai_workflow", None)
|
|
||||||
if workflow_raw is not None:
|
|
||||||
try:
|
|
||||||
workflow = WorkflowFieldValidator.validate_json(workflow_raw)
|
|
||||||
except ValidationError:
|
|
||||||
ApiDependencies.invoker.services.logger.warn("Failed to parse metadata for uploaded image")
|
|
||||||
pass
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
image_dto = ApiDependencies.invoker.services.images.create(
|
image_dto = ApiDependencies.invoker.services.images.create(
|
||||||
image=pil_image,
|
image=pil_image,
|
||||||
@@ -85,8 +63,6 @@ async def upload_image(
|
|||||||
image_category=image_category,
|
image_category=image_category,
|
||||||
session_id=session_id,
|
session_id=session_id,
|
||||||
board_id=board_id,
|
board_id=board_id,
|
||||||
metadata=metadata,
|
|
||||||
workflow=workflow,
|
|
||||||
is_intermediate=is_intermediate,
|
is_intermediate=is_intermediate,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -95,7 +71,6 @@ async def upload_image(
|
|||||||
|
|
||||||
return image_dto
|
return image_dto
|
||||||
except Exception:
|
except Exception:
|
||||||
ApiDependencies.invoker.services.logger.error(traceback.format_exc())
|
|
||||||
raise HTTPException(status_code=500, detail="Failed to create image")
|
raise HTTPException(status_code=500, detail="Failed to create image")
|
||||||
|
|
||||||
|
|
||||||
@@ -112,7 +87,7 @@ async def delete_image(
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@images_router.delete("/intermediates", operation_id="clear_intermediates")
|
@images_router.post("/clear-intermediates", operation_id="clear_intermediates")
|
||||||
async def clear_intermediates() -> int:
|
async def clear_intermediates() -> int:
|
||||||
"""Clears all intermediates"""
|
"""Clears all intermediates"""
|
||||||
|
|
||||||
@@ -124,17 +99,6 @@ async def clear_intermediates() -> int:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@images_router.get("/intermediates", operation_id="get_intermediates_count")
|
|
||||||
async def get_intermediates_count() -> int:
|
|
||||||
"""Gets the count of intermediate images"""
|
|
||||||
|
|
||||||
try:
|
|
||||||
return ApiDependencies.invoker.services.images.get_intermediates_count()
|
|
||||||
except Exception:
|
|
||||||
raise HTTPException(status_code=500, detail="Failed to get intermediates")
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@images_router.patch(
|
@images_router.patch(
|
||||||
"/i/{image_name}",
|
"/i/{image_name}",
|
||||||
operation_id="update_image",
|
operation_id="update_image",
|
||||||
@@ -171,11 +135,11 @@ async def get_image_dto(
|
|||||||
@images_router.get(
|
@images_router.get(
|
||||||
"/i/{image_name}/metadata",
|
"/i/{image_name}/metadata",
|
||||||
operation_id="get_image_metadata",
|
operation_id="get_image_metadata",
|
||||||
response_model=Optional[MetadataField],
|
response_model=ImageMetadata,
|
||||||
)
|
)
|
||||||
async def get_image_metadata(
|
async def get_image_metadata(
|
||||||
image_name: str = Path(description="The name of image to get"),
|
image_name: str = Path(description="The name of image to get"),
|
||||||
) -> Optional[MetadataField]:
|
) -> ImageMetadata:
|
||||||
"""Gets an image's metadata"""
|
"""Gets an image's metadata"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -1,164 +0,0 @@
|
|||||||
# Copyright (c) 2023 Lincoln D. Stein
|
|
||||||
"""FastAPI route for model configuration records."""
|
|
||||||
|
|
||||||
|
|
||||||
from hashlib import sha1
|
|
||||||
from random import randbytes
|
|
||||||
from typing import List, Optional
|
|
||||||
|
|
||||||
from fastapi import Body, Path, Query, Response
|
|
||||||
from fastapi.routing import APIRouter
|
|
||||||
from pydantic import BaseModel, ConfigDict
|
|
||||||
from starlette.exceptions import HTTPException
|
|
||||||
from typing_extensions import Annotated
|
|
||||||
|
|
||||||
from invokeai.app.services.model_records import (
|
|
||||||
DuplicateModelException,
|
|
||||||
InvalidModelException,
|
|
||||||
UnknownModelException,
|
|
||||||
)
|
|
||||||
from invokeai.backend.model_manager.config import (
|
|
||||||
AnyModelConfig,
|
|
||||||
BaseModelType,
|
|
||||||
ModelType,
|
|
||||||
)
|
|
||||||
|
|
||||||
from ..dependencies import ApiDependencies
|
|
||||||
|
|
||||||
model_records_router = APIRouter(prefix="/v1/model/record", tags=["models"])
|
|
||||||
|
|
||||||
|
|
||||||
class ModelsList(BaseModel):
|
|
||||||
"""Return list of configs."""
|
|
||||||
|
|
||||||
models: list[AnyModelConfig]
|
|
||||||
|
|
||||||
model_config = ConfigDict(use_enum_values=True)
|
|
||||||
|
|
||||||
|
|
||||||
@model_records_router.get(
|
|
||||||
"/",
|
|
||||||
operation_id="list_model_records",
|
|
||||||
)
|
|
||||||
async def list_model_records(
|
|
||||||
base_models: Optional[List[BaseModelType]] = Query(default=None, description="Base models to include"),
|
|
||||||
model_type: Optional[ModelType] = Query(default=None, description="The type of model to get"),
|
|
||||||
) -> ModelsList:
|
|
||||||
"""Get a list of models."""
|
|
||||||
record_store = ApiDependencies.invoker.services.model_records
|
|
||||||
found_models: list[AnyModelConfig] = []
|
|
||||||
if base_models:
|
|
||||||
for base_model in base_models:
|
|
||||||
found_models.extend(record_store.search_by_attr(base_model=base_model, model_type=model_type))
|
|
||||||
else:
|
|
||||||
found_models.extend(record_store.search_by_attr(model_type=model_type))
|
|
||||||
return ModelsList(models=found_models)
|
|
||||||
|
|
||||||
|
|
||||||
@model_records_router.get(
|
|
||||||
"/i/{key}",
|
|
||||||
operation_id="get_model_record",
|
|
||||||
responses={
|
|
||||||
200: {"description": "Success"},
|
|
||||||
400: {"description": "Bad request"},
|
|
||||||
404: {"description": "The model could not be found"},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
async def get_model_record(
|
|
||||||
key: str = Path(description="Key of the model record to fetch."),
|
|
||||||
) -> AnyModelConfig:
|
|
||||||
"""Get a model record"""
|
|
||||||
record_store = ApiDependencies.invoker.services.model_records
|
|
||||||
try:
|
|
||||||
return record_store.get_model(key)
|
|
||||||
except UnknownModelException as e:
|
|
||||||
raise HTTPException(status_code=404, detail=str(e))
|
|
||||||
|
|
||||||
|
|
||||||
@model_records_router.patch(
|
|
||||||
"/i/{key}",
|
|
||||||
operation_id="update_model_record",
|
|
||||||
responses={
|
|
||||||
200: {"description": "The model was updated successfully"},
|
|
||||||
400: {"description": "Bad request"},
|
|
||||||
404: {"description": "The model could not be found"},
|
|
||||||
409: {"description": "There is already a model corresponding to the new name"},
|
|
||||||
},
|
|
||||||
status_code=200,
|
|
||||||
response_model=AnyModelConfig,
|
|
||||||
)
|
|
||||||
async def update_model_record(
|
|
||||||
key: Annotated[str, Path(description="Unique key of model")],
|
|
||||||
info: Annotated[AnyModelConfig, Body(description="Model config", discriminator="type")],
|
|
||||||
) -> AnyModelConfig:
|
|
||||||
"""Update model contents with a new config. If the model name or base fields are changed, then the model is renamed."""
|
|
||||||
logger = ApiDependencies.invoker.services.logger
|
|
||||||
record_store = ApiDependencies.invoker.services.model_records
|
|
||||||
try:
|
|
||||||
model_response = record_store.update_model(key, config=info)
|
|
||||||
logger.info(f"Updated model: {key}")
|
|
||||||
except UnknownModelException as e:
|
|
||||||
raise HTTPException(status_code=404, detail=str(e))
|
|
||||||
except ValueError as e:
|
|
||||||
logger.error(str(e))
|
|
||||||
raise HTTPException(status_code=409, detail=str(e))
|
|
||||||
return model_response
|
|
||||||
|
|
||||||
|
|
||||||
@model_records_router.delete(
|
|
||||||
"/i/{key}",
|
|
||||||
operation_id="del_model_record",
|
|
||||||
responses={
|
|
||||||
204: {"description": "Model deleted successfully"},
|
|
||||||
404: {"description": "Model not found"},
|
|
||||||
},
|
|
||||||
status_code=204,
|
|
||||||
)
|
|
||||||
async def del_model_record(
|
|
||||||
key: str = Path(description="Unique key of model to remove from model registry."),
|
|
||||||
) -> Response:
|
|
||||||
"""Delete Model"""
|
|
||||||
logger = ApiDependencies.invoker.services.logger
|
|
||||||
|
|
||||||
try:
|
|
||||||
record_store = ApiDependencies.invoker.services.model_records
|
|
||||||
record_store.del_model(key)
|
|
||||||
logger.info(f"Deleted model: {key}")
|
|
||||||
return Response(status_code=204)
|
|
||||||
except UnknownModelException as e:
|
|
||||||
logger.error(str(e))
|
|
||||||
raise HTTPException(status_code=404, detail=str(e))
|
|
||||||
|
|
||||||
|
|
||||||
@model_records_router.post(
|
|
||||||
"/i/",
|
|
||||||
operation_id="add_model_record",
|
|
||||||
responses={
|
|
||||||
201: {"description": "The model added successfully"},
|
|
||||||
409: {"description": "There is already a model corresponding to this path or repo_id"},
|
|
||||||
415: {"description": "Unrecognized file/folder format"},
|
|
||||||
},
|
|
||||||
status_code=201,
|
|
||||||
)
|
|
||||||
async def add_model_record(
|
|
||||||
config: Annotated[AnyModelConfig, Body(description="Model config", discriminator="type")]
|
|
||||||
) -> AnyModelConfig:
|
|
||||||
"""
|
|
||||||
Add a model using the configuration information appropriate for its type.
|
|
||||||
"""
|
|
||||||
logger = ApiDependencies.invoker.services.logger
|
|
||||||
record_store = ApiDependencies.invoker.services.model_records
|
|
||||||
if config.key == "<NOKEY>":
|
|
||||||
config.key = sha1(randbytes(100)).hexdigest()
|
|
||||||
logger.info(f"Created model {config.key} for {config.name}")
|
|
||||||
try:
|
|
||||||
record_store.add_model(config.key, config)
|
|
||||||
except DuplicateModelException as e:
|
|
||||||
logger.error(str(e))
|
|
||||||
raise HTTPException(status_code=409, detail=str(e))
|
|
||||||
except InvalidModelException as e:
|
|
||||||
logger.error(str(e))
|
|
||||||
raise HTTPException(status_code=415)
|
|
||||||
|
|
||||||
# now fetch it out
|
|
||||||
return record_store.get_model(config.key)
|
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
# Copyright (c) 2023 Kyle Schouviller (https://github.com/kyle0654), 2023 Kent Keirsey (https://github.com/hipsterusername), 2023 Lincoln D. Stein
|
# Copyright (c) 2023 Kyle Schouviller (https://github.com/kyle0654), 2023 Kent Keirsey (https://github.com/hipsterusername), 2023 Lincoln D. Stein
|
||||||
|
|
||||||
|
|
||||||
import pathlib
|
import pathlib
|
||||||
from typing import Annotated, List, Literal, Optional, Union
|
from typing import List, Literal, Optional, Union
|
||||||
|
|
||||||
from fastapi import Body, Path, Query, Response
|
from fastapi import Body, Path, Query, Response
|
||||||
from fastapi.routing import APIRouter
|
from fastapi.routing import APIRouter
|
||||||
from pydantic import BaseModel, ConfigDict, Field, TypeAdapter
|
from pydantic import BaseModel, parse_obj_as
|
||||||
from starlette.exceptions import HTTPException
|
from starlette.exceptions import HTTPException
|
||||||
|
|
||||||
from invokeai.backend import BaseModelType, ModelType
|
from invokeai.backend import BaseModelType, ModelType
|
||||||
@@ -22,14 +23,8 @@ from ..dependencies import ApiDependencies
|
|||||||
models_router = APIRouter(prefix="/v1/models", tags=["models"])
|
models_router = APIRouter(prefix="/v1/models", tags=["models"])
|
||||||
|
|
||||||
UpdateModelResponse = Union[tuple(OPENAPI_MODEL_CONFIGS)]
|
UpdateModelResponse = Union[tuple(OPENAPI_MODEL_CONFIGS)]
|
||||||
UpdateModelResponseValidator = TypeAdapter(UpdateModelResponse)
|
|
||||||
|
|
||||||
ImportModelResponse = Union[tuple(OPENAPI_MODEL_CONFIGS)]
|
ImportModelResponse = Union[tuple(OPENAPI_MODEL_CONFIGS)]
|
||||||
ImportModelResponseValidator = TypeAdapter(ImportModelResponse)
|
|
||||||
|
|
||||||
ConvertModelResponse = Union[tuple(OPENAPI_MODEL_CONFIGS)]
|
ConvertModelResponse = Union[tuple(OPENAPI_MODEL_CONFIGS)]
|
||||||
ConvertModelResponseValidator = TypeAdapter(ConvertModelResponse)
|
|
||||||
|
|
||||||
MergeModelResponse = Union[tuple(OPENAPI_MODEL_CONFIGS)]
|
MergeModelResponse = Union[tuple(OPENAPI_MODEL_CONFIGS)]
|
||||||
ImportModelAttributes = Union[tuple(OPENAPI_MODEL_CONFIGS)]
|
ImportModelAttributes = Union[tuple(OPENAPI_MODEL_CONFIGS)]
|
||||||
|
|
||||||
@@ -37,11 +32,6 @@ ImportModelAttributes = Union[tuple(OPENAPI_MODEL_CONFIGS)]
|
|||||||
class ModelsList(BaseModel):
|
class ModelsList(BaseModel):
|
||||||
models: list[Union[tuple(OPENAPI_MODEL_CONFIGS)]]
|
models: list[Union[tuple(OPENAPI_MODEL_CONFIGS)]]
|
||||||
|
|
||||||
model_config = ConfigDict(use_enum_values=True)
|
|
||||||
|
|
||||||
|
|
||||||
ModelsListValidator = TypeAdapter(ModelsList)
|
|
||||||
|
|
||||||
|
|
||||||
@models_router.get(
|
@models_router.get(
|
||||||
"/",
|
"/",
|
||||||
@@ -54,12 +44,12 @@ async def list_models(
|
|||||||
) -> ModelsList:
|
) -> ModelsList:
|
||||||
"""Gets a list of models"""
|
"""Gets a list of models"""
|
||||||
if base_models and len(base_models) > 0:
|
if base_models and len(base_models) > 0:
|
||||||
models_raw = []
|
models_raw = list()
|
||||||
for base_model in base_models:
|
for base_model in base_models:
|
||||||
models_raw.extend(ApiDependencies.invoker.services.model_manager.list_models(base_model, model_type))
|
models_raw.extend(ApiDependencies.invoker.services.model_manager.list_models(base_model, model_type))
|
||||||
else:
|
else:
|
||||||
models_raw = ApiDependencies.invoker.services.model_manager.list_models(None, model_type)
|
models_raw = ApiDependencies.invoker.services.model_manager.list_models(None, model_type)
|
||||||
models = ModelsListValidator.validate_python({"models": models_raw})
|
models = parse_obj_as(ModelsList, {"models": models_raw})
|
||||||
return models
|
return models
|
||||||
|
|
||||||
|
|
||||||
@@ -115,14 +105,11 @@ async def update_model(
|
|||||||
info.path = new_info.get("path")
|
info.path = new_info.get("path")
|
||||||
|
|
||||||
# replace empty string values with None/null to avoid phenomenon of vae: ''
|
# replace empty string values with None/null to avoid phenomenon of vae: ''
|
||||||
info_dict = info.model_dump()
|
info_dict = info.dict()
|
||||||
info_dict = {x: info_dict[x] if info_dict[x] else None for x in info_dict.keys()}
|
info_dict = {x: info_dict[x] if info_dict[x] else None for x in info_dict.keys()}
|
||||||
|
|
||||||
ApiDependencies.invoker.services.model_manager.update_model(
|
ApiDependencies.invoker.services.model_manager.update_model(
|
||||||
model_name=model_name,
|
model_name=model_name, base_model=base_model, model_type=model_type, model_attributes=info_dict
|
||||||
base_model=base_model,
|
|
||||||
model_type=model_type,
|
|
||||||
model_attributes=info_dict,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
model_raw = ApiDependencies.invoker.services.model_manager.list_model(
|
model_raw = ApiDependencies.invoker.services.model_manager.list_model(
|
||||||
@@ -130,7 +117,7 @@ async def update_model(
|
|||||||
base_model=base_model,
|
base_model=base_model,
|
||||||
model_type=model_type,
|
model_type=model_type,
|
||||||
)
|
)
|
||||||
model_response = UpdateModelResponseValidator.validate_python(model_raw)
|
model_response = parse_obj_as(UpdateModelResponse, model_raw)
|
||||||
except ModelNotFoundException as e:
|
except ModelNotFoundException as e:
|
||||||
raise HTTPException(status_code=404, detail=str(e))
|
raise HTTPException(status_code=404, detail=str(e))
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
@@ -165,15 +152,13 @@ async def import_model(
|
|||||||
) -> ImportModelResponse:
|
) -> ImportModelResponse:
|
||||||
"""Add a model using its local path, repo_id, or remote URL. Model characteristics will be probed and configured automatically"""
|
"""Add a model using its local path, repo_id, or remote URL. Model characteristics will be probed and configured automatically"""
|
||||||
|
|
||||||
location = location.strip("\"' ")
|
|
||||||
items_to_import = {location}
|
items_to_import = {location}
|
||||||
prediction_types = {x.value: x for x in SchedulerPredictionType}
|
prediction_types = {x.value: x for x in SchedulerPredictionType}
|
||||||
logger = ApiDependencies.invoker.services.logger
|
logger = ApiDependencies.invoker.services.logger
|
||||||
|
|
||||||
try:
|
try:
|
||||||
installed_models = ApiDependencies.invoker.services.model_manager.heuristic_import(
|
installed_models = ApiDependencies.invoker.services.model_manager.heuristic_import(
|
||||||
items_to_import=items_to_import,
|
items_to_import=items_to_import, prediction_type_helper=lambda x: prediction_types.get(prediction_type)
|
||||||
prediction_type_helper=lambda x: prediction_types.get(prediction_type),
|
|
||||||
)
|
)
|
||||||
info = installed_models.get(location)
|
info = installed_models.get(location)
|
||||||
|
|
||||||
@@ -185,7 +170,7 @@ async def import_model(
|
|||||||
model_raw = ApiDependencies.invoker.services.model_manager.list_model(
|
model_raw = ApiDependencies.invoker.services.model_manager.list_model(
|
||||||
model_name=info.name, base_model=info.base_model, model_type=info.model_type
|
model_name=info.name, base_model=info.base_model, model_type=info.model_type
|
||||||
)
|
)
|
||||||
return ImportModelResponseValidator.validate_python(model_raw)
|
return parse_obj_as(ImportModelResponse, model_raw)
|
||||||
|
|
||||||
except ModelNotFoundException as e:
|
except ModelNotFoundException as e:
|
||||||
logger.error(str(e))
|
logger.error(str(e))
|
||||||
@@ -219,18 +204,13 @@ async def add_model(
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
ApiDependencies.invoker.services.model_manager.add_model(
|
ApiDependencies.invoker.services.model_manager.add_model(
|
||||||
info.model_name,
|
info.model_name, info.base_model, info.model_type, model_attributes=info.dict()
|
||||||
info.base_model,
|
|
||||||
info.model_type,
|
|
||||||
model_attributes=info.model_dump(),
|
|
||||||
)
|
)
|
||||||
logger.info(f"Successfully added {info.model_name}")
|
logger.info(f"Successfully added {info.model_name}")
|
||||||
model_raw = ApiDependencies.invoker.services.model_manager.list_model(
|
model_raw = ApiDependencies.invoker.services.model_manager.list_model(
|
||||||
model_name=info.model_name,
|
model_name=info.model_name, base_model=info.base_model, model_type=info.model_type
|
||||||
base_model=info.base_model,
|
|
||||||
model_type=info.model_type,
|
|
||||||
)
|
)
|
||||||
return ImportModelResponseValidator.validate_python(model_raw)
|
return parse_obj_as(ImportModelResponse, model_raw)
|
||||||
except ModelNotFoundException as e:
|
except ModelNotFoundException as e:
|
||||||
logger.error(str(e))
|
logger.error(str(e))
|
||||||
raise HTTPException(status_code=404, detail=str(e))
|
raise HTTPException(status_code=404, detail=str(e))
|
||||||
@@ -242,10 +222,7 @@ async def add_model(
|
|||||||
@models_router.delete(
|
@models_router.delete(
|
||||||
"/{base_model}/{model_type}/{model_name}",
|
"/{base_model}/{model_type}/{model_name}",
|
||||||
operation_id="del_model",
|
operation_id="del_model",
|
||||||
responses={
|
responses={204: {"description": "Model deleted successfully"}, 404: {"description": "Model not found"}},
|
||||||
204: {"description": "Model deleted successfully"},
|
|
||||||
404: {"description": "Model not found"},
|
|
||||||
},
|
|
||||||
status_code=204,
|
status_code=204,
|
||||||
response_model=None,
|
response_model=None,
|
||||||
)
|
)
|
||||||
@@ -301,7 +278,7 @@ async def convert_model(
|
|||||||
model_raw = ApiDependencies.invoker.services.model_manager.list_model(
|
model_raw = ApiDependencies.invoker.services.model_manager.list_model(
|
||||||
model_name, base_model=base_model, model_type=model_type
|
model_name, base_model=base_model, model_type=model_type
|
||||||
)
|
)
|
||||||
response = ConvertModelResponseValidator.validate_python(model_raw)
|
response = parse_obj_as(ConvertModelResponse, model_raw)
|
||||||
except ModelNotFoundException as e:
|
except ModelNotFoundException as e:
|
||||||
raise HTTPException(status_code=404, detail=f"Model '{model_name}' not found: {str(e)}")
|
raise HTTPException(status_code=404, detail=f"Model '{model_name}' not found: {str(e)}")
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
@@ -324,8 +301,7 @@ async def search_for_models(
|
|||||||
) -> List[pathlib.Path]:
|
) -> List[pathlib.Path]:
|
||||||
if not search_path.is_dir():
|
if not search_path.is_dir():
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=404,
|
status_code=404, detail=f"The search path '{search_path}' does not exist or is not directory"
|
||||||
detail=f"The search path '{search_path}' does not exist or is not directory",
|
|
||||||
)
|
)
|
||||||
return ApiDependencies.invoker.services.model_manager.search_for_models(search_path)
|
return ApiDependencies.invoker.services.model_manager.search_for_models(search_path)
|
||||||
|
|
||||||
@@ -360,26 +336,6 @@ async def sync_to_config() -> bool:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
# There's some weird pydantic-fastapi behaviour that requires this to be a separate class
|
|
||||||
# TODO: After a few updates, see if it works inside the route operation handler?
|
|
||||||
class MergeModelsBody(BaseModel):
|
|
||||||
model_names: List[str] = Field(description="model name", min_length=2, max_length=3)
|
|
||||||
merged_model_name: Optional[str] = Field(description="Name of destination model")
|
|
||||||
alpha: Optional[float] = Field(description="Alpha weighting strength to apply to 2d and 3d models", default=0.5)
|
|
||||||
interp: Optional[MergeInterpolationMethod] = Field(description="Interpolation method")
|
|
||||||
force: Optional[bool] = Field(
|
|
||||||
description="Force merging of models created with different versions of diffusers",
|
|
||||||
default=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
merge_dest_directory: Optional[str] = Field(
|
|
||||||
description="Save the merged model to the designated directory (with 'merged_model_name' appended)",
|
|
||||||
default=None,
|
|
||||||
)
|
|
||||||
|
|
||||||
model_config = ConfigDict(protected_namespaces=())
|
|
||||||
|
|
||||||
|
|
||||||
@models_router.put(
|
@models_router.put(
|
||||||
"/merge/{base_model}",
|
"/merge/{base_model}",
|
||||||
operation_id="merge_models",
|
operation_id="merge_models",
|
||||||
@@ -392,23 +348,31 @@ class MergeModelsBody(BaseModel):
|
|||||||
response_model=MergeModelResponse,
|
response_model=MergeModelResponse,
|
||||||
)
|
)
|
||||||
async def merge_models(
|
async def merge_models(
|
||||||
body: Annotated[MergeModelsBody, Body(description="Model configuration", embed=True)],
|
|
||||||
base_model: BaseModelType = Path(description="Base model"),
|
base_model: BaseModelType = Path(description="Base model"),
|
||||||
|
model_names: List[str] = Body(description="model name", min_items=2, max_items=3),
|
||||||
|
merged_model_name: Optional[str] = Body(description="Name of destination model"),
|
||||||
|
alpha: Optional[float] = Body(description="Alpha weighting strength to apply to 2d and 3d models", default=0.5),
|
||||||
|
interp: Optional[MergeInterpolationMethod] = Body(description="Interpolation method"),
|
||||||
|
force: Optional[bool] = Body(
|
||||||
|
description="Force merging of models created with different versions of diffusers", default=False
|
||||||
|
),
|
||||||
|
merge_dest_directory: Optional[str] = Body(
|
||||||
|
description="Save the merged model to the designated directory (with 'merged_model_name' appended)",
|
||||||
|
default=None,
|
||||||
|
),
|
||||||
) -> MergeModelResponse:
|
) -> MergeModelResponse:
|
||||||
"""Convert a checkpoint model into a diffusers model"""
|
"""Convert a checkpoint model into a diffusers model"""
|
||||||
logger = ApiDependencies.invoker.services.logger
|
logger = ApiDependencies.invoker.services.logger
|
||||||
try:
|
try:
|
||||||
logger.info(
|
logger.info(f"Merging models: {model_names} into {merge_dest_directory or '<MODELS>'}/{merged_model_name}")
|
||||||
f"Merging models: {body.model_names} into {body.merge_dest_directory or '<MODELS>'}/{body.merged_model_name}"
|
dest = pathlib.Path(merge_dest_directory) if merge_dest_directory else None
|
||||||
)
|
|
||||||
dest = pathlib.Path(body.merge_dest_directory) if body.merge_dest_directory else None
|
|
||||||
result = ApiDependencies.invoker.services.model_manager.merge_models(
|
result = ApiDependencies.invoker.services.model_manager.merge_models(
|
||||||
model_names=body.model_names,
|
model_names,
|
||||||
base_model=base_model,
|
base_model,
|
||||||
merged_model_name=body.merged_model_name or "+".join(body.model_names),
|
merged_model_name=merged_model_name or "+".join(model_names),
|
||||||
alpha=body.alpha,
|
alpha=alpha,
|
||||||
interp=body.interp,
|
interp=interp,
|
||||||
force=body.force,
|
force=force,
|
||||||
merge_dest_directory=dest,
|
merge_dest_directory=dest,
|
||||||
)
|
)
|
||||||
model_raw = ApiDependencies.invoker.services.model_manager.list_model(
|
model_raw = ApiDependencies.invoker.services.model_manager.list_model(
|
||||||
@@ -416,12 +380,9 @@ async def merge_models(
|
|||||||
base_model=base_model,
|
base_model=base_model,
|
||||||
model_type=ModelType.Main,
|
model_type=ModelType.Main,
|
||||||
)
|
)
|
||||||
response = ConvertModelResponseValidator.validate_python(model_raw)
|
response = parse_obj_as(ConvertModelResponse, model_raw)
|
||||||
except ModelNotFoundException:
|
except ModelNotFoundException:
|
||||||
raise HTTPException(
|
raise HTTPException(status_code=404, detail=f"One or more of the models '{model_names}' not found")
|
||||||
status_code=404,
|
|
||||||
detail=f"One or more of the models '{body.model_names}' not found",
|
|
||||||
)
|
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
return response
|
return response
|
||||||
|
|||||||
@@ -12,13 +12,15 @@ from invokeai.app.services.session_queue.session_queue_common import (
|
|||||||
CancelByBatchIDsResult,
|
CancelByBatchIDsResult,
|
||||||
ClearResult,
|
ClearResult,
|
||||||
EnqueueBatchResult,
|
EnqueueBatchResult,
|
||||||
|
EnqueueGraphResult,
|
||||||
PruneResult,
|
PruneResult,
|
||||||
SessionQueueItem,
|
SessionQueueItem,
|
||||||
SessionQueueItemDTO,
|
SessionQueueItemDTO,
|
||||||
SessionQueueStatus,
|
SessionQueueStatus,
|
||||||
)
|
)
|
||||||
from invokeai.app.services.shared.pagination import CursorPaginatedResults
|
from invokeai.app.services.shared.models import CursorPaginatedResults
|
||||||
|
|
||||||
|
from ...services.graph import Graph
|
||||||
from ..dependencies import ApiDependencies
|
from ..dependencies import ApiDependencies
|
||||||
|
|
||||||
session_queue_router = APIRouter(prefix="/v1/queue", tags=["queue"])
|
session_queue_router = APIRouter(prefix="/v1/queue", tags=["queue"])
|
||||||
@@ -31,6 +33,23 @@ class SessionQueueAndProcessorStatus(BaseModel):
|
|||||||
processor: SessionProcessorStatus
|
processor: SessionProcessorStatus
|
||||||
|
|
||||||
|
|
||||||
|
@session_queue_router.post(
|
||||||
|
"/{queue_id}/enqueue_graph",
|
||||||
|
operation_id="enqueue_graph",
|
||||||
|
responses={
|
||||||
|
201: {"model": EnqueueGraphResult},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
async def enqueue_graph(
|
||||||
|
queue_id: str = Path(description="The queue id to perform this operation on"),
|
||||||
|
graph: Graph = Body(description="The graph to enqueue"),
|
||||||
|
prepend: bool = Body(default=False, description="Whether or not to prepend this batch in the queue"),
|
||||||
|
) -> EnqueueGraphResult:
|
||||||
|
"""Enqueues a graph for single execution."""
|
||||||
|
|
||||||
|
return ApiDependencies.invoker.services.session_queue.enqueue_graph(queue_id=queue_id, graph=graph, prepend=prepend)
|
||||||
|
|
||||||
|
|
||||||
@session_queue_router.post(
|
@session_queue_router.post(
|
||||||
"/{queue_id}/enqueue_batch",
|
"/{queue_id}/enqueue_batch",
|
||||||
operation_id="enqueue_batch",
|
operation_id="enqueue_batch",
|
||||||
@@ -93,18 +112,6 @@ async def Pause(
|
|||||||
return ApiDependencies.invoker.services.session_processor.pause()
|
return ApiDependencies.invoker.services.session_processor.pause()
|
||||||
|
|
||||||
|
|
||||||
@session_queue_router.put(
|
|
||||||
"/{queue_id}/processor/take_one",
|
|
||||||
operation_id="take_one",
|
|
||||||
responses={200: {"model": SessionProcessorStatus}},
|
|
||||||
)
|
|
||||||
async def take_one(
|
|
||||||
queue_id: str = Path(description="The queue id to perform this operation on"),
|
|
||||||
) -> SessionProcessorStatus:
|
|
||||||
"""Executes the next-in-line queue item, pausing the processor afterwards. Has no effect if the queue is resumed."""
|
|
||||||
return ApiDependencies.invoker.services.session_processor.take_one()
|
|
||||||
|
|
||||||
|
|
||||||
@session_queue_router.put(
|
@session_queue_router.put(
|
||||||
"/{queue_id}/cancel_by_batch_ids",
|
"/{queue_id}/cancel_by_batch_ids",
|
||||||
operation_id="cancel_by_batch_ids",
|
operation_id="cancel_by_batch_ids",
|
||||||
|
|||||||
@@ -1,50 +1,56 @@
|
|||||||
# Copyright (c) 2022 Kyle Schouviller (https://github.com/kyle0654)
|
# Copyright (c) 2022 Kyle Schouviller (https://github.com/kyle0654)
|
||||||
|
|
||||||
|
from typing import Annotated, Optional, Union
|
||||||
|
|
||||||
from fastapi import HTTPException, Path
|
from fastapi import Body, HTTPException, Path, Query, Response
|
||||||
from fastapi.routing import APIRouter
|
from fastapi.routing import APIRouter
|
||||||
|
from pydantic.fields import Field
|
||||||
|
|
||||||
from ...services.shared.graph import GraphExecutionState
|
# Importing * is bad karma but needed here for node detection
|
||||||
|
from ...invocations import * # noqa: F401 F403
|
||||||
|
from ...invocations.baseinvocation import BaseInvocation
|
||||||
|
from ...services.graph import Edge, EdgeConnection, Graph, GraphExecutionState, NodeAlreadyExecutedError
|
||||||
|
from ...services.item_storage import PaginatedResults
|
||||||
from ..dependencies import ApiDependencies
|
from ..dependencies import ApiDependencies
|
||||||
|
|
||||||
session_router = APIRouter(prefix="/v1/sessions", tags=["sessions"])
|
session_router = APIRouter(prefix="/v1/sessions", tags=["sessions"])
|
||||||
|
|
||||||
|
|
||||||
# @session_router.post(
|
@session_router.post(
|
||||||
# "/",
|
"/",
|
||||||
# operation_id="create_session",
|
operation_id="create_session",
|
||||||
# responses={
|
responses={
|
||||||
# 200: {"model": GraphExecutionState},
|
200: {"model": GraphExecutionState},
|
||||||
# 400: {"description": "Invalid json"},
|
400: {"description": "Invalid json"},
|
||||||
# },
|
},
|
||||||
# deprecated=True,
|
deprecated=True,
|
||||||
# )
|
)
|
||||||
# async def create_session(
|
async def create_session(
|
||||||
# queue_id: str = Query(default="", description="The id of the queue to associate the session with"),
|
queue_id: str = Query(default="", description="The id of the queue to associate the session with"),
|
||||||
# graph: Optional[Graph] = Body(default=None, description="The graph to initialize the session with"),
|
graph: Optional[Graph] = Body(default=None, description="The graph to initialize the session with"),
|
||||||
# ) -> GraphExecutionState:
|
) -> GraphExecutionState:
|
||||||
# """Creates a new session, optionally initializing it with an invocation graph"""
|
"""Creates a new session, optionally initializing it with an invocation graph"""
|
||||||
# session = ApiDependencies.invoker.create_execution_state(queue_id=queue_id, graph=graph)
|
session = ApiDependencies.invoker.create_execution_state(queue_id=queue_id, graph=graph)
|
||||||
# return session
|
return session
|
||||||
|
|
||||||
|
|
||||||
# @session_router.get(
|
@session_router.get(
|
||||||
# "/",
|
"/",
|
||||||
# operation_id="list_sessions",
|
operation_id="list_sessions",
|
||||||
# responses={200: {"model": PaginatedResults[GraphExecutionState]}},
|
responses={200: {"model": PaginatedResults[GraphExecutionState]}},
|
||||||
# deprecated=True,
|
deprecated=True,
|
||||||
# )
|
)
|
||||||
# async def list_sessions(
|
async def list_sessions(
|
||||||
# page: int = Query(default=0, description="The page of results to get"),
|
page: int = Query(default=0, description="The page of results to get"),
|
||||||
# per_page: int = Query(default=10, description="The number of results per page"),
|
per_page: int = Query(default=10, description="The number of results per page"),
|
||||||
# query: str = Query(default="", description="The query string to search for"),
|
query: str = Query(default="", description="The query string to search for"),
|
||||||
# ) -> PaginatedResults[GraphExecutionState]:
|
) -> PaginatedResults[GraphExecutionState]:
|
||||||
# """Gets a list of sessions, optionally searching"""
|
"""Gets a list of sessions, optionally searching"""
|
||||||
# if query == "":
|
if query == "":
|
||||||
# result = ApiDependencies.invoker.services.graph_execution_manager.list(page, per_page)
|
result = ApiDependencies.invoker.services.graph_execution_manager.list(page, per_page)
|
||||||
# else:
|
else:
|
||||||
# result = ApiDependencies.invoker.services.graph_execution_manager.search(query, page, per_page)
|
result = ApiDependencies.invoker.services.graph_execution_manager.search(query, page, per_page)
|
||||||
# return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@session_router.get(
|
@session_router.get(
|
||||||
@@ -54,6 +60,7 @@ session_router = APIRouter(prefix="/v1/sessions", tags=["sessions"])
|
|||||||
200: {"model": GraphExecutionState},
|
200: {"model": GraphExecutionState},
|
||||||
404: {"description": "Session not found"},
|
404: {"description": "Session not found"},
|
||||||
},
|
},
|
||||||
|
deprecated=True,
|
||||||
)
|
)
|
||||||
async def get_session(
|
async def get_session(
|
||||||
session_id: str = Path(description="The id of the session to get"),
|
session_id: str = Path(description="The id of the session to get"),
|
||||||
@@ -66,211 +73,211 @@ async def get_session(
|
|||||||
return session
|
return session
|
||||||
|
|
||||||
|
|
||||||
# @session_router.post(
|
@session_router.post(
|
||||||
# "/{session_id}/nodes",
|
"/{session_id}/nodes",
|
||||||
# operation_id="add_node",
|
operation_id="add_node",
|
||||||
# responses={
|
responses={
|
||||||
# 200: {"model": str},
|
200: {"model": str},
|
||||||
# 400: {"description": "Invalid node or link"},
|
400: {"description": "Invalid node or link"},
|
||||||
# 404: {"description": "Session not found"},
|
404: {"description": "Session not found"},
|
||||||
# },
|
},
|
||||||
# deprecated=True,
|
deprecated=True,
|
||||||
# )
|
)
|
||||||
# async def add_node(
|
async def add_node(
|
||||||
# session_id: str = Path(description="The id of the session"),
|
session_id: str = Path(description="The id of the session"),
|
||||||
# node: Annotated[Union[BaseInvocation.get_invocations()], Field(discriminator="type")] = Body( # type: ignore
|
node: Annotated[Union[BaseInvocation.get_invocations()], Field(discriminator="type")] = Body( # type: ignore
|
||||||
# description="The node to add"
|
description="The node to add"
|
||||||
# ),
|
),
|
||||||
# ) -> str:
|
) -> str:
|
||||||
# """Adds a node to the graph"""
|
"""Adds a node to the graph"""
|
||||||
# session = ApiDependencies.invoker.services.graph_execution_manager.get(session_id)
|
session = ApiDependencies.invoker.services.graph_execution_manager.get(session_id)
|
||||||
# if session is None:
|
if session is None:
|
||||||
# raise HTTPException(status_code=404)
|
raise HTTPException(status_code=404)
|
||||||
|
|
||||||
# try:
|
try:
|
||||||
# session.add_node(node)
|
session.add_node(node)
|
||||||
# ApiDependencies.invoker.services.graph_execution_manager.set(
|
ApiDependencies.invoker.services.graph_execution_manager.set(
|
||||||
# session
|
session
|
||||||
# ) # TODO: can this be done automatically, or add node through an API?
|
) # TODO: can this be done automatically, or add node through an API?
|
||||||
# return session.id
|
return session.id
|
||||||
# except NodeAlreadyExecutedError:
|
except NodeAlreadyExecutedError:
|
||||||
# raise HTTPException(status_code=400)
|
raise HTTPException(status_code=400)
|
||||||
# except IndexError:
|
except IndexError:
|
||||||
# raise HTTPException(status_code=400)
|
raise HTTPException(status_code=400)
|
||||||
|
|
||||||
|
|
||||||
# @session_router.put(
|
@session_router.put(
|
||||||
# "/{session_id}/nodes/{node_path}",
|
"/{session_id}/nodes/{node_path}",
|
||||||
# operation_id="update_node",
|
operation_id="update_node",
|
||||||
# responses={
|
responses={
|
||||||
# 200: {"model": GraphExecutionState},
|
200: {"model": GraphExecutionState},
|
||||||
# 400: {"description": "Invalid node or link"},
|
400: {"description": "Invalid node or link"},
|
||||||
# 404: {"description": "Session not found"},
|
404: {"description": "Session not found"},
|
||||||
# },
|
},
|
||||||
# deprecated=True,
|
deprecated=True,
|
||||||
# )
|
)
|
||||||
# async def update_node(
|
async def update_node(
|
||||||
# session_id: str = Path(description="The id of the session"),
|
session_id: str = Path(description="The id of the session"),
|
||||||
# node_path: str = Path(description="The path to the node in the graph"),
|
node_path: str = Path(description="The path to the node in the graph"),
|
||||||
# node: Annotated[Union[BaseInvocation.get_invocations()], Field(discriminator="type")] = Body( # type: ignore
|
node: Annotated[Union[BaseInvocation.get_invocations()], Field(discriminator="type")] = Body( # type: ignore
|
||||||
# description="The new node"
|
description="The new node"
|
||||||
# ),
|
),
|
||||||
# ) -> GraphExecutionState:
|
) -> GraphExecutionState:
|
||||||
# """Updates a node in the graph and removes all linked edges"""
|
"""Updates a node in the graph and removes all linked edges"""
|
||||||
# session = ApiDependencies.invoker.services.graph_execution_manager.get(session_id)
|
session = ApiDependencies.invoker.services.graph_execution_manager.get(session_id)
|
||||||
# if session is None:
|
if session is None:
|
||||||
# raise HTTPException(status_code=404)
|
raise HTTPException(status_code=404)
|
||||||
|
|
||||||
# try:
|
try:
|
||||||
# session.update_node(node_path, node)
|
session.update_node(node_path, node)
|
||||||
# ApiDependencies.invoker.services.graph_execution_manager.set(
|
ApiDependencies.invoker.services.graph_execution_manager.set(
|
||||||
# session
|
session
|
||||||
# ) # TODO: can this be done automatically, or add node through an API?
|
) # TODO: can this be done automatically, or add node through an API?
|
||||||
# return session
|
return session
|
||||||
# except NodeAlreadyExecutedError:
|
except NodeAlreadyExecutedError:
|
||||||
# raise HTTPException(status_code=400)
|
raise HTTPException(status_code=400)
|
||||||
# except IndexError:
|
except IndexError:
|
||||||
# raise HTTPException(status_code=400)
|
raise HTTPException(status_code=400)
|
||||||
|
|
||||||
|
|
||||||
# @session_router.delete(
|
@session_router.delete(
|
||||||
# "/{session_id}/nodes/{node_path}",
|
"/{session_id}/nodes/{node_path}",
|
||||||
# operation_id="delete_node",
|
operation_id="delete_node",
|
||||||
# responses={
|
responses={
|
||||||
# 200: {"model": GraphExecutionState},
|
200: {"model": GraphExecutionState},
|
||||||
# 400: {"description": "Invalid node or link"},
|
400: {"description": "Invalid node or link"},
|
||||||
# 404: {"description": "Session not found"},
|
404: {"description": "Session not found"},
|
||||||
# },
|
},
|
||||||
# deprecated=True,
|
deprecated=True,
|
||||||
# )
|
)
|
||||||
# async def delete_node(
|
async def delete_node(
|
||||||
# session_id: str = Path(description="The id of the session"),
|
session_id: str = Path(description="The id of the session"),
|
||||||
# node_path: str = Path(description="The path to the node to delete"),
|
node_path: str = Path(description="The path to the node to delete"),
|
||||||
# ) -> GraphExecutionState:
|
) -> GraphExecutionState:
|
||||||
# """Deletes a node in the graph and removes all linked edges"""
|
"""Deletes a node in the graph and removes all linked edges"""
|
||||||
# session = ApiDependencies.invoker.services.graph_execution_manager.get(session_id)
|
session = ApiDependencies.invoker.services.graph_execution_manager.get(session_id)
|
||||||
# if session is None:
|
if session is None:
|
||||||
# raise HTTPException(status_code=404)
|
raise HTTPException(status_code=404)
|
||||||
|
|
||||||
# try:
|
try:
|
||||||
# session.delete_node(node_path)
|
session.delete_node(node_path)
|
||||||
# ApiDependencies.invoker.services.graph_execution_manager.set(
|
ApiDependencies.invoker.services.graph_execution_manager.set(
|
||||||
# session
|
session
|
||||||
# ) # TODO: can this be done automatically, or add node through an API?
|
) # TODO: can this be done automatically, or add node through an API?
|
||||||
# return session
|
return session
|
||||||
# except NodeAlreadyExecutedError:
|
except NodeAlreadyExecutedError:
|
||||||
# raise HTTPException(status_code=400)
|
raise HTTPException(status_code=400)
|
||||||
# except IndexError:
|
except IndexError:
|
||||||
# raise HTTPException(status_code=400)
|
raise HTTPException(status_code=400)
|
||||||
|
|
||||||
|
|
||||||
# @session_router.post(
|
@session_router.post(
|
||||||
# "/{session_id}/edges",
|
"/{session_id}/edges",
|
||||||
# operation_id="add_edge",
|
operation_id="add_edge",
|
||||||
# responses={
|
responses={
|
||||||
# 200: {"model": GraphExecutionState},
|
200: {"model": GraphExecutionState},
|
||||||
# 400: {"description": "Invalid node or link"},
|
400: {"description": "Invalid node or link"},
|
||||||
# 404: {"description": "Session not found"},
|
404: {"description": "Session not found"},
|
||||||
# },
|
},
|
||||||
# deprecated=True,
|
deprecated=True,
|
||||||
# )
|
)
|
||||||
# async def add_edge(
|
async def add_edge(
|
||||||
# session_id: str = Path(description="The id of the session"),
|
session_id: str = Path(description="The id of the session"),
|
||||||
# edge: Edge = Body(description="The edge to add"),
|
edge: Edge = Body(description="The edge to add"),
|
||||||
# ) -> GraphExecutionState:
|
) -> GraphExecutionState:
|
||||||
# """Adds an edge to the graph"""
|
"""Adds an edge to the graph"""
|
||||||
# session = ApiDependencies.invoker.services.graph_execution_manager.get(session_id)
|
session = ApiDependencies.invoker.services.graph_execution_manager.get(session_id)
|
||||||
# if session is None:
|
if session is None:
|
||||||
# raise HTTPException(status_code=404)
|
raise HTTPException(status_code=404)
|
||||||
|
|
||||||
# try:
|
try:
|
||||||
# session.add_edge(edge)
|
session.add_edge(edge)
|
||||||
# ApiDependencies.invoker.services.graph_execution_manager.set(
|
ApiDependencies.invoker.services.graph_execution_manager.set(
|
||||||
# session
|
session
|
||||||
# ) # TODO: can this be done automatically, or add node through an API?
|
) # TODO: can this be done automatically, or add node through an API?
|
||||||
# return session
|
return session
|
||||||
# except NodeAlreadyExecutedError:
|
except NodeAlreadyExecutedError:
|
||||||
# raise HTTPException(status_code=400)
|
raise HTTPException(status_code=400)
|
||||||
# except IndexError:
|
except IndexError:
|
||||||
# raise HTTPException(status_code=400)
|
raise HTTPException(status_code=400)
|
||||||
|
|
||||||
|
|
||||||
# # TODO: the edge being in the path here is really ugly, find a better solution
|
# TODO: the edge being in the path here is really ugly, find a better solution
|
||||||
# @session_router.delete(
|
@session_router.delete(
|
||||||
# "/{session_id}/edges/{from_node_id}/{from_field}/{to_node_id}/{to_field}",
|
"/{session_id}/edges/{from_node_id}/{from_field}/{to_node_id}/{to_field}",
|
||||||
# operation_id="delete_edge",
|
operation_id="delete_edge",
|
||||||
# responses={
|
responses={
|
||||||
# 200: {"model": GraphExecutionState},
|
200: {"model": GraphExecutionState},
|
||||||
# 400: {"description": "Invalid node or link"},
|
400: {"description": "Invalid node or link"},
|
||||||
# 404: {"description": "Session not found"},
|
404: {"description": "Session not found"},
|
||||||
# },
|
},
|
||||||
# deprecated=True,
|
deprecated=True,
|
||||||
# )
|
)
|
||||||
# async def delete_edge(
|
async def delete_edge(
|
||||||
# session_id: str = Path(description="The id of the session"),
|
session_id: str = Path(description="The id of the session"),
|
||||||
# from_node_id: str = Path(description="The id of the node the edge is coming from"),
|
from_node_id: str = Path(description="The id of the node the edge is coming from"),
|
||||||
# from_field: str = Path(description="The field of the node the edge is coming from"),
|
from_field: str = Path(description="The field of the node the edge is coming from"),
|
||||||
# to_node_id: str = Path(description="The id of the node the edge is going to"),
|
to_node_id: str = Path(description="The id of the node the edge is going to"),
|
||||||
# to_field: str = Path(description="The field of the node the edge is going to"),
|
to_field: str = Path(description="The field of the node the edge is going to"),
|
||||||
# ) -> GraphExecutionState:
|
) -> GraphExecutionState:
|
||||||
# """Deletes an edge from the graph"""
|
"""Deletes an edge from the graph"""
|
||||||
# session = ApiDependencies.invoker.services.graph_execution_manager.get(session_id)
|
session = ApiDependencies.invoker.services.graph_execution_manager.get(session_id)
|
||||||
# if session is None:
|
if session is None:
|
||||||
# raise HTTPException(status_code=404)
|
raise HTTPException(status_code=404)
|
||||||
|
|
||||||
# try:
|
try:
|
||||||
# edge = Edge(
|
edge = Edge(
|
||||||
# source=EdgeConnection(node_id=from_node_id, field=from_field),
|
source=EdgeConnection(node_id=from_node_id, field=from_field),
|
||||||
# destination=EdgeConnection(node_id=to_node_id, field=to_field),
|
destination=EdgeConnection(node_id=to_node_id, field=to_field),
|
||||||
# )
|
)
|
||||||
# session.delete_edge(edge)
|
session.delete_edge(edge)
|
||||||
# ApiDependencies.invoker.services.graph_execution_manager.set(
|
ApiDependencies.invoker.services.graph_execution_manager.set(
|
||||||
# session
|
session
|
||||||
# ) # TODO: can this be done automatically, or add node through an API?
|
) # TODO: can this be done automatically, or add node through an API?
|
||||||
# return session
|
return session
|
||||||
# except NodeAlreadyExecutedError:
|
except NodeAlreadyExecutedError:
|
||||||
# raise HTTPException(status_code=400)
|
raise HTTPException(status_code=400)
|
||||||
# except IndexError:
|
except IndexError:
|
||||||
# raise HTTPException(status_code=400)
|
raise HTTPException(status_code=400)
|
||||||
|
|
||||||
|
|
||||||
# @session_router.put(
|
@session_router.put(
|
||||||
# "/{session_id}/invoke",
|
"/{session_id}/invoke",
|
||||||
# operation_id="invoke_session",
|
operation_id="invoke_session",
|
||||||
# responses={
|
responses={
|
||||||
# 200: {"model": None},
|
200: {"model": None},
|
||||||
# 202: {"description": "The invocation is queued"},
|
202: {"description": "The invocation is queued"},
|
||||||
# 400: {"description": "The session has no invocations ready to invoke"},
|
400: {"description": "The session has no invocations ready to invoke"},
|
||||||
# 404: {"description": "Session not found"},
|
404: {"description": "Session not found"},
|
||||||
# },
|
},
|
||||||
# deprecated=True,
|
deprecated=True,
|
||||||
# )
|
)
|
||||||
# async def invoke_session(
|
async def invoke_session(
|
||||||
# queue_id: str = Query(description="The id of the queue to associate the session with"),
|
queue_id: str = Query(description="The id of the queue to associate the session with"),
|
||||||
# session_id: str = Path(description="The id of the session to invoke"),
|
session_id: str = Path(description="The id of the session to invoke"),
|
||||||
# all: bool = Query(default=False, description="Whether or not to invoke all remaining invocations"),
|
all: bool = Query(default=False, description="Whether or not to invoke all remaining invocations"),
|
||||||
# ) -> Response:
|
) -> Response:
|
||||||
# """Invokes a session"""
|
"""Invokes a session"""
|
||||||
# session = ApiDependencies.invoker.services.graph_execution_manager.get(session_id)
|
session = ApiDependencies.invoker.services.graph_execution_manager.get(session_id)
|
||||||
# if session is None:
|
if session is None:
|
||||||
# raise HTTPException(status_code=404)
|
raise HTTPException(status_code=404)
|
||||||
|
|
||||||
# if session.is_complete():
|
if session.is_complete():
|
||||||
# raise HTTPException(status_code=400)
|
raise HTTPException(status_code=400)
|
||||||
|
|
||||||
# ApiDependencies.invoker.invoke(queue_id, session, invoke_all=all)
|
ApiDependencies.invoker.invoke(queue_id, session, invoke_all=all)
|
||||||
# return Response(status_code=202)
|
return Response(status_code=202)
|
||||||
|
|
||||||
|
|
||||||
# @session_router.delete(
|
@session_router.delete(
|
||||||
# "/{session_id}/invoke",
|
"/{session_id}/invoke",
|
||||||
# operation_id="cancel_session_invoke",
|
operation_id="cancel_session_invoke",
|
||||||
# responses={202: {"description": "The invocation is canceled"}},
|
responses={202: {"description": "The invocation is canceled"}},
|
||||||
# deprecated=True,
|
deprecated=True,
|
||||||
# )
|
)
|
||||||
# async def cancel_session_invoke(
|
async def cancel_session_invoke(
|
||||||
# session_id: str = Path(description="The id of the session to cancel"),
|
session_id: str = Path(description="The id of the session to cancel"),
|
||||||
# ) -> Response:
|
) -> Response:
|
||||||
# """Invokes a session"""
|
"""Invokes a session"""
|
||||||
# ApiDependencies.invoker.cancel(session_id)
|
ApiDependencies.invoker.cancel(session_id)
|
||||||
# return Response(status_code=202)
|
return Response(status_code=202)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from typing import Optional, Union
|
from typing import Optional
|
||||||
|
|
||||||
from dynamicprompts.generators import CombinatorialPromptGenerator, RandomPromptGenerator
|
from dynamicprompts.generators import CombinatorialPromptGenerator, RandomPromptGenerator
|
||||||
from fastapi import Body
|
from fastapi import Body
|
||||||
@@ -27,7 +27,6 @@ async def parse_dynamicprompts(
|
|||||||
combinatorial: bool = Body(default=True, description="Whether to use the combinatorial generator"),
|
combinatorial: bool = Body(default=True, description="Whether to use the combinatorial generator"),
|
||||||
) -> DynamicPromptsResponse:
|
) -> DynamicPromptsResponse:
|
||||||
"""Creates a batch process"""
|
"""Creates a batch process"""
|
||||||
generator: Union[RandomPromptGenerator, CombinatorialPromptGenerator]
|
|
||||||
try:
|
try:
|
||||||
error: Optional[str] = None
|
error: Optional[str] = None
|
||||||
if combinatorial:
|
if combinatorial:
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
from fastapi import APIRouter, Path
|
|
||||||
|
|
||||||
from invokeai.app.api.dependencies import ApiDependencies
|
|
||||||
from invokeai.app.invocations.baseinvocation import WorkflowField
|
|
||||||
|
|
||||||
workflows_router = APIRouter(prefix="/v1/workflows", tags=["workflows"])
|
|
||||||
|
|
||||||
|
|
||||||
@workflows_router.get(
|
|
||||||
"/i/{workflow_id}",
|
|
||||||
operation_id="get_workflow",
|
|
||||||
responses={
|
|
||||||
200: {"model": WorkflowField},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
async def get_workflow(
|
|
||||||
workflow_id: str = Path(description="The workflow to get"),
|
|
||||||
) -> WorkflowField:
|
|
||||||
"""Gets a workflow"""
|
|
||||||
return ApiDependencies.invoker.services.workflow_records.get(workflow_id)
|
|
||||||
@@ -5,7 +5,7 @@ from fastapi_events.handlers.local import local_handler
|
|||||||
from fastapi_events.typing import Event
|
from fastapi_events.typing import Event
|
||||||
from socketio import ASGIApp, AsyncServer
|
from socketio import ASGIApp, AsyncServer
|
||||||
|
|
||||||
from ..services.events.events_base import EventServiceBase
|
from ..services.events import EventServiceBase
|
||||||
|
|
||||||
|
|
||||||
class SocketIO:
|
class SocketIO:
|
||||||
@@ -34,4 +34,4 @@ class SocketIO:
|
|||||||
|
|
||||||
async def _handle_unsub_queue(self, sid, data, *args, **kwargs):
|
async def _handle_unsub_queue(self, sid, data, *args, **kwargs):
|
||||||
if "queue_id" in data:
|
if "queue_id" in data:
|
||||||
await self.__sio.leave_room(sid, data["queue_id"])
|
await self.__sio.enter_room(sid, data["queue_id"])
|
||||||
|
|||||||
@@ -1,17 +1,10 @@
|
|||||||
|
from .services.config import InvokeAIAppConfig
|
||||||
|
|
||||||
# parse_args() must be called before any other imports. if it is not called first, consumers of the config
|
# parse_args() must be called before any other imports. if it is not called first, consumers of the config
|
||||||
# which are imported/used before parse_args() is called will get the default config values instead of the
|
# which are imported/used before parse_args() is called will get the default config values instead of the
|
||||||
# values from the command line or config file.
|
# values from the command line or config file.
|
||||||
import sys
|
|
||||||
|
|
||||||
from invokeai.version.invokeai_version import __version__
|
|
||||||
|
|
||||||
from .services.config import InvokeAIAppConfig
|
|
||||||
|
|
||||||
app_config = InvokeAIAppConfig.get_config()
|
app_config = InvokeAIAppConfig.get_config()
|
||||||
app_config.parse_args()
|
app_config.parse_args()
|
||||||
if app_config.version:
|
|
||||||
print(f"InvokeAI version {__version__}")
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
if True: # hack to make flake8 happy with imports coming after setting up the config
|
if True: # hack to make flake8 happy with imports coming after setting up the config
|
||||||
import asyncio
|
import asyncio
|
||||||
@@ -19,55 +12,38 @@ if True: # hack to make flake8 happy with imports coming after setting up the c
|
|||||||
import socket
|
import socket
|
||||||
from inspect import signature
|
from inspect import signature
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
|
import torch
|
||||||
import uvicorn
|
import uvicorn
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from fastapi.middleware.gzip import GZipMiddleware
|
|
||||||
from fastapi.openapi.docs import get_redoc_html, get_swagger_ui_html
|
from fastapi.openapi.docs import get_redoc_html, get_swagger_ui_html
|
||||||
from fastapi.openapi.utils import get_openapi
|
from fastapi.openapi.utils import get_openapi
|
||||||
from fastapi.responses import FileResponse, HTMLResponse
|
|
||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
from fastapi_events.handlers.local import local_handler
|
from fastapi_events.handlers.local import local_handler
|
||||||
from fastapi_events.middleware import EventHandlerASGIMiddleware
|
from fastapi_events.middleware import EventHandlerASGIMiddleware
|
||||||
from pydantic.json_schema import models_json_schema
|
from pydantic.schema import schema
|
||||||
from torch.backends.mps import is_available as is_mps_available
|
|
||||||
|
|
||||||
# for PyCharm:
|
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
import invokeai.backend.util.hotfixes # noqa: F401 (monkeypatching on import)
|
import invokeai.backend.util.hotfixes # noqa: F401 (monkeypatching on import)
|
||||||
import invokeai.frontend.web as web_dir
|
import invokeai.frontend.web as web_dir
|
||||||
|
from invokeai.version.invokeai_version import __version__
|
||||||
|
|
||||||
from ..backend.util.logging import InvokeAILogger
|
from ..backend.util.logging import InvokeAILogger
|
||||||
from .api.dependencies import ApiDependencies
|
from .api.dependencies import ApiDependencies
|
||||||
from .api.routers import (
|
from .api.routers import app_info, board_images, boards, images, models, session_queue, sessions, utilities
|
||||||
app_info,
|
|
||||||
board_images,
|
|
||||||
boards,
|
|
||||||
images,
|
|
||||||
model_records,
|
|
||||||
models,
|
|
||||||
session_queue,
|
|
||||||
sessions,
|
|
||||||
utilities,
|
|
||||||
workflows,
|
|
||||||
)
|
|
||||||
from .api.sockets import SocketIO
|
from .api.sockets import SocketIO
|
||||||
from .invocations.baseinvocation import (
|
from .invocations.baseinvocation import BaseInvocation, UIConfigBase, _InputField, _OutputField
|
||||||
BaseInvocation,
|
|
||||||
InputFieldJSONSchemaExtra,
|
|
||||||
OutputFieldJSONSchemaExtra,
|
|
||||||
UIConfigBase,
|
|
||||||
)
|
|
||||||
|
|
||||||
if is_mps_available():
|
if torch.backends.mps.is_available():
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
import invokeai.backend.util.mps_fixes # noqa: F401 (monkeypatching on import)
|
import invokeai.backend.util.mps_fixes # noqa: F401 (monkeypatching on import)
|
||||||
|
|
||||||
|
|
||||||
app_config = InvokeAIAppConfig.get_config()
|
app_config = InvokeAIAppConfig.get_config()
|
||||||
app_config.parse_args()
|
app_config.parse_args()
|
||||||
logger = InvokeAILogger.get_logger(config=app_config)
|
logger = InvokeAILogger.get_logger(config=app_config)
|
||||||
|
|
||||||
# fix for windows mimetypes registry entries being borked
|
# fix for windows mimetypes registry entries being borked
|
||||||
# see https://github.com/invoke-ai/InvokeAI/discussions/3684#discussioncomment-6391352
|
# see https://github.com/invoke-ai/InvokeAI/discussions/3684#discussioncomment-6391352
|
||||||
mimetypes.add_type("application/javascript", ".js")
|
mimetypes.add_type("application/javascript", ".js")
|
||||||
@@ -75,7 +51,7 @@ mimetypes.add_type("text/css", ".css")
|
|||||||
|
|
||||||
# Create the app
|
# Create the app
|
||||||
# TODO: create this all in a method so configuration/etc. can be passed in?
|
# TODO: create this all in a method so configuration/etc. can be passed in?
|
||||||
app = FastAPI(title="Invoke AI", docs_url=None, redoc_url=None, separate_input_output_schemas=False)
|
app = FastAPI(title="Invoke AI", docs_url=None, redoc_url=None)
|
||||||
|
|
||||||
# Add event handler
|
# Add event handler
|
||||||
event_handler_id: int = id(app)
|
event_handler_id: int = id(app)
|
||||||
@@ -87,46 +63,53 @@ app.add_middleware(
|
|||||||
|
|
||||||
socket_io = SocketIO(app)
|
socket_io = SocketIO(app)
|
||||||
|
|
||||||
app.add_middleware(
|
|
||||||
CORSMiddleware,
|
|
||||||
allow_origins=app_config.allow_origins,
|
|
||||||
allow_credentials=app_config.allow_credentials,
|
|
||||||
allow_methods=app_config.allow_methods,
|
|
||||||
allow_headers=app_config.allow_headers,
|
|
||||||
)
|
|
||||||
|
|
||||||
app.add_middleware(GZipMiddleware, minimum_size=1000)
|
|
||||||
|
|
||||||
|
|
||||||
# Add startup event to load dependencies
|
# Add startup event to load dependencies
|
||||||
@app.on_event("startup")
|
@app.on_event("startup")
|
||||||
async def startup_event() -> None:
|
async def startup_event():
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=app_config.allow_origins,
|
||||||
|
allow_credentials=app_config.allow_credentials,
|
||||||
|
allow_methods=app_config.allow_methods,
|
||||||
|
allow_headers=app_config.allow_headers,
|
||||||
|
)
|
||||||
|
|
||||||
ApiDependencies.initialize(config=app_config, event_handler_id=event_handler_id, logger=logger)
|
ApiDependencies.initialize(config=app_config, event_handler_id=event_handler_id, logger=logger)
|
||||||
|
|
||||||
|
|
||||||
# Shut down threads
|
# Shut down threads
|
||||||
@app.on_event("shutdown")
|
@app.on_event("shutdown")
|
||||||
async def shutdown_event() -> None:
|
async def shutdown_event():
|
||||||
ApiDependencies.shutdown()
|
ApiDependencies.shutdown()
|
||||||
|
|
||||||
|
|
||||||
# Include all routers
|
# Include all routers
|
||||||
|
# TODO: REMOVE
|
||||||
|
# app.include_router(
|
||||||
|
# invocation.invocation_router,
|
||||||
|
# prefix = '/api')
|
||||||
|
|
||||||
app.include_router(sessions.session_router, prefix="/api")
|
app.include_router(sessions.session_router, prefix="/api")
|
||||||
|
|
||||||
app.include_router(utilities.utilities_router, prefix="/api")
|
app.include_router(utilities.utilities_router, prefix="/api")
|
||||||
|
|
||||||
app.include_router(models.models_router, prefix="/api")
|
app.include_router(models.models_router, prefix="/api")
|
||||||
app.include_router(model_records.model_records_router, prefix="/api")
|
|
||||||
app.include_router(images.images_router, prefix="/api")
|
app.include_router(images.images_router, prefix="/api")
|
||||||
|
|
||||||
app.include_router(boards.boards_router, prefix="/api")
|
app.include_router(boards.boards_router, prefix="/api")
|
||||||
|
|
||||||
app.include_router(board_images.board_images_router, prefix="/api")
|
app.include_router(board_images.board_images_router, prefix="/api")
|
||||||
|
|
||||||
app.include_router(app_info.app_router, prefix="/api")
|
app.include_router(app_info.app_router, prefix="/api")
|
||||||
|
|
||||||
app.include_router(session_queue.session_queue_router, prefix="/api")
|
app.include_router(session_queue.session_queue_router, prefix="/api")
|
||||||
app.include_router(workflows.workflows_router, prefix="/api")
|
|
||||||
|
|
||||||
|
|
||||||
# Build a custom OpenAPI to include all outputs
|
# Build a custom OpenAPI to include all outputs
|
||||||
# TODO: can outputs be included on metadata of invocation schemas somehow?
|
# TODO: can outputs be included on metadata of invocation schemas somehow?
|
||||||
def custom_openapi() -> dict[str, Any]:
|
def custom_openapi():
|
||||||
if app.openapi_schema:
|
if app.openapi_schema:
|
||||||
return app.openapi_schema
|
return app.openapi_schema
|
||||||
openapi_schema = get_openapi(
|
openapi_schema = get_openapi(
|
||||||
@@ -134,47 +117,39 @@ def custom_openapi() -> dict[str, Any]:
|
|||||||
description="An API for invoking AI image operations",
|
description="An API for invoking AI image operations",
|
||||||
version="1.0.0",
|
version="1.0.0",
|
||||||
routes=app.routes,
|
routes=app.routes,
|
||||||
separate_input_output_schemas=False, # https://fastapi.tiangolo.com/how-to/separate-openapi-schemas/
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add all outputs
|
# Add all outputs
|
||||||
all_invocations = BaseInvocation.get_invocations()
|
all_invocations = BaseInvocation.get_invocations()
|
||||||
output_types = set()
|
output_types = set()
|
||||||
output_type_titles = {}
|
output_type_titles = dict()
|
||||||
for invoker in all_invocations:
|
for invoker in all_invocations:
|
||||||
output_type = signature(invoker.invoke).return_annotation
|
output_type = signature(invoker.invoke).return_annotation
|
||||||
output_types.add(output_type)
|
output_types.add(output_type)
|
||||||
|
|
||||||
output_schemas = models_json_schema(
|
output_schemas = schema(output_types, ref_prefix="#/components/schemas/")
|
||||||
models=[(o, "serialization") for o in output_types], ref_template="#/components/schemas/{model}"
|
for schema_key, output_schema in output_schemas["definitions"].items():
|
||||||
)
|
output_schema["class"] = "output"
|
||||||
for schema_key, output_schema in output_schemas[1]["$defs"].items():
|
openapi_schema["components"]["schemas"][schema_key] = output_schema
|
||||||
|
|
||||||
# TODO: note that we assume the schema_key here is the TYPE.__name__
|
# TODO: note that we assume the schema_key here is the TYPE.__name__
|
||||||
# This could break in some cases, figure out a better way to do it
|
# This could break in some cases, figure out a better way to do it
|
||||||
output_type_titles[schema_key] = output_schema["title"]
|
output_type_titles[schema_key] = output_schema["title"]
|
||||||
|
|
||||||
# Add Node Editor UI helper schemas
|
# Add Node Editor UI helper schemas
|
||||||
ui_config_schemas = models_json_schema(
|
ui_config_schemas = schema([UIConfigBase, _InputField, _OutputField], ref_prefix="#/components/schemas/")
|
||||||
[
|
for schema_key, ui_config_schema in ui_config_schemas["definitions"].items():
|
||||||
(UIConfigBase, "serialization"),
|
|
||||||
(InputFieldJSONSchemaExtra, "serialization"),
|
|
||||||
(OutputFieldJSONSchemaExtra, "serialization"),
|
|
||||||
],
|
|
||||||
ref_template="#/components/schemas/{model}",
|
|
||||||
)
|
|
||||||
for schema_key, ui_config_schema in ui_config_schemas[1]["$defs"].items():
|
|
||||||
openapi_schema["components"]["schemas"][schema_key] = ui_config_schema
|
openapi_schema["components"]["schemas"][schema_key] = ui_config_schema
|
||||||
|
|
||||||
# Add a reference to the output type to additionalProperties of the invoker schema
|
# Add a reference to the output type to additionalProperties of the invoker schema
|
||||||
for invoker in all_invocations:
|
for invoker in all_invocations:
|
||||||
invoker_name = invoker.__name__ # type: ignore [attr-defined] # this is a valid attribute
|
invoker_name = invoker.__name__
|
||||||
output_type = signature(obj=invoker.invoke).return_annotation
|
output_type = signature(invoker.invoke).return_annotation
|
||||||
output_type_title = output_type_titles[output_type.__name__]
|
output_type_title = output_type_titles[output_type.__name__]
|
||||||
invoker_schema = openapi_schema["components"]["schemas"][f"{invoker_name}"]
|
invoker_schema = openapi_schema["components"]["schemas"][invoker_name]
|
||||||
outputs_ref = {"$ref": f"#/components/schemas/{output_type_title}"}
|
outputs_ref = {"$ref": f"#/components/schemas/{output_type_title}"}
|
||||||
invoker_schema["output"] = outputs_ref
|
invoker_schema["output"] = outputs_ref
|
||||||
invoker_schema["class"] = "invocation"
|
invoker_schema["class"] = "invocation"
|
||||||
openapi_schema["components"]["schemas"][f"{output_type_title}"]["class"] = "output"
|
|
||||||
|
|
||||||
from invokeai.backend.model_management.models import get_model_config_enums
|
from invokeai.backend.model_management.models import get_model_config_enums
|
||||||
|
|
||||||
@@ -185,56 +160,48 @@ def custom_openapi() -> dict[str, Any]:
|
|||||||
# print(f"Config with name {name} already defined")
|
# print(f"Config with name {name} already defined")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
openapi_schema["components"]["schemas"][name] = {
|
# "BaseModelType":{"title":"BaseModelType","description":"An enumeration.","enum":["sd-1","sd-2"],"type":"string"}
|
||||||
"title": name,
|
openapi_schema["components"]["schemas"][name] = dict(
|
||||||
"description": "An enumeration.",
|
title=name,
|
||||||
"type": "string",
|
description="An enumeration.",
|
||||||
"enum": [v.value for v in model_config_format_enum],
|
type="string",
|
||||||
}
|
enum=list(v.value for v in model_config_format_enum),
|
||||||
|
)
|
||||||
|
|
||||||
app.openapi_schema = openapi_schema
|
app.openapi_schema = openapi_schema
|
||||||
return app.openapi_schema
|
return app.openapi_schema
|
||||||
|
|
||||||
|
|
||||||
app.openapi = custom_openapi # type: ignore [method-assign] # this is a valid assignment
|
app.openapi = custom_openapi
|
||||||
|
|
||||||
|
# Override API doc favicons
|
||||||
|
app.mount("/static", StaticFiles(directory=Path(web_dir.__path__[0], "static/dream_web")), name="static")
|
||||||
|
|
||||||
|
|
||||||
@app.get("/docs", include_in_schema=False)
|
@app.get("/docs", include_in_schema=False)
|
||||||
def overridden_swagger() -> HTMLResponse:
|
def overridden_swagger():
|
||||||
return get_swagger_ui_html(
|
return get_swagger_ui_html(
|
||||||
openapi_url=app.openapi_url, # type: ignore [arg-type] # this is always a string
|
openapi_url=app.openapi_url,
|
||||||
title=app.title,
|
title=app.title,
|
||||||
swagger_favicon_url="/static/docs/favicon.ico",
|
swagger_favicon_url="/static/favicon.ico",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/redoc", include_in_schema=False)
|
@app.get("/redoc", include_in_schema=False)
|
||||||
def overridden_redoc() -> HTMLResponse:
|
def overridden_redoc():
|
||||||
return get_redoc_html(
|
return get_redoc_html(
|
||||||
openapi_url=app.openapi_url, # type: ignore [arg-type] # this is always a string
|
openapi_url=app.openapi_url,
|
||||||
title=app.title,
|
title=app.title,
|
||||||
redoc_favicon_url="/static/docs/favicon.ico",
|
redoc_favicon_url="/static/favicon.ico",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
web_root_path = Path(list(web_dir.__path__)[0])
|
# Must mount *after* the other routes else it borks em
|
||||||
|
app.mount("/", StaticFiles(directory=Path(web_dir.__path__[0], "dist"), html=True), name="ui")
|
||||||
|
|
||||||
|
|
||||||
# Cannot add headers to StaticFiles, so we must serve index.html with a custom route
|
def invoke_api():
|
||||||
# Add cache-control: no-store header to prevent caching of index.html, which leads to broken UIs at release
|
def find_port(port: int):
|
||||||
@app.get("/", include_in_schema=False, name="ui_root")
|
|
||||||
def get_index() -> FileResponse:
|
|
||||||
return FileResponse(Path(web_root_path, "dist/index.html"), headers={"Cache-Control": "no-store"})
|
|
||||||
|
|
||||||
|
|
||||||
# # Must mount *after* the other routes else it borks em
|
|
||||||
app.mount("/static", StaticFiles(directory=Path(web_root_path, "static/")), name="static") # docs favicon is in here
|
|
||||||
app.mount("/assets", StaticFiles(directory=Path(web_root_path, "dist/assets/")), name="assets")
|
|
||||||
app.mount("/locales", StaticFiles(directory=Path(web_root_path, "dist/locales/")), name="locales")
|
|
||||||
|
|
||||||
|
|
||||||
def invoke_api() -> None:
|
|
||||||
def find_port(port: int) -> int:
|
|
||||||
"""Find a port not in use starting at given port"""
|
"""Find a port not in use starting at given port"""
|
||||||
# Taken from https://waylonwalker.com/python-find-available-port/, thanks Waylon!
|
# Taken from https://waylonwalker.com/python-find-available-port/, thanks Waylon!
|
||||||
# https://github.com/WaylonWalker
|
# https://github.com/WaylonWalker
|
||||||
@@ -269,7 +236,7 @@ def invoke_api() -> None:
|
|||||||
app=app,
|
app=app,
|
||||||
host=app_config.host,
|
host=app_config.host,
|
||||||
port=port,
|
port=port,
|
||||||
loop="asyncio",
|
loop=loop,
|
||||||
log_level=app_config.log_level,
|
log_level=app_config.log_level,
|
||||||
)
|
)
|
||||||
server = uvicorn.Server(config)
|
server = uvicorn.Server(config)
|
||||||
@@ -285,4 +252,7 @@ def invoke_api() -> None:
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
invoke_api()
|
if app_config.version:
|
||||||
|
print(f"InvokeAI version {__version__}")
|
||||||
|
else:
|
||||||
|
invoke_api()
|
||||||
|
|||||||
313
invokeai/app/cli/commands.py
Normal file
313
invokeai/app/cli/commands.py
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
# Copyright (c) 2023 Kyle Schouviller (https://github.com/kyle0654)
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from typing import Any, Callable, Iterable, Literal, Union, get_args, get_origin, get_type_hints
|
||||||
|
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import networkx as nx
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
import invokeai.backend.util.logging as logger
|
||||||
|
|
||||||
|
from ..invocations.baseinvocation import BaseInvocation
|
||||||
|
from ..invocations.image import ImageField
|
||||||
|
from ..services.graph import Edge, GraphExecutionState, LibraryGraph
|
||||||
|
from ..services.invoker import Invoker
|
||||||
|
|
||||||
|
|
||||||
|
def add_field_argument(command_parser, name: str, field, default_override=None):
|
||||||
|
default = (
|
||||||
|
default_override
|
||||||
|
if default_override is not None
|
||||||
|
else field.default
|
||||||
|
if field.default_factory is None
|
||||||
|
else field.default_factory()
|
||||||
|
)
|
||||||
|
if get_origin(field.type_) == Literal:
|
||||||
|
allowed_values = get_args(field.type_)
|
||||||
|
allowed_types = set()
|
||||||
|
for val in allowed_values:
|
||||||
|
allowed_types.add(type(val))
|
||||||
|
allowed_types_list = list(allowed_types)
|
||||||
|
field_type = allowed_types_list[0] if len(allowed_types) == 1 else Union[allowed_types_list] # type: ignore
|
||||||
|
|
||||||
|
command_parser.add_argument(
|
||||||
|
f"--{name}",
|
||||||
|
dest=name,
|
||||||
|
type=field_type,
|
||||||
|
default=default,
|
||||||
|
choices=allowed_values,
|
||||||
|
help=field.field_info.description,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
command_parser.add_argument(
|
||||||
|
f"--{name}",
|
||||||
|
dest=name,
|
||||||
|
type=field.type_,
|
||||||
|
default=default,
|
||||||
|
help=field.field_info.description,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def add_parsers(
|
||||||
|
subparsers,
|
||||||
|
commands: list[type],
|
||||||
|
command_field: str = "type",
|
||||||
|
exclude_fields: list[str] = ["id", "type"],
|
||||||
|
add_arguments: Union[Callable[[argparse.ArgumentParser], None], None] = None,
|
||||||
|
):
|
||||||
|
"""Adds parsers for each command to the subparsers"""
|
||||||
|
|
||||||
|
# Create subparsers for each command
|
||||||
|
for command in commands:
|
||||||
|
hints = get_type_hints(command)
|
||||||
|
cmd_name = get_args(hints[command_field])[0]
|
||||||
|
command_parser = subparsers.add_parser(cmd_name, help=command.__doc__)
|
||||||
|
|
||||||
|
if add_arguments is not None:
|
||||||
|
add_arguments(command_parser)
|
||||||
|
|
||||||
|
# Convert all fields to arguments
|
||||||
|
fields = command.__fields__ # type: ignore
|
||||||
|
for name, field in fields.items():
|
||||||
|
if name in exclude_fields:
|
||||||
|
continue
|
||||||
|
|
||||||
|
add_field_argument(command_parser, name, field)
|
||||||
|
|
||||||
|
|
||||||
|
def add_graph_parsers(
|
||||||
|
subparsers, graphs: list[LibraryGraph], add_arguments: Union[Callable[[argparse.ArgumentParser], None], None] = None
|
||||||
|
):
|
||||||
|
for graph in graphs:
|
||||||
|
command_parser = subparsers.add_parser(graph.name, help=graph.description)
|
||||||
|
|
||||||
|
if add_arguments is not None:
|
||||||
|
add_arguments(command_parser)
|
||||||
|
|
||||||
|
# Add arguments for inputs
|
||||||
|
for exposed_input in graph.exposed_inputs:
|
||||||
|
node = graph.graph.get_node(exposed_input.node_path)
|
||||||
|
field = node.__fields__[exposed_input.field]
|
||||||
|
default_override = getattr(node, exposed_input.field)
|
||||||
|
add_field_argument(command_parser, exposed_input.alias, field, default_override)
|
||||||
|
|
||||||
|
|
||||||
|
class CliContext:
|
||||||
|
invoker: Invoker
|
||||||
|
session: GraphExecutionState
|
||||||
|
parser: argparse.ArgumentParser
|
||||||
|
defaults: dict[str, Any]
|
||||||
|
graph_nodes: dict[str, str]
|
||||||
|
nodes_added: list[str]
|
||||||
|
|
||||||
|
def __init__(self, invoker: Invoker, session: GraphExecutionState, parser: argparse.ArgumentParser):
|
||||||
|
self.invoker = invoker
|
||||||
|
self.session = session
|
||||||
|
self.parser = parser
|
||||||
|
self.defaults = dict()
|
||||||
|
self.graph_nodes = dict()
|
||||||
|
self.nodes_added = list()
|
||||||
|
|
||||||
|
def get_session(self):
|
||||||
|
self.session = self.invoker.services.graph_execution_manager.get(self.session.id)
|
||||||
|
return self.session
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self.session = self.invoker.create_execution_state()
|
||||||
|
self.graph_nodes = dict()
|
||||||
|
self.nodes_added = list()
|
||||||
|
# Leave defaults unchanged
|
||||||
|
|
||||||
|
def add_node(self, node: BaseInvocation):
|
||||||
|
self.get_session()
|
||||||
|
self.session.graph.add_node(node)
|
||||||
|
self.nodes_added.append(node.id)
|
||||||
|
self.invoker.services.graph_execution_manager.set(self.session)
|
||||||
|
|
||||||
|
def add_edge(self, edge: Edge):
|
||||||
|
self.get_session()
|
||||||
|
self.session.add_edge(edge)
|
||||||
|
self.invoker.services.graph_execution_manager.set(self.session)
|
||||||
|
|
||||||
|
|
||||||
|
class ExitCli(Exception):
|
||||||
|
"""Exception to exit the CLI"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BaseCommand(ABC, BaseModel):
|
||||||
|
"""A CLI command"""
|
||||||
|
|
||||||
|
# All commands must include a type name like this:
|
||||||
|
# type: Literal['your_command_name'] = 'your_command_name'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_all_subclasses(cls):
|
||||||
|
subclasses = []
|
||||||
|
toprocess = [cls]
|
||||||
|
while len(toprocess) > 0:
|
||||||
|
next = toprocess.pop(0)
|
||||||
|
next_subclasses = next.__subclasses__()
|
||||||
|
subclasses.extend(next_subclasses)
|
||||||
|
toprocess.extend(next_subclasses)
|
||||||
|
return subclasses
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_commands(cls):
|
||||||
|
return tuple(BaseCommand.get_all_subclasses())
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_commands_map(cls):
|
||||||
|
# Get the type strings out of the literals and into a dictionary
|
||||||
|
return dict(map(lambda t: (get_args(get_type_hints(t)["type"])[0], t), BaseCommand.get_all_subclasses()))
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def run(self, context: CliContext) -> None:
|
||||||
|
"""Run the command. Raise ExitCli to exit."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ExitCommand(BaseCommand):
|
||||||
|
"""Exits the CLI"""
|
||||||
|
|
||||||
|
type: Literal["exit"] = "exit"
|
||||||
|
|
||||||
|
def run(self, context: CliContext) -> None:
|
||||||
|
raise ExitCli()
|
||||||
|
|
||||||
|
|
||||||
|
class HelpCommand(BaseCommand):
|
||||||
|
"""Shows help"""
|
||||||
|
|
||||||
|
type: Literal["help"] = "help"
|
||||||
|
|
||||||
|
def run(self, context: CliContext) -> None:
|
||||||
|
context.parser.print_help()
|
||||||
|
|
||||||
|
|
||||||
|
def get_graph_execution_history(
|
||||||
|
graph_execution_state: GraphExecutionState,
|
||||||
|
) -> Iterable[str]:
|
||||||
|
"""Gets the history of fully-executed invocations for a graph execution"""
|
||||||
|
return (n for n in reversed(graph_execution_state.executed_history) if n in graph_execution_state.graph.nodes)
|
||||||
|
|
||||||
|
|
||||||
|
def get_invocation_command(invocation) -> str:
|
||||||
|
fields = invocation.__fields__.items()
|
||||||
|
type_hints = get_type_hints(type(invocation))
|
||||||
|
command = [invocation.type]
|
||||||
|
for name, field in fields:
|
||||||
|
if name in ["id", "type"]:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# TODO: add links
|
||||||
|
|
||||||
|
# Skip image fields when serializing command
|
||||||
|
type_hint = type_hints.get(name) or None
|
||||||
|
if type_hint is ImageField or ImageField in get_args(type_hint):
|
||||||
|
continue
|
||||||
|
|
||||||
|
field_value = getattr(invocation, name)
|
||||||
|
field_default = field.default
|
||||||
|
if field_value != field_default:
|
||||||
|
if type_hint is str or str in get_args(type_hint):
|
||||||
|
command.append(f'--{name} "{field_value}"')
|
||||||
|
else:
|
||||||
|
command.append(f"--{name} {field_value}")
|
||||||
|
|
||||||
|
return " ".join(command)
|
||||||
|
|
||||||
|
|
||||||
|
class HistoryCommand(BaseCommand):
|
||||||
|
"""Shows the invocation history"""
|
||||||
|
|
||||||
|
type: Literal["history"] = "history"
|
||||||
|
|
||||||
|
# Inputs
|
||||||
|
# fmt: off
|
||||||
|
count: int = Field(default=5, gt=0, description="The number of history entries to show")
|
||||||
|
# fmt: on
|
||||||
|
|
||||||
|
def run(self, context: CliContext) -> None:
|
||||||
|
history = list(get_graph_execution_history(context.get_session()))
|
||||||
|
for i in range(min(self.count, len(history))):
|
||||||
|
entry_id = history[-1 - i]
|
||||||
|
entry = context.get_session().graph.get_node(entry_id)
|
||||||
|
logger.info(f"{entry_id}: {get_invocation_command(entry)}")
|
||||||
|
|
||||||
|
|
||||||
|
class SetDefaultCommand(BaseCommand):
|
||||||
|
"""Sets a default value for a field"""
|
||||||
|
|
||||||
|
type: Literal["default"] = "default"
|
||||||
|
|
||||||
|
# Inputs
|
||||||
|
# fmt: off
|
||||||
|
field: str = Field(description="The field to set the default for")
|
||||||
|
value: str = Field(description="The value to set the default to, or None to clear the default")
|
||||||
|
# fmt: on
|
||||||
|
|
||||||
|
def run(self, context: CliContext) -> None:
|
||||||
|
if self.value is None:
|
||||||
|
if self.field in context.defaults:
|
||||||
|
del context.defaults[self.field]
|
||||||
|
else:
|
||||||
|
context.defaults[self.field] = self.value
|
||||||
|
|
||||||
|
|
||||||
|
class DrawGraphCommand(BaseCommand):
|
||||||
|
"""Debugs a graph"""
|
||||||
|
|
||||||
|
type: Literal["draw_graph"] = "draw_graph"
|
||||||
|
|
||||||
|
def run(self, context: CliContext) -> None:
|
||||||
|
session: GraphExecutionState = context.invoker.services.graph_execution_manager.get(context.session.id)
|
||||||
|
nxgraph = session.graph.nx_graph_flat()
|
||||||
|
|
||||||
|
# Draw the networkx graph
|
||||||
|
plt.figure(figsize=(20, 20))
|
||||||
|
pos = nx.spectral_layout(nxgraph)
|
||||||
|
nx.draw_networkx_nodes(nxgraph, pos, node_size=1000)
|
||||||
|
nx.draw_networkx_edges(nxgraph, pos, width=2)
|
||||||
|
nx.draw_networkx_labels(nxgraph, pos, font_size=20, font_family="sans-serif")
|
||||||
|
plt.axis("off")
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
|
class DrawExecutionGraphCommand(BaseCommand):
|
||||||
|
"""Debugs an execution graph"""
|
||||||
|
|
||||||
|
type: Literal["draw_xgraph"] = "draw_xgraph"
|
||||||
|
|
||||||
|
def run(self, context: CliContext) -> None:
|
||||||
|
session: GraphExecutionState = context.invoker.services.graph_execution_manager.get(context.session.id)
|
||||||
|
nxgraph = session.execution_graph.nx_graph_flat()
|
||||||
|
|
||||||
|
# Draw the networkx graph
|
||||||
|
plt.figure(figsize=(20, 20))
|
||||||
|
pos = nx.spectral_layout(nxgraph)
|
||||||
|
nx.draw_networkx_nodes(nxgraph, pos, node_size=1000)
|
||||||
|
nx.draw_networkx_edges(nxgraph, pos, width=2)
|
||||||
|
nx.draw_networkx_labels(nxgraph, pos, font_size=20, font_family="sans-serif")
|
||||||
|
plt.axis("off")
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
|
class SortedHelpFormatter(argparse.HelpFormatter):
|
||||||
|
def _iter_indented_subactions(self, action):
|
||||||
|
try:
|
||||||
|
get_subactions = action._get_subactions
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self._indent()
|
||||||
|
if isinstance(action, argparse._SubParsersAction):
|
||||||
|
for subaction in sorted(get_subactions(), key=lambda x: x.dest):
|
||||||
|
yield subaction
|
||||||
|
else:
|
||||||
|
for subaction in get_subactions():
|
||||||
|
yield subaction
|
||||||
|
self._dedent()
|
||||||
171
invokeai/app/cli/completer.py
Normal file
171
invokeai/app/cli/completer.py
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
"""
|
||||||
|
Readline helper functions for cli_app.py
|
||||||
|
You may import the global singleton `completer` to get access to the
|
||||||
|
completer object.
|
||||||
|
"""
|
||||||
|
import atexit
|
||||||
|
import readline
|
||||||
|
import shlex
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, List, Literal, get_args, get_origin, get_type_hints
|
||||||
|
|
||||||
|
import invokeai.backend.util.logging as logger
|
||||||
|
|
||||||
|
from ...backend import ModelManager
|
||||||
|
from ..invocations.baseinvocation import BaseInvocation
|
||||||
|
from ..services.invocation_services import InvocationServices
|
||||||
|
from .commands import BaseCommand
|
||||||
|
|
||||||
|
# singleton object, class variable
|
||||||
|
completer = None
|
||||||
|
|
||||||
|
|
||||||
|
class Completer(object):
|
||||||
|
def __init__(self, model_manager: ModelManager):
|
||||||
|
self.commands = self.get_commands()
|
||||||
|
self.matches = None
|
||||||
|
self.linebuffer = None
|
||||||
|
self.manager = model_manager
|
||||||
|
return
|
||||||
|
|
||||||
|
def complete(self, text, state):
|
||||||
|
"""
|
||||||
|
Complete commands and switches fromm the node CLI command line.
|
||||||
|
Switches are determined in a context-specific manner.
|
||||||
|
"""
|
||||||
|
|
||||||
|
buffer = readline.get_line_buffer()
|
||||||
|
if state == 0:
|
||||||
|
options = None
|
||||||
|
try:
|
||||||
|
current_command, current_switch = self.get_current_command(buffer)
|
||||||
|
options = self.get_command_options(current_command, current_switch)
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
options = options or list(self.parse_commands().keys())
|
||||||
|
|
||||||
|
if not text: # first time
|
||||||
|
self.matches = options
|
||||||
|
else:
|
||||||
|
self.matches = [s for s in options if s and s.startswith(text)]
|
||||||
|
|
||||||
|
try:
|
||||||
|
match = self.matches[state]
|
||||||
|
except IndexError:
|
||||||
|
match = None
|
||||||
|
return match
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_commands(self) -> List[object]:
|
||||||
|
"""
|
||||||
|
Return a list of all the client commands and invocations.
|
||||||
|
"""
|
||||||
|
return BaseCommand.get_commands() + BaseInvocation.get_invocations()
|
||||||
|
|
||||||
|
def get_current_command(self, buffer: str) -> tuple[str, str]:
|
||||||
|
"""
|
||||||
|
Parse the readline buffer to find the most recent command and its switch.
|
||||||
|
"""
|
||||||
|
if len(buffer) == 0:
|
||||||
|
return None, None
|
||||||
|
tokens = shlex.split(buffer)
|
||||||
|
command = None
|
||||||
|
switch = None
|
||||||
|
for t in tokens:
|
||||||
|
if t[0].isalpha():
|
||||||
|
if switch is None:
|
||||||
|
command = t
|
||||||
|
else:
|
||||||
|
switch = t
|
||||||
|
# don't try to autocomplete switches that are already complete
|
||||||
|
if switch and buffer.endswith(" "):
|
||||||
|
switch = None
|
||||||
|
return command or "", switch or ""
|
||||||
|
|
||||||
|
def parse_commands(self) -> Dict[str, List[str]]:
|
||||||
|
"""
|
||||||
|
Return a dict in which the keys are the command name
|
||||||
|
and the values are the parameters the command takes.
|
||||||
|
"""
|
||||||
|
result = dict()
|
||||||
|
for command in self.commands:
|
||||||
|
hints = get_type_hints(command)
|
||||||
|
name = get_args(hints["type"])[0]
|
||||||
|
result.update({name: hints})
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_command_options(self, command: str, switch: str) -> List[str]:
|
||||||
|
"""
|
||||||
|
Return all the parameters that can be passed to the command as
|
||||||
|
command-line switches. Returns None if the command is unrecognized.
|
||||||
|
"""
|
||||||
|
parsed_commands = self.parse_commands()
|
||||||
|
if command not in parsed_commands:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# handle switches in the format "-foo=bar"
|
||||||
|
argument = None
|
||||||
|
if switch and "=" in switch:
|
||||||
|
switch, argument = switch.split("=")
|
||||||
|
|
||||||
|
parameter = switch.strip("-")
|
||||||
|
if parameter in parsed_commands[command]:
|
||||||
|
if argument is None:
|
||||||
|
return self.get_parameter_options(parameter, parsed_commands[command][parameter])
|
||||||
|
else:
|
||||||
|
return [
|
||||||
|
f"--{parameter}={x}"
|
||||||
|
for x in self.get_parameter_options(parameter, parsed_commands[command][parameter])
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
return [f"--{x}" for x in parsed_commands[command].keys()]
|
||||||
|
|
||||||
|
def get_parameter_options(self, parameter: str, typehint) -> List[str]:
|
||||||
|
"""
|
||||||
|
Given a parameter type (such as Literal), offers autocompletions.
|
||||||
|
"""
|
||||||
|
if get_origin(typehint) == Literal:
|
||||||
|
return get_args(typehint)
|
||||||
|
if parameter == "model":
|
||||||
|
return self.manager.model_names()
|
||||||
|
|
||||||
|
def _pre_input_hook(self):
|
||||||
|
if self.linebuffer:
|
||||||
|
readline.insert_text(self.linebuffer)
|
||||||
|
readline.redisplay()
|
||||||
|
self.linebuffer = None
|
||||||
|
|
||||||
|
|
||||||
|
def set_autocompleter(services: InvocationServices) -> Completer:
|
||||||
|
global completer
|
||||||
|
|
||||||
|
if completer:
|
||||||
|
return completer
|
||||||
|
|
||||||
|
completer = Completer(services.model_manager)
|
||||||
|
|
||||||
|
readline.set_completer(completer.complete)
|
||||||
|
try:
|
||||||
|
readline.set_auto_history(True)
|
||||||
|
except AttributeError:
|
||||||
|
# pyreadline3 does not have a set_auto_history() method
|
||||||
|
pass
|
||||||
|
readline.set_pre_input_hook(completer._pre_input_hook)
|
||||||
|
readline.set_completer_delims(" ")
|
||||||
|
readline.parse_and_bind("tab: complete")
|
||||||
|
readline.parse_and_bind("set print-completions-horizontally off")
|
||||||
|
readline.parse_and_bind("set page-completions on")
|
||||||
|
readline.parse_and_bind("set skip-completed-text on")
|
||||||
|
readline.parse_and_bind("set show-all-if-ambiguous on")
|
||||||
|
|
||||||
|
histfile = Path(services.configuration.root_dir / ".invoke_history")
|
||||||
|
try:
|
||||||
|
readline.read_history_file(histfile)
|
||||||
|
readline.set_history_length(1000)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
except OSError: # file likely corrupted
|
||||||
|
newname = f"{histfile}.old"
|
||||||
|
logger.error(f"Your history file {histfile} couldn't be loaded and may be corrupted. Renaming it to {newname}")
|
||||||
|
histfile.replace(Path(newname))
|
||||||
|
atexit.register(readline.write_history_file, histfile)
|
||||||
484
invokeai/app/cli_app.py
Normal file
484
invokeai/app/cli_app.py
Normal file
@@ -0,0 +1,484 @@
|
|||||||
|
# Copyright (c) 2022 Kyle Schouviller (https://github.com/kyle0654) and the InvokeAI Team
|
||||||
|
|
||||||
|
from invokeai.app.services.invocation_cache.invocation_cache_memory import MemoryInvocationCache
|
||||||
|
|
||||||
|
from .services.config import InvokeAIAppConfig
|
||||||
|
|
||||||
|
# parse_args() must be called before any other imports. if it is not called first, consumers of the config
|
||||||
|
# which are imported/used before parse_args() is called will get the default config values instead of the
|
||||||
|
# values from the command line or config file.
|
||||||
|
|
||||||
|
if True: # hack to make flake8 happy with imports coming after setting up the config
|
||||||
|
import argparse
|
||||||
|
import re
|
||||||
|
import shlex
|
||||||
|
import sqlite3
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
from typing import Optional, Union, get_type_hints
|
||||||
|
|
||||||
|
import torch
|
||||||
|
from pydantic import BaseModel, ValidationError
|
||||||
|
from pydantic.fields import Field
|
||||||
|
|
||||||
|
import invokeai.backend.util.hotfixes # noqa: F401 (monkeypatching on import)
|
||||||
|
from invokeai.app.services.board_image_record_storage import SqliteBoardImageRecordStorage
|
||||||
|
from invokeai.app.services.board_images import BoardImagesService, BoardImagesServiceDependencies
|
||||||
|
from invokeai.app.services.board_record_storage import SqliteBoardRecordStorage
|
||||||
|
from invokeai.app.services.boards import BoardService, BoardServiceDependencies
|
||||||
|
from invokeai.app.services.image_record_storage import SqliteImageRecordStorage
|
||||||
|
from invokeai.app.services.images import ImageService, ImageServiceDependencies
|
||||||
|
from invokeai.app.services.invocation_stats import InvocationStatsService
|
||||||
|
from invokeai.app.services.resource_name import SimpleNameService
|
||||||
|
from invokeai.app.services.urls import LocalUrlService
|
||||||
|
from invokeai.backend.util.logging import InvokeAILogger
|
||||||
|
from invokeai.version.invokeai_version import __version__
|
||||||
|
|
||||||
|
from .cli.commands import BaseCommand, CliContext, ExitCli, SortedHelpFormatter, add_graph_parsers, add_parsers
|
||||||
|
from .cli.completer import set_autocompleter
|
||||||
|
from .invocations.baseinvocation import BaseInvocation
|
||||||
|
from .services.default_graphs import create_system_graphs, default_text_to_image_graph_id
|
||||||
|
from .services.events import EventServiceBase
|
||||||
|
from .services.graph import (
|
||||||
|
Edge,
|
||||||
|
EdgeConnection,
|
||||||
|
GraphExecutionState,
|
||||||
|
GraphInvocation,
|
||||||
|
LibraryGraph,
|
||||||
|
are_connection_types_compatible,
|
||||||
|
)
|
||||||
|
from .services.image_file_storage import DiskImageFileStorage
|
||||||
|
from .services.invocation_queue import MemoryInvocationQueue
|
||||||
|
from .services.invocation_services import InvocationServices
|
||||||
|
from .services.invoker import Invoker
|
||||||
|
from .services.latent_storage import DiskLatentsStorage, ForwardCacheLatentsStorage
|
||||||
|
from .services.model_manager_service import ModelManagerService
|
||||||
|
from .services.processor import DefaultInvocationProcessor
|
||||||
|
from .services.sqlite import SqliteItemStorage
|
||||||
|
|
||||||
|
if torch.backends.mps.is_available():
|
||||||
|
import invokeai.backend.util.mps_fixes # noqa: F401 (monkeypatching on import)
|
||||||
|
|
||||||
|
config = InvokeAIAppConfig.get_config()
|
||||||
|
config.parse_args()
|
||||||
|
logger = InvokeAILogger().get_logger(config=config)
|
||||||
|
|
||||||
|
|
||||||
|
class CliCommand(BaseModel):
|
||||||
|
command: Union[BaseCommand.get_commands() + BaseInvocation.get_invocations()] = Field(discriminator="type") # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidArgs(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def add_invocation_args(command_parser):
|
||||||
|
# Add linking capability
|
||||||
|
command_parser.add_argument(
|
||||||
|
"--link",
|
||||||
|
"-l",
|
||||||
|
action="append",
|
||||||
|
nargs=3,
|
||||||
|
help="A link in the format 'source_node source_field dest_field'. source_node can be relative to history (e.g. -1)",
|
||||||
|
)
|
||||||
|
|
||||||
|
command_parser.add_argument(
|
||||||
|
"--link_node",
|
||||||
|
"-ln",
|
||||||
|
action="append",
|
||||||
|
help="A link from all fields in the specified node. Node can be relative to history (e.g. -1)",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_command_parser(services: InvocationServices) -> argparse.ArgumentParser:
|
||||||
|
# Create invocation parser
|
||||||
|
parser = argparse.ArgumentParser(formatter_class=SortedHelpFormatter)
|
||||||
|
|
||||||
|
def exit(*args, **kwargs):
|
||||||
|
raise InvalidArgs
|
||||||
|
|
||||||
|
parser.exit = exit
|
||||||
|
subparsers = parser.add_subparsers(dest="type")
|
||||||
|
|
||||||
|
# Create subparsers for each invocation
|
||||||
|
invocations = BaseInvocation.get_all_subclasses()
|
||||||
|
add_parsers(subparsers, invocations, add_arguments=add_invocation_args)
|
||||||
|
|
||||||
|
# Create subparsers for each command
|
||||||
|
commands = BaseCommand.get_all_subclasses()
|
||||||
|
add_parsers(subparsers, commands, exclude_fields=["type"])
|
||||||
|
|
||||||
|
# Create subparsers for exposed CLI graphs
|
||||||
|
# TODO: add a way to identify these graphs
|
||||||
|
text_to_image = services.graph_library.get(default_text_to_image_graph_id)
|
||||||
|
add_graph_parsers(subparsers, [text_to_image], add_arguments=add_invocation_args)
|
||||||
|
|
||||||
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
class NodeField:
|
||||||
|
alias: str
|
||||||
|
node_path: str
|
||||||
|
field: str
|
||||||
|
field_type: type
|
||||||
|
|
||||||
|
def __init__(self, alias: str, node_path: str, field: str, field_type: type):
|
||||||
|
self.alias = alias
|
||||||
|
self.node_path = node_path
|
||||||
|
self.field = field
|
||||||
|
self.field_type = field_type
|
||||||
|
|
||||||
|
|
||||||
|
def fields_from_type_hints(hints: dict[str, type], node_path: str) -> dict[str, NodeField]:
|
||||||
|
return {k: NodeField(alias=k, node_path=node_path, field=k, field_type=v) for k, v in hints.items()}
|
||||||
|
|
||||||
|
|
||||||
|
def get_node_input_field(graph: LibraryGraph, field_alias: str, node_id: str) -> NodeField:
|
||||||
|
"""Gets the node field for the specified field alias"""
|
||||||
|
exposed_input = next(e for e in graph.exposed_inputs if e.alias == field_alias)
|
||||||
|
node_type = type(graph.graph.get_node(exposed_input.node_path))
|
||||||
|
return NodeField(
|
||||||
|
alias=exposed_input.alias,
|
||||||
|
node_path=f"{node_id}.{exposed_input.node_path}",
|
||||||
|
field=exposed_input.field,
|
||||||
|
field_type=get_type_hints(node_type)[exposed_input.field],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_node_output_field(graph: LibraryGraph, field_alias: str, node_id: str) -> NodeField:
|
||||||
|
"""Gets the node field for the specified field alias"""
|
||||||
|
exposed_output = next(e for e in graph.exposed_outputs if e.alias == field_alias)
|
||||||
|
node_type = type(graph.graph.get_node(exposed_output.node_path))
|
||||||
|
node_output_type = node_type.get_output_type()
|
||||||
|
return NodeField(
|
||||||
|
alias=exposed_output.alias,
|
||||||
|
node_path=f"{node_id}.{exposed_output.node_path}",
|
||||||
|
field=exposed_output.field,
|
||||||
|
field_type=get_type_hints(node_output_type)[exposed_output.field],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_node_inputs(invocation: BaseInvocation, context: CliContext) -> dict[str, NodeField]:
|
||||||
|
"""Gets the inputs for the specified invocation from the context"""
|
||||||
|
node_type = type(invocation)
|
||||||
|
if node_type is not GraphInvocation:
|
||||||
|
return fields_from_type_hints(get_type_hints(node_type), invocation.id)
|
||||||
|
else:
|
||||||
|
graph: LibraryGraph = context.invoker.services.graph_library.get(context.graph_nodes[invocation.id])
|
||||||
|
return {e.alias: get_node_input_field(graph, e.alias, invocation.id) for e in graph.exposed_inputs}
|
||||||
|
|
||||||
|
|
||||||
|
def get_node_outputs(invocation: BaseInvocation, context: CliContext) -> dict[str, NodeField]:
|
||||||
|
"""Gets the outputs for the specified invocation from the context"""
|
||||||
|
node_type = type(invocation)
|
||||||
|
if node_type is not GraphInvocation:
|
||||||
|
return fields_from_type_hints(get_type_hints(node_type.get_output_type()), invocation.id)
|
||||||
|
else:
|
||||||
|
graph: LibraryGraph = context.invoker.services.graph_library.get(context.graph_nodes[invocation.id])
|
||||||
|
return {e.alias: get_node_output_field(graph, e.alias, invocation.id) for e in graph.exposed_outputs}
|
||||||
|
|
||||||
|
|
||||||
|
def generate_matching_edges(a: BaseInvocation, b: BaseInvocation, context: CliContext) -> list[Edge]:
|
||||||
|
"""Generates all possible edges between two invocations"""
|
||||||
|
afields = get_node_outputs(a, context)
|
||||||
|
bfields = get_node_inputs(b, context)
|
||||||
|
|
||||||
|
matching_fields = set(afields.keys()).intersection(bfields.keys())
|
||||||
|
|
||||||
|
# Remove invalid fields
|
||||||
|
invalid_fields = set(["type", "id"])
|
||||||
|
matching_fields = matching_fields.difference(invalid_fields)
|
||||||
|
|
||||||
|
# Validate types
|
||||||
|
matching_fields = [
|
||||||
|
f for f in matching_fields if are_connection_types_compatible(afields[f].field_type, bfields[f].field_type)
|
||||||
|
]
|
||||||
|
|
||||||
|
edges = [
|
||||||
|
Edge(
|
||||||
|
source=EdgeConnection(node_id=afields[alias].node_path, field=afields[alias].field),
|
||||||
|
destination=EdgeConnection(node_id=bfields[alias].node_path, field=bfields[alias].field),
|
||||||
|
)
|
||||||
|
for alias in matching_fields
|
||||||
|
]
|
||||||
|
return edges
|
||||||
|
|
||||||
|
|
||||||
|
class SessionError(Exception):
|
||||||
|
"""Raised when a session error has occurred"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def invoke_all(context: CliContext):
|
||||||
|
"""Runs all invocations in the specified session"""
|
||||||
|
context.invoker.invoke(context.session, invoke_all=True)
|
||||||
|
while not context.get_session().is_complete():
|
||||||
|
# Wait some time
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
# Print any errors
|
||||||
|
if context.session.has_error():
|
||||||
|
for n in context.session.errors:
|
||||||
|
context.invoker.services.logger.error(
|
||||||
|
f"Error in node {n} (source node {context.session.prepared_source_mapping[n]}): {context.session.errors[n]}"
|
||||||
|
)
|
||||||
|
|
||||||
|
raise SessionError()
|
||||||
|
|
||||||
|
|
||||||
|
def invoke_cli():
|
||||||
|
logger.info(f"InvokeAI version {__version__}")
|
||||||
|
# get the optional list of invocations to execute on the command line
|
||||||
|
parser = config.get_parser()
|
||||||
|
parser.add_argument("commands", nargs="*")
|
||||||
|
invocation_commands = parser.parse_args().commands
|
||||||
|
|
||||||
|
# get the optional file to read commands from.
|
||||||
|
# Simplest is to use it for STDIN
|
||||||
|
if infile := config.from_file:
|
||||||
|
sys.stdin = open(infile, "r")
|
||||||
|
|
||||||
|
model_manager = ModelManagerService(config, logger)
|
||||||
|
|
||||||
|
events = EventServiceBase()
|
||||||
|
output_folder = config.output_path
|
||||||
|
|
||||||
|
# TODO: build a file/path manager?
|
||||||
|
if config.use_memory_db:
|
||||||
|
db_location = ":memory:"
|
||||||
|
else:
|
||||||
|
db_location = config.db_path
|
||||||
|
db_location.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
db_conn = sqlite3.connect(db_location, check_same_thread=False) # TODO: figure out a better threading solution
|
||||||
|
logger.info(f'InvokeAI database location is "{db_location}"')
|
||||||
|
|
||||||
|
graph_execution_manager = SqliteItemStorage[GraphExecutionState](conn=db_conn, table_name="graph_executions")
|
||||||
|
|
||||||
|
urls = LocalUrlService()
|
||||||
|
image_record_storage = SqliteImageRecordStorage(conn=db_conn)
|
||||||
|
image_file_storage = DiskImageFileStorage(f"{output_folder}/images")
|
||||||
|
names = SimpleNameService()
|
||||||
|
|
||||||
|
board_record_storage = SqliteBoardRecordStorage(conn=db_conn)
|
||||||
|
board_image_record_storage = SqliteBoardImageRecordStorage(conn=db_conn)
|
||||||
|
|
||||||
|
boards = BoardService(
|
||||||
|
services=BoardServiceDependencies(
|
||||||
|
board_image_record_storage=board_image_record_storage,
|
||||||
|
board_record_storage=board_record_storage,
|
||||||
|
image_record_storage=image_record_storage,
|
||||||
|
url=urls,
|
||||||
|
logger=logger,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
board_images = BoardImagesService(
|
||||||
|
services=BoardImagesServiceDependencies(
|
||||||
|
board_image_record_storage=board_image_record_storage,
|
||||||
|
board_record_storage=board_record_storage,
|
||||||
|
image_record_storage=image_record_storage,
|
||||||
|
url=urls,
|
||||||
|
logger=logger,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
images = ImageService(
|
||||||
|
services=ImageServiceDependencies(
|
||||||
|
board_image_record_storage=board_image_record_storage,
|
||||||
|
image_record_storage=image_record_storage,
|
||||||
|
image_file_storage=image_file_storage,
|
||||||
|
url=urls,
|
||||||
|
logger=logger,
|
||||||
|
names=names,
|
||||||
|
graph_execution_manager=graph_execution_manager,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
services = InvocationServices(
|
||||||
|
model_manager=model_manager,
|
||||||
|
events=events,
|
||||||
|
latents=ForwardCacheLatentsStorage(DiskLatentsStorage(f"{output_folder}/latents")),
|
||||||
|
images=images,
|
||||||
|
boards=boards,
|
||||||
|
board_images=board_images,
|
||||||
|
queue=MemoryInvocationQueue(),
|
||||||
|
graph_library=SqliteItemStorage[LibraryGraph](conn=db_conn, table_name="graphs"),
|
||||||
|
graph_execution_manager=graph_execution_manager,
|
||||||
|
processor=DefaultInvocationProcessor(),
|
||||||
|
performance_statistics=InvocationStatsService(graph_execution_manager),
|
||||||
|
logger=logger,
|
||||||
|
configuration=config,
|
||||||
|
invocation_cache=MemoryInvocationCache(max_cache_size=config.node_cache_size),
|
||||||
|
)
|
||||||
|
|
||||||
|
system_graphs = create_system_graphs(services.graph_library)
|
||||||
|
system_graph_names = set([g.name for g in system_graphs])
|
||||||
|
set_autocompleter(services)
|
||||||
|
|
||||||
|
invoker = Invoker(services)
|
||||||
|
session: GraphExecutionState = invoker.create_execution_state()
|
||||||
|
parser = get_command_parser(services)
|
||||||
|
|
||||||
|
re_negid = re.compile("^-[0-9]+$")
|
||||||
|
|
||||||
|
# Uncomment to print out previous sessions at startup
|
||||||
|
# print(services.session_manager.list())
|
||||||
|
|
||||||
|
context = CliContext(invoker, session, parser)
|
||||||
|
set_autocompleter(services)
|
||||||
|
|
||||||
|
command_line_args_exist = len(invocation_commands) > 0
|
||||||
|
done = False
|
||||||
|
|
||||||
|
while not done:
|
||||||
|
try:
|
||||||
|
if command_line_args_exist:
|
||||||
|
cmd_input = invocation_commands.pop(0)
|
||||||
|
done = len(invocation_commands) == 0
|
||||||
|
else:
|
||||||
|
cmd_input = input("invoke> ")
|
||||||
|
except (KeyboardInterrupt, EOFError):
|
||||||
|
# Ctrl-c exits
|
||||||
|
break
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Refresh the state of the session
|
||||||
|
# history = list(get_graph_execution_history(context.session))
|
||||||
|
history = list(reversed(context.nodes_added))
|
||||||
|
|
||||||
|
# Split the command for piping
|
||||||
|
cmds = cmd_input.split("|")
|
||||||
|
start_id = len(context.nodes_added)
|
||||||
|
current_id = start_id
|
||||||
|
new_invocations = list()
|
||||||
|
for cmd in cmds:
|
||||||
|
if cmd is None or cmd.strip() == "":
|
||||||
|
raise InvalidArgs("Empty command")
|
||||||
|
|
||||||
|
# Parse args to create invocation
|
||||||
|
args = vars(context.parser.parse_args(shlex.split(cmd.strip())))
|
||||||
|
|
||||||
|
# Override defaults
|
||||||
|
for field_name, field_default in context.defaults.items():
|
||||||
|
if field_name in args:
|
||||||
|
args[field_name] = field_default
|
||||||
|
|
||||||
|
# Parse invocation
|
||||||
|
command: CliCommand = None # type:ignore
|
||||||
|
system_graph: Optional[LibraryGraph] = None
|
||||||
|
if args["type"] in system_graph_names:
|
||||||
|
system_graph = next(filter(lambda g: g.name == args["type"], system_graphs))
|
||||||
|
invocation = GraphInvocation(graph=system_graph.graph, id=str(current_id))
|
||||||
|
for exposed_input in system_graph.exposed_inputs:
|
||||||
|
if exposed_input.alias in args:
|
||||||
|
node = invocation.graph.get_node(exposed_input.node_path)
|
||||||
|
field = exposed_input.field
|
||||||
|
setattr(node, field, args[exposed_input.alias])
|
||||||
|
command = CliCommand(command=invocation)
|
||||||
|
context.graph_nodes[invocation.id] = system_graph.id
|
||||||
|
else:
|
||||||
|
args["id"] = current_id
|
||||||
|
command = CliCommand(command=args)
|
||||||
|
|
||||||
|
if command is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Run any CLI commands immediately
|
||||||
|
if isinstance(command.command, BaseCommand):
|
||||||
|
# Invoke all current nodes to preserve operation order
|
||||||
|
invoke_all(context)
|
||||||
|
|
||||||
|
# Run the command
|
||||||
|
command.command.run(context)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# TODO: handle linking with library graphs
|
||||||
|
# Pipe previous command output (if there was a previous command)
|
||||||
|
edges: list[Edge] = list()
|
||||||
|
if len(history) > 0 or current_id != start_id:
|
||||||
|
from_id = history[0] if current_id == start_id else str(current_id - 1)
|
||||||
|
from_node = (
|
||||||
|
next(filter(lambda n: n[0].id == from_id, new_invocations))[0]
|
||||||
|
if current_id != start_id
|
||||||
|
else context.session.graph.get_node(from_id)
|
||||||
|
)
|
||||||
|
matching_edges = generate_matching_edges(from_node, command.command, context)
|
||||||
|
edges.extend(matching_edges)
|
||||||
|
|
||||||
|
# Parse provided links
|
||||||
|
if "link_node" in args and args["link_node"]:
|
||||||
|
for link in args["link_node"]:
|
||||||
|
node_id = link
|
||||||
|
if re_negid.match(node_id):
|
||||||
|
node_id = str(current_id + int(node_id))
|
||||||
|
|
||||||
|
link_node = context.session.graph.get_node(node_id)
|
||||||
|
matching_edges = generate_matching_edges(link_node, command.command, context)
|
||||||
|
matching_destinations = [e.destination for e in matching_edges]
|
||||||
|
edges = [e for e in edges if e.destination not in matching_destinations]
|
||||||
|
edges.extend(matching_edges)
|
||||||
|
|
||||||
|
if "link" in args and args["link"]:
|
||||||
|
for link in args["link"]:
|
||||||
|
edges = [
|
||||||
|
e
|
||||||
|
for e in edges
|
||||||
|
if e.destination.node_id != command.command.id or e.destination.field != link[2]
|
||||||
|
]
|
||||||
|
|
||||||
|
node_id = link[0]
|
||||||
|
if re_negid.match(node_id):
|
||||||
|
node_id = str(current_id + int(node_id))
|
||||||
|
|
||||||
|
# TODO: handle missing input/output
|
||||||
|
node_output = get_node_outputs(context.session.graph.get_node(node_id), context)[link[1]]
|
||||||
|
node_input = get_node_inputs(command.command, context)[link[2]]
|
||||||
|
|
||||||
|
edges.append(
|
||||||
|
Edge(
|
||||||
|
source=EdgeConnection(node_id=node_output.node_path, field=node_output.field),
|
||||||
|
destination=EdgeConnection(node_id=node_input.node_path, field=node_input.field),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
new_invocations.append((command.command, edges))
|
||||||
|
|
||||||
|
current_id = current_id + 1
|
||||||
|
|
||||||
|
# Add the node to the session
|
||||||
|
context.add_node(command.command)
|
||||||
|
for edge in edges:
|
||||||
|
print(edge)
|
||||||
|
context.add_edge(edge)
|
||||||
|
|
||||||
|
# Execute all remaining nodes
|
||||||
|
invoke_all(context)
|
||||||
|
|
||||||
|
except InvalidArgs:
|
||||||
|
invoker.services.logger.warning('Invalid command, use "help" to list commands')
|
||||||
|
continue
|
||||||
|
|
||||||
|
except ValidationError:
|
||||||
|
invoker.services.logger.warning('Invalid command arguments, run "<command> --help" for summary')
|
||||||
|
|
||||||
|
except SessionError:
|
||||||
|
# Start a new session
|
||||||
|
invoker.services.logger.warning("Session error: creating a new session")
|
||||||
|
context.reset()
|
||||||
|
|
||||||
|
except ExitCli:
|
||||||
|
break
|
||||||
|
|
||||||
|
except SystemExit:
|
||||||
|
continue
|
||||||
|
|
||||||
|
invoker.stop()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if config.version:
|
||||||
|
print(f"InvokeAI version {__version__}")
|
||||||
|
else:
|
||||||
|
invoke_cli()
|
||||||
@@ -1,28 +1,8 @@
|
|||||||
import shutil
|
import os
|
||||||
import sys
|
|
||||||
from importlib.util import module_from_spec, spec_from_file_location
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from invokeai.app.services.config.config_default import InvokeAIAppConfig
|
__all__ = []
|
||||||
|
|
||||||
custom_nodes_path = Path(InvokeAIAppConfig.get_config().custom_nodes_path.resolve())
|
dirname = os.path.dirname(os.path.abspath(__file__))
|
||||||
custom_nodes_path.mkdir(parents=True, exist_ok=True)
|
for f in os.listdir(dirname):
|
||||||
|
if f != "__init__.py" and os.path.isfile("%s/%s" % (dirname, f)) and f[-3:] == ".py":
|
||||||
custom_nodes_init_path = str(custom_nodes_path / "__init__.py")
|
__all__.append(f[:-3])
|
||||||
custom_nodes_readme_path = str(custom_nodes_path / "README.md")
|
|
||||||
|
|
||||||
# copy our custom nodes __init__.py to the custom nodes directory
|
|
||||||
shutil.copy(Path(__file__).parent / "custom_nodes/init.py", custom_nodes_init_path)
|
|
||||||
shutil.copy(Path(__file__).parent / "custom_nodes/README.md", custom_nodes_readme_path)
|
|
||||||
|
|
||||||
# Import custom nodes, see https://docs.python.org/3/library/importlib.html#importing-programmatically
|
|
||||||
spec = spec_from_file_location("custom_nodes", custom_nodes_init_path)
|
|
||||||
if spec is None or spec.loader is None:
|
|
||||||
raise RuntimeError(f"Could not load custom nodes from {custom_nodes_init_path}")
|
|
||||||
module = module_from_spec(spec)
|
|
||||||
sys.modules[spec.name] = module
|
|
||||||
spec.loader.exec_module(module)
|
|
||||||
|
|
||||||
# add core nodes to __all__
|
|
||||||
python_files = filter(lambda f: not f.name.startswith("_"), Path(__file__).parent.glob("*.py"))
|
|
||||||
__all__ = [f.stem for f in python_files] # type: ignore
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from pydantic import ValidationInfo, field_validator
|
from pydantic import validator
|
||||||
|
|
||||||
from invokeai.app.invocations.primitives import IntegerCollectionOutput
|
from invokeai.app.invocations.primitives import IntegerCollectionOutput
|
||||||
from invokeai.app.util.misc import SEED_MAX
|
from invokeai.app.util.misc import SEED_MAX, get_random_seed
|
||||||
|
|
||||||
from .baseinvocation import BaseInvocation, InputField, InvocationContext, invocation
|
from .baseinvocation import BaseInvocation, InputField, InvocationContext, invocation
|
||||||
|
|
||||||
@@ -20,9 +20,9 @@ class RangeInvocation(BaseInvocation):
|
|||||||
stop: int = InputField(default=10, description="The stop of the range")
|
stop: int = InputField(default=10, description="The stop of the range")
|
||||||
step: int = InputField(default=1, description="The step of the range")
|
step: int = InputField(default=1, description="The step of the range")
|
||||||
|
|
||||||
@field_validator("stop")
|
@validator("stop")
|
||||||
def stop_gt_start(cls, v: int, info: ValidationInfo):
|
def stop_gt_start(cls, v, values):
|
||||||
if "start" in info.data and v <= info.data["start"]:
|
if "start" in values and v <= values["start"]:
|
||||||
raise ValueError("stop must be greater than start")
|
raise ValueError("stop must be greater than start")
|
||||||
return v
|
return v
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ class RangeOfSizeInvocation(BaseInvocation):
|
|||||||
title="Random Range",
|
title="Random Range",
|
||||||
tags=["range", "integer", "random", "collection"],
|
tags=["range", "integer", "random", "collection"],
|
||||||
category="collections",
|
category="collections",
|
||||||
version="1.0.1",
|
version="1.0.0",
|
||||||
use_cache=False,
|
use_cache=False,
|
||||||
)
|
)
|
||||||
class RandomRangeInvocation(BaseInvocation):
|
class RandomRangeInvocation(BaseInvocation):
|
||||||
@@ -65,10 +65,10 @@ class RandomRangeInvocation(BaseInvocation):
|
|||||||
high: int = InputField(default=np.iinfo(np.int32).max, description="The exclusive high value")
|
high: int = InputField(default=np.iinfo(np.int32).max, description="The exclusive high value")
|
||||||
size: int = InputField(default=1, description="The number of values to generate")
|
size: int = InputField(default=1, description="The number of values to generate")
|
||||||
seed: int = InputField(
|
seed: int = InputField(
|
||||||
default=0,
|
|
||||||
ge=0,
|
ge=0,
|
||||||
le=SEED_MAX,
|
le=SEED_MAX,
|
||||||
description="The seed for the RNG (omit for random)",
|
description="The seed for the RNG (omit for random)",
|
||||||
|
default_factory=get_random_seed,
|
||||||
)
|
)
|
||||||
|
|
||||||
def invoke(self, context: InvocationContext) -> IntegerCollectionOutput:
|
def invoke(self, context: InvocationContext) -> IntegerCollectionOutput:
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import re
|
import re
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import List, Optional, Union
|
from typing import List, Union
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
from compel import Compel, ReturnedEmbeddingsType
|
from compel import Compel, ReturnedEmbeddingsType
|
||||||
from compel.prompt_parser import Blend, Conjunction, CrossAttentionControlSubstitute, FlattenedPrompt, Fragment
|
from compel.prompt_parser import Blend, Conjunction, CrossAttentionControlSubstitute, FlattenedPrompt, Fragment
|
||||||
|
|
||||||
from invokeai.app.invocations.primitives import ConditioningField, ConditioningOutput
|
from invokeai.app.invocations.primitives import ConditioningField, ConditioningOutput
|
||||||
from invokeai.app.shared.fields import FieldDescriptions
|
|
||||||
from invokeai.backend.stable_diffusion.diffusion.conditioning_data import (
|
from invokeai.backend.stable_diffusion.diffusion.conditioning_data import (
|
||||||
BasicConditioningInfo,
|
BasicConditioningInfo,
|
||||||
ExtraConditioningInfo,
|
ExtraConditioningInfo,
|
||||||
@@ -20,6 +19,7 @@ from ...backend.util.devices import torch_dtype
|
|||||||
from .baseinvocation import (
|
from .baseinvocation import (
|
||||||
BaseInvocation,
|
BaseInvocation,
|
||||||
BaseInvocationOutput,
|
BaseInvocationOutput,
|
||||||
|
FieldDescriptions,
|
||||||
Input,
|
Input,
|
||||||
InputField,
|
InputField,
|
||||||
InvocationContext,
|
InvocationContext,
|
||||||
@@ -43,13 +43,7 @@ class ConditioningFieldData:
|
|||||||
# PerpNeg = "perp_neg"
|
# PerpNeg = "perp_neg"
|
||||||
|
|
||||||
|
|
||||||
@invocation(
|
@invocation("compel", title="Prompt", tags=["prompt", "compel"], category="conditioning", version="1.0.0")
|
||||||
"compel",
|
|
||||||
title="Prompt",
|
|
||||||
tags=["prompt", "compel"],
|
|
||||||
category="conditioning",
|
|
||||||
version="1.0.0",
|
|
||||||
)
|
|
||||||
class CompelInvocation(BaseInvocation):
|
class CompelInvocation(BaseInvocation):
|
||||||
"""Parse prompt using compel package to conditioning."""
|
"""Parse prompt using compel package to conditioning."""
|
||||||
|
|
||||||
@@ -67,19 +61,17 @@ class CompelInvocation(BaseInvocation):
|
|||||||
@torch.no_grad()
|
@torch.no_grad()
|
||||||
def invoke(self, context: InvocationContext) -> ConditioningOutput:
|
def invoke(self, context: InvocationContext) -> ConditioningOutput:
|
||||||
tokenizer_info = context.services.model_manager.get_model(
|
tokenizer_info = context.services.model_manager.get_model(
|
||||||
**self.clip.tokenizer.model_dump(),
|
**self.clip.tokenizer.dict(),
|
||||||
context=context,
|
context=context,
|
||||||
)
|
)
|
||||||
text_encoder_info = context.services.model_manager.get_model(
|
text_encoder_info = context.services.model_manager.get_model(
|
||||||
**self.clip.text_encoder.model_dump(),
|
**self.clip.text_encoder.dict(),
|
||||||
context=context,
|
context=context,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _lora_loader():
|
def _lora_loader():
|
||||||
for lora in self.clip.loras:
|
for lora in self.clip.loras:
|
||||||
lora_info = context.services.model_manager.get_model(
|
lora_info = context.services.model_manager.get_model(**lora.dict(exclude={"weight"}), context=context)
|
||||||
**lora.model_dump(exclude={"weight"}), context=context
|
|
||||||
)
|
|
||||||
yield (lora_info.context.model, lora.weight)
|
yield (lora_info.context.model, lora.weight)
|
||||||
del lora_info
|
del lora_info
|
||||||
return
|
return
|
||||||
@@ -108,15 +100,13 @@ class CompelInvocation(BaseInvocation):
|
|||||||
print(f'Warn: trigger: "{trigger}" not found')
|
print(f'Warn: trigger: "{trigger}" not found')
|
||||||
|
|
||||||
with (
|
with (
|
||||||
|
ModelPatcher.apply_lora_text_encoder(text_encoder_info.context.model, _lora_loader()),
|
||||||
ModelPatcher.apply_ti(tokenizer_info.context.model, text_encoder_info.context.model, ti_list) as (
|
ModelPatcher.apply_ti(tokenizer_info.context.model, text_encoder_info.context.model, ti_list) as (
|
||||||
tokenizer,
|
tokenizer,
|
||||||
ti_manager,
|
ti_manager,
|
||||||
),
|
),
|
||||||
text_encoder_info as text_encoder,
|
|
||||||
# Apply the LoRA after text_encoder has been moved to its target device for faster patching.
|
|
||||||
ModelPatcher.apply_lora_text_encoder(text_encoder, _lora_loader()),
|
|
||||||
# Apply CLIP Skip after LoRA to prevent LoRA application from failing on skipped layers.
|
|
||||||
ModelPatcher.apply_clip_skip(text_encoder_info.context.model, self.clip.skipped_layers),
|
ModelPatcher.apply_clip_skip(text_encoder_info.context.model, self.clip.skipped_layers),
|
||||||
|
text_encoder_info as text_encoder,
|
||||||
):
|
):
|
||||||
compel = Compel(
|
compel = Compel(
|
||||||
tokenizer=tokenizer,
|
tokenizer=tokenizer,
|
||||||
@@ -170,11 +160,11 @@ class SDXLPromptInvocationBase:
|
|||||||
zero_on_empty: bool,
|
zero_on_empty: bool,
|
||||||
):
|
):
|
||||||
tokenizer_info = context.services.model_manager.get_model(
|
tokenizer_info = context.services.model_manager.get_model(
|
||||||
**clip_field.tokenizer.model_dump(),
|
**clip_field.tokenizer.dict(),
|
||||||
context=context,
|
context=context,
|
||||||
)
|
)
|
||||||
text_encoder_info = context.services.model_manager.get_model(
|
text_encoder_info = context.services.model_manager.get_model(
|
||||||
**clip_field.text_encoder.model_dump(),
|
**clip_field.text_encoder.dict(),
|
||||||
context=context,
|
context=context,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -182,11 +172,7 @@ class SDXLPromptInvocationBase:
|
|||||||
if prompt == "" and zero_on_empty:
|
if prompt == "" and zero_on_empty:
|
||||||
cpu_text_encoder = text_encoder_info.context.model
|
cpu_text_encoder = text_encoder_info.context.model
|
||||||
c = torch.zeros(
|
c = torch.zeros(
|
||||||
(
|
(1, cpu_text_encoder.config.max_position_embeddings, cpu_text_encoder.config.hidden_size),
|
||||||
1,
|
|
||||||
cpu_text_encoder.config.max_position_embeddings,
|
|
||||||
cpu_text_encoder.config.hidden_size,
|
|
||||||
),
|
|
||||||
dtype=text_encoder_info.context.cache.precision,
|
dtype=text_encoder_info.context.cache.precision,
|
||||||
)
|
)
|
||||||
if get_pooled:
|
if get_pooled:
|
||||||
@@ -200,9 +186,7 @@ class SDXLPromptInvocationBase:
|
|||||||
|
|
||||||
def _lora_loader():
|
def _lora_loader():
|
||||||
for lora in clip_field.loras:
|
for lora in clip_field.loras:
|
||||||
lora_info = context.services.model_manager.get_model(
|
lora_info = context.services.model_manager.get_model(**lora.dict(exclude={"weight"}), context=context)
|
||||||
**lora.model_dump(exclude={"weight"}), context=context
|
|
||||||
)
|
|
||||||
yield (lora_info.context.model, lora.weight)
|
yield (lora_info.context.model, lora.weight)
|
||||||
del lora_info
|
del lora_info
|
||||||
return
|
return
|
||||||
@@ -231,15 +215,13 @@ class SDXLPromptInvocationBase:
|
|||||||
print(f'Warn: trigger: "{trigger}" not found')
|
print(f'Warn: trigger: "{trigger}" not found')
|
||||||
|
|
||||||
with (
|
with (
|
||||||
|
ModelPatcher.apply_lora(text_encoder_info.context.model, _lora_loader(), lora_prefix),
|
||||||
ModelPatcher.apply_ti(tokenizer_info.context.model, text_encoder_info.context.model, ti_list) as (
|
ModelPatcher.apply_ti(tokenizer_info.context.model, text_encoder_info.context.model, ti_list) as (
|
||||||
tokenizer,
|
tokenizer,
|
||||||
ti_manager,
|
ti_manager,
|
||||||
),
|
),
|
||||||
text_encoder_info as text_encoder,
|
|
||||||
# Apply the LoRA after text_encoder has been moved to its target device for faster patching.
|
|
||||||
ModelPatcher.apply_lora(text_encoder, _lora_loader(), lora_prefix),
|
|
||||||
# Apply CLIP Skip after LoRA to prevent LoRA application from failing on skipped layers.
|
|
||||||
ModelPatcher.apply_clip_skip(text_encoder_info.context.model, clip_field.skipped_layers),
|
ModelPatcher.apply_clip_skip(text_encoder_info.context.model, clip_field.skipped_layers),
|
||||||
|
text_encoder_info as text_encoder,
|
||||||
):
|
):
|
||||||
compel = Compel(
|
compel = Compel(
|
||||||
tokenizer=tokenizer,
|
tokenizer=tokenizer,
|
||||||
@@ -291,16 +273,8 @@ class SDXLPromptInvocationBase:
|
|||||||
class SDXLCompelPromptInvocation(BaseInvocation, SDXLPromptInvocationBase):
|
class SDXLCompelPromptInvocation(BaseInvocation, SDXLPromptInvocationBase):
|
||||||
"""Parse prompt using compel package to conditioning."""
|
"""Parse prompt using compel package to conditioning."""
|
||||||
|
|
||||||
prompt: str = InputField(
|
prompt: str = InputField(default="", description=FieldDescriptions.compel_prompt, ui_component=UIComponent.Textarea)
|
||||||
default="",
|
style: str = InputField(default="", description=FieldDescriptions.compel_prompt, ui_component=UIComponent.Textarea)
|
||||||
description=FieldDescriptions.compel_prompt,
|
|
||||||
ui_component=UIComponent.Textarea,
|
|
||||||
)
|
|
||||||
style: str = InputField(
|
|
||||||
default="",
|
|
||||||
description=FieldDescriptions.compel_prompt,
|
|
||||||
ui_component=UIComponent.Textarea,
|
|
||||||
)
|
|
||||||
original_width: int = InputField(default=1024, description="")
|
original_width: int = InputField(default=1024, description="")
|
||||||
original_height: int = InputField(default=1024, description="")
|
original_height: int = InputField(default=1024, description="")
|
||||||
crop_top: int = InputField(default=0, description="")
|
crop_top: int = InputField(default=0, description="")
|
||||||
@@ -336,9 +310,7 @@ class SDXLCompelPromptInvocation(BaseInvocation, SDXLPromptInvocationBase):
|
|||||||
[
|
[
|
||||||
c1,
|
c1,
|
||||||
torch.zeros(
|
torch.zeros(
|
||||||
(c1.shape[0], c2.shape[1] - c1.shape[1], c1.shape[2]),
|
(c1.shape[0], c2.shape[1] - c1.shape[1], c1.shape[2]), device=c1.device, dtype=c1.dtype
|
||||||
device=c1.device,
|
|
||||||
dtype=c1.dtype,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
dim=1,
|
dim=1,
|
||||||
@@ -349,9 +321,7 @@ class SDXLCompelPromptInvocation(BaseInvocation, SDXLPromptInvocationBase):
|
|||||||
[
|
[
|
||||||
c2,
|
c2,
|
||||||
torch.zeros(
|
torch.zeros(
|
||||||
(c2.shape[0], c1.shape[1] - c2.shape[1], c2.shape[2]),
|
(c2.shape[0], c1.shape[1] - c2.shape[1], c2.shape[2]), device=c2.device, dtype=c2.dtype
|
||||||
device=c2.device,
|
|
||||||
dtype=c2.dtype,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
dim=1,
|
dim=1,
|
||||||
@@ -389,9 +359,7 @@ class SDXLRefinerCompelPromptInvocation(BaseInvocation, SDXLPromptInvocationBase
|
|||||||
"""Parse prompt using compel package to conditioning."""
|
"""Parse prompt using compel package to conditioning."""
|
||||||
|
|
||||||
style: str = InputField(
|
style: str = InputField(
|
||||||
default="",
|
default="", description=FieldDescriptions.compel_prompt, ui_component=UIComponent.Textarea
|
||||||
description=FieldDescriptions.compel_prompt,
|
|
||||||
ui_component=UIComponent.Textarea,
|
|
||||||
) # TODO: ?
|
) # TODO: ?
|
||||||
original_width: int = InputField(default=1024, description="")
|
original_width: int = InputField(default=1024, description="")
|
||||||
original_height: int = InputField(default=1024, description="")
|
original_height: int = InputField(default=1024, description="")
|
||||||
@@ -435,16 +403,10 @@ class SDXLRefinerCompelPromptInvocation(BaseInvocation, SDXLPromptInvocationBase
|
|||||||
class ClipSkipInvocationOutput(BaseInvocationOutput):
|
class ClipSkipInvocationOutput(BaseInvocationOutput):
|
||||||
"""Clip skip node output"""
|
"""Clip skip node output"""
|
||||||
|
|
||||||
clip: Optional[ClipField] = OutputField(default=None, description=FieldDescriptions.clip, title="CLIP")
|
clip: ClipField = OutputField(default=None, description=FieldDescriptions.clip, title="CLIP")
|
||||||
|
|
||||||
|
|
||||||
@invocation(
|
@invocation("clip_skip", title="CLIP Skip", tags=["clipskip", "clip", "skip"], category="conditioning", version="1.0.0")
|
||||||
"clip_skip",
|
|
||||||
title="CLIP Skip",
|
|
||||||
tags=["clipskip", "clip", "skip"],
|
|
||||||
category="conditioning",
|
|
||||||
version="1.0.0",
|
|
||||||
)
|
|
||||||
class ClipSkipInvocation(BaseInvocation):
|
class ClipSkipInvocation(BaseInvocation):
|
||||||
"""Skip layers in clip text_encoder model."""
|
"""Skip layers in clip text_encoder model."""
|
||||||
|
|
||||||
@@ -459,9 +421,7 @@ class ClipSkipInvocation(BaseInvocation):
|
|||||||
|
|
||||||
|
|
||||||
def get_max_token_count(
|
def get_max_token_count(
|
||||||
tokenizer,
|
tokenizer, prompt: Union[FlattenedPrompt, Blend, Conjunction], truncate_if_too_long=False
|
||||||
prompt: Union[FlattenedPrompt, Blend, Conjunction],
|
|
||||||
truncate_if_too_long=False,
|
|
||||||
) -> int:
|
) -> int:
|
||||||
if type(prompt) is Blend:
|
if type(prompt) is Blend:
|
||||||
blend: Blend = prompt
|
blend: Blend = prompt
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# initial implementation by Gregg Helt, 2023
|
# initial implementation by Gregg Helt, 2023
|
||||||
# heavily leverages controlnet_aux package: https://github.com/patrickvonplaten/controlnet_aux
|
# heavily leverages controlnet_aux package: https://github.com/patrickvonplaten/controlnet_aux
|
||||||
from builtins import bool, float
|
from builtins import bool, float
|
||||||
from typing import Dict, List, Literal, Union
|
from typing import Dict, List, Literal, Optional, Union
|
||||||
|
|
||||||
import cv2
|
import cv2
|
||||||
import numpy as np
|
import numpy as np
|
||||||
@@ -24,22 +24,20 @@ from controlnet_aux import (
|
|||||||
)
|
)
|
||||||
from controlnet_aux.util import HWC3, ade_palette
|
from controlnet_aux.util import HWC3, ade_palette
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
from pydantic import BaseModel, Field, validator
|
||||||
|
|
||||||
from invokeai.app.invocations.primitives import ImageField, ImageOutput
|
from invokeai.app.invocations.primitives import ImageField, ImageOutput
|
||||||
from invokeai.app.services.image_records.image_records_common import ImageCategory, ResourceOrigin
|
|
||||||
from invokeai.app.shared.fields import FieldDescriptions
|
|
||||||
|
|
||||||
from ...backend.model_management import BaseModelType
|
from ...backend.model_management import BaseModelType
|
||||||
|
from ..models.image import ImageCategory, ResourceOrigin
|
||||||
from .baseinvocation import (
|
from .baseinvocation import (
|
||||||
BaseInvocation,
|
BaseInvocation,
|
||||||
BaseInvocationOutput,
|
BaseInvocationOutput,
|
||||||
|
FieldDescriptions,
|
||||||
Input,
|
Input,
|
||||||
InputField,
|
InputField,
|
||||||
InvocationContext,
|
InvocationContext,
|
||||||
OutputField,
|
OutputField,
|
||||||
WithMetadata,
|
|
||||||
WithWorkflow,
|
|
||||||
invocation,
|
invocation,
|
||||||
invocation_output,
|
invocation_output,
|
||||||
)
|
)
|
||||||
@@ -59,8 +57,6 @@ class ControlNetModelField(BaseModel):
|
|||||||
model_name: str = Field(description="Name of the ControlNet model")
|
model_name: str = Field(description="Name of the ControlNet model")
|
||||||
base_model: BaseModelType = Field(description="Base model")
|
base_model: BaseModelType = Field(description="Base model")
|
||||||
|
|
||||||
model_config = ConfigDict(protected_namespaces=())
|
|
||||||
|
|
||||||
|
|
||||||
class ControlField(BaseModel):
|
class ControlField(BaseModel):
|
||||||
image: ImageField = Field(description="The control image")
|
image: ImageField = Field(description="The control image")
|
||||||
@@ -75,7 +71,7 @@ class ControlField(BaseModel):
|
|||||||
control_mode: CONTROLNET_MODE_VALUES = Field(default="balanced", description="The control mode to use")
|
control_mode: CONTROLNET_MODE_VALUES = Field(default="balanced", description="The control mode to use")
|
||||||
resize_mode: CONTROLNET_RESIZE_VALUES = Field(default="just_resize", description="The resize mode to use")
|
resize_mode: CONTROLNET_RESIZE_VALUES = Field(default="just_resize", description="The resize mode to use")
|
||||||
|
|
||||||
@field_validator("control_weight")
|
@validator("control_weight")
|
||||||
def validate_control_weight(cls, v):
|
def validate_control_weight(cls, v):
|
||||||
"""Validate that all control weights in the valid range"""
|
"""Validate that all control weights in the valid range"""
|
||||||
if isinstance(v, list):
|
if isinstance(v, list):
|
||||||
@@ -96,7 +92,7 @@ class ControlOutput(BaseInvocationOutput):
|
|||||||
control: ControlField = OutputField(description=FieldDescriptions.control)
|
control: ControlField = OutputField(description=FieldDescriptions.control)
|
||||||
|
|
||||||
|
|
||||||
@invocation("controlnet", title="ControlNet", tags=["controlnet"], category="controlnet", version="1.1.0")
|
@invocation("controlnet", title="ControlNet", tags=["controlnet"], category="controlnet", version="1.0.0")
|
||||||
class ControlNetInvocation(BaseInvocation):
|
class ControlNetInvocation(BaseInvocation):
|
||||||
"""Collects ControlNet info to pass to other nodes"""
|
"""Collects ControlNet info to pass to other nodes"""
|
||||||
|
|
||||||
@@ -128,13 +124,15 @@ class ControlNetInvocation(BaseInvocation):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# This invocation exists for other invocations to subclass it - do not register with @invocation!
|
@invocation(
|
||||||
class ImageProcessorInvocation(BaseInvocation, WithMetadata, WithWorkflow):
|
"image_processor", title="Base Image Processor", tags=["controlnet"], category="controlnet", version="1.0.0"
|
||||||
|
)
|
||||||
|
class ImageProcessorInvocation(BaseInvocation):
|
||||||
"""Base class for invocations that preprocess images for ControlNet"""
|
"""Base class for invocations that preprocess images for ControlNet"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to process")
|
image: ImageField = InputField(description="The image to process")
|
||||||
|
|
||||||
def run_processor(self, image: Image.Image) -> Image.Image:
|
def run_processor(self, image):
|
||||||
# superclass just passes through image without processing
|
# superclass just passes through image without processing
|
||||||
return image
|
return image
|
||||||
|
|
||||||
@@ -152,7 +150,6 @@ class ImageProcessorInvocation(BaseInvocation, WithMetadata, WithWorkflow):
|
|||||||
session_id=context.graph_execution_state_id,
|
session_id=context.graph_execution_state_id,
|
||||||
node_id=self.id,
|
node_id=self.id,
|
||||||
is_intermediate=self.is_intermediate,
|
is_intermediate=self.is_intermediate,
|
||||||
metadata=self.metadata,
|
|
||||||
workflow=self.workflow,
|
workflow=self.workflow,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -173,7 +170,7 @@ class ImageProcessorInvocation(BaseInvocation, WithMetadata, WithWorkflow):
|
|||||||
title="Canny Processor",
|
title="Canny Processor",
|
||||||
tags=["controlnet", "canny"],
|
tags=["controlnet", "canny"],
|
||||||
category="controlnet",
|
category="controlnet",
|
||||||
version="1.1.0",
|
version="1.0.0",
|
||||||
)
|
)
|
||||||
class CannyImageProcessorInvocation(ImageProcessorInvocation):
|
class CannyImageProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Canny edge detection for ControlNet"""
|
"""Canny edge detection for ControlNet"""
|
||||||
@@ -196,7 +193,7 @@ class CannyImageProcessorInvocation(ImageProcessorInvocation):
|
|||||||
title="HED (softedge) Processor",
|
title="HED (softedge) Processor",
|
||||||
tags=["controlnet", "hed", "softedge"],
|
tags=["controlnet", "hed", "softedge"],
|
||||||
category="controlnet",
|
category="controlnet",
|
||||||
version="1.1.0",
|
version="1.0.0",
|
||||||
)
|
)
|
||||||
class HedImageProcessorInvocation(ImageProcessorInvocation):
|
class HedImageProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Applies HED edge detection to image"""
|
"""Applies HED edge detection to image"""
|
||||||
@@ -225,7 +222,7 @@ class HedImageProcessorInvocation(ImageProcessorInvocation):
|
|||||||
title="Lineart Processor",
|
title="Lineart Processor",
|
||||||
tags=["controlnet", "lineart"],
|
tags=["controlnet", "lineart"],
|
||||||
category="controlnet",
|
category="controlnet",
|
||||||
version="1.1.0",
|
version="1.0.0",
|
||||||
)
|
)
|
||||||
class LineartImageProcessorInvocation(ImageProcessorInvocation):
|
class LineartImageProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Applies line art processing to image"""
|
"""Applies line art processing to image"""
|
||||||
@@ -247,7 +244,7 @@ class LineartImageProcessorInvocation(ImageProcessorInvocation):
|
|||||||
title="Lineart Anime Processor",
|
title="Lineart Anime Processor",
|
||||||
tags=["controlnet", "lineart", "anime"],
|
tags=["controlnet", "lineart", "anime"],
|
||||||
category="controlnet",
|
category="controlnet",
|
||||||
version="1.1.0",
|
version="1.0.0",
|
||||||
)
|
)
|
||||||
class LineartAnimeImageProcessorInvocation(ImageProcessorInvocation):
|
class LineartAnimeImageProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Applies line art anime processing to image"""
|
"""Applies line art anime processing to image"""
|
||||||
@@ -270,7 +267,7 @@ class LineartAnimeImageProcessorInvocation(ImageProcessorInvocation):
|
|||||||
title="Openpose Processor",
|
title="Openpose Processor",
|
||||||
tags=["controlnet", "openpose", "pose"],
|
tags=["controlnet", "openpose", "pose"],
|
||||||
category="controlnet",
|
category="controlnet",
|
||||||
version="1.1.0",
|
version="1.0.0",
|
||||||
)
|
)
|
||||||
class OpenposeImageProcessorInvocation(ImageProcessorInvocation):
|
class OpenposeImageProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Applies Openpose processing to image"""
|
"""Applies Openpose processing to image"""
|
||||||
@@ -295,7 +292,7 @@ class OpenposeImageProcessorInvocation(ImageProcessorInvocation):
|
|||||||
title="Midas Depth Processor",
|
title="Midas Depth Processor",
|
||||||
tags=["controlnet", "midas"],
|
tags=["controlnet", "midas"],
|
||||||
category="controlnet",
|
category="controlnet",
|
||||||
version="1.1.0",
|
version="1.0.0",
|
||||||
)
|
)
|
||||||
class MidasDepthImageProcessorInvocation(ImageProcessorInvocation):
|
class MidasDepthImageProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Applies Midas depth processing to image"""
|
"""Applies Midas depth processing to image"""
|
||||||
@@ -322,7 +319,7 @@ class MidasDepthImageProcessorInvocation(ImageProcessorInvocation):
|
|||||||
title="Normal BAE Processor",
|
title="Normal BAE Processor",
|
||||||
tags=["controlnet"],
|
tags=["controlnet"],
|
||||||
category="controlnet",
|
category="controlnet",
|
||||||
version="1.1.0",
|
version="1.0.0",
|
||||||
)
|
)
|
||||||
class NormalbaeImageProcessorInvocation(ImageProcessorInvocation):
|
class NormalbaeImageProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Applies NormalBae processing to image"""
|
"""Applies NormalBae processing to image"""
|
||||||
@@ -339,7 +336,7 @@ class NormalbaeImageProcessorInvocation(ImageProcessorInvocation):
|
|||||||
|
|
||||||
|
|
||||||
@invocation(
|
@invocation(
|
||||||
"mlsd_image_processor", title="MLSD Processor", tags=["controlnet", "mlsd"], category="controlnet", version="1.1.0"
|
"mlsd_image_processor", title="MLSD Processor", tags=["controlnet", "mlsd"], category="controlnet", version="1.0.0"
|
||||||
)
|
)
|
||||||
class MlsdImageProcessorInvocation(ImageProcessorInvocation):
|
class MlsdImageProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Applies MLSD processing to image"""
|
"""Applies MLSD processing to image"""
|
||||||
@@ -362,7 +359,7 @@ class MlsdImageProcessorInvocation(ImageProcessorInvocation):
|
|||||||
|
|
||||||
|
|
||||||
@invocation(
|
@invocation(
|
||||||
"pidi_image_processor", title="PIDI Processor", tags=["controlnet", "pidi"], category="controlnet", version="1.1.0"
|
"pidi_image_processor", title="PIDI Processor", tags=["controlnet", "pidi"], category="controlnet", version="1.0.0"
|
||||||
)
|
)
|
||||||
class PidiImageProcessorInvocation(ImageProcessorInvocation):
|
class PidiImageProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Applies PIDI processing to image"""
|
"""Applies PIDI processing to image"""
|
||||||
@@ -389,16 +386,16 @@ class PidiImageProcessorInvocation(ImageProcessorInvocation):
|
|||||||
title="Content Shuffle Processor",
|
title="Content Shuffle Processor",
|
||||||
tags=["controlnet", "contentshuffle"],
|
tags=["controlnet", "contentshuffle"],
|
||||||
category="controlnet",
|
category="controlnet",
|
||||||
version="1.1.0",
|
version="1.0.0",
|
||||||
)
|
)
|
||||||
class ContentShuffleImageProcessorInvocation(ImageProcessorInvocation):
|
class ContentShuffleImageProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Applies content shuffle processing to image"""
|
"""Applies content shuffle processing to image"""
|
||||||
|
|
||||||
detect_resolution: int = InputField(default=512, ge=0, description=FieldDescriptions.detect_res)
|
detect_resolution: int = InputField(default=512, ge=0, description=FieldDescriptions.detect_res)
|
||||||
image_resolution: int = InputField(default=512, ge=0, description=FieldDescriptions.image_res)
|
image_resolution: int = InputField(default=512, ge=0, description=FieldDescriptions.image_res)
|
||||||
h: int = InputField(default=512, ge=0, description="Content shuffle `h` parameter")
|
h: Optional[int] = InputField(default=512, ge=0, description="Content shuffle `h` parameter")
|
||||||
w: int = InputField(default=512, ge=0, description="Content shuffle `w` parameter")
|
w: Optional[int] = InputField(default=512, ge=0, description="Content shuffle `w` parameter")
|
||||||
f: int = InputField(default=256, ge=0, description="Content shuffle `f` parameter")
|
f: Optional[int] = InputField(default=256, ge=0, description="Content shuffle `f` parameter")
|
||||||
|
|
||||||
def run_processor(self, image):
|
def run_processor(self, image):
|
||||||
content_shuffle_processor = ContentShuffleDetector()
|
content_shuffle_processor = ContentShuffleDetector()
|
||||||
@@ -419,7 +416,7 @@ class ContentShuffleImageProcessorInvocation(ImageProcessorInvocation):
|
|||||||
title="Zoe (Depth) Processor",
|
title="Zoe (Depth) Processor",
|
||||||
tags=["controlnet", "zoe", "depth"],
|
tags=["controlnet", "zoe", "depth"],
|
||||||
category="controlnet",
|
category="controlnet",
|
||||||
version="1.1.0",
|
version="1.0.0",
|
||||||
)
|
)
|
||||||
class ZoeDepthImageProcessorInvocation(ImageProcessorInvocation):
|
class ZoeDepthImageProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Applies Zoe depth processing to image"""
|
"""Applies Zoe depth processing to image"""
|
||||||
@@ -435,7 +432,7 @@ class ZoeDepthImageProcessorInvocation(ImageProcessorInvocation):
|
|||||||
title="Mediapipe Face Processor",
|
title="Mediapipe Face Processor",
|
||||||
tags=["controlnet", "mediapipe", "face"],
|
tags=["controlnet", "mediapipe", "face"],
|
||||||
category="controlnet",
|
category="controlnet",
|
||||||
version="1.1.0",
|
version="1.0.0",
|
||||||
)
|
)
|
||||||
class MediapipeFaceProcessorInvocation(ImageProcessorInvocation):
|
class MediapipeFaceProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Applies mediapipe face processing to image"""
|
"""Applies mediapipe face processing to image"""
|
||||||
@@ -458,7 +455,7 @@ class MediapipeFaceProcessorInvocation(ImageProcessorInvocation):
|
|||||||
title="Leres (Depth) Processor",
|
title="Leres (Depth) Processor",
|
||||||
tags=["controlnet", "leres", "depth"],
|
tags=["controlnet", "leres", "depth"],
|
||||||
category="controlnet",
|
category="controlnet",
|
||||||
version="1.1.0",
|
version="1.0.0",
|
||||||
)
|
)
|
||||||
class LeresImageProcessorInvocation(ImageProcessorInvocation):
|
class LeresImageProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Applies leres processing to image"""
|
"""Applies leres processing to image"""
|
||||||
@@ -487,7 +484,7 @@ class LeresImageProcessorInvocation(ImageProcessorInvocation):
|
|||||||
title="Tile Resample Processor",
|
title="Tile Resample Processor",
|
||||||
tags=["controlnet", "tile"],
|
tags=["controlnet", "tile"],
|
||||||
category="controlnet",
|
category="controlnet",
|
||||||
version="1.1.0",
|
version="1.0.0",
|
||||||
)
|
)
|
||||||
class TileResamplerProcessorInvocation(ImageProcessorInvocation):
|
class TileResamplerProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Tile resampler processor"""
|
"""Tile resampler processor"""
|
||||||
@@ -527,7 +524,7 @@ class TileResamplerProcessorInvocation(ImageProcessorInvocation):
|
|||||||
title="Segment Anything Processor",
|
title="Segment Anything Processor",
|
||||||
tags=["controlnet", "segmentanything"],
|
tags=["controlnet", "segmentanything"],
|
||||||
category="controlnet",
|
category="controlnet",
|
||||||
version="1.1.0",
|
version="1.0.0",
|
||||||
)
|
)
|
||||||
class SegmentAnythingProcessorInvocation(ImageProcessorInvocation):
|
class SegmentAnythingProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Applies segment anything processing to image"""
|
"""Applies segment anything processing to image"""
|
||||||
@@ -569,7 +566,7 @@ class SamDetectorReproducibleColors(SamDetector):
|
|||||||
title="Color Map Processor",
|
title="Color Map Processor",
|
||||||
tags=["controlnet"],
|
tags=["controlnet"],
|
||||||
category="controlnet",
|
category="controlnet",
|
||||||
version="1.1.0",
|
version="1.0.0",
|
||||||
)
|
)
|
||||||
class ColorMapImageProcessorInvocation(ImageProcessorInvocation):
|
class ColorMapImageProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Generates a color map from the provided image"""
|
"""Generates a color map from the provided image"""
|
||||||
@@ -578,14 +575,14 @@ class ColorMapImageProcessorInvocation(ImageProcessorInvocation):
|
|||||||
|
|
||||||
def run_processor(self, image: Image.Image):
|
def run_processor(self, image: Image.Image):
|
||||||
image = image.convert("RGB")
|
image = image.convert("RGB")
|
||||||
np_image = np.array(image, dtype=np.uint8)
|
image = np.array(image, dtype=np.uint8)
|
||||||
height, width = np_image.shape[:2]
|
height, width = image.shape[:2]
|
||||||
|
|
||||||
width_tile_size = min(self.color_map_tile_size, width)
|
width_tile_size = min(self.color_map_tile_size, width)
|
||||||
height_tile_size = min(self.color_map_tile_size, height)
|
height_tile_size = min(self.color_map_tile_size, height)
|
||||||
|
|
||||||
color_map = cv2.resize(
|
color_map = cv2.resize(
|
||||||
np_image,
|
image,
|
||||||
(width // width_tile_size, height // height_tile_size),
|
(width // width_tile_size, height // height_tile_size),
|
||||||
interpolation=cv2.INTER_CUBIC,
|
interpolation=cv2.INTER_CUBIC,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
# Custom Nodes / Node Packs
|
|
||||||
|
|
||||||
Copy your node packs to this directory.
|
|
||||||
|
|
||||||
When nodes are added or changed, you must restart the app to see the changes.
|
|
||||||
|
|
||||||
## Directory Structure
|
|
||||||
|
|
||||||
For a node pack to be loaded, it must be placed in a directory alongside this
|
|
||||||
file. Here's an example structure:
|
|
||||||
|
|
||||||
```py
|
|
||||||
.
|
|
||||||
├── __init__.py # Invoke-managed custom node loader
|
|
||||||
│
|
|
||||||
├── cool_node
|
|
||||||
│ ├── __init__.py # see example below
|
|
||||||
│ └── cool_node.py
|
|
||||||
│
|
|
||||||
└── my_node_pack
|
|
||||||
├── __init__.py # see example below
|
|
||||||
├── tasty_node.py
|
|
||||||
├── bodacious_node.py
|
|
||||||
├── utils.py
|
|
||||||
└── extra_nodes
|
|
||||||
└── fancy_node.py
|
|
||||||
```
|
|
||||||
|
|
||||||
## Node Pack `__init__.py`
|
|
||||||
|
|
||||||
Each node pack must have an `__init__.py` file that imports its nodes.
|
|
||||||
|
|
||||||
The structure of each node or node pack is otherwise not important.
|
|
||||||
|
|
||||||
Here are examples, based on the example directory structure.
|
|
||||||
|
|
||||||
### `cool_node/__init__.py`
|
|
||||||
|
|
||||||
```py
|
|
||||||
from .cool_node import CoolInvocation
|
|
||||||
```
|
|
||||||
|
|
||||||
### `my_node_pack/__init__.py`
|
|
||||||
|
|
||||||
```py
|
|
||||||
from .tasty_node import TastyInvocation
|
|
||||||
from .bodacious_node import BodaciousInvocation
|
|
||||||
from .extra_nodes.fancy_node import FancyInvocation
|
|
||||||
```
|
|
||||||
|
|
||||||
Only nodes imported in the `__init__.py` file are loaded.
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
"""
|
|
||||||
Invoke-managed custom node loader. See README.md for more information.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
from importlib.util import module_from_spec, spec_from_file_location
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from invokeai.app.invocations.baseinvocation import CUSTOM_NODE_PACK_SUFFIX
|
|
||||||
from invokeai.backend.util.logging import InvokeAILogger
|
|
||||||
|
|
||||||
logger = InvokeAILogger.get_logger()
|
|
||||||
loaded_count = 0
|
|
||||||
|
|
||||||
|
|
||||||
for d in Path(__file__).parent.iterdir():
|
|
||||||
# skip files
|
|
||||||
if not d.is_dir():
|
|
||||||
continue
|
|
||||||
|
|
||||||
# skip hidden directories
|
|
||||||
if d.name.startswith("_") or d.name.startswith("."):
|
|
||||||
continue
|
|
||||||
|
|
||||||
# skip directories without an `__init__.py`
|
|
||||||
init = d / "__init__.py"
|
|
||||||
if not init.exists():
|
|
||||||
continue
|
|
||||||
|
|
||||||
module_name = init.parent.stem
|
|
||||||
|
|
||||||
# skip if already imported
|
|
||||||
if module_name in globals():
|
|
||||||
continue
|
|
||||||
|
|
||||||
# load the module, appending adding a suffix to identify it as a custom node pack
|
|
||||||
spec = spec_from_file_location(f"{module_name}{CUSTOM_NODE_PACK_SUFFIX}", init.absolute())
|
|
||||||
|
|
||||||
if spec is None or spec.loader is None:
|
|
||||||
logger.warn(f"Could not load {init}")
|
|
||||||
continue
|
|
||||||
|
|
||||||
logger.info(f"Loading node pack {module_name}")
|
|
||||||
|
|
||||||
module = module_from_spec(spec)
|
|
||||||
sys.modules[spec.name] = module
|
|
||||||
spec.loader.exec_module(module)
|
|
||||||
|
|
||||||
loaded_count += 1
|
|
||||||
|
|
||||||
del init, module_name
|
|
||||||
|
|
||||||
if loaded_count > 0:
|
|
||||||
logger.info(f"Loaded {loaded_count} node packs from {Path(__file__).parent}")
|
|
||||||
@@ -6,13 +6,13 @@ import numpy
|
|||||||
from PIL import Image, ImageOps
|
from PIL import Image, ImageOps
|
||||||
|
|
||||||
from invokeai.app.invocations.primitives import ImageField, ImageOutput
|
from invokeai.app.invocations.primitives import ImageField, ImageOutput
|
||||||
from invokeai.app.services.image_records.image_records_common import ImageCategory, ResourceOrigin
|
from invokeai.app.models.image import ImageCategory, ResourceOrigin
|
||||||
|
|
||||||
from .baseinvocation import BaseInvocation, InputField, InvocationContext, WithMetadata, WithWorkflow, invocation
|
from .baseinvocation import BaseInvocation, InputField, InvocationContext, invocation
|
||||||
|
|
||||||
|
|
||||||
@invocation("cv_inpaint", title="OpenCV Inpaint", tags=["opencv", "inpaint"], category="inpaint", version="1.1.0")
|
@invocation("cv_inpaint", title="OpenCV Inpaint", tags=["opencv", "inpaint"], category="inpaint", version="1.0.0")
|
||||||
class CvInpaintInvocation(BaseInvocation, WithMetadata, WithWorkflow):
|
class CvInpaintInvocation(BaseInvocation):
|
||||||
"""Simple inpaint using opencv."""
|
"""Simple inpaint using opencv."""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to inpaint")
|
image: ImageField = InputField(description="The image to inpaint")
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import numpy as np
|
|||||||
from mediapipe.python.solutions.face_mesh import FaceMesh # type: ignore[import]
|
from mediapipe.python.solutions.face_mesh import FaceMesh # type: ignore[import]
|
||||||
from PIL import Image, ImageDraw, ImageFilter, ImageFont, ImageOps
|
from PIL import Image, ImageDraw, ImageFilter, ImageFont, ImageOps
|
||||||
from PIL.Image import Image as ImageType
|
from PIL.Image import Image as ImageType
|
||||||
from pydantic import field_validator
|
from pydantic import validator
|
||||||
|
|
||||||
import invokeai.assets.fonts as font_assets
|
import invokeai.assets.fonts as font_assets
|
||||||
from invokeai.app.invocations.baseinvocation import (
|
from invokeai.app.invocations.baseinvocation import (
|
||||||
@@ -16,13 +16,11 @@ from invokeai.app.invocations.baseinvocation import (
|
|||||||
InputField,
|
InputField,
|
||||||
InvocationContext,
|
InvocationContext,
|
||||||
OutputField,
|
OutputField,
|
||||||
WithMetadata,
|
|
||||||
WithWorkflow,
|
|
||||||
invocation,
|
invocation,
|
||||||
invocation_output,
|
invocation_output,
|
||||||
)
|
)
|
||||||
from invokeai.app.invocations.primitives import ImageField, ImageOutput
|
from invokeai.app.invocations.primitives import ImageField, ImageOutput
|
||||||
from invokeai.app.services.image_records.image_records_common import ImageCategory, ResourceOrigin
|
from invokeai.app.models.image import ImageCategory, ResourceOrigin
|
||||||
|
|
||||||
|
|
||||||
@invocation_output("face_mask_output")
|
@invocation_output("face_mask_output")
|
||||||
@@ -131,7 +129,7 @@ def prepare_faces_list(
|
|||||||
deduped_faces: list[FaceResultData] = []
|
deduped_faces: list[FaceResultData] = []
|
||||||
|
|
||||||
if len(face_result_list) == 0:
|
if len(face_result_list) == 0:
|
||||||
return []
|
return list()
|
||||||
|
|
||||||
for candidate in face_result_list:
|
for candidate in face_result_list:
|
||||||
should_add = True
|
should_add = True
|
||||||
@@ -210,7 +208,7 @@ def generate_face_box_mask(
|
|||||||
# Check if any face is detected.
|
# Check if any face is detected.
|
||||||
if results.multi_face_landmarks: # type: ignore # this are via protobuf and not typed
|
if results.multi_face_landmarks: # type: ignore # this are via protobuf and not typed
|
||||||
# Search for the face_id in the detected faces.
|
# Search for the face_id in the detected faces.
|
||||||
for _face_id, face_landmarks in enumerate(results.multi_face_landmarks): # type: ignore #this are via protobuf and not typed
|
for face_id, face_landmarks in enumerate(results.multi_face_landmarks): # type: ignore #this are via protobuf and not typed
|
||||||
# Get the bounding box of the face mesh.
|
# Get the bounding box of the face mesh.
|
||||||
x_coordinates = [landmark.x for landmark in face_landmarks.landmark]
|
x_coordinates = [landmark.x for landmark in face_landmarks.landmark]
|
||||||
y_coordinates = [landmark.y for landmark in face_landmarks.landmark]
|
y_coordinates = [landmark.y for landmark in face_landmarks.landmark]
|
||||||
@@ -438,8 +436,8 @@ def get_faces_list(
|
|||||||
return all_faces
|
return all_faces
|
||||||
|
|
||||||
|
|
||||||
@invocation("face_off", title="FaceOff", tags=["image", "faceoff", "face", "mask"], category="image", version="1.1.0")
|
@invocation("face_off", title="FaceOff", tags=["image", "faceoff", "face", "mask"], category="image", version="1.0.2")
|
||||||
class FaceOffInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
class FaceOffInvocation(BaseInvocation):
|
||||||
"""Bound, extract, and mask a face from an image using MediaPipe detection"""
|
"""Bound, extract, and mask a face from an image using MediaPipe detection"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="Image for face detection")
|
image: ImageField = InputField(description="Image for face detection")
|
||||||
@@ -532,8 +530,8 @@ class FaceOffInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
@invocation("face_mask_detection", title="FaceMask", tags=["image", "face", "mask"], category="image", version="1.1.0")
|
@invocation("face_mask_detection", title="FaceMask", tags=["image", "face", "mask"], category="image", version="1.0.2")
|
||||||
class FaceMaskInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
class FaceMaskInvocation(BaseInvocation):
|
||||||
"""Face mask creation using mediapipe face detection"""
|
"""Face mask creation using mediapipe face detection"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="Image to face detect")
|
image: ImageField = InputField(description="Image to face detect")
|
||||||
@@ -552,7 +550,7 @@ class FaceMaskInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
)
|
)
|
||||||
invert_mask: bool = InputField(default=False, description="Toggle to invert the mask")
|
invert_mask: bool = InputField(default=False, description="Toggle to invert the mask")
|
||||||
|
|
||||||
@field_validator("face_ids")
|
@validator("face_ids")
|
||||||
def validate_comma_separated_ints(cls, v) -> str:
|
def validate_comma_separated_ints(cls, v) -> str:
|
||||||
comma_separated_ints_regex = re.compile(r"^\d*(,\d+)*$")
|
comma_separated_ints_regex = re.compile(r"^\d*(,\d+)*$")
|
||||||
if comma_separated_ints_regex.match(v) is None:
|
if comma_separated_ints_regex.match(v) is None:
|
||||||
@@ -650,9 +648,9 @@ class FaceMaskInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
|
|
||||||
|
|
||||||
@invocation(
|
@invocation(
|
||||||
"face_identifier", title="FaceIdentifier", tags=["image", "face", "identifier"], category="image", version="1.1.0"
|
"face_identifier", title="FaceIdentifier", tags=["image", "face", "identifier"], category="image", version="1.0.2"
|
||||||
)
|
)
|
||||||
class FaceIdentifierInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
class FaceIdentifierInvocation(BaseInvocation):
|
||||||
"""Outputs an image with detected face IDs printed on each face. For use with other FaceTools."""
|
"""Outputs an image with detected face IDs printed on each face. For use with other FaceTools."""
|
||||||
|
|
||||||
image: ImageField = InputField(description="Image to face detect")
|
image: ImageField = InputField(description="Image to face detect")
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ import cv2
|
|||||||
import numpy
|
import numpy
|
||||||
from PIL import Image, ImageChops, ImageFilter, ImageOps
|
from PIL import Image, ImageChops, ImageFilter, ImageOps
|
||||||
|
|
||||||
|
from invokeai.app.invocations.metadata import CoreMetadata
|
||||||
from invokeai.app.invocations.primitives import BoardField, ColorField, ImageField, ImageOutput
|
from invokeai.app.invocations.primitives import BoardField, ColorField, ImageField, ImageOutput
|
||||||
from invokeai.app.services.image_records.image_records_common import ImageCategory, ImageRecordChanges, ResourceOrigin
|
|
||||||
from invokeai.app.shared.fields import FieldDescriptions
|
|
||||||
from invokeai.backend.image_util.invisible_watermark import InvisibleWatermark
|
from invokeai.backend.image_util.invisible_watermark import InvisibleWatermark
|
||||||
from invokeai.backend.image_util.safety_checker import SafetyChecker
|
from invokeai.backend.image_util.safety_checker import SafetyChecker
|
||||||
|
|
||||||
from .baseinvocation import BaseInvocation, Input, InputField, InvocationContext, WithMetadata, WithWorkflow, invocation
|
from ..models.image import ImageCategory, ResourceOrigin
|
||||||
|
from .baseinvocation import BaseInvocation, FieldDescriptions, Input, InputField, InvocationContext, invocation
|
||||||
|
|
||||||
|
|
||||||
@invocation("show_image", title="Show Image", tags=["image"], category="image", version="1.0.0")
|
@invocation("show_image", title="Show Image", tags=["image"], category="image", version="1.0.0")
|
||||||
@@ -36,8 +36,8 @@ class ShowImageInvocation(BaseInvocation):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@invocation("blank_image", title="Blank Image", tags=["image"], category="image", version="1.1.0")
|
@invocation("blank_image", title="Blank Image", tags=["image"], category="image", version="1.0.0")
|
||||||
class BlankImageInvocation(BaseInvocation, WithMetadata, WithWorkflow):
|
class BlankImageInvocation(BaseInvocation):
|
||||||
"""Creates a blank image and forwards it to the pipeline"""
|
"""Creates a blank image and forwards it to the pipeline"""
|
||||||
|
|
||||||
width: int = InputField(default=512, description="The width of the image")
|
width: int = InputField(default=512, description="The width of the image")
|
||||||
@@ -55,7 +55,6 @@ class BlankImageInvocation(BaseInvocation, WithMetadata, WithWorkflow):
|
|||||||
node_id=self.id,
|
node_id=self.id,
|
||||||
session_id=context.graph_execution_state_id,
|
session_id=context.graph_execution_state_id,
|
||||||
is_intermediate=self.is_intermediate,
|
is_intermediate=self.is_intermediate,
|
||||||
metadata=self.metadata,
|
|
||||||
workflow=self.workflow,
|
workflow=self.workflow,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -66,8 +65,8 @@ class BlankImageInvocation(BaseInvocation, WithMetadata, WithWorkflow):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@invocation("img_crop", title="Crop Image", tags=["image", "crop"], category="image", version="1.1.0")
|
@invocation("img_crop", title="Crop Image", tags=["image", "crop"], category="image", version="1.0.0")
|
||||||
class ImageCropInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
class ImageCropInvocation(BaseInvocation):
|
||||||
"""Crops an image to a specified box. The box can be outside of the image."""
|
"""Crops an image to a specified box. The box can be outside of the image."""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to crop")
|
image: ImageField = InputField(description="The image to crop")
|
||||||
@@ -89,7 +88,6 @@ class ImageCropInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
node_id=self.id,
|
node_id=self.id,
|
||||||
session_id=context.graph_execution_state_id,
|
session_id=context.graph_execution_state_id,
|
||||||
is_intermediate=self.is_intermediate,
|
is_intermediate=self.is_intermediate,
|
||||||
metadata=self.metadata,
|
|
||||||
workflow=self.workflow,
|
workflow=self.workflow,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -100,63 +98,8 @@ class ImageCropInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@invocation(
|
@invocation("img_paste", title="Paste Image", tags=["image", "paste"], category="image", version="1.0.1")
|
||||||
invocation_type="img_pad_crop",
|
class ImagePasteInvocation(BaseInvocation):
|
||||||
title="Center Pad or Crop Image",
|
|
||||||
category="image",
|
|
||||||
tags=["image", "pad", "crop"],
|
|
||||||
version="1.0.0",
|
|
||||||
)
|
|
||||||
class CenterPadCropInvocation(BaseInvocation):
|
|
||||||
"""Pad or crop an image's sides from the center by specified pixels. Positive values are outside of the image."""
|
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to crop")
|
|
||||||
left: int = InputField(
|
|
||||||
default=0,
|
|
||||||
description="Number of pixels to pad/crop from the left (negative values crop inwards, positive values pad outwards)",
|
|
||||||
)
|
|
||||||
right: int = InputField(
|
|
||||||
default=0,
|
|
||||||
description="Number of pixels to pad/crop from the right (negative values crop inwards, positive values pad outwards)",
|
|
||||||
)
|
|
||||||
top: int = InputField(
|
|
||||||
default=0,
|
|
||||||
description="Number of pixels to pad/crop from the top (negative values crop inwards, positive values pad outwards)",
|
|
||||||
)
|
|
||||||
bottom: int = InputField(
|
|
||||||
default=0,
|
|
||||||
description="Number of pixels to pad/crop from the bottom (negative values crop inwards, positive values pad outwards)",
|
|
||||||
)
|
|
||||||
|
|
||||||
def invoke(self, context: InvocationContext) -> ImageOutput:
|
|
||||||
image = context.services.images.get_pil_image(self.image.image_name)
|
|
||||||
|
|
||||||
# Calculate and create new image dimensions
|
|
||||||
new_width = image.width + self.right + self.left
|
|
||||||
new_height = image.height + self.top + self.bottom
|
|
||||||
image_crop = Image.new(mode="RGBA", size=(new_width, new_height), color=(0, 0, 0, 0))
|
|
||||||
|
|
||||||
# Paste new image onto input
|
|
||||||
image_crop.paste(image, (self.left, self.top))
|
|
||||||
|
|
||||||
image_dto = context.services.images.create(
|
|
||||||
image=image_crop,
|
|
||||||
image_origin=ResourceOrigin.INTERNAL,
|
|
||||||
image_category=ImageCategory.GENERAL,
|
|
||||||
node_id=self.id,
|
|
||||||
session_id=context.graph_execution_state_id,
|
|
||||||
is_intermediate=self.is_intermediate,
|
|
||||||
)
|
|
||||||
|
|
||||||
return ImageOutput(
|
|
||||||
image=ImageField(image_name=image_dto.image_name),
|
|
||||||
width=image_dto.width,
|
|
||||||
height=image_dto.height,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@invocation("img_paste", title="Paste Image", tags=["image", "paste"], category="image", version="1.1.0")
|
|
||||||
class ImagePasteInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|
||||||
"""Pastes an image into another image."""
|
"""Pastes an image into another image."""
|
||||||
|
|
||||||
base_image: ImageField = InputField(description="The base image")
|
base_image: ImageField = InputField(description="The base image")
|
||||||
@@ -198,7 +141,6 @@ class ImagePasteInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
node_id=self.id,
|
node_id=self.id,
|
||||||
session_id=context.graph_execution_state_id,
|
session_id=context.graph_execution_state_id,
|
||||||
is_intermediate=self.is_intermediate,
|
is_intermediate=self.is_intermediate,
|
||||||
metadata=self.metadata,
|
|
||||||
workflow=self.workflow,
|
workflow=self.workflow,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -209,8 +151,8 @@ class ImagePasteInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@invocation("tomask", title="Mask from Alpha", tags=["image", "mask"], category="image", version="1.1.0")
|
@invocation("tomask", title="Mask from Alpha", tags=["image", "mask"], category="image", version="1.0.0")
|
||||||
class MaskFromAlphaInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
class MaskFromAlphaInvocation(BaseInvocation):
|
||||||
"""Extracts the alpha channel of an image as a mask."""
|
"""Extracts the alpha channel of an image as a mask."""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to create the mask from")
|
image: ImageField = InputField(description="The image to create the mask from")
|
||||||
@@ -230,7 +172,6 @@ class MaskFromAlphaInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
node_id=self.id,
|
node_id=self.id,
|
||||||
session_id=context.graph_execution_state_id,
|
session_id=context.graph_execution_state_id,
|
||||||
is_intermediate=self.is_intermediate,
|
is_intermediate=self.is_intermediate,
|
||||||
metadata=self.metadata,
|
|
||||||
workflow=self.workflow,
|
workflow=self.workflow,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -241,8 +182,8 @@ class MaskFromAlphaInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@invocation("img_mul", title="Multiply Images", tags=["image", "multiply"], category="image", version="1.1.0")
|
@invocation("img_mul", title="Multiply Images", tags=["image", "multiply"], category="image", version="1.0.0")
|
||||||
class ImageMultiplyInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
class ImageMultiplyInvocation(BaseInvocation):
|
||||||
"""Multiplies two images together using `PIL.ImageChops.multiply()`."""
|
"""Multiplies two images together using `PIL.ImageChops.multiply()`."""
|
||||||
|
|
||||||
image1: ImageField = InputField(description="The first image to multiply")
|
image1: ImageField = InputField(description="The first image to multiply")
|
||||||
@@ -261,7 +202,6 @@ class ImageMultiplyInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
node_id=self.id,
|
node_id=self.id,
|
||||||
session_id=context.graph_execution_state_id,
|
session_id=context.graph_execution_state_id,
|
||||||
is_intermediate=self.is_intermediate,
|
is_intermediate=self.is_intermediate,
|
||||||
metadata=self.metadata,
|
|
||||||
workflow=self.workflow,
|
workflow=self.workflow,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -275,8 +215,8 @@ class ImageMultiplyInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
IMAGE_CHANNELS = Literal["A", "R", "G", "B"]
|
IMAGE_CHANNELS = Literal["A", "R", "G", "B"]
|
||||||
|
|
||||||
|
|
||||||
@invocation("img_chan", title="Extract Image Channel", tags=["image", "channel"], category="image", version="1.1.0")
|
@invocation("img_chan", title="Extract Image Channel", tags=["image", "channel"], category="image", version="1.0.0")
|
||||||
class ImageChannelInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
class ImageChannelInvocation(BaseInvocation):
|
||||||
"""Gets a channel from an image."""
|
"""Gets a channel from an image."""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to get the channel from")
|
image: ImageField = InputField(description="The image to get the channel from")
|
||||||
@@ -294,7 +234,6 @@ class ImageChannelInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
node_id=self.id,
|
node_id=self.id,
|
||||||
session_id=context.graph_execution_state_id,
|
session_id=context.graph_execution_state_id,
|
||||||
is_intermediate=self.is_intermediate,
|
is_intermediate=self.is_intermediate,
|
||||||
metadata=self.metadata,
|
|
||||||
workflow=self.workflow,
|
workflow=self.workflow,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -308,8 +247,8 @@ class ImageChannelInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
IMAGE_MODES = Literal["L", "RGB", "RGBA", "CMYK", "YCbCr", "LAB", "HSV", "I", "F"]
|
IMAGE_MODES = Literal["L", "RGB", "RGBA", "CMYK", "YCbCr", "LAB", "HSV", "I", "F"]
|
||||||
|
|
||||||
|
|
||||||
@invocation("img_conv", title="Convert Image Mode", tags=["image", "convert"], category="image", version="1.1.0")
|
@invocation("img_conv", title="Convert Image Mode", tags=["image", "convert"], category="image", version="1.0.0")
|
||||||
class ImageConvertInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
class ImageConvertInvocation(BaseInvocation):
|
||||||
"""Converts an image to a different mode."""
|
"""Converts an image to a different mode."""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to convert")
|
image: ImageField = InputField(description="The image to convert")
|
||||||
@@ -327,7 +266,6 @@ class ImageConvertInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
node_id=self.id,
|
node_id=self.id,
|
||||||
session_id=context.graph_execution_state_id,
|
session_id=context.graph_execution_state_id,
|
||||||
is_intermediate=self.is_intermediate,
|
is_intermediate=self.is_intermediate,
|
||||||
metadata=self.metadata,
|
|
||||||
workflow=self.workflow,
|
workflow=self.workflow,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -338,8 +276,8 @@ class ImageConvertInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@invocation("img_blur", title="Blur Image", tags=["image", "blur"], category="image", version="1.1.0")
|
@invocation("img_blur", title="Blur Image", tags=["image", "blur"], category="image", version="1.0.0")
|
||||||
class ImageBlurInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
class ImageBlurInvocation(BaseInvocation):
|
||||||
"""Blurs an image"""
|
"""Blurs an image"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to blur")
|
image: ImageField = InputField(description="The image to blur")
|
||||||
@@ -362,7 +300,6 @@ class ImageBlurInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
node_id=self.id,
|
node_id=self.id,
|
||||||
session_id=context.graph_execution_state_id,
|
session_id=context.graph_execution_state_id,
|
||||||
is_intermediate=self.is_intermediate,
|
is_intermediate=self.is_intermediate,
|
||||||
metadata=self.metadata,
|
|
||||||
workflow=self.workflow,
|
workflow=self.workflow,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -393,14 +330,17 @@ PIL_RESAMPLING_MAP = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@invocation("img_resize", title="Resize Image", tags=["image", "resize"], category="image", version="1.1.0")
|
@invocation("img_resize", title="Resize Image", tags=["image", "resize"], category="image", version="1.0.0")
|
||||||
class ImageResizeInvocation(BaseInvocation, WithMetadata, WithWorkflow):
|
class ImageResizeInvocation(BaseInvocation):
|
||||||
"""Resizes an image to specific dimensions"""
|
"""Resizes an image to specific dimensions"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to resize")
|
image: ImageField = InputField(description="The image to resize")
|
||||||
width: int = InputField(default=512, gt=0, description="The width to resize to (px)")
|
width: int = InputField(default=512, gt=0, description="The width to resize to (px)")
|
||||||
height: int = InputField(default=512, gt=0, description="The height to resize to (px)")
|
height: int = InputField(default=512, gt=0, description="The height to resize to (px)")
|
||||||
resample_mode: PIL_RESAMPLING_MODES = InputField(default="bicubic", description="The resampling mode")
|
resample_mode: PIL_RESAMPLING_MODES = InputField(default="bicubic", description="The resampling mode")
|
||||||
|
metadata: Optional[CoreMetadata] = InputField(
|
||||||
|
default=None, description=FieldDescriptions.core_metadata, ui_hidden=True
|
||||||
|
)
|
||||||
|
|
||||||
def invoke(self, context: InvocationContext) -> ImageOutput:
|
def invoke(self, context: InvocationContext) -> ImageOutput:
|
||||||
image = context.services.images.get_pil_image(self.image.image_name)
|
image = context.services.images.get_pil_image(self.image.image_name)
|
||||||
@@ -419,7 +359,7 @@ class ImageResizeInvocation(BaseInvocation, WithMetadata, WithWorkflow):
|
|||||||
node_id=self.id,
|
node_id=self.id,
|
||||||
session_id=context.graph_execution_state_id,
|
session_id=context.graph_execution_state_id,
|
||||||
is_intermediate=self.is_intermediate,
|
is_intermediate=self.is_intermediate,
|
||||||
metadata=self.metadata,
|
metadata=self.metadata.dict() if self.metadata else None,
|
||||||
workflow=self.workflow,
|
workflow=self.workflow,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -430,8 +370,8 @@ class ImageResizeInvocation(BaseInvocation, WithMetadata, WithWorkflow):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@invocation("img_scale", title="Scale Image", tags=["image", "scale"], category="image", version="1.1.0")
|
@invocation("img_scale", title="Scale Image", tags=["image", "scale"], category="image", version="1.0.0")
|
||||||
class ImageScaleInvocation(BaseInvocation, WithMetadata, WithWorkflow):
|
class ImageScaleInvocation(BaseInvocation):
|
||||||
"""Scales an image by a factor"""
|
"""Scales an image by a factor"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to scale")
|
image: ImageField = InputField(description="The image to scale")
|
||||||
@@ -461,7 +401,6 @@ class ImageScaleInvocation(BaseInvocation, WithMetadata, WithWorkflow):
|
|||||||
node_id=self.id,
|
node_id=self.id,
|
||||||
session_id=context.graph_execution_state_id,
|
session_id=context.graph_execution_state_id,
|
||||||
is_intermediate=self.is_intermediate,
|
is_intermediate=self.is_intermediate,
|
||||||
metadata=self.metadata,
|
|
||||||
workflow=self.workflow,
|
workflow=self.workflow,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -472,8 +411,8 @@ class ImageScaleInvocation(BaseInvocation, WithMetadata, WithWorkflow):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@invocation("img_lerp", title="Lerp Image", tags=["image", "lerp"], category="image", version="1.1.0")
|
@invocation("img_lerp", title="Lerp Image", tags=["image", "lerp"], category="image", version="1.0.0")
|
||||||
class ImageLerpInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
class ImageLerpInvocation(BaseInvocation):
|
||||||
"""Linear interpolation of all pixels of an image"""
|
"""Linear interpolation of all pixels of an image"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to lerp")
|
image: ImageField = InputField(description="The image to lerp")
|
||||||
@@ -495,7 +434,6 @@ class ImageLerpInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
node_id=self.id,
|
node_id=self.id,
|
||||||
session_id=context.graph_execution_state_id,
|
session_id=context.graph_execution_state_id,
|
||||||
is_intermediate=self.is_intermediate,
|
is_intermediate=self.is_intermediate,
|
||||||
metadata=self.metadata,
|
|
||||||
workflow=self.workflow,
|
workflow=self.workflow,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -506,8 +444,8 @@ class ImageLerpInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@invocation("img_ilerp", title="Inverse Lerp Image", tags=["image", "ilerp"], category="image", version="1.1.0")
|
@invocation("img_ilerp", title="Inverse Lerp Image", tags=["image", "ilerp"], category="image", version="1.0.0")
|
||||||
class ImageInverseLerpInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
class ImageInverseLerpInvocation(BaseInvocation):
|
||||||
"""Inverse linear interpolation of all pixels of an image"""
|
"""Inverse linear interpolation of all pixels of an image"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to lerp")
|
image: ImageField = InputField(description="The image to lerp")
|
||||||
@@ -518,7 +456,7 @@ class ImageInverseLerpInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
image = context.services.images.get_pil_image(self.image.image_name)
|
image = context.services.images.get_pil_image(self.image.image_name)
|
||||||
|
|
||||||
image_arr = numpy.asarray(image, dtype=numpy.float32)
|
image_arr = numpy.asarray(image, dtype=numpy.float32)
|
||||||
image_arr = numpy.minimum(numpy.maximum(image_arr - self.min, 0) / float(self.max - self.min), 1) * 255 # type: ignore [assignment]
|
image_arr = numpy.minimum(numpy.maximum(image_arr - self.min, 0) / float(self.max - self.min), 1) * 255
|
||||||
|
|
||||||
ilerp_image = Image.fromarray(numpy.uint8(image_arr))
|
ilerp_image = Image.fromarray(numpy.uint8(image_arr))
|
||||||
|
|
||||||
@@ -529,7 +467,6 @@ class ImageInverseLerpInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
node_id=self.id,
|
node_id=self.id,
|
||||||
session_id=context.graph_execution_state_id,
|
session_id=context.graph_execution_state_id,
|
||||||
is_intermediate=self.is_intermediate,
|
is_intermediate=self.is_intermediate,
|
||||||
metadata=self.metadata,
|
|
||||||
workflow=self.workflow,
|
workflow=self.workflow,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -540,11 +477,14 @@ class ImageInverseLerpInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@invocation("img_nsfw", title="Blur NSFW Image", tags=["image", "nsfw"], category="image", version="1.1.0")
|
@invocation("img_nsfw", title="Blur NSFW Image", tags=["image", "nsfw"], category="image", version="1.0.0")
|
||||||
class ImageNSFWBlurInvocation(BaseInvocation, WithMetadata, WithWorkflow):
|
class ImageNSFWBlurInvocation(BaseInvocation):
|
||||||
"""Add blur to NSFW-flagged images"""
|
"""Add blur to NSFW-flagged images"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to check")
|
image: ImageField = InputField(description="The image to check")
|
||||||
|
metadata: Optional[CoreMetadata] = InputField(
|
||||||
|
default=None, description=FieldDescriptions.core_metadata, ui_hidden=True
|
||||||
|
)
|
||||||
|
|
||||||
def invoke(self, context: InvocationContext) -> ImageOutput:
|
def invoke(self, context: InvocationContext) -> ImageOutput:
|
||||||
image = context.services.images.get_pil_image(self.image.image_name)
|
image = context.services.images.get_pil_image(self.image.image_name)
|
||||||
@@ -565,7 +505,7 @@ class ImageNSFWBlurInvocation(BaseInvocation, WithMetadata, WithWorkflow):
|
|||||||
node_id=self.id,
|
node_id=self.id,
|
||||||
session_id=context.graph_execution_state_id,
|
session_id=context.graph_execution_state_id,
|
||||||
is_intermediate=self.is_intermediate,
|
is_intermediate=self.is_intermediate,
|
||||||
metadata=self.metadata,
|
metadata=self.metadata.dict() if self.metadata else None,
|
||||||
workflow=self.workflow,
|
workflow=self.workflow,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -575,7 +515,7 @@ class ImageNSFWBlurInvocation(BaseInvocation, WithMetadata, WithWorkflow):
|
|||||||
height=image_dto.height,
|
height=image_dto.height,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _get_caution_img(self) -> Image.Image:
|
def _get_caution_img(self) -> Image:
|
||||||
import invokeai.app.assets.images as image_assets
|
import invokeai.app.assets.images as image_assets
|
||||||
|
|
||||||
caution = Image.open(Path(image_assets.__path__[0]) / "caution.png")
|
caution = Image.open(Path(image_assets.__path__[0]) / "caution.png")
|
||||||
@@ -583,17 +523,16 @@ class ImageNSFWBlurInvocation(BaseInvocation, WithMetadata, WithWorkflow):
|
|||||||
|
|
||||||
|
|
||||||
@invocation(
|
@invocation(
|
||||||
"img_watermark",
|
"img_watermark", title="Add Invisible Watermark", tags=["image", "watermark"], category="image", version="1.0.0"
|
||||||
title="Add Invisible Watermark",
|
|
||||||
tags=["image", "watermark"],
|
|
||||||
category="image",
|
|
||||||
version="1.1.0",
|
|
||||||
)
|
)
|
||||||
class ImageWatermarkInvocation(BaseInvocation, WithMetadata, WithWorkflow):
|
class ImageWatermarkInvocation(BaseInvocation):
|
||||||
"""Add an invisible watermark to an image"""
|
"""Add an invisible watermark to an image"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to check")
|
image: ImageField = InputField(description="The image to check")
|
||||||
text: str = InputField(default="InvokeAI", description="Watermark text")
|
text: str = InputField(default="InvokeAI", description="Watermark text")
|
||||||
|
metadata: Optional[CoreMetadata] = InputField(
|
||||||
|
default=None, description=FieldDescriptions.core_metadata, ui_hidden=True
|
||||||
|
)
|
||||||
|
|
||||||
def invoke(self, context: InvocationContext) -> ImageOutput:
|
def invoke(self, context: InvocationContext) -> ImageOutput:
|
||||||
image = context.services.images.get_pil_image(self.image.image_name)
|
image = context.services.images.get_pil_image(self.image.image_name)
|
||||||
@@ -605,7 +544,7 @@ class ImageWatermarkInvocation(BaseInvocation, WithMetadata, WithWorkflow):
|
|||||||
node_id=self.id,
|
node_id=self.id,
|
||||||
session_id=context.graph_execution_state_id,
|
session_id=context.graph_execution_state_id,
|
||||||
is_intermediate=self.is_intermediate,
|
is_intermediate=self.is_intermediate,
|
||||||
metadata=self.metadata,
|
metadata=self.metadata.dict() if self.metadata else None,
|
||||||
workflow=self.workflow,
|
workflow=self.workflow,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -616,8 +555,8 @@ class ImageWatermarkInvocation(BaseInvocation, WithMetadata, WithWorkflow):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@invocation("mask_edge", title="Mask Edge", tags=["image", "mask", "inpaint"], category="image", version="1.1.0")
|
@invocation("mask_edge", title="Mask Edge", tags=["image", "mask", "inpaint"], category="image", version="1.0.0")
|
||||||
class MaskEdgeInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
class MaskEdgeInvocation(BaseInvocation):
|
||||||
"""Applies an edge mask to an image"""
|
"""Applies an edge mask to an image"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to apply the mask to")
|
image: ImageField = InputField(description="The image to apply the mask to")
|
||||||
@@ -651,7 +590,6 @@ class MaskEdgeInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
node_id=self.id,
|
node_id=self.id,
|
||||||
session_id=context.graph_execution_state_id,
|
session_id=context.graph_execution_state_id,
|
||||||
is_intermediate=self.is_intermediate,
|
is_intermediate=self.is_intermediate,
|
||||||
metadata=self.metadata,
|
|
||||||
workflow=self.workflow,
|
workflow=self.workflow,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -663,13 +601,9 @@ class MaskEdgeInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
|
|
||||||
|
|
||||||
@invocation(
|
@invocation(
|
||||||
"mask_combine",
|
"mask_combine", title="Combine Masks", tags=["image", "mask", "multiply"], category="image", version="1.0.0"
|
||||||
title="Combine Masks",
|
|
||||||
tags=["image", "mask", "multiply"],
|
|
||||||
category="image",
|
|
||||||
version="1.1.0",
|
|
||||||
)
|
)
|
||||||
class MaskCombineInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
class MaskCombineInvocation(BaseInvocation):
|
||||||
"""Combine two masks together by multiplying them using `PIL.ImageChops.multiply()`."""
|
"""Combine two masks together by multiplying them using `PIL.ImageChops.multiply()`."""
|
||||||
|
|
||||||
mask1: ImageField = InputField(description="The first mask to combine")
|
mask1: ImageField = InputField(description="The first mask to combine")
|
||||||
@@ -688,7 +622,6 @@ class MaskCombineInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
node_id=self.id,
|
node_id=self.id,
|
||||||
session_id=context.graph_execution_state_id,
|
session_id=context.graph_execution_state_id,
|
||||||
is_intermediate=self.is_intermediate,
|
is_intermediate=self.is_intermediate,
|
||||||
metadata=self.metadata,
|
|
||||||
workflow=self.workflow,
|
workflow=self.workflow,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -699,8 +632,8 @@ class MaskCombineInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@invocation("color_correct", title="Color Correct", tags=["image", "color"], category="image", version="1.1.0")
|
@invocation("color_correct", title="Color Correct", tags=["image", "color"], category="image", version="1.0.0")
|
||||||
class ColorCorrectInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
class ColorCorrectInvocation(BaseInvocation):
|
||||||
"""
|
"""
|
||||||
Shifts the colors of a target image to match the reference image, optionally
|
Shifts the colors of a target image to match the reference image, optionally
|
||||||
using a mask to only color-correct certain regions of the target image.
|
using a mask to only color-correct certain regions of the target image.
|
||||||
@@ -799,7 +732,6 @@ class ColorCorrectInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
node_id=self.id,
|
node_id=self.id,
|
||||||
session_id=context.graph_execution_state_id,
|
session_id=context.graph_execution_state_id,
|
||||||
is_intermediate=self.is_intermediate,
|
is_intermediate=self.is_intermediate,
|
||||||
metadata=self.metadata,
|
|
||||||
workflow=self.workflow,
|
workflow=self.workflow,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -810,8 +742,8 @@ class ColorCorrectInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@invocation("img_hue_adjust", title="Adjust Image Hue", tags=["image", "hue"], category="image", version="1.1.0")
|
@invocation("img_hue_adjust", title="Adjust Image Hue", tags=["image", "hue"], category="image", version="1.0.0")
|
||||||
class ImageHueAdjustmentInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
class ImageHueAdjustmentInvocation(BaseInvocation):
|
||||||
"""Adjusts the Hue of an image."""
|
"""Adjusts the Hue of an image."""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to adjust")
|
image: ImageField = InputField(description="The image to adjust")
|
||||||
@@ -839,7 +771,6 @@ class ImageHueAdjustmentInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
node_id=self.id,
|
node_id=self.id,
|
||||||
is_intermediate=self.is_intermediate,
|
is_intermediate=self.is_intermediate,
|
||||||
session_id=context.graph_execution_state_id,
|
session_id=context.graph_execution_state_id,
|
||||||
metadata=self.metadata,
|
|
||||||
workflow=self.workflow,
|
workflow=self.workflow,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -913,9 +844,9 @@ CHANNEL_FORMATS = {
|
|||||||
"value",
|
"value",
|
||||||
],
|
],
|
||||||
category="image",
|
category="image",
|
||||||
version="1.1.0",
|
version="1.0.0",
|
||||||
)
|
)
|
||||||
class ImageChannelOffsetInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
class ImageChannelOffsetInvocation(BaseInvocation):
|
||||||
"""Add or subtract a value from a specific color channel of an image."""
|
"""Add or subtract a value from a specific color channel of an image."""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to adjust")
|
image: ImageField = InputField(description="The image to adjust")
|
||||||
@@ -949,7 +880,6 @@ class ImageChannelOffsetInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
node_id=self.id,
|
node_id=self.id,
|
||||||
is_intermediate=self.is_intermediate,
|
is_intermediate=self.is_intermediate,
|
||||||
session_id=context.graph_execution_state_id,
|
session_id=context.graph_execution_state_id,
|
||||||
metadata=self.metadata,
|
|
||||||
workflow=self.workflow,
|
workflow=self.workflow,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -984,9 +914,9 @@ class ImageChannelOffsetInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
"value",
|
"value",
|
||||||
],
|
],
|
||||||
category="image",
|
category="image",
|
||||||
version="1.1.0",
|
version="1.0.0",
|
||||||
)
|
)
|
||||||
class ImageChannelMultiplyInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
class ImageChannelMultiplyInvocation(BaseInvocation):
|
||||||
"""Scale a specific color channel of an image."""
|
"""Scale a specific color channel of an image."""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to adjust")
|
image: ImageField = InputField(description="The image to adjust")
|
||||||
@@ -1026,7 +956,6 @@ class ImageChannelMultiplyInvocation(BaseInvocation, WithWorkflow, WithMetadata)
|
|||||||
is_intermediate=self.is_intermediate,
|
is_intermediate=self.is_intermediate,
|
||||||
session_id=context.graph_execution_state_id,
|
session_id=context.graph_execution_state_id,
|
||||||
workflow=self.workflow,
|
workflow=self.workflow,
|
||||||
metadata=self.metadata,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return ImageOutput(
|
return ImageOutput(
|
||||||
@@ -1043,14 +972,19 @@ class ImageChannelMultiplyInvocation(BaseInvocation, WithWorkflow, WithMetadata)
|
|||||||
title="Save Image",
|
title="Save Image",
|
||||||
tags=["primitives", "image"],
|
tags=["primitives", "image"],
|
||||||
category="primitives",
|
category="primitives",
|
||||||
version="1.1.0",
|
version="1.0.1",
|
||||||
use_cache=False,
|
use_cache=False,
|
||||||
)
|
)
|
||||||
class SaveImageInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
class SaveImageInvocation(BaseInvocation):
|
||||||
"""Saves an image. Unlike an image primitive, this invocation stores a copy of the image."""
|
"""Saves an image. Unlike an image primitive, this invocation stores a copy of the image."""
|
||||||
|
|
||||||
image: ImageField = InputField(description=FieldDescriptions.image)
|
image: ImageField = InputField(description=FieldDescriptions.image)
|
||||||
board: BoardField = InputField(default=None, description=FieldDescriptions.board, input=Input.Direct)
|
board: Optional[BoardField] = InputField(default=None, description=FieldDescriptions.board, input=Input.Direct)
|
||||||
|
metadata: CoreMetadata = InputField(
|
||||||
|
default=None,
|
||||||
|
description=FieldDescriptions.core_metadata,
|
||||||
|
ui_hidden=True,
|
||||||
|
)
|
||||||
|
|
||||||
def invoke(self, context: InvocationContext) -> ImageOutput:
|
def invoke(self, context: InvocationContext) -> ImageOutput:
|
||||||
image = context.services.images.get_pil_image(self.image.image_name)
|
image = context.services.images.get_pil_image(self.image.image_name)
|
||||||
@@ -1063,7 +997,7 @@ class SaveImageInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
node_id=self.id,
|
node_id=self.id,
|
||||||
session_id=context.graph_execution_state_id,
|
session_id=context.graph_execution_state_id,
|
||||||
is_intermediate=self.is_intermediate,
|
is_intermediate=self.is_intermediate,
|
||||||
metadata=self.metadata,
|
metadata=self.metadata.dict() if self.metadata else None,
|
||||||
workflow=self.workflow,
|
workflow=self.workflow,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1072,35 +1006,3 @@ class SaveImageInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
width=image_dto.width,
|
width=image_dto.width,
|
||||||
height=image_dto.height,
|
height=image_dto.height,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@invocation(
|
|
||||||
"linear_ui_output",
|
|
||||||
title="Linear UI Image Output",
|
|
||||||
tags=["primitives", "image"],
|
|
||||||
category="primitives",
|
|
||||||
version="1.0.1",
|
|
||||||
use_cache=False,
|
|
||||||
)
|
|
||||||
class LinearUIOutputInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|
||||||
"""Handles Linear UI Image Outputting tasks."""
|
|
||||||
|
|
||||||
image: ImageField = InputField(description=FieldDescriptions.image)
|
|
||||||
board: Optional[BoardField] = InputField(default=None, description=FieldDescriptions.board, input=Input.Direct)
|
|
||||||
|
|
||||||
def invoke(self, context: InvocationContext) -> ImageOutput:
|
|
||||||
image_dto = context.services.images.get_dto(self.image.image_name)
|
|
||||||
|
|
||||||
if self.board:
|
|
||||||
context.services.board_images.add_image_to_board(self.board.board_id, self.image.image_name)
|
|
||||||
|
|
||||||
if image_dto.is_intermediate != self.is_intermediate:
|
|
||||||
context.services.images.update(
|
|
||||||
self.image.image_name, changes=ImageRecordChanges(is_intermediate=self.is_intermediate)
|
|
||||||
)
|
|
||||||
|
|
||||||
return ImageOutput(
|
|
||||||
image=ImageField(image_name=self.image.image_name),
|
|
||||||
width=image_dto.width,
|
|
||||||
height=image_dto.height,
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ import numpy as np
|
|||||||
from PIL import Image, ImageOps
|
from PIL import Image, ImageOps
|
||||||
|
|
||||||
from invokeai.app.invocations.primitives import ColorField, ImageField, ImageOutput
|
from invokeai.app.invocations.primitives import ColorField, ImageField, ImageOutput
|
||||||
from invokeai.app.services.image_records.image_records_common import ImageCategory, ResourceOrigin
|
from invokeai.app.util.misc import SEED_MAX, get_random_seed
|
||||||
from invokeai.app.util.misc import SEED_MAX
|
|
||||||
from invokeai.backend.image_util.cv2_inpaint import cv2_inpaint
|
from invokeai.backend.image_util.cv2_inpaint import cv2_inpaint
|
||||||
from invokeai.backend.image_util.lama import LaMA
|
from invokeai.backend.image_util.lama import LaMA
|
||||||
from invokeai.backend.image_util.patchmatch import PatchMatch
|
from invokeai.backend.image_util.patchmatch import PatchMatch
|
||||||
|
|
||||||
from .baseinvocation import BaseInvocation, InputField, InvocationContext, WithMetadata, WithWorkflow, invocation
|
from ..models.image import ImageCategory, ResourceOrigin
|
||||||
|
from .baseinvocation import BaseInvocation, InputField, InvocationContext, invocation
|
||||||
from .image import PIL_RESAMPLING_MAP, PIL_RESAMPLING_MODES
|
from .image import PIL_RESAMPLING_MAP, PIL_RESAMPLING_MODES
|
||||||
|
|
||||||
|
|
||||||
@@ -118,8 +118,8 @@ def tile_fill_missing(im: Image.Image, tile_size: int = 16, seed: Optional[int]
|
|||||||
return si
|
return si
|
||||||
|
|
||||||
|
|
||||||
@invocation("infill_rgba", title="Solid Color Infill", tags=["image", "inpaint"], category="inpaint", version="1.1.0")
|
@invocation("infill_rgba", title="Solid Color Infill", tags=["image", "inpaint"], category="inpaint", version="1.0.0")
|
||||||
class InfillColorInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
class InfillColorInvocation(BaseInvocation):
|
||||||
"""Infills transparent areas of an image with a solid color"""
|
"""Infills transparent areas of an image with a solid color"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to infill")
|
image: ImageField = InputField(description="The image to infill")
|
||||||
@@ -143,7 +143,6 @@ class InfillColorInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
node_id=self.id,
|
node_id=self.id,
|
||||||
session_id=context.graph_execution_state_id,
|
session_id=context.graph_execution_state_id,
|
||||||
is_intermediate=self.is_intermediate,
|
is_intermediate=self.is_intermediate,
|
||||||
metadata=self.metadata,
|
|
||||||
workflow=self.workflow,
|
workflow=self.workflow,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -154,17 +153,17 @@ class InfillColorInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@invocation("infill_tile", title="Tile Infill", tags=["image", "inpaint"], category="inpaint", version="1.1.1")
|
@invocation("infill_tile", title="Tile Infill", tags=["image", "inpaint"], category="inpaint", version="1.0.0")
|
||||||
class InfillTileInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
class InfillTileInvocation(BaseInvocation):
|
||||||
"""Infills transparent areas of an image with tiles of the image"""
|
"""Infills transparent areas of an image with tiles of the image"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to infill")
|
image: ImageField = InputField(description="The image to infill")
|
||||||
tile_size: int = InputField(default=32, ge=1, description="The tile size (px)")
|
tile_size: int = InputField(default=32, ge=1, description="The tile size (px)")
|
||||||
seed: int = InputField(
|
seed: int = InputField(
|
||||||
default=0,
|
|
||||||
ge=0,
|
ge=0,
|
||||||
le=SEED_MAX,
|
le=SEED_MAX,
|
||||||
description="The seed to use for tile generation (omit for random)",
|
description="The seed to use for tile generation (omit for random)",
|
||||||
|
default_factory=get_random_seed,
|
||||||
)
|
)
|
||||||
|
|
||||||
def invoke(self, context: InvocationContext) -> ImageOutput:
|
def invoke(self, context: InvocationContext) -> ImageOutput:
|
||||||
@@ -180,7 +179,6 @@ class InfillTileInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
node_id=self.id,
|
node_id=self.id,
|
||||||
session_id=context.graph_execution_state_id,
|
session_id=context.graph_execution_state_id,
|
||||||
is_intermediate=self.is_intermediate,
|
is_intermediate=self.is_intermediate,
|
||||||
metadata=self.metadata,
|
|
||||||
workflow=self.workflow,
|
workflow=self.workflow,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -192,9 +190,9 @@ class InfillTileInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
|
|
||||||
|
|
||||||
@invocation(
|
@invocation(
|
||||||
"infill_patchmatch", title="PatchMatch Infill", tags=["image", "inpaint"], category="inpaint", version="1.1.0"
|
"infill_patchmatch", title="PatchMatch Infill", tags=["image", "inpaint"], category="inpaint", version="1.0.0"
|
||||||
)
|
)
|
||||||
class InfillPatchMatchInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
class InfillPatchMatchInvocation(BaseInvocation):
|
||||||
"""Infills transparent areas of an image using the PatchMatch algorithm"""
|
"""Infills transparent areas of an image using the PatchMatch algorithm"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to infill")
|
image: ImageField = InputField(description="The image to infill")
|
||||||
@@ -234,7 +232,6 @@ class InfillPatchMatchInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
node_id=self.id,
|
node_id=self.id,
|
||||||
session_id=context.graph_execution_state_id,
|
session_id=context.graph_execution_state_id,
|
||||||
is_intermediate=self.is_intermediate,
|
is_intermediate=self.is_intermediate,
|
||||||
metadata=self.metadata,
|
|
||||||
workflow=self.workflow,
|
workflow=self.workflow,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -245,8 +242,8 @@ class InfillPatchMatchInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@invocation("infill_lama", title="LaMa Infill", tags=["image", "inpaint"], category="inpaint", version="1.1.0")
|
@invocation("infill_lama", title="LaMa Infill", tags=["image", "inpaint"], category="inpaint", version="1.0.0")
|
||||||
class LaMaInfillInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
class LaMaInfillInvocation(BaseInvocation):
|
||||||
"""Infills transparent areas of an image using the LaMa model"""
|
"""Infills transparent areas of an image using the LaMa model"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to infill")
|
image: ImageField = InputField(description="The image to infill")
|
||||||
@@ -263,8 +260,6 @@ class LaMaInfillInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
node_id=self.id,
|
node_id=self.id,
|
||||||
session_id=context.graph_execution_state_id,
|
session_id=context.graph_execution_state_id,
|
||||||
is_intermediate=self.is_intermediate,
|
is_intermediate=self.is_intermediate,
|
||||||
metadata=self.metadata,
|
|
||||||
workflow=self.workflow,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return ImageOutput(
|
return ImageOutput(
|
||||||
@@ -274,8 +269,8 @@ class LaMaInfillInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@invocation("infill_cv2", title="CV2 Infill", tags=["image", "inpaint"], category="inpaint", version="1.1.0")
|
@invocation("infill_cv2", title="CV2 Infill", tags=["image", "inpaint"], category="inpaint", version="1.0.0")
|
||||||
class CV2InfillInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
class CV2InfillInvocation(BaseInvocation):
|
||||||
"""Infills transparent areas of an image using OpenCV Inpainting"""
|
"""Infills transparent areas of an image using OpenCV Inpainting"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to infill")
|
image: ImageField = InputField(description="The image to infill")
|
||||||
@@ -292,8 +287,6 @@ class CV2InfillInvocation(BaseInvocation, WithWorkflow, WithMetadata):
|
|||||||
node_id=self.id,
|
node_id=self.id,
|
||||||
session_id=context.graph_execution_state_id,
|
session_id=context.graph_execution_state_id,
|
||||||
is_intermediate=self.is_intermediate,
|
is_intermediate=self.is_intermediate,
|
||||||
metadata=self.metadata,
|
|
||||||
workflow=self.workflow,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return ImageOutput(
|
return ImageOutput(
|
||||||
|
|||||||
@@ -2,20 +2,21 @@ import os
|
|||||||
from builtins import float
|
from builtins import float
|
||||||
from typing import List, Union
|
from typing import List, Union
|
||||||
|
|
||||||
from pydantic import BaseModel, ConfigDict, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
from invokeai.app.invocations.baseinvocation import (
|
from invokeai.app.invocations.baseinvocation import (
|
||||||
BaseInvocation,
|
BaseInvocation,
|
||||||
BaseInvocationOutput,
|
BaseInvocationOutput,
|
||||||
|
FieldDescriptions,
|
||||||
Input,
|
Input,
|
||||||
InputField,
|
InputField,
|
||||||
InvocationContext,
|
InvocationContext,
|
||||||
OutputField,
|
OutputField,
|
||||||
|
UIType,
|
||||||
invocation,
|
invocation,
|
||||||
invocation_output,
|
invocation_output,
|
||||||
)
|
)
|
||||||
from invokeai.app.invocations.primitives import ImageField
|
from invokeai.app.invocations.primitives import ImageField
|
||||||
from invokeai.app.shared.fields import FieldDescriptions
|
|
||||||
from invokeai.backend.model_management.models.base import BaseModelType, ModelType
|
from invokeai.backend.model_management.models.base import BaseModelType, ModelType
|
||||||
from invokeai.backend.model_management.models.ip_adapter import get_ip_adapter_image_encoder_model_id
|
from invokeai.backend.model_management.models.ip_adapter import get_ip_adapter_image_encoder_model_id
|
||||||
|
|
||||||
@@ -24,18 +25,14 @@ class IPAdapterModelField(BaseModel):
|
|||||||
model_name: str = Field(description="Name of the IP-Adapter model")
|
model_name: str = Field(description="Name of the IP-Adapter model")
|
||||||
base_model: BaseModelType = Field(description="Base model")
|
base_model: BaseModelType = Field(description="Base model")
|
||||||
|
|
||||||
model_config = ConfigDict(protected_namespaces=())
|
|
||||||
|
|
||||||
|
|
||||||
class CLIPVisionModelField(BaseModel):
|
class CLIPVisionModelField(BaseModel):
|
||||||
model_name: str = Field(description="Name of the CLIP Vision image encoder model")
|
model_name: str = Field(description="Name of the CLIP Vision image encoder model")
|
||||||
base_model: BaseModelType = Field(description="Base model (usually 'Any')")
|
base_model: BaseModelType = Field(description="Base model (usually 'Any')")
|
||||||
|
|
||||||
model_config = ConfigDict(protected_namespaces=())
|
|
||||||
|
|
||||||
|
|
||||||
class IPAdapterField(BaseModel):
|
class IPAdapterField(BaseModel):
|
||||||
image: Union[ImageField, List[ImageField]] = Field(description="The IP-Adapter image prompt(s).")
|
image: ImageField = Field(description="The IP-Adapter image prompt.")
|
||||||
ip_adapter_model: IPAdapterModelField = Field(description="The IP-Adapter model to use.")
|
ip_adapter_model: IPAdapterModelField = Field(description="The IP-Adapter model to use.")
|
||||||
image_encoder_model: CLIPVisionModelField = Field(description="The name of the CLIP image encoder model.")
|
image_encoder_model: CLIPVisionModelField = Field(description="The name of the CLIP image encoder model.")
|
||||||
weight: Union[float, List[float]] = Field(default=1, description="The weight given to the ControlNet")
|
weight: Union[float, List[float]] = Field(default=1, description="The weight given to the ControlNet")
|
||||||
@@ -54,19 +51,19 @@ class IPAdapterOutput(BaseInvocationOutput):
|
|||||||
ip_adapter: IPAdapterField = OutputField(description=FieldDescriptions.ip_adapter, title="IP-Adapter")
|
ip_adapter: IPAdapterField = OutputField(description=FieldDescriptions.ip_adapter, title="IP-Adapter")
|
||||||
|
|
||||||
|
|
||||||
@invocation("ip_adapter", title="IP-Adapter", tags=["ip_adapter", "control"], category="ip_adapter", version="1.1.0")
|
@invocation("ip_adapter", title="IP-Adapter", tags=["ip_adapter", "control"], category="ip_adapter", version="1.0.0")
|
||||||
class IPAdapterInvocation(BaseInvocation):
|
class IPAdapterInvocation(BaseInvocation):
|
||||||
"""Collects IP-Adapter info to pass to other nodes."""
|
"""Collects IP-Adapter info to pass to other nodes."""
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
image: Union[ImageField, List[ImageField]] = InputField(description="The IP-Adapter image prompt(s).")
|
image: ImageField = InputField(description="The IP-Adapter image prompt.")
|
||||||
ip_adapter_model: IPAdapterModelField = InputField(
|
ip_adapter_model: IPAdapterModelField = InputField(
|
||||||
description="The IP-Adapter model.", title="IP-Adapter Model", input=Input.Direct, ui_order=-1
|
description="The IP-Adapter model.", title="IP-Adapter Model", input=Input.Direct, ui_order=-1
|
||||||
)
|
)
|
||||||
|
|
||||||
# weight: float = InputField(default=1.0, description="The weight of the IP-Adapter.", ui_type=UIType.Float)
|
# weight: float = InputField(default=1.0, description="The weight of the IP-Adapter.", ui_type=UIType.Float)
|
||||||
weight: Union[float, List[float]] = InputField(
|
weight: Union[float, List[float]] = InputField(
|
||||||
default=1, ge=-1, description="The weight given to the IP-Adapter", title="Weight"
|
default=1, ge=0, description="The weight given to the IP-Adapter", ui_type=UIType.Float, title="Weight"
|
||||||
)
|
)
|
||||||
|
|
||||||
begin_step_percent: float = InputField(
|
begin_step_percent: float = InputField(
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import torch
|
|||||||
import torchvision.transforms as T
|
import torchvision.transforms as T
|
||||||
from diffusers import AutoencoderKL, AutoencoderTiny
|
from diffusers import AutoencoderKL, AutoencoderTiny
|
||||||
from diffusers.image_processor import VaeImageProcessor
|
from diffusers.image_processor import VaeImageProcessor
|
||||||
from diffusers.models.adapter import T2IAdapter
|
from diffusers.models.adapter import FullAdapterXL, T2IAdapter
|
||||||
from diffusers.models.attention_processor import (
|
from diffusers.models.attention_processor import (
|
||||||
AttnProcessor2_0,
|
AttnProcessor2_0,
|
||||||
LoRAAttnProcessor2_0,
|
LoRAAttnProcessor2_0,
|
||||||
@@ -19,10 +19,11 @@ from diffusers.models.attention_processor import (
|
|||||||
)
|
)
|
||||||
from diffusers.schedulers import DPMSolverSDEScheduler
|
from diffusers.schedulers import DPMSolverSDEScheduler
|
||||||
from diffusers.schedulers import SchedulerMixin as Scheduler
|
from diffusers.schedulers import SchedulerMixin as Scheduler
|
||||||
from pydantic import field_validator
|
from pydantic import validator
|
||||||
from torchvision.transforms.functional import resize as tv_resize
|
from torchvision.transforms.functional import resize as tv_resize
|
||||||
|
|
||||||
from invokeai.app.invocations.ip_adapter import IPAdapterField
|
from invokeai.app.invocations.ip_adapter import IPAdapterField
|
||||||
|
from invokeai.app.invocations.metadata import CoreMetadata
|
||||||
from invokeai.app.invocations.primitives import (
|
from invokeai.app.invocations.primitives import (
|
||||||
DenoiseMaskField,
|
DenoiseMaskField,
|
||||||
DenoiseMaskOutput,
|
DenoiseMaskOutput,
|
||||||
@@ -33,8 +34,6 @@ from invokeai.app.invocations.primitives import (
|
|||||||
build_latents_output,
|
build_latents_output,
|
||||||
)
|
)
|
||||||
from invokeai.app.invocations.t2i_adapter import T2IAdapterField
|
from invokeai.app.invocations.t2i_adapter import T2IAdapterField
|
||||||
from invokeai.app.services.image_records.image_records_common import ImageCategory, ResourceOrigin
|
|
||||||
from invokeai.app.shared.fields import FieldDescriptions
|
|
||||||
from invokeai.app.util.controlnet_utils import prepare_control_image
|
from invokeai.app.util.controlnet_utils import prepare_control_image
|
||||||
from invokeai.app.util.step_callback import stable_diffusion_step_callback
|
from invokeai.app.util.step_callback import stable_diffusion_step_callback
|
||||||
from invokeai.backend.ip_adapter.ip_adapter import IPAdapter, IPAdapterPlus
|
from invokeai.backend.ip_adapter.ip_adapter import IPAdapter, IPAdapterPlus
|
||||||
@@ -55,16 +54,16 @@ from ...backend.stable_diffusion.diffusers_pipeline import (
|
|||||||
from ...backend.stable_diffusion.diffusion.shared_invokeai_diffusion import PostprocessingSettings
|
from ...backend.stable_diffusion.diffusion.shared_invokeai_diffusion import PostprocessingSettings
|
||||||
from ...backend.stable_diffusion.schedulers import SCHEDULER_MAP
|
from ...backend.stable_diffusion.schedulers import SCHEDULER_MAP
|
||||||
from ...backend.util.devices import choose_precision, choose_torch_device
|
from ...backend.util.devices import choose_precision, choose_torch_device
|
||||||
|
from ..models.image import ImageCategory, ResourceOrigin
|
||||||
from .baseinvocation import (
|
from .baseinvocation import (
|
||||||
BaseInvocation,
|
BaseInvocation,
|
||||||
BaseInvocationOutput,
|
BaseInvocationOutput,
|
||||||
|
FieldDescriptions,
|
||||||
Input,
|
Input,
|
||||||
InputField,
|
InputField,
|
||||||
InvocationContext,
|
InvocationContext,
|
||||||
OutputField,
|
OutputField,
|
||||||
UIType,
|
UIType,
|
||||||
WithMetadata,
|
|
||||||
WithWorkflow,
|
|
||||||
invocation,
|
invocation,
|
||||||
invocation_output,
|
invocation_output,
|
||||||
)
|
)
|
||||||
@@ -77,7 +76,7 @@ if choose_torch_device() == torch.device("mps"):
|
|||||||
|
|
||||||
DEFAULT_PRECISION = choose_precision(choose_torch_device())
|
DEFAULT_PRECISION = choose_precision(choose_torch_device())
|
||||||
|
|
||||||
SAMPLER_NAME_VALUES = Literal[tuple(SCHEDULER_MAP.keys())]
|
SAMPLER_NAME_VALUES = Literal[tuple(list(SCHEDULER_MAP.keys()))]
|
||||||
|
|
||||||
|
|
||||||
@invocation_output("scheduler_output")
|
@invocation_output("scheduler_output")
|
||||||
@@ -85,20 +84,12 @@ class SchedulerOutput(BaseInvocationOutput):
|
|||||||
scheduler: SAMPLER_NAME_VALUES = OutputField(description=FieldDescriptions.scheduler, ui_type=UIType.Scheduler)
|
scheduler: SAMPLER_NAME_VALUES = OutputField(description=FieldDescriptions.scheduler, ui_type=UIType.Scheduler)
|
||||||
|
|
||||||
|
|
||||||
@invocation(
|
@invocation("scheduler", title="Scheduler", tags=["scheduler"], category="latents", version="1.0.0")
|
||||||
"scheduler",
|
|
||||||
title="Scheduler",
|
|
||||||
tags=["scheduler"],
|
|
||||||
category="latents",
|
|
||||||
version="1.0.0",
|
|
||||||
)
|
|
||||||
class SchedulerInvocation(BaseInvocation):
|
class SchedulerInvocation(BaseInvocation):
|
||||||
"""Selects a scheduler."""
|
"""Selects a scheduler."""
|
||||||
|
|
||||||
scheduler: SAMPLER_NAME_VALUES = InputField(
|
scheduler: SAMPLER_NAME_VALUES = InputField(
|
||||||
default="euler",
|
default="euler", description=FieldDescriptions.scheduler, ui_type=UIType.Scheduler
|
||||||
description=FieldDescriptions.scheduler,
|
|
||||||
ui_type=UIType.Scheduler,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def invoke(self, context: InvocationContext) -> SchedulerOutput:
|
def invoke(self, context: InvocationContext) -> SchedulerOutput:
|
||||||
@@ -106,11 +97,7 @@ class SchedulerInvocation(BaseInvocation):
|
|||||||
|
|
||||||
|
|
||||||
@invocation(
|
@invocation(
|
||||||
"create_denoise_mask",
|
"create_denoise_mask", title="Create Denoise Mask", tags=["mask", "denoise"], category="latents", version="1.0.0"
|
||||||
title="Create Denoise Mask",
|
|
||||||
tags=["mask", "denoise"],
|
|
||||||
category="latents",
|
|
||||||
version="1.0.0",
|
|
||||||
)
|
)
|
||||||
class CreateDenoiseMaskInvocation(BaseInvocation):
|
class CreateDenoiseMaskInvocation(BaseInvocation):
|
||||||
"""Creates mask for denoising model run."""
|
"""Creates mask for denoising model run."""
|
||||||
@@ -119,11 +106,7 @@ class CreateDenoiseMaskInvocation(BaseInvocation):
|
|||||||
image: Optional[ImageField] = InputField(default=None, description="Image which will be masked", ui_order=1)
|
image: Optional[ImageField] = InputField(default=None, description="Image which will be masked", ui_order=1)
|
||||||
mask: ImageField = InputField(description="The mask to use when pasting", ui_order=2)
|
mask: ImageField = InputField(description="The mask to use when pasting", ui_order=2)
|
||||||
tiled: bool = InputField(default=False, description=FieldDescriptions.tiled, ui_order=3)
|
tiled: bool = InputField(default=False, description=FieldDescriptions.tiled, ui_order=3)
|
||||||
fp32: bool = InputField(
|
fp32: bool = InputField(default=DEFAULT_PRECISION == "float32", description=FieldDescriptions.fp32, ui_order=4)
|
||||||
default=DEFAULT_PRECISION == "float32",
|
|
||||||
description=FieldDescriptions.fp32,
|
|
||||||
ui_order=4,
|
|
||||||
)
|
|
||||||
|
|
||||||
def prep_mask_tensor(self, mask_image):
|
def prep_mask_tensor(self, mask_image):
|
||||||
if mask_image.mode != "L":
|
if mask_image.mode != "L":
|
||||||
@@ -151,7 +134,7 @@ class CreateDenoiseMaskInvocation(BaseInvocation):
|
|||||||
|
|
||||||
if image is not None:
|
if image is not None:
|
||||||
vae_info = context.services.model_manager.get_model(
|
vae_info = context.services.model_manager.get_model(
|
||||||
**self.vae.vae.model_dump(),
|
**self.vae.vae.dict(),
|
||||||
context=context,
|
context=context,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -184,7 +167,7 @@ def get_scheduler(
|
|||||||
) -> Scheduler:
|
) -> Scheduler:
|
||||||
scheduler_class, scheduler_extra_config = SCHEDULER_MAP.get(scheduler_name, SCHEDULER_MAP["ddim"])
|
scheduler_class, scheduler_extra_config = SCHEDULER_MAP.get(scheduler_name, SCHEDULER_MAP["ddim"])
|
||||||
orig_scheduler_info = context.services.model_manager.get_model(
|
orig_scheduler_info = context.services.model_manager.get_model(
|
||||||
**scheduler_info.model_dump(),
|
**scheduler_info.dict(),
|
||||||
context=context,
|
context=context,
|
||||||
)
|
)
|
||||||
with orig_scheduler_info as orig_scheduler:
|
with orig_scheduler_info as orig_scheduler:
|
||||||
@@ -215,7 +198,7 @@ def get_scheduler(
|
|||||||
title="Denoise Latents",
|
title="Denoise Latents",
|
||||||
tags=["latents", "denoise", "txt2img", "t2i", "t2l", "img2img", "i2i", "l2l"],
|
tags=["latents", "denoise", "txt2img", "t2i", "t2l", "img2img", "i2i", "l2l"],
|
||||||
category="latents",
|
category="latents",
|
||||||
version="1.5.0",
|
version="1.3.0",
|
||||||
)
|
)
|
||||||
class DenoiseLatentsInvocation(BaseInvocation):
|
class DenoiseLatentsInvocation(BaseInvocation):
|
||||||
"""Denoises noisy latents to decodable images"""
|
"""Denoises noisy latents to decodable images"""
|
||||||
@@ -226,70 +209,34 @@ class DenoiseLatentsInvocation(BaseInvocation):
|
|||||||
negative_conditioning: ConditioningField = InputField(
|
negative_conditioning: ConditioningField = InputField(
|
||||||
description=FieldDescriptions.negative_cond, input=Input.Connection, ui_order=1
|
description=FieldDescriptions.negative_cond, input=Input.Connection, ui_order=1
|
||||||
)
|
)
|
||||||
noise: Optional[LatentsField] = InputField(
|
noise: Optional[LatentsField] = InputField(description=FieldDescriptions.noise, input=Input.Connection, ui_order=3)
|
||||||
default=None,
|
|
||||||
description=FieldDescriptions.noise,
|
|
||||||
input=Input.Connection,
|
|
||||||
ui_order=3,
|
|
||||||
)
|
|
||||||
steps: int = InputField(default=10, gt=0, description=FieldDescriptions.steps)
|
steps: int = InputField(default=10, gt=0, description=FieldDescriptions.steps)
|
||||||
cfg_scale: Union[float, List[float]] = InputField(
|
cfg_scale: Union[float, List[float]] = InputField(
|
||||||
default=7.5, ge=1, description=FieldDescriptions.cfg_scale, title="CFG Scale"
|
default=7.5, ge=1, description=FieldDescriptions.cfg_scale, title="CFG Scale"
|
||||||
)
|
)
|
||||||
denoising_start: float = InputField(
|
denoising_start: float = InputField(default=0.0, ge=0, le=1, description=FieldDescriptions.denoising_start)
|
||||||
default=0.0,
|
|
||||||
ge=0,
|
|
||||||
le=1,
|
|
||||||
description=FieldDescriptions.denoising_start,
|
|
||||||
)
|
|
||||||
denoising_end: float = InputField(default=1.0, ge=0, le=1, description=FieldDescriptions.denoising_end)
|
denoising_end: float = InputField(default=1.0, ge=0, le=1, description=FieldDescriptions.denoising_end)
|
||||||
scheduler: SAMPLER_NAME_VALUES = InputField(
|
scheduler: SAMPLER_NAME_VALUES = InputField(
|
||||||
default="euler",
|
default="euler", description=FieldDescriptions.scheduler, ui_type=UIType.Scheduler
|
||||||
description=FieldDescriptions.scheduler,
|
|
||||||
ui_type=UIType.Scheduler,
|
|
||||||
)
|
)
|
||||||
unet: UNetField = InputField(
|
unet: UNetField = InputField(description=FieldDescriptions.unet, input=Input.Connection, title="UNet", ui_order=2)
|
||||||
description=FieldDescriptions.unet,
|
control: Union[ControlField, list[ControlField]] = InputField(
|
||||||
input=Input.Connection,
|
|
||||||
title="UNet",
|
|
||||||
ui_order=2,
|
|
||||||
)
|
|
||||||
control: Optional[Union[ControlField, list[ControlField]]] = InputField(
|
|
||||||
default=None,
|
default=None,
|
||||||
input=Input.Connection,
|
input=Input.Connection,
|
||||||
ui_order=5,
|
ui_order=5,
|
||||||
)
|
)
|
||||||
ip_adapter: Optional[Union[IPAdapterField, list[IPAdapterField]]] = InputField(
|
ip_adapter: Optional[Union[IPAdapterField, list[IPAdapterField]]] = InputField(
|
||||||
description=FieldDescriptions.ip_adapter,
|
description=FieldDescriptions.ip_adapter, title="IP-Adapter", default=None, input=Input.Connection, ui_order=6
|
||||||
title="IP-Adapter",
|
|
||||||
default=None,
|
|
||||||
input=Input.Connection,
|
|
||||||
ui_order=6,
|
|
||||||
)
|
)
|
||||||
t2i_adapter: Optional[Union[T2IAdapterField, list[T2IAdapterField]]] = InputField(
|
t2i_adapter: Union[T2IAdapterField, list[T2IAdapterField]] = InputField(
|
||||||
description=FieldDescriptions.t2i_adapter,
|
description=FieldDescriptions.t2i_adapter, title="T2I-Adapter", default=None, input=Input.Connection, ui_order=7
|
||||||
title="T2I-Adapter",
|
|
||||||
default=None,
|
|
||||||
input=Input.Connection,
|
|
||||||
ui_order=7,
|
|
||||||
)
|
|
||||||
cfg_rescale_multiplier: float = InputField(
|
|
||||||
default=0, ge=0, lt=1, description=FieldDescriptions.cfg_rescale_multiplier
|
|
||||||
)
|
|
||||||
latents: Optional[LatentsField] = InputField(
|
|
||||||
default=None,
|
|
||||||
description=FieldDescriptions.latents,
|
|
||||||
input=Input.Connection,
|
|
||||||
ui_order=4,
|
|
||||||
)
|
)
|
||||||
|
latents: Optional[LatentsField] = InputField(description=FieldDescriptions.latents, input=Input.Connection)
|
||||||
denoise_mask: Optional[DenoiseMaskField] = InputField(
|
denoise_mask: Optional[DenoiseMaskField] = InputField(
|
||||||
default=None,
|
default=None, description=FieldDescriptions.mask, input=Input.Connection, ui_order=8
|
||||||
description=FieldDescriptions.mask,
|
|
||||||
input=Input.Connection,
|
|
||||||
ui_order=8,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@field_validator("cfg_scale")
|
@validator("cfg_scale")
|
||||||
def ge_one(cls, v):
|
def ge_one(cls, v):
|
||||||
"""validate that all cfg_scale values are >= 1"""
|
"""validate that all cfg_scale values are >= 1"""
|
||||||
if isinstance(v, list):
|
if isinstance(v, list):
|
||||||
@@ -312,7 +259,7 @@ class DenoiseLatentsInvocation(BaseInvocation):
|
|||||||
stable_diffusion_step_callback(
|
stable_diffusion_step_callback(
|
||||||
context=context,
|
context=context,
|
||||||
intermediate_state=intermediate_state,
|
intermediate_state=intermediate_state,
|
||||||
node=self.model_dump(),
|
node=self.dict(),
|
||||||
source_node_id=source_node_id,
|
source_node_id=source_node_id,
|
||||||
base_model=base_model,
|
base_model=base_model,
|
||||||
)
|
)
|
||||||
@@ -335,7 +282,6 @@ class DenoiseLatentsInvocation(BaseInvocation):
|
|||||||
unconditioned_embeddings=uc,
|
unconditioned_embeddings=uc,
|
||||||
text_embeddings=c,
|
text_embeddings=c,
|
||||||
guidance_scale=self.cfg_scale,
|
guidance_scale=self.cfg_scale,
|
||||||
guidance_rescale_multiplier=self.cfg_rescale_multiplier,
|
|
||||||
extra=extra_conditioning_info,
|
extra=extra_conditioning_info,
|
||||||
postprocessing_settings=PostprocessingSettings(
|
postprocessing_settings=PostprocessingSettings(
|
||||||
threshold=0.0, # threshold,
|
threshold=0.0, # threshold,
|
||||||
@@ -499,21 +445,15 @@ class DenoiseLatentsInvocation(BaseInvocation):
|
|||||||
context=context,
|
context=context,
|
||||||
)
|
)
|
||||||
|
|
||||||
# `single_ip_adapter.image` could be a list or a single ImageField. Normalize to a list here.
|
input_image = context.services.images.get_pil_image(single_ip_adapter.image.image_name)
|
||||||
single_ipa_images = single_ip_adapter.image
|
|
||||||
if not isinstance(single_ipa_images, list):
|
|
||||||
single_ipa_images = [single_ipa_images]
|
|
||||||
|
|
||||||
single_ipa_images = [context.services.images.get_pil_image(image.image_name) for image in single_ipa_images]
|
|
||||||
|
|
||||||
# TODO(ryand): With some effort, the step of running the CLIP Vision encoder could be done before any other
|
# TODO(ryand): With some effort, the step of running the CLIP Vision encoder could be done before any other
|
||||||
# models are needed in memory. This would help to reduce peak memory utilization in low-memory environments.
|
# models are needed in memory. This would help to reduce peak memory utilization in low-memory environments.
|
||||||
with image_encoder_model_info as image_encoder_model:
|
with image_encoder_model_info as image_encoder_model:
|
||||||
# Get image embeddings from CLIP and ImageProjModel.
|
# Get image embeddings from CLIP and ImageProjModel.
|
||||||
image_prompt_embeds, uncond_image_prompt_embeds = ip_adapter_model.get_image_embeds(
|
image_prompt_embeds, uncond_image_prompt_embeds = ip_adapter_model.get_image_embeds(
|
||||||
single_ipa_images, image_encoder_model
|
input_image, image_encoder_model
|
||||||
)
|
)
|
||||||
|
|
||||||
conditioning_data.ip_adapter_conditioning.append(
|
conditioning_data.ip_adapter_conditioning.append(
|
||||||
IPAdapterConditioningInfo(image_prompt_embeds, uncond_image_prompt_embeds)
|
IPAdapterConditioningInfo(image_prompt_embeds, uncond_image_prompt_embeds)
|
||||||
)
|
)
|
||||||
@@ -569,6 +509,10 @@ class DenoiseLatentsInvocation(BaseInvocation):
|
|||||||
t2i_adapter_model: T2IAdapter
|
t2i_adapter_model: T2IAdapter
|
||||||
with t2i_adapter_model_info as t2i_adapter_model:
|
with t2i_adapter_model_info as t2i_adapter_model:
|
||||||
total_downscale_factor = t2i_adapter_model.total_downscale_factor
|
total_downscale_factor = t2i_adapter_model.total_downscale_factor
|
||||||
|
if isinstance(t2i_adapter_model.adapter, FullAdapterXL):
|
||||||
|
# HACK(ryand): Work around a bug in FullAdapterXL. This is being addressed upstream in diffusers by
|
||||||
|
# this PR: https://github.com/huggingface/diffusers/pull/5134.
|
||||||
|
total_downscale_factor = total_downscale_factor // 2
|
||||||
|
|
||||||
# Resize the T2I-Adapter input image.
|
# Resize the T2I-Adapter input image.
|
||||||
# We select the resize dimensions so that after the T2I-Adapter's total_downscale_factor is applied, the
|
# We select the resize dimensions so that after the T2I-Adapter's total_downscale_factor is applied, the
|
||||||
@@ -684,10 +628,7 @@ class DenoiseLatentsInvocation(BaseInvocation):
|
|||||||
# TODO(ryand): I have hard-coded `do_classifier_free_guidance=True` to mirror the behaviour of ControlNets,
|
# TODO(ryand): I have hard-coded `do_classifier_free_guidance=True` to mirror the behaviour of ControlNets,
|
||||||
# below. Investigate whether this is appropriate.
|
# below. Investigate whether this is appropriate.
|
||||||
t2i_adapter_data = self.run_t2i_adapters(
|
t2i_adapter_data = self.run_t2i_adapters(
|
||||||
context,
|
context, self.t2i_adapter, latents.shape, do_classifier_free_guidance=True
|
||||||
self.t2i_adapter,
|
|
||||||
latents.shape,
|
|
||||||
do_classifier_free_guidance=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Get the source node id (we are invoking the prepared node)
|
# Get the source node id (we are invoking the prepared node)
|
||||||
@@ -700,7 +641,7 @@ class DenoiseLatentsInvocation(BaseInvocation):
|
|||||||
def _lora_loader():
|
def _lora_loader():
|
||||||
for lora in self.unet.loras:
|
for lora in self.unet.loras:
|
||||||
lora_info = context.services.model_manager.get_model(
|
lora_info = context.services.model_manager.get_model(
|
||||||
**lora.model_dump(exclude={"weight"}),
|
**lora.dict(exclude={"weight"}),
|
||||||
context=context,
|
context=context,
|
||||||
)
|
)
|
||||||
yield (lora_info.context.model, lora.weight)
|
yield (lora_info.context.model, lora.weight)
|
||||||
@@ -708,16 +649,14 @@ class DenoiseLatentsInvocation(BaseInvocation):
|
|||||||
return
|
return
|
||||||
|
|
||||||
unet_info = context.services.model_manager.get_model(
|
unet_info = context.services.model_manager.get_model(
|
||||||
**self.unet.unet.model_dump(),
|
**self.unet.unet.dict(),
|
||||||
context=context,
|
context=context,
|
||||||
)
|
)
|
||||||
with (
|
with (
|
||||||
ExitStack() as exit_stack,
|
ExitStack() as exit_stack,
|
||||||
ModelPatcher.apply_freeu(unet_info.context.model, self.unet.freeu_config),
|
ModelPatcher.apply_lora_unet(unet_info.context.model, _lora_loader()),
|
||||||
set_seamless(unet_info.context.model, self.unet.seamless_axes),
|
set_seamless(unet_info.context.model, self.unet.seamless_axes),
|
||||||
unet_info as unet,
|
unet_info as unet,
|
||||||
# Apply the LoRA after unet has been moved to its target device for faster patching.
|
|
||||||
ModelPatcher.apply_lora_unet(unet, _lora_loader()),
|
|
||||||
):
|
):
|
||||||
latents = latents.to(device=unet.device, dtype=unet.dtype)
|
latents = latents.to(device=unet.device, dtype=unet.dtype)
|
||||||
if noise is not None:
|
if noise is not None:
|
||||||
@@ -761,10 +700,7 @@ class DenoiseLatentsInvocation(BaseInvocation):
|
|||||||
denoising_end=self.denoising_end,
|
denoising_end=self.denoising_end,
|
||||||
)
|
)
|
||||||
|
|
||||||
(
|
result_latents, result_attention_map_saver = pipeline.latents_from_embeddings(
|
||||||
result_latents,
|
|
||||||
result_attention_map_saver,
|
|
||||||
) = pipeline.latents_from_embeddings(
|
|
||||||
latents=latents,
|
latents=latents,
|
||||||
timesteps=timesteps,
|
timesteps=timesteps,
|
||||||
init_timestep=init_timestep,
|
init_timestep=init_timestep,
|
||||||
@@ -792,13 +728,9 @@ class DenoiseLatentsInvocation(BaseInvocation):
|
|||||||
|
|
||||||
|
|
||||||
@invocation(
|
@invocation(
|
||||||
"l2i",
|
"l2i", title="Latents to Image", tags=["latents", "image", "vae", "l2i"], category="latents", version="1.0.0"
|
||||||
title="Latents to Image",
|
|
||||||
tags=["latents", "image", "vae", "l2i"],
|
|
||||||
category="latents",
|
|
||||||
version="1.1.0",
|
|
||||||
)
|
)
|
||||||
class LatentsToImageInvocation(BaseInvocation, WithMetadata, WithWorkflow):
|
class LatentsToImageInvocation(BaseInvocation):
|
||||||
"""Generates an image from latents."""
|
"""Generates an image from latents."""
|
||||||
|
|
||||||
latents: LatentsField = InputField(
|
latents: LatentsField = InputField(
|
||||||
@@ -811,13 +743,18 @@ class LatentsToImageInvocation(BaseInvocation, WithMetadata, WithWorkflow):
|
|||||||
)
|
)
|
||||||
tiled: bool = InputField(default=False, description=FieldDescriptions.tiled)
|
tiled: bool = InputField(default=False, description=FieldDescriptions.tiled)
|
||||||
fp32: bool = InputField(default=DEFAULT_PRECISION == "float32", description=FieldDescriptions.fp32)
|
fp32: bool = InputField(default=DEFAULT_PRECISION == "float32", description=FieldDescriptions.fp32)
|
||||||
|
metadata: CoreMetadata = InputField(
|
||||||
|
default=None,
|
||||||
|
description=FieldDescriptions.core_metadata,
|
||||||
|
ui_hidden=True,
|
||||||
|
)
|
||||||
|
|
||||||
@torch.no_grad()
|
@torch.no_grad()
|
||||||
def invoke(self, context: InvocationContext) -> ImageOutput:
|
def invoke(self, context: InvocationContext) -> ImageOutput:
|
||||||
latents = context.services.latents.get(self.latents.latents_name)
|
latents = context.services.latents.get(self.latents.latents_name)
|
||||||
|
|
||||||
vae_info = context.services.model_manager.get_model(
|
vae_info = context.services.model_manager.get_model(
|
||||||
**self.vae.vae.model_dump(),
|
**self.vae.vae.dict(),
|
||||||
context=context,
|
context=context,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -879,7 +816,7 @@ class LatentsToImageInvocation(BaseInvocation, WithMetadata, WithWorkflow):
|
|||||||
node_id=self.id,
|
node_id=self.id,
|
||||||
session_id=context.graph_execution_state_id,
|
session_id=context.graph_execution_state_id,
|
||||||
is_intermediate=self.is_intermediate,
|
is_intermediate=self.is_intermediate,
|
||||||
metadata=self.metadata,
|
metadata=self.metadata.dict() if self.metadata else None,
|
||||||
workflow=self.workflow,
|
workflow=self.workflow,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -893,13 +830,7 @@ class LatentsToImageInvocation(BaseInvocation, WithMetadata, WithWorkflow):
|
|||||||
LATENTS_INTERPOLATION_MODE = Literal["nearest", "linear", "bilinear", "bicubic", "trilinear", "area", "nearest-exact"]
|
LATENTS_INTERPOLATION_MODE = Literal["nearest", "linear", "bilinear", "bicubic", "trilinear", "area", "nearest-exact"]
|
||||||
|
|
||||||
|
|
||||||
@invocation(
|
@invocation("lresize", title="Resize Latents", tags=["latents", "resize"], category="latents", version="1.0.0")
|
||||||
"lresize",
|
|
||||||
title="Resize Latents",
|
|
||||||
tags=["latents", "resize"],
|
|
||||||
category="latents",
|
|
||||||
version="1.0.0",
|
|
||||||
)
|
|
||||||
class ResizeLatentsInvocation(BaseInvocation):
|
class ResizeLatentsInvocation(BaseInvocation):
|
||||||
"""Resizes latents to explicit width/height (in pixels). Provided dimensions are floor-divided by 8."""
|
"""Resizes latents to explicit width/height (in pixels). Provided dimensions are floor-divided by 8."""
|
||||||
|
|
||||||
@@ -945,13 +876,7 @@ class ResizeLatentsInvocation(BaseInvocation):
|
|||||||
return build_latents_output(latents_name=name, latents=resized_latents, seed=self.latents.seed)
|
return build_latents_output(latents_name=name, latents=resized_latents, seed=self.latents.seed)
|
||||||
|
|
||||||
|
|
||||||
@invocation(
|
@invocation("lscale", title="Scale Latents", tags=["latents", "resize"], category="latents", version="1.0.0")
|
||||||
"lscale",
|
|
||||||
title="Scale Latents",
|
|
||||||
tags=["latents", "resize"],
|
|
||||||
category="latents",
|
|
||||||
version="1.0.0",
|
|
||||||
)
|
|
||||||
class ScaleLatentsInvocation(BaseInvocation):
|
class ScaleLatentsInvocation(BaseInvocation):
|
||||||
"""Scales latents by a given factor."""
|
"""Scales latents by a given factor."""
|
||||||
|
|
||||||
@@ -990,11 +915,7 @@ class ScaleLatentsInvocation(BaseInvocation):
|
|||||||
|
|
||||||
|
|
||||||
@invocation(
|
@invocation(
|
||||||
"i2l",
|
"i2l", title="Image to Latents", tags=["latents", "image", "vae", "i2l"], category="latents", version="1.0.0"
|
||||||
title="Image to Latents",
|
|
||||||
tags=["latents", "image", "vae", "i2l"],
|
|
||||||
category="latents",
|
|
||||||
version="1.0.0",
|
|
||||||
)
|
)
|
||||||
class ImageToLatentsInvocation(BaseInvocation):
|
class ImageToLatentsInvocation(BaseInvocation):
|
||||||
"""Encodes an image into latents."""
|
"""Encodes an image into latents."""
|
||||||
@@ -1058,7 +979,7 @@ class ImageToLatentsInvocation(BaseInvocation):
|
|||||||
image = context.services.images.get_pil_image(self.image.image_name)
|
image = context.services.images.get_pil_image(self.image.image_name)
|
||||||
|
|
||||||
vae_info = context.services.model_manager.get_model(
|
vae_info = context.services.model_manager.get_model(
|
||||||
**self.vae.vae.model_dump(),
|
**self.vae.vae.dict(),
|
||||||
context=context,
|
context=context,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1086,13 +1007,7 @@ class ImageToLatentsInvocation(BaseInvocation):
|
|||||||
return vae.encode(image_tensor).latents
|
return vae.encode(image_tensor).latents
|
||||||
|
|
||||||
|
|
||||||
@invocation(
|
@invocation("lblend", title="Blend Latents", tags=["latents", "blend"], category="latents", version="1.0.0")
|
||||||
"lblend",
|
|
||||||
title="Blend Latents",
|
|
||||||
tags=["latents", "blend"],
|
|
||||||
category="latents",
|
|
||||||
version="1.0.0",
|
|
||||||
)
|
|
||||||
class BlendLatentsInvocation(BaseInvocation):
|
class BlendLatentsInvocation(BaseInvocation):
|
||||||
"""Blend two latents using a given alpha. Latents must have same size."""
|
"""Blend two latents using a given alpha. Latents must have same size."""
|
||||||
|
|
||||||
@@ -1111,7 +1026,7 @@ class BlendLatentsInvocation(BaseInvocation):
|
|||||||
latents_b = context.services.latents.get(self.latents_b.latents_name)
|
latents_b = context.services.latents.get(self.latents_b.latents_name)
|
||||||
|
|
||||||
if latents_a.shape != latents_b.shape:
|
if latents_a.shape != latents_b.shape:
|
||||||
raise Exception("Latents to blend must be the same size.")
|
raise "Latents to blend must be the same size."
|
||||||
|
|
||||||
# TODO:
|
# TODO:
|
||||||
device = choose_torch_device()
|
device = choose_torch_device()
|
||||||
|
|||||||
@@ -3,12 +3,11 @@
|
|||||||
from typing import Literal
|
from typing import Literal
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from pydantic import ValidationInfo, field_validator
|
from pydantic import validator
|
||||||
|
|
||||||
from invokeai.app.invocations.primitives import FloatOutput, IntegerOutput
|
from invokeai.app.invocations.primitives import FloatOutput, IntegerOutput
|
||||||
from invokeai.app.shared.fields import FieldDescriptions
|
|
||||||
|
|
||||||
from .baseinvocation import BaseInvocation, InputField, InvocationContext, invocation
|
from .baseinvocation import BaseInvocation, FieldDescriptions, InputField, InvocationContext, invocation
|
||||||
|
|
||||||
|
|
||||||
@invocation("add", title="Add Integers", tags=["math", "add"], category="math", version="1.0.0")
|
@invocation("add", title="Add Integers", tags=["math", "add"], category="math", version="1.0.0")
|
||||||
@@ -73,14 +72,7 @@ class RandomIntInvocation(BaseInvocation):
|
|||||||
return IntegerOutput(value=np.random.randint(self.low, self.high))
|
return IntegerOutput(value=np.random.randint(self.low, self.high))
|
||||||
|
|
||||||
|
|
||||||
@invocation(
|
@invocation("rand_float", title="Random Float", tags=["math", "float", "random"], category="math", version="1.0.0")
|
||||||
"rand_float",
|
|
||||||
title="Random Float",
|
|
||||||
tags=["math", "float", "random"],
|
|
||||||
category="math",
|
|
||||||
version="1.0.1",
|
|
||||||
use_cache=False,
|
|
||||||
)
|
|
||||||
class RandomFloatInvocation(BaseInvocation):
|
class RandomFloatInvocation(BaseInvocation):
|
||||||
"""Outputs a single random float"""
|
"""Outputs a single random float"""
|
||||||
|
|
||||||
@@ -145,17 +137,17 @@ INTEGER_OPERATIONS = Literal[
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
INTEGER_OPERATIONS_LABELS = {
|
INTEGER_OPERATIONS_LABELS = dict(
|
||||||
"ADD": "Add A+B",
|
ADD="Add A+B",
|
||||||
"SUB": "Subtract A-B",
|
SUB="Subtract A-B",
|
||||||
"MUL": "Multiply A*B",
|
MUL="Multiply A*B",
|
||||||
"DIV": "Divide A/B",
|
DIV="Divide A/B",
|
||||||
"EXP": "Exponentiate A^B",
|
EXP="Exponentiate A^B",
|
||||||
"MOD": "Modulus A%B",
|
MOD="Modulus A%B",
|
||||||
"ABS": "Absolute Value of A",
|
ABS="Absolute Value of A",
|
||||||
"MIN": "Minimum(A,B)",
|
MIN="Minimum(A,B)",
|
||||||
"MAX": "Maximum(A,B)",
|
MAX="Maximum(A,B)",
|
||||||
}
|
)
|
||||||
|
|
||||||
|
|
||||||
@invocation(
|
@invocation(
|
||||||
@@ -183,16 +175,16 @@ class IntegerMathInvocation(BaseInvocation):
|
|||||||
operation: INTEGER_OPERATIONS = InputField(
|
operation: INTEGER_OPERATIONS = InputField(
|
||||||
default="ADD", description="The operation to perform", ui_choice_labels=INTEGER_OPERATIONS_LABELS
|
default="ADD", description="The operation to perform", ui_choice_labels=INTEGER_OPERATIONS_LABELS
|
||||||
)
|
)
|
||||||
a: int = InputField(default=1, description=FieldDescriptions.num_1)
|
a: int = InputField(default=0, description=FieldDescriptions.num_1)
|
||||||
b: int = InputField(default=1, description=FieldDescriptions.num_2)
|
b: int = InputField(default=0, description=FieldDescriptions.num_2)
|
||||||
|
|
||||||
@field_validator("b")
|
@validator("b")
|
||||||
def no_unrepresentable_results(cls, v: int, info: ValidationInfo):
|
def no_unrepresentable_results(cls, v, values):
|
||||||
if info.data["operation"] == "DIV" and v == 0:
|
if values["operation"] == "DIV" and v == 0:
|
||||||
raise ValueError("Cannot divide by zero")
|
raise ValueError("Cannot divide by zero")
|
||||||
elif info.data["operation"] == "MOD" and v == 0:
|
elif values["operation"] == "MOD" and v == 0:
|
||||||
raise ValueError("Cannot divide by zero")
|
raise ValueError("Cannot divide by zero")
|
||||||
elif info.data["operation"] == "EXP" and v < 0:
|
elif values["operation"] == "EXP" and v < 0:
|
||||||
raise ValueError("Result of exponentiation is not an integer")
|
raise ValueError("Result of exponentiation is not an integer")
|
||||||
return v
|
return v
|
||||||
|
|
||||||
@@ -231,17 +223,17 @@ FLOAT_OPERATIONS = Literal[
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
FLOAT_OPERATIONS_LABELS = {
|
FLOAT_OPERATIONS_LABELS = dict(
|
||||||
"ADD": "Add A+B",
|
ADD="Add A+B",
|
||||||
"SUB": "Subtract A-B",
|
SUB="Subtract A-B",
|
||||||
"MUL": "Multiply A*B",
|
MUL="Multiply A*B",
|
||||||
"DIV": "Divide A/B",
|
DIV="Divide A/B",
|
||||||
"EXP": "Exponentiate A^B",
|
EXP="Exponentiate A^B",
|
||||||
"ABS": "Absolute Value of A",
|
ABS="Absolute Value of A",
|
||||||
"SQRT": "Square Root of A",
|
SQRT="Square Root of A",
|
||||||
"MIN": "Minimum(A,B)",
|
MIN="Minimum(A,B)",
|
||||||
"MAX": "Maximum(A,B)",
|
MAX="Maximum(A,B)",
|
||||||
}
|
)
|
||||||
|
|
||||||
|
|
||||||
@invocation(
|
@invocation(
|
||||||
@@ -257,16 +249,16 @@ class FloatMathInvocation(BaseInvocation):
|
|||||||
operation: FLOAT_OPERATIONS = InputField(
|
operation: FLOAT_OPERATIONS = InputField(
|
||||||
default="ADD", description="The operation to perform", ui_choice_labels=FLOAT_OPERATIONS_LABELS
|
default="ADD", description="The operation to perform", ui_choice_labels=FLOAT_OPERATIONS_LABELS
|
||||||
)
|
)
|
||||||
a: float = InputField(default=1, description=FieldDescriptions.num_1)
|
a: float = InputField(default=0, description=FieldDescriptions.num_1)
|
||||||
b: float = InputField(default=1, description=FieldDescriptions.num_2)
|
b: float = InputField(default=0, description=FieldDescriptions.num_2)
|
||||||
|
|
||||||
@field_validator("b")
|
@validator("b")
|
||||||
def no_unrepresentable_results(cls, v: float, info: ValidationInfo):
|
def no_unrepresentable_results(cls, v, values):
|
||||||
if info.data["operation"] == "DIV" and v == 0:
|
if values["operation"] == "DIV" and v == 0:
|
||||||
raise ValueError("Cannot divide by zero")
|
raise ValueError("Cannot divide by zero")
|
||||||
elif info.data["operation"] == "EXP" and info.data["a"] == 0 and v < 0:
|
elif values["operation"] == "EXP" and values["a"] == 0 and v < 0:
|
||||||
raise ValueError("Cannot raise zero to a negative power")
|
raise ValueError("Cannot raise zero to a negative power")
|
||||||
elif info.data["operation"] == "EXP" and isinstance(info.data["a"] ** v, complex):
|
elif values["operation"] == "EXP" and type(values["a"] ** v) is complex:
|
||||||
raise ValueError("Root operation resulted in a complex number")
|
raise ValueError("Root operation resulted in a complex number")
|
||||||
return v
|
return v
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
from typing import Any, Literal, Optional, Union
|
from typing import Optional
|
||||||
|
|
||||||
from pydantic import BaseModel, ConfigDict, Field
|
from pydantic import Field
|
||||||
|
|
||||||
from invokeai.app.invocations.baseinvocation import (
|
from invokeai.app.invocations.baseinvocation import (
|
||||||
BaseInvocation,
|
BaseInvocation,
|
||||||
BaseInvocationOutput,
|
BaseInvocationOutput,
|
||||||
InputField,
|
InputField,
|
||||||
InvocationContext,
|
InvocationContext,
|
||||||
MetadataField,
|
|
||||||
OutputField,
|
OutputField,
|
||||||
UIType,
|
|
||||||
invocation,
|
invocation,
|
||||||
invocation_output,
|
invocation_output,
|
||||||
)
|
)
|
||||||
@@ -18,105 +16,116 @@ from invokeai.app.invocations.ip_adapter import IPAdapterModelField
|
|||||||
from invokeai.app.invocations.model import LoRAModelField, MainModelField, VAEModelField
|
from invokeai.app.invocations.model import LoRAModelField, MainModelField, VAEModelField
|
||||||
from invokeai.app.invocations.primitives import ImageField
|
from invokeai.app.invocations.primitives import ImageField
|
||||||
from invokeai.app.invocations.t2i_adapter import T2IAdapterField
|
from invokeai.app.invocations.t2i_adapter import T2IAdapterField
|
||||||
from invokeai.app.shared.fields import FieldDescriptions
|
from invokeai.app.util.model_exclude_null import BaseModelExcludeNull
|
||||||
|
|
||||||
from ...version import __version__
|
from ...version import __version__
|
||||||
|
|
||||||
|
|
||||||
class MetadataItemField(BaseModel):
|
class LoRAMetadataField(BaseModelExcludeNull):
|
||||||
label: str = Field(description=FieldDescriptions.metadata_item_label)
|
"""LoRA metadata for an image generated in InvokeAI."""
|
||||||
value: Any = Field(description=FieldDescriptions.metadata_item_value)
|
|
||||||
|
lora: LoRAModelField = Field(description="The LoRA model")
|
||||||
|
weight: float = Field(description="The weight of the LoRA model")
|
||||||
|
|
||||||
|
|
||||||
class LoRAMetadataField(BaseModel):
|
class IPAdapterMetadataField(BaseModelExcludeNull):
|
||||||
"""LoRA Metadata Field"""
|
|
||||||
|
|
||||||
lora: LoRAModelField = Field(description=FieldDescriptions.lora_model)
|
|
||||||
weight: float = Field(description=FieldDescriptions.lora_weight)
|
|
||||||
|
|
||||||
|
|
||||||
class IPAdapterMetadataField(BaseModel):
|
|
||||||
"""IP Adapter Field, minus the CLIP Vision Encoder model"""
|
|
||||||
|
|
||||||
image: ImageField = Field(description="The IP-Adapter image prompt.")
|
image: ImageField = Field(description="The IP-Adapter image prompt.")
|
||||||
ip_adapter_model: IPAdapterModelField = Field(
|
ip_adapter_model: IPAdapterModelField = Field(description="The IP-Adapter model to use.")
|
||||||
description="The IP-Adapter model.",
|
weight: float = Field(description="The weight of the IP-Adapter model")
|
||||||
|
begin_step_percent: float = Field(
|
||||||
|
default=0, ge=0, le=1, description="When the IP-Adapter is first applied (% of total steps)"
|
||||||
)
|
)
|
||||||
weight: Union[float, list[float]] = Field(
|
end_step_percent: float = Field(
|
||||||
description="The weight given to the IP-Adapter",
|
default=1, ge=0, le=1, description="When the IP-Adapter is last applied (% of total steps)"
|
||||||
)
|
|
||||||
begin_step_percent: float = Field(description="When the IP-Adapter is first applied (% of total steps)")
|
|
||||||
end_step_percent: float = Field(description="When the IP-Adapter is last applied (% of total steps)")
|
|
||||||
|
|
||||||
|
|
||||||
@invocation_output("metadata_item_output")
|
|
||||||
class MetadataItemOutput(BaseInvocationOutput):
|
|
||||||
"""Metadata Item Output"""
|
|
||||||
|
|
||||||
item: MetadataItemField = OutputField(description="Metadata Item")
|
|
||||||
|
|
||||||
|
|
||||||
@invocation("metadata_item", title="Metadata Item", tags=["metadata"], category="metadata", version="1.0.0")
|
|
||||||
class MetadataItemInvocation(BaseInvocation):
|
|
||||||
"""Used to create an arbitrary metadata item. Provide "label" and make a connection to "value" to store that data as the value."""
|
|
||||||
|
|
||||||
label: str = InputField(description=FieldDescriptions.metadata_item_label)
|
|
||||||
value: Any = InputField(description=FieldDescriptions.metadata_item_value, ui_type=UIType.Any)
|
|
||||||
|
|
||||||
def invoke(self, context: InvocationContext) -> MetadataItemOutput:
|
|
||||||
return MetadataItemOutput(item=MetadataItemField(label=self.label, value=self.value))
|
|
||||||
|
|
||||||
|
|
||||||
@invocation_output("metadata_output")
|
|
||||||
class MetadataOutput(BaseInvocationOutput):
|
|
||||||
metadata: MetadataField = OutputField(description="Metadata Dict")
|
|
||||||
|
|
||||||
|
|
||||||
@invocation("metadata", title="Metadata", tags=["metadata"], category="metadata", version="1.0.0")
|
|
||||||
class MetadataInvocation(BaseInvocation):
|
|
||||||
"""Takes a MetadataItem or collection of MetadataItems and outputs a MetadataDict."""
|
|
||||||
|
|
||||||
items: Union[list[MetadataItemField], MetadataItemField] = InputField(
|
|
||||||
description=FieldDescriptions.metadata_item_polymorphic
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def invoke(self, context: InvocationContext) -> MetadataOutput:
|
|
||||||
if isinstance(self.items, MetadataItemField):
|
|
||||||
# single metadata item
|
|
||||||
data = {self.items.label: self.items.value}
|
|
||||||
else:
|
|
||||||
# collection of metadata items
|
|
||||||
data = {item.label: item.value for item in self.items}
|
|
||||||
|
|
||||||
# add app version
|
class CoreMetadata(BaseModelExcludeNull):
|
||||||
data.update({"app_version": __version__})
|
"""Core generation metadata for an image generated in InvokeAI."""
|
||||||
return MetadataOutput(metadata=MetadataField.model_validate(data))
|
|
||||||
|
app_version: str = Field(default=__version__, description="The version of InvokeAI used to generate this image")
|
||||||
|
generation_mode: Optional[str] = Field(
|
||||||
|
default=None,
|
||||||
|
description="The generation mode that output this image",
|
||||||
|
)
|
||||||
|
created_by: Optional[str] = Field(description="The name of the creator of the image")
|
||||||
|
positive_prompt: Optional[str] = Field(default=None, description="The positive prompt parameter")
|
||||||
|
negative_prompt: Optional[str] = Field(default=None, description="The negative prompt parameter")
|
||||||
|
width: Optional[int] = Field(default=None, description="The width parameter")
|
||||||
|
height: Optional[int] = Field(default=None, description="The height parameter")
|
||||||
|
seed: Optional[int] = Field(default=None, description="The seed used for noise generation")
|
||||||
|
rand_device: Optional[str] = Field(default=None, description="The device used for random number generation")
|
||||||
|
cfg_scale: Optional[float] = Field(default=None, description="The classifier-free guidance scale parameter")
|
||||||
|
steps: Optional[int] = Field(default=None, description="The number of steps used for inference")
|
||||||
|
scheduler: Optional[str] = Field(default=None, description="The scheduler used for inference")
|
||||||
|
clip_skip: Optional[int] = Field(
|
||||||
|
default=None,
|
||||||
|
description="The number of skipped CLIP layers",
|
||||||
|
)
|
||||||
|
model: Optional[MainModelField] = Field(default=None, description="The main model used for inference")
|
||||||
|
controlnets: Optional[list[ControlField]] = Field(default=None, description="The ControlNets used for inference")
|
||||||
|
ipAdapters: Optional[list[IPAdapterMetadataField]] = Field(
|
||||||
|
default=None, description="The IP Adapters used for inference"
|
||||||
|
)
|
||||||
|
t2iAdapters: Optional[list[T2IAdapterField]] = Field(default=None, description="The IP Adapters used for inference")
|
||||||
|
loras: Optional[list[LoRAMetadataField]] = Field(default=None, description="The LoRAs used for inference")
|
||||||
|
vae: Optional[VAEModelField] = Field(
|
||||||
|
default=None,
|
||||||
|
description="The VAE used for decoding, if the main model's default was not used",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Latents-to-Latents
|
||||||
|
strength: Optional[float] = Field(
|
||||||
|
default=None,
|
||||||
|
description="The strength used for latents-to-latents",
|
||||||
|
)
|
||||||
|
init_image: Optional[str] = Field(default=None, description="The name of the initial image")
|
||||||
|
|
||||||
|
# SDXL
|
||||||
|
positive_style_prompt: Optional[str] = Field(default=None, description="The positive style prompt parameter")
|
||||||
|
negative_style_prompt: Optional[str] = Field(default=None, description="The negative style prompt parameter")
|
||||||
|
|
||||||
|
# SDXL Refiner
|
||||||
|
refiner_model: Optional[MainModelField] = Field(default=None, description="The SDXL Refiner model used")
|
||||||
|
refiner_cfg_scale: Optional[float] = Field(
|
||||||
|
default=None,
|
||||||
|
description="The classifier-free guidance scale parameter used for the refiner",
|
||||||
|
)
|
||||||
|
refiner_steps: Optional[int] = Field(default=None, description="The number of steps used for the refiner")
|
||||||
|
refiner_scheduler: Optional[str] = Field(default=None, description="The scheduler used for the refiner")
|
||||||
|
refiner_positive_aesthetic_score: Optional[float] = Field(
|
||||||
|
default=None, description="The aesthetic score used for the refiner"
|
||||||
|
)
|
||||||
|
refiner_negative_aesthetic_score: Optional[float] = Field(
|
||||||
|
default=None, description="The aesthetic score used for the refiner"
|
||||||
|
)
|
||||||
|
refiner_start: Optional[float] = Field(default=None, description="The start value used for refiner denoising")
|
||||||
|
|
||||||
|
|
||||||
@invocation("merge_metadata", title="Metadata Merge", tags=["metadata"], category="metadata", version="1.0.0")
|
class ImageMetadata(BaseModelExcludeNull):
|
||||||
class MergeMetadataInvocation(BaseInvocation):
|
"""An image's generation metadata"""
|
||||||
"""Merged a collection of MetadataDict into a single MetadataDict."""
|
|
||||||
|
|
||||||
collection: list[MetadataField] = InputField(description=FieldDescriptions.metadata_collection)
|
metadata: Optional[dict] = Field(
|
||||||
|
default=None,
|
||||||
def invoke(self, context: InvocationContext) -> MetadataOutput:
|
description="The image's core metadata, if it was created in the Linear or Canvas UI",
|
||||||
data = {}
|
)
|
||||||
for item in self.collection:
|
graph: Optional[dict] = Field(default=None, description="The graph that created the image")
|
||||||
data.update(item.model_dump())
|
|
||||||
|
|
||||||
return MetadataOutput(metadata=MetadataField.model_validate(data))
|
|
||||||
|
|
||||||
|
|
||||||
GENERATION_MODES = Literal[
|
@invocation_output("metadata_accumulator_output")
|
||||||
"txt2img", "img2img", "inpaint", "outpaint", "sdxl_txt2img", "sdxl_img2img", "sdxl_inpaint", "sdxl_outpaint"
|
class MetadataAccumulatorOutput(BaseInvocationOutput):
|
||||||
]
|
"""The output of the MetadataAccumulator node"""
|
||||||
|
|
||||||
|
metadata: CoreMetadata = OutputField(description="The core metadata for the image")
|
||||||
|
|
||||||
|
|
||||||
@invocation("core_metadata", title="Core Metadata", tags=["metadata"], category="metadata", version="1.0.1")
|
@invocation(
|
||||||
class CoreMetadataInvocation(BaseInvocation):
|
"metadata_accumulator", title="Metadata Accumulator", tags=["metadata"], category="metadata", version="1.0.0"
|
||||||
"""Collects core generation metadata into a MetadataField"""
|
)
|
||||||
|
class MetadataAccumulatorInvocation(BaseInvocation):
|
||||||
|
"""Outputs a Core Metadata Object"""
|
||||||
|
|
||||||
generation_mode: Optional[GENERATION_MODES] = InputField(
|
generation_mode: Optional[str] = InputField(
|
||||||
default=None,
|
default=None,
|
||||||
description="The generation mode that output this image",
|
description="The generation mode that output this image",
|
||||||
)
|
)
|
||||||
@@ -127,13 +136,8 @@ class CoreMetadataInvocation(BaseInvocation):
|
|||||||
seed: Optional[int] = InputField(default=None, description="The seed used for noise generation")
|
seed: Optional[int] = InputField(default=None, description="The seed used for noise generation")
|
||||||
rand_device: Optional[str] = InputField(default=None, description="The device used for random number generation")
|
rand_device: Optional[str] = InputField(default=None, description="The device used for random number generation")
|
||||||
cfg_scale: Optional[float] = InputField(default=None, description="The classifier-free guidance scale parameter")
|
cfg_scale: Optional[float] = InputField(default=None, description="The classifier-free guidance scale parameter")
|
||||||
cfg_rescale_multiplier: Optional[float] = InputField(
|
|
||||||
default=None, description=FieldDescriptions.cfg_rescale_multiplier
|
|
||||||
)
|
|
||||||
steps: Optional[int] = InputField(default=None, description="The number of steps used for inference")
|
steps: Optional[int] = InputField(default=None, description="The number of steps used for inference")
|
||||||
scheduler: Optional[str] = InputField(default=None, description="The scheduler used for inference")
|
scheduler: Optional[str] = InputField(default=None, description="The scheduler used for inference")
|
||||||
seamless_x: Optional[bool] = InputField(default=None, description="Whether seamless tiling was used on the X axis")
|
|
||||||
seamless_y: Optional[bool] = InputField(default=None, description="Whether seamless tiling was used on the Y axis")
|
|
||||||
clip_skip: Optional[int] = InputField(
|
clip_skip: Optional[int] = InputField(
|
||||||
default=None,
|
default=None,
|
||||||
description="The number of skipped CLIP layers",
|
description="The number of skipped CLIP layers",
|
||||||
@@ -163,14 +167,13 @@ class CoreMetadataInvocation(BaseInvocation):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# High resolution fix metadata.
|
# High resolution fix metadata.
|
||||||
hrf_enabled: Optional[bool] = InputField(
|
hrf_width: Optional[int] = InputField(
|
||||||
default=None,
|
default=None,
|
||||||
description="Whether or not high resolution fix was enabled.",
|
description="The high resolution fix height and width multipler.",
|
||||||
)
|
)
|
||||||
# TODO: should this be stricter or do we just let the UI handle it?
|
hrf_height: Optional[int] = InputField(
|
||||||
hrf_method: Optional[str] = InputField(
|
|
||||||
default=None,
|
default=None,
|
||||||
description="The high resolution fix upscale method.",
|
description="The high resolution fix height and width multipler.",
|
||||||
)
|
)
|
||||||
hrf_strength: Optional[float] = InputField(
|
hrf_strength: Optional[float] = InputField(
|
||||||
default=None,
|
default=None,
|
||||||
@@ -217,13 +220,7 @@ class CoreMetadataInvocation(BaseInvocation):
|
|||||||
description="The start value used for refiner denoising",
|
description="The start value used for refiner denoising",
|
||||||
)
|
)
|
||||||
|
|
||||||
def invoke(self, context: InvocationContext) -> MetadataOutput:
|
def invoke(self, context: InvocationContext) -> MetadataAccumulatorOutput:
|
||||||
"""Collects and outputs a CoreMetadata object"""
|
"""Collects and outputs a CoreMetadata object"""
|
||||||
|
|
||||||
return MetadataOutput(
|
return MetadataAccumulatorOutput(metadata=CoreMetadata(**self.dict()))
|
||||||
metadata=MetadataField.model_validate(
|
|
||||||
self.model_dump(exclude_none=True, exclude={"id", "type", "is_intermediate", "use_cache"})
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
model_config = ConfigDict(extra="allow")
|
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
import copy
|
import copy
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
from pydantic import BaseModel, ConfigDict, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
from invokeai.app.shared.fields import FieldDescriptions
|
|
||||||
from invokeai.app.shared.models import FreeUConfig
|
|
||||||
|
|
||||||
from ...backend.model_management import BaseModelType, ModelType, SubModelType
|
from ...backend.model_management import BaseModelType, ModelType, SubModelType
|
||||||
from .baseinvocation import (
|
from .baseinvocation import (
|
||||||
BaseInvocation,
|
BaseInvocation,
|
||||||
BaseInvocationOutput,
|
BaseInvocationOutput,
|
||||||
|
FieldDescriptions,
|
||||||
Input,
|
Input,
|
||||||
InputField,
|
InputField,
|
||||||
InvocationContext,
|
InvocationContext,
|
||||||
OutputField,
|
OutputField,
|
||||||
|
UIType,
|
||||||
invocation,
|
invocation,
|
||||||
invocation_output,
|
invocation_output,
|
||||||
)
|
)
|
||||||
@@ -25,8 +24,6 @@ class ModelInfo(BaseModel):
|
|||||||
model_type: ModelType = Field(description="Info to load submodel")
|
model_type: ModelType = Field(description="Info to load submodel")
|
||||||
submodel: Optional[SubModelType] = Field(default=None, description="Info to load submodel")
|
submodel: Optional[SubModelType] = Field(default=None, description="Info to load submodel")
|
||||||
|
|
||||||
model_config = ConfigDict(protected_namespaces=())
|
|
||||||
|
|
||||||
|
|
||||||
class LoraInfo(ModelInfo):
|
class LoraInfo(ModelInfo):
|
||||||
weight: float = Field(description="Lora's weight which to use when apply to model")
|
weight: float = Field(description="Lora's weight which to use when apply to model")
|
||||||
@@ -37,7 +34,6 @@ class UNetField(BaseModel):
|
|||||||
scheduler: ModelInfo = Field(description="Info to load scheduler submodel")
|
scheduler: ModelInfo = Field(description="Info to load scheduler submodel")
|
||||||
loras: List[LoraInfo] = Field(description="Loras to apply on model loading")
|
loras: List[LoraInfo] = Field(description="Loras to apply on model loading")
|
||||||
seamless_axes: List[str] = Field(default_factory=list, description='Axes("x" and "y") to which apply seamless')
|
seamless_axes: List[str] = Field(default_factory=list, description='Axes("x" and "y") to which apply seamless')
|
||||||
freeu_config: Optional[FreeUConfig] = Field(default=None, description="FreeU configuration")
|
|
||||||
|
|
||||||
|
|
||||||
class ClipField(BaseModel):
|
class ClipField(BaseModel):
|
||||||
@@ -53,32 +49,13 @@ class VaeField(BaseModel):
|
|||||||
seamless_axes: List[str] = Field(default_factory=list, description='Axes("x" and "y") to which apply seamless')
|
seamless_axes: List[str] = Field(default_factory=list, description='Axes("x" and "y") to which apply seamless')
|
||||||
|
|
||||||
|
|
||||||
@invocation_output("unet_output")
|
|
||||||
class UNetOutput(BaseInvocationOutput):
|
|
||||||
"""Base class for invocations that output a UNet field"""
|
|
||||||
|
|
||||||
unet: UNetField = OutputField(description=FieldDescriptions.unet, title="UNet")
|
|
||||||
|
|
||||||
|
|
||||||
@invocation_output("vae_output")
|
|
||||||
class VAEOutput(BaseInvocationOutput):
|
|
||||||
"""Base class for invocations that output a VAE field"""
|
|
||||||
|
|
||||||
vae: VaeField = OutputField(description=FieldDescriptions.vae, title="VAE")
|
|
||||||
|
|
||||||
|
|
||||||
@invocation_output("clip_output")
|
|
||||||
class CLIPOutput(BaseInvocationOutput):
|
|
||||||
"""Base class for invocations that output a CLIP field"""
|
|
||||||
|
|
||||||
clip: ClipField = OutputField(description=FieldDescriptions.clip, title="CLIP")
|
|
||||||
|
|
||||||
|
|
||||||
@invocation_output("model_loader_output")
|
@invocation_output("model_loader_output")
|
||||||
class ModelLoaderOutput(UNetOutput, CLIPOutput, VAEOutput):
|
class ModelLoaderOutput(BaseInvocationOutput):
|
||||||
"""Model loader output"""
|
"""Model loader output"""
|
||||||
|
|
||||||
pass
|
unet: UNetField = OutputField(description=FieldDescriptions.unet, title="UNet")
|
||||||
|
clip: ClipField = OutputField(description=FieldDescriptions.clip, title="CLIP")
|
||||||
|
vae: VaeField = OutputField(description=FieldDescriptions.vae, title="VAE")
|
||||||
|
|
||||||
|
|
||||||
class MainModelField(BaseModel):
|
class MainModelField(BaseModel):
|
||||||
@@ -88,8 +65,6 @@ class MainModelField(BaseModel):
|
|||||||
base_model: BaseModelType = Field(description="Base model")
|
base_model: BaseModelType = Field(description="Base model")
|
||||||
model_type: ModelType = Field(description="Model Type")
|
model_type: ModelType = Field(description="Model Type")
|
||||||
|
|
||||||
model_config = ConfigDict(protected_namespaces=())
|
|
||||||
|
|
||||||
|
|
||||||
class LoRAModelField(BaseModel):
|
class LoRAModelField(BaseModel):
|
||||||
"""LoRA model field"""
|
"""LoRA model field"""
|
||||||
@@ -97,16 +72,8 @@ class LoRAModelField(BaseModel):
|
|||||||
model_name: str = Field(description="Name of the LoRA model")
|
model_name: str = Field(description="Name of the LoRA model")
|
||||||
base_model: BaseModelType = Field(description="Base model")
|
base_model: BaseModelType = Field(description="Base model")
|
||||||
|
|
||||||
model_config = ConfigDict(protected_namespaces=())
|
|
||||||
|
|
||||||
|
@invocation("main_model_loader", title="Main Model", tags=["model"], category="model", version="1.0.0")
|
||||||
@invocation(
|
|
||||||
"main_model_loader",
|
|
||||||
title="Main Model",
|
|
||||||
tags=["model"],
|
|
||||||
category="model",
|
|
||||||
version="1.0.0",
|
|
||||||
)
|
|
||||||
class MainModelLoaderInvocation(BaseInvocation):
|
class MainModelLoaderInvocation(BaseInvocation):
|
||||||
"""Loads a main model, outputting its submodels."""
|
"""Loads a main model, outputting its submodels."""
|
||||||
|
|
||||||
@@ -213,16 +180,10 @@ class LoraLoaderInvocation(BaseInvocation):
|
|||||||
lora: LoRAModelField = InputField(description=FieldDescriptions.lora_model, input=Input.Direct, title="LoRA")
|
lora: LoRAModelField = InputField(description=FieldDescriptions.lora_model, input=Input.Direct, title="LoRA")
|
||||||
weight: float = InputField(default=0.75, description=FieldDescriptions.lora_weight)
|
weight: float = InputField(default=0.75, description=FieldDescriptions.lora_weight)
|
||||||
unet: Optional[UNetField] = InputField(
|
unet: Optional[UNetField] = InputField(
|
||||||
default=None,
|
default=None, description=FieldDescriptions.unet, input=Input.Connection, title="UNet"
|
||||||
description=FieldDescriptions.unet,
|
|
||||||
input=Input.Connection,
|
|
||||||
title="UNet",
|
|
||||||
)
|
)
|
||||||
clip: Optional[ClipField] = InputField(
|
clip: Optional[ClipField] = InputField(
|
||||||
default=None,
|
default=None, description=FieldDescriptions.clip, input=Input.Connection, title="CLIP"
|
||||||
description=FieldDescriptions.clip,
|
|
||||||
input=Input.Connection,
|
|
||||||
title="CLIP",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def invoke(self, context: InvocationContext) -> LoraLoaderOutput:
|
def invoke(self, context: InvocationContext) -> LoraLoaderOutput:
|
||||||
@@ -283,35 +244,20 @@ class SDXLLoraLoaderOutput(BaseInvocationOutput):
|
|||||||
clip2: Optional[ClipField] = OutputField(default=None, description=FieldDescriptions.clip, title="CLIP 2")
|
clip2: Optional[ClipField] = OutputField(default=None, description=FieldDescriptions.clip, title="CLIP 2")
|
||||||
|
|
||||||
|
|
||||||
@invocation(
|
@invocation("sdxl_lora_loader", title="SDXL LoRA", tags=["lora", "model"], category="model", version="1.0.0")
|
||||||
"sdxl_lora_loader",
|
|
||||||
title="SDXL LoRA",
|
|
||||||
tags=["lora", "model"],
|
|
||||||
category="model",
|
|
||||||
version="1.0.0",
|
|
||||||
)
|
|
||||||
class SDXLLoraLoaderInvocation(BaseInvocation):
|
class SDXLLoraLoaderInvocation(BaseInvocation):
|
||||||
"""Apply selected lora to unet and text_encoder."""
|
"""Apply selected lora to unet and text_encoder."""
|
||||||
|
|
||||||
lora: LoRAModelField = InputField(description=FieldDescriptions.lora_model, input=Input.Direct, title="LoRA")
|
lora: LoRAModelField = InputField(description=FieldDescriptions.lora_model, input=Input.Direct, title="LoRA")
|
||||||
weight: float = InputField(default=0.75, description=FieldDescriptions.lora_weight)
|
weight: float = InputField(default=0.75, description=FieldDescriptions.lora_weight)
|
||||||
unet: Optional[UNetField] = InputField(
|
unet: Optional[UNetField] = InputField(
|
||||||
default=None,
|
default=None, description=FieldDescriptions.unet, input=Input.Connection, title="UNet"
|
||||||
description=FieldDescriptions.unet,
|
|
||||||
input=Input.Connection,
|
|
||||||
title="UNet",
|
|
||||||
)
|
)
|
||||||
clip: Optional[ClipField] = InputField(
|
clip: Optional[ClipField] = InputField(
|
||||||
default=None,
|
default=None, description=FieldDescriptions.clip, input=Input.Connection, title="CLIP 1"
|
||||||
description=FieldDescriptions.clip,
|
|
||||||
input=Input.Connection,
|
|
||||||
title="CLIP 1",
|
|
||||||
)
|
)
|
||||||
clip2: Optional[ClipField] = InputField(
|
clip2: Optional[ClipField] = InputField(
|
||||||
default=None,
|
default=None, description=FieldDescriptions.clip, input=Input.Connection, title="CLIP 2"
|
||||||
description=FieldDescriptions.clip,
|
|
||||||
input=Input.Connection,
|
|
||||||
title="CLIP 2",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def invoke(self, context: InvocationContext) -> SDXLLoraLoaderOutput:
|
def invoke(self, context: InvocationContext) -> SDXLLoraLoaderOutput:
|
||||||
@@ -384,7 +330,12 @@ class VAEModelField(BaseModel):
|
|||||||
model_name: str = Field(description="Name of the model")
|
model_name: str = Field(description="Name of the model")
|
||||||
base_model: BaseModelType = Field(description="Base model")
|
base_model: BaseModelType = Field(description="Base model")
|
||||||
|
|
||||||
model_config = ConfigDict(protected_namespaces=())
|
|
||||||
|
@invocation_output("vae_loader_output")
|
||||||
|
class VaeLoaderOutput(BaseInvocationOutput):
|
||||||
|
"""VAE output"""
|
||||||
|
|
||||||
|
vae: VaeField = OutputField(description=FieldDescriptions.vae, title="VAE")
|
||||||
|
|
||||||
|
|
||||||
@invocation("vae_loader", title="VAE", tags=["vae", "model"], category="model", version="1.0.0")
|
@invocation("vae_loader", title="VAE", tags=["vae", "model"], category="model", version="1.0.0")
|
||||||
@@ -392,12 +343,10 @@ class VaeLoaderInvocation(BaseInvocation):
|
|||||||
"""Loads a VAE model, outputting a VaeLoaderOutput"""
|
"""Loads a VAE model, outputting a VaeLoaderOutput"""
|
||||||
|
|
||||||
vae_model: VAEModelField = InputField(
|
vae_model: VAEModelField = InputField(
|
||||||
description=FieldDescriptions.vae_model,
|
description=FieldDescriptions.vae_model, input=Input.Direct, ui_type=UIType.VaeModel, title="VAE"
|
||||||
input=Input.Direct,
|
|
||||||
title="VAE",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def invoke(self, context: InvocationContext) -> VAEOutput:
|
def invoke(self, context: InvocationContext) -> VaeLoaderOutput:
|
||||||
base_model = self.vae_model.base_model
|
base_model = self.vae_model.base_model
|
||||||
model_name = self.vae_model.model_name
|
model_name = self.vae_model.model_name
|
||||||
model_type = ModelType.Vae
|
model_type = ModelType.Vae
|
||||||
@@ -408,7 +357,7 @@ class VaeLoaderInvocation(BaseInvocation):
|
|||||||
model_type=model_type,
|
model_type=model_type,
|
||||||
):
|
):
|
||||||
raise Exception(f"Unkown vae name: {model_name}!")
|
raise Exception(f"Unkown vae name: {model_name}!")
|
||||||
return VAEOutput(
|
return VaeLoaderOutput(
|
||||||
vae=VaeField(
|
vae=VaeField(
|
||||||
vae=ModelInfo(
|
vae=ModelInfo(
|
||||||
model_name=model_name,
|
model_name=model_name,
|
||||||
@@ -423,31 +372,19 @@ class VaeLoaderInvocation(BaseInvocation):
|
|||||||
class SeamlessModeOutput(BaseInvocationOutput):
|
class SeamlessModeOutput(BaseInvocationOutput):
|
||||||
"""Modified Seamless Model output"""
|
"""Modified Seamless Model output"""
|
||||||
|
|
||||||
unet: Optional[UNetField] = OutputField(default=None, description=FieldDescriptions.unet, title="UNet")
|
unet: Optional[UNetField] = OutputField(description=FieldDescriptions.unet, title="UNet")
|
||||||
vae: Optional[VaeField] = OutputField(default=None, description=FieldDescriptions.vae, title="VAE")
|
vae: Optional[VaeField] = OutputField(description=FieldDescriptions.vae, title="VAE")
|
||||||
|
|
||||||
|
|
||||||
@invocation(
|
@invocation("seamless", title="Seamless", tags=["seamless", "model"], category="model", version="1.0.0")
|
||||||
"seamless",
|
|
||||||
title="Seamless",
|
|
||||||
tags=["seamless", "model"],
|
|
||||||
category="model",
|
|
||||||
version="1.0.0",
|
|
||||||
)
|
|
||||||
class SeamlessModeInvocation(BaseInvocation):
|
class SeamlessModeInvocation(BaseInvocation):
|
||||||
"""Applies the seamless transformation to the Model UNet and VAE."""
|
"""Applies the seamless transformation to the Model UNet and VAE."""
|
||||||
|
|
||||||
unet: Optional[UNetField] = InputField(
|
unet: Optional[UNetField] = InputField(
|
||||||
default=None,
|
default=None, description=FieldDescriptions.unet, input=Input.Connection, title="UNet"
|
||||||
description=FieldDescriptions.unet,
|
|
||||||
input=Input.Connection,
|
|
||||||
title="UNet",
|
|
||||||
)
|
)
|
||||||
vae: Optional[VaeField] = InputField(
|
vae: Optional[VaeField] = InputField(
|
||||||
default=None,
|
default=None, description=FieldDescriptions.vae_model, input=Input.Connection, title="VAE"
|
||||||
description=FieldDescriptions.vae_model,
|
|
||||||
input=Input.Connection,
|
|
||||||
title="VAE",
|
|
||||||
)
|
)
|
||||||
seamless_y: bool = InputField(default=True, input=Input.Any, description="Specify whether Y axis is seamless")
|
seamless_y: bool = InputField(default=True, input=Input.Any, description="Specify whether Y axis is seamless")
|
||||||
seamless_x: bool = InputField(default=True, input=Input.Any, description="Specify whether X axis is seamless")
|
seamless_x: bool = InputField(default=True, input=Input.Any, description="Specify whether X axis is seamless")
|
||||||
@@ -470,24 +407,3 @@ class SeamlessModeInvocation(BaseInvocation):
|
|||||||
vae.seamless_axes = seamless_axes_list
|
vae.seamless_axes = seamless_axes_list
|
||||||
|
|
||||||
return SeamlessModeOutput(unet=unet, vae=vae)
|
return SeamlessModeOutput(unet=unet, vae=vae)
|
||||||
|
|
||||||
|
|
||||||
@invocation("freeu", title="FreeU", tags=["freeu"], category="unet", version="1.0.0")
|
|
||||||
class FreeUInvocation(BaseInvocation):
|
|
||||||
"""
|
|
||||||
Applies FreeU to the UNet. Suggested values (b1/b2/s1/s2):
|
|
||||||
|
|
||||||
SD1.5: 1.2/1.4/0.9/0.2,
|
|
||||||
SD2: 1.1/1.2/0.9/0.2,
|
|
||||||
SDXL: 1.1/1.2/0.6/0.4,
|
|
||||||
"""
|
|
||||||
|
|
||||||
unet: UNetField = InputField(description=FieldDescriptions.unet, input=Input.Connection, title="UNet")
|
|
||||||
b1: float = InputField(default=1.2, ge=-1, le=3, description=FieldDescriptions.freeu_b1)
|
|
||||||
b2: float = InputField(default=1.4, ge=-1, le=3, description=FieldDescriptions.freeu_b2)
|
|
||||||
s1: float = InputField(default=0.9, ge=-1, le=3, description=FieldDescriptions.freeu_s1)
|
|
||||||
s2: float = InputField(default=0.2, ge=-1, le=3, description=FieldDescriptions.freeu_s2)
|
|
||||||
|
|
||||||
def invoke(self, context: InvocationContext) -> UNetOutput:
|
|
||||||
self.unet.freeu_config = FreeUConfig(s1=self.s1, s2=self.s2, b1=self.b1, b2=self.b2)
|
|
||||||
return UNetOutput(unet=self.unet)
|
|
||||||
|
|||||||
@@ -2,16 +2,16 @@
|
|||||||
|
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
from pydantic import field_validator
|
from pydantic import validator
|
||||||
|
|
||||||
from invokeai.app.invocations.latent import LatentsField
|
from invokeai.app.invocations.latent import LatentsField
|
||||||
from invokeai.app.shared.fields import FieldDescriptions
|
from invokeai.app.util.misc import SEED_MAX, get_random_seed
|
||||||
from invokeai.app.util.misc import SEED_MAX
|
|
||||||
|
|
||||||
from ...backend.util.devices import choose_torch_device, torch_dtype
|
from ...backend.util.devices import choose_torch_device, torch_dtype
|
||||||
from .baseinvocation import (
|
from .baseinvocation import (
|
||||||
BaseInvocation,
|
BaseInvocation,
|
||||||
BaseInvocationOutput,
|
BaseInvocationOutput,
|
||||||
|
FieldDescriptions,
|
||||||
InputField,
|
InputField,
|
||||||
InvocationContext,
|
InvocationContext,
|
||||||
OutputField,
|
OutputField,
|
||||||
@@ -65,7 +65,7 @@ Nodes
|
|||||||
class NoiseOutput(BaseInvocationOutput):
|
class NoiseOutput(BaseInvocationOutput):
|
||||||
"""Invocation noise output"""
|
"""Invocation noise output"""
|
||||||
|
|
||||||
noise: LatentsField = OutputField(description=FieldDescriptions.noise)
|
noise: LatentsField = OutputField(default=None, description=FieldDescriptions.noise)
|
||||||
width: int = OutputField(description=FieldDescriptions.width)
|
width: int = OutputField(description=FieldDescriptions.width)
|
||||||
height: int = OutputField(description=FieldDescriptions.height)
|
height: int = OutputField(description=FieldDescriptions.height)
|
||||||
|
|
||||||
@@ -78,21 +78,15 @@ def build_noise_output(latents_name: str, latents: torch.Tensor, seed: int):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@invocation(
|
@invocation("noise", title="Noise", tags=["latents", "noise"], category="latents", version="1.0.0")
|
||||||
"noise",
|
|
||||||
title="Noise",
|
|
||||||
tags=["latents", "noise"],
|
|
||||||
category="latents",
|
|
||||||
version="1.0.1",
|
|
||||||
)
|
|
||||||
class NoiseInvocation(BaseInvocation):
|
class NoiseInvocation(BaseInvocation):
|
||||||
"""Generates latent noise."""
|
"""Generates latent noise."""
|
||||||
|
|
||||||
seed: int = InputField(
|
seed: int = InputField(
|
||||||
default=0,
|
|
||||||
ge=0,
|
ge=0,
|
||||||
le=SEED_MAX,
|
le=SEED_MAX,
|
||||||
description=FieldDescriptions.seed,
|
description=FieldDescriptions.seed,
|
||||||
|
default_factory=get_random_seed,
|
||||||
)
|
)
|
||||||
width: int = InputField(
|
width: int = InputField(
|
||||||
default=512,
|
default=512,
|
||||||
@@ -111,7 +105,7 @@ class NoiseInvocation(BaseInvocation):
|
|||||||
description="Use CPU for noise generation (for reproducible results across platforms)",
|
description="Use CPU for noise generation (for reproducible results across platforms)",
|
||||||
)
|
)
|
||||||
|
|
||||||
@field_validator("seed", mode="before")
|
@validator("seed", pre=True)
|
||||||
def modulo_seed(cls, v):
|
def modulo_seed(cls, v):
|
||||||
"""Returns the seed modulo (SEED_MAX + 1) to ensure it is within the valid range."""
|
"""Returns the seed modulo (SEED_MAX + 1) to ensure it is within the valid range."""
|
||||||
return v % (SEED_MAX + 1)
|
return v % (SEED_MAX + 1)
|
||||||
|
|||||||
@@ -4,34 +4,33 @@ import inspect
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
# from contextlib import ExitStack
|
# from contextlib import ExitStack
|
||||||
from typing import List, Literal, Union
|
from typing import List, Literal, Optional, Union
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import torch
|
import torch
|
||||||
from diffusers.image_processor import VaeImageProcessor
|
from diffusers.image_processor import VaeImageProcessor
|
||||||
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
from pydantic import BaseModel, Field, validator
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
|
|
||||||
|
from invokeai.app.invocations.metadata import CoreMetadata
|
||||||
from invokeai.app.invocations.primitives import ConditioningField, ConditioningOutput, ImageField, ImageOutput
|
from invokeai.app.invocations.primitives import ConditioningField, ConditioningOutput, ImageField, ImageOutput
|
||||||
from invokeai.app.services.image_records.image_records_common import ImageCategory, ResourceOrigin
|
|
||||||
from invokeai.app.shared.fields import FieldDescriptions
|
|
||||||
from invokeai.app.util.step_callback import stable_diffusion_step_callback
|
from invokeai.app.util.step_callback import stable_diffusion_step_callback
|
||||||
from invokeai.backend import BaseModelType, ModelType, SubModelType
|
from invokeai.backend import BaseModelType, ModelType, SubModelType
|
||||||
|
|
||||||
from ...backend.model_management import ONNXModelPatcher
|
from ...backend.model_management import ONNXModelPatcher
|
||||||
from ...backend.stable_diffusion import PipelineIntermediateState
|
from ...backend.stable_diffusion import PipelineIntermediateState
|
||||||
from ...backend.util import choose_torch_device
|
from ...backend.util import choose_torch_device
|
||||||
|
from ..models.image import ImageCategory, ResourceOrigin
|
||||||
from .baseinvocation import (
|
from .baseinvocation import (
|
||||||
BaseInvocation,
|
BaseInvocation,
|
||||||
BaseInvocationOutput,
|
BaseInvocationOutput,
|
||||||
|
FieldDescriptions,
|
||||||
Input,
|
Input,
|
||||||
InputField,
|
InputField,
|
||||||
InvocationContext,
|
InvocationContext,
|
||||||
OutputField,
|
OutputField,
|
||||||
UIComponent,
|
UIComponent,
|
||||||
UIType,
|
UIType,
|
||||||
WithMetadata,
|
|
||||||
WithWorkflow,
|
|
||||||
invocation,
|
invocation,
|
||||||
invocation_output,
|
invocation_output,
|
||||||
)
|
)
|
||||||
@@ -54,7 +53,7 @@ ORT_TO_NP_TYPE = {
|
|||||||
"tensor(double)": np.float64,
|
"tensor(double)": np.float64,
|
||||||
}
|
}
|
||||||
|
|
||||||
PRECISION_VALUES = Literal[tuple(ORT_TO_NP_TYPE.keys())]
|
PRECISION_VALUES = Literal[tuple(list(ORT_TO_NP_TYPE.keys()))]
|
||||||
|
|
||||||
|
|
||||||
@invocation("prompt_onnx", title="ONNX Prompt (Raw)", tags=["prompt", "onnx"], category="conditioning", version="1.0.0")
|
@invocation("prompt_onnx", title="ONNX Prompt (Raw)", tags=["prompt", "onnx"], category="conditioning", version="1.0.0")
|
||||||
@@ -64,17 +63,14 @@ class ONNXPromptInvocation(BaseInvocation):
|
|||||||
|
|
||||||
def invoke(self, context: InvocationContext) -> ConditioningOutput:
|
def invoke(self, context: InvocationContext) -> ConditioningOutput:
|
||||||
tokenizer_info = context.services.model_manager.get_model(
|
tokenizer_info = context.services.model_manager.get_model(
|
||||||
**self.clip.tokenizer.model_dump(),
|
**self.clip.tokenizer.dict(),
|
||||||
)
|
)
|
||||||
text_encoder_info = context.services.model_manager.get_model(
|
text_encoder_info = context.services.model_manager.get_model(
|
||||||
**self.clip.text_encoder.model_dump(),
|
**self.clip.text_encoder.dict(),
|
||||||
)
|
)
|
||||||
with tokenizer_info as orig_tokenizer, text_encoder_info as text_encoder: # , ExitStack() as stack:
|
with tokenizer_info as orig_tokenizer, text_encoder_info as text_encoder: # , ExitStack() as stack:
|
||||||
loras = [
|
loras = [
|
||||||
(
|
(context.services.model_manager.get_model(**lora.dict(exclude={"weight"})).context.model, lora.weight)
|
||||||
context.services.model_manager.get_model(**lora.model_dump(exclude={"weight"})).context.model,
|
|
||||||
lora.weight,
|
|
||||||
)
|
|
||||||
for lora in self.clip.loras
|
for lora in self.clip.loras
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -179,14 +175,14 @@ class ONNXTextToLatentsInvocation(BaseInvocation):
|
|||||||
description=FieldDescriptions.unet,
|
description=FieldDescriptions.unet,
|
||||||
input=Input.Connection,
|
input=Input.Connection,
|
||||||
)
|
)
|
||||||
control: Union[ControlField, list[ControlField]] = InputField(
|
control: Optional[Union[ControlField, list[ControlField]]] = InputField(
|
||||||
default=None,
|
default=None,
|
||||||
description=FieldDescriptions.control,
|
description=FieldDescriptions.control,
|
||||||
)
|
)
|
||||||
# seamless: bool = InputField(default=False, description="Whether or not to generate an image that can tile without seams", )
|
# seamless: bool = InputField(default=False, description="Whether or not to generate an image that can tile without seams", )
|
||||||
# seamless_axes: str = InputField(default="", description="The axes to tile the image on, 'x' and/or 'y'")
|
# seamless_axes: str = InputField(default="", description="The axes to tile the image on, 'x' and/or 'y'")
|
||||||
|
|
||||||
@field_validator("cfg_scale")
|
@validator("cfg_scale")
|
||||||
def ge_one(cls, v):
|
def ge_one(cls, v):
|
||||||
"""validate that all cfg_scale values are >= 1"""
|
"""validate that all cfg_scale values are >= 1"""
|
||||||
if isinstance(v, list):
|
if isinstance(v, list):
|
||||||
@@ -245,28 +241,25 @@ class ONNXTextToLatentsInvocation(BaseInvocation):
|
|||||||
stable_diffusion_step_callback(
|
stable_diffusion_step_callback(
|
||||||
context=context,
|
context=context,
|
||||||
intermediate_state=intermediate_state,
|
intermediate_state=intermediate_state,
|
||||||
node=self.model_dump(),
|
node=self.dict(),
|
||||||
source_node_id=source_node_id,
|
source_node_id=source_node_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
scheduler.set_timesteps(self.steps)
|
scheduler.set_timesteps(self.steps)
|
||||||
latents = latents * np.float64(scheduler.init_noise_sigma)
|
latents = latents * np.float64(scheduler.init_noise_sigma)
|
||||||
|
|
||||||
extra_step_kwargs = {}
|
extra_step_kwargs = dict()
|
||||||
if "eta" in set(inspect.signature(scheduler.step).parameters.keys()):
|
if "eta" in set(inspect.signature(scheduler.step).parameters.keys()):
|
||||||
extra_step_kwargs.update(
|
extra_step_kwargs.update(
|
||||||
eta=0.0,
|
eta=0.0,
|
||||||
)
|
)
|
||||||
|
|
||||||
unet_info = context.services.model_manager.get_model(**self.unet.unet.model_dump())
|
unet_info = context.services.model_manager.get_model(**self.unet.unet.dict())
|
||||||
|
|
||||||
with unet_info as unet: # , ExitStack() as stack:
|
with unet_info as unet: # , ExitStack() as stack:
|
||||||
# loras = [(stack.enter_context(context.services.model_manager.get_model(**lora.dict(exclude={"weight"}))), lora.weight) for lora in self.unet.loras]
|
# loras = [(stack.enter_context(context.services.model_manager.get_model(**lora.dict(exclude={"weight"}))), lora.weight) for lora in self.unet.loras]
|
||||||
loras = [
|
loras = [
|
||||||
(
|
(context.services.model_manager.get_model(**lora.dict(exclude={"weight"})).context.model, lora.weight)
|
||||||
context.services.model_manager.get_model(**lora.model_dump(exclude={"weight"})).context.model,
|
|
||||||
lora.weight,
|
|
||||||
)
|
|
||||||
for lora in self.unet.loras
|
for lora in self.unet.loras
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -326,9 +319,9 @@ class ONNXTextToLatentsInvocation(BaseInvocation):
|
|||||||
title="ONNX Latents to Image",
|
title="ONNX Latents to Image",
|
||||||
tags=["latents", "image", "vae", "onnx"],
|
tags=["latents", "image", "vae", "onnx"],
|
||||||
category="image",
|
category="image",
|
||||||
version="1.1.0",
|
version="1.0.0",
|
||||||
)
|
)
|
||||||
class ONNXLatentsToImageInvocation(BaseInvocation, WithMetadata, WithWorkflow):
|
class ONNXLatentsToImageInvocation(BaseInvocation):
|
||||||
"""Generates an image from latents."""
|
"""Generates an image from latents."""
|
||||||
|
|
||||||
latents: LatentsField = InputField(
|
latents: LatentsField = InputField(
|
||||||
@@ -339,6 +332,11 @@ class ONNXLatentsToImageInvocation(BaseInvocation, WithMetadata, WithWorkflow):
|
|||||||
description=FieldDescriptions.vae,
|
description=FieldDescriptions.vae,
|
||||||
input=Input.Connection,
|
input=Input.Connection,
|
||||||
)
|
)
|
||||||
|
metadata: Optional[CoreMetadata] = InputField(
|
||||||
|
default=None,
|
||||||
|
description=FieldDescriptions.core_metadata,
|
||||||
|
ui_hidden=True,
|
||||||
|
)
|
||||||
# tiled: bool = InputField(default=False, description="Decode latents by overlaping tiles(less memory consumption)")
|
# tiled: bool = InputField(default=False, description="Decode latents by overlaping tiles(less memory consumption)")
|
||||||
|
|
||||||
def invoke(self, context: InvocationContext) -> ImageOutput:
|
def invoke(self, context: InvocationContext) -> ImageOutput:
|
||||||
@@ -348,7 +346,7 @@ class ONNXLatentsToImageInvocation(BaseInvocation, WithMetadata, WithWorkflow):
|
|||||||
raise Exception(f"Expected vae_decoder, found: {self.vae.vae.model_type}")
|
raise Exception(f"Expected vae_decoder, found: {self.vae.vae.model_type}")
|
||||||
|
|
||||||
vae_info = context.services.model_manager.get_model(
|
vae_info = context.services.model_manager.get_model(
|
||||||
**self.vae.vae.model_dump(),
|
**self.vae.vae.dict(),
|
||||||
)
|
)
|
||||||
|
|
||||||
# clear memory as vae decode can request a lot
|
# clear memory as vae decode can request a lot
|
||||||
@@ -377,7 +375,7 @@ class ONNXLatentsToImageInvocation(BaseInvocation, WithMetadata, WithWorkflow):
|
|||||||
node_id=self.id,
|
node_id=self.id,
|
||||||
session_id=context.graph_execution_state_id,
|
session_id=context.graph_execution_state_id,
|
||||||
is_intermediate=self.is_intermediate,
|
is_intermediate=self.is_intermediate,
|
||||||
metadata=self.metadata,
|
metadata=self.metadata.dict() if self.metadata else None,
|
||||||
workflow=self.workflow,
|
workflow=self.workflow,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -405,8 +403,6 @@ class OnnxModelField(BaseModel):
|
|||||||
base_model: BaseModelType = Field(description="Base model")
|
base_model: BaseModelType = Field(description="Base model")
|
||||||
model_type: ModelType = Field(description="Model Type")
|
model_type: ModelType = Field(description="Model Type")
|
||||||
|
|
||||||
model_config = ConfigDict(protected_namespaces=())
|
|
||||||
|
|
||||||
|
|
||||||
@invocation("onnx_model_loader", title="ONNX Main Model", tags=["onnx", "model"], category="model", version="1.0.0")
|
@invocation("onnx_model_loader", title="ONNX Main Model", tags=["onnx", "model"], category="model", version="1.0.0")
|
||||||
class OnnxModelLoaderInvocation(BaseInvocation):
|
class OnnxModelLoaderInvocation(BaseInvocation):
|
||||||
|
|||||||
@@ -44,22 +44,13 @@ from invokeai.app.invocations.primitives import FloatCollectionOutput
|
|||||||
from .baseinvocation import BaseInvocation, InputField, InvocationContext, invocation
|
from .baseinvocation import BaseInvocation, InputField, InvocationContext, invocation
|
||||||
|
|
||||||
|
|
||||||
@invocation(
|
@invocation("float_range", title="Float Range", tags=["math", "range"], category="math", version="1.0.0")
|
||||||
"float_range",
|
|
||||||
title="Float Range",
|
|
||||||
tags=["math", "range"],
|
|
||||||
category="math",
|
|
||||||
version="1.0.0",
|
|
||||||
)
|
|
||||||
class FloatLinearRangeInvocation(BaseInvocation):
|
class FloatLinearRangeInvocation(BaseInvocation):
|
||||||
"""Creates a range"""
|
"""Creates a range"""
|
||||||
|
|
||||||
start: float = InputField(default=5, description="The first value of the range")
|
start: float = InputField(default=5, description="The first value of the range")
|
||||||
stop: float = InputField(default=10, description="The last value of the range")
|
stop: float = InputField(default=10, description="The last value of the range")
|
||||||
steps: int = InputField(
|
steps: int = InputField(default=30, description="number of values to interpolate over (including start and stop)")
|
||||||
default=30,
|
|
||||||
description="number of values to interpolate over (including start and stop)",
|
|
||||||
)
|
|
||||||
|
|
||||||
def invoke(self, context: InvocationContext) -> FloatCollectionOutput:
|
def invoke(self, context: InvocationContext) -> FloatCollectionOutput:
|
||||||
param_list = list(np.linspace(self.start, self.stop, self.steps))
|
param_list = list(np.linspace(self.start, self.stop, self.steps))
|
||||||
@@ -100,17 +91,11 @@ EASING_FUNCTIONS_MAP = {
|
|||||||
"BounceInOut": BounceEaseInOut,
|
"BounceInOut": BounceEaseInOut,
|
||||||
}
|
}
|
||||||
|
|
||||||
EASING_FUNCTION_KEYS = Literal[tuple(EASING_FUNCTIONS_MAP.keys())]
|
EASING_FUNCTION_KEYS = Literal[tuple(list(EASING_FUNCTIONS_MAP.keys()))]
|
||||||
|
|
||||||
|
|
||||||
# actually I think for now could just use CollectionOutput (which is list[Any]
|
# actually I think for now could just use CollectionOutput (which is list[Any]
|
||||||
@invocation(
|
@invocation("step_param_easing", title="Step Param Easing", tags=["step", "easing"], category="step", version="1.0.0")
|
||||||
"step_param_easing",
|
|
||||||
title="Step Param Easing",
|
|
||||||
tags=["step", "easing"],
|
|
||||||
category="step",
|
|
||||||
version="1.0.0",
|
|
||||||
)
|
|
||||||
class StepParamEasingInvocation(BaseInvocation):
|
class StepParamEasingInvocation(BaseInvocation):
|
||||||
"""Experimental per-step parameter easing for denoising steps"""
|
"""Experimental per-step parameter easing for denoising steps"""
|
||||||
|
|
||||||
@@ -161,7 +146,7 @@ class StepParamEasingInvocation(BaseInvocation):
|
|||||||
easing_class = EASING_FUNCTIONS_MAP[self.easing]
|
easing_class = EASING_FUNCTIONS_MAP[self.easing]
|
||||||
if log_diagnostics:
|
if log_diagnostics:
|
||||||
context.services.logger.debug("easing class: " + str(easing_class))
|
context.services.logger.debug("easing class: " + str(easing_class))
|
||||||
easing_list = []
|
easing_list = list()
|
||||||
if self.mirror: # "expected" mirroring
|
if self.mirror: # "expected" mirroring
|
||||||
# if number of steps is even, squeeze duration down to (number_of_steps)/2
|
# if number of steps is even, squeeze duration down to (number_of_steps)/2
|
||||||
# and create reverse copy of list to append
|
# and create reverse copy of list to append
|
||||||
@@ -174,11 +159,9 @@ class StepParamEasingInvocation(BaseInvocation):
|
|||||||
context.services.logger.debug("base easing duration: " + str(base_easing_duration))
|
context.services.logger.debug("base easing duration: " + str(base_easing_duration))
|
||||||
even_num_steps = num_easing_steps % 2 == 0 # even number of steps
|
even_num_steps = num_easing_steps % 2 == 0 # even number of steps
|
||||||
easing_function = easing_class(
|
easing_function = easing_class(
|
||||||
start=self.start_value,
|
start=self.start_value, end=self.end_value, duration=base_easing_duration - 1
|
||||||
end=self.end_value,
|
|
||||||
duration=base_easing_duration - 1,
|
|
||||||
)
|
)
|
||||||
base_easing_vals = []
|
base_easing_vals = list()
|
||||||
for step_index in range(base_easing_duration):
|
for step_index in range(base_easing_duration):
|
||||||
easing_val = easing_function.ease(step_index)
|
easing_val = easing_function.ease(step_index)
|
||||||
base_easing_vals.append(easing_val)
|
base_easing_vals.append(easing_val)
|
||||||
@@ -216,11 +199,7 @@ class StepParamEasingInvocation(BaseInvocation):
|
|||||||
#
|
#
|
||||||
|
|
||||||
else: # no mirroring (default)
|
else: # no mirroring (default)
|
||||||
easing_function = easing_class(
|
easing_function = easing_class(start=self.start_value, end=self.end_value, duration=num_easing_steps - 1)
|
||||||
start=self.start_value,
|
|
||||||
end=self.end_value,
|
|
||||||
duration=num_easing_steps - 1,
|
|
||||||
)
|
|
||||||
for step_index in range(num_easing_steps):
|
for step_index in range(num_easing_steps):
|
||||||
step_val = easing_function.ease(step_index)
|
step_val = easing_function.ease(step_index)
|
||||||
easing_list.append(step_val)
|
easing_list.append(step_val)
|
||||||
|
|||||||
@@ -5,11 +5,10 @@ from typing import Optional, Tuple
|
|||||||
import torch
|
import torch
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
from invokeai.app.shared.fields import FieldDescriptions
|
|
||||||
|
|
||||||
from .baseinvocation import (
|
from .baseinvocation import (
|
||||||
BaseInvocation,
|
BaseInvocation,
|
||||||
BaseInvocationOutput,
|
BaseInvocationOutput,
|
||||||
|
FieldDescriptions,
|
||||||
Input,
|
Input,
|
||||||
InputField,
|
InputField,
|
||||||
InvocationContext,
|
InvocationContext,
|
||||||
@@ -62,12 +61,12 @@ class BooleanInvocation(BaseInvocation):
|
|||||||
title="Boolean Collection Primitive",
|
title="Boolean Collection Primitive",
|
||||||
tags=["primitives", "boolean", "collection"],
|
tags=["primitives", "boolean", "collection"],
|
||||||
category="primitives",
|
category="primitives",
|
||||||
version="1.0.1",
|
version="1.0.0",
|
||||||
)
|
)
|
||||||
class BooleanCollectionInvocation(BaseInvocation):
|
class BooleanCollectionInvocation(BaseInvocation):
|
||||||
"""A collection of boolean primitive values"""
|
"""A collection of boolean primitive values"""
|
||||||
|
|
||||||
collection: list[bool] = InputField(default=[], description="The collection of boolean values")
|
collection: list[bool] = InputField(default_factory=list, description="The collection of boolean values")
|
||||||
|
|
||||||
def invoke(self, context: InvocationContext) -> BooleanCollectionOutput:
|
def invoke(self, context: InvocationContext) -> BooleanCollectionOutput:
|
||||||
return BooleanCollectionOutput(collection=self.collection)
|
return BooleanCollectionOutput(collection=self.collection)
|
||||||
@@ -111,12 +110,12 @@ class IntegerInvocation(BaseInvocation):
|
|||||||
title="Integer Collection Primitive",
|
title="Integer Collection Primitive",
|
||||||
tags=["primitives", "integer", "collection"],
|
tags=["primitives", "integer", "collection"],
|
||||||
category="primitives",
|
category="primitives",
|
||||||
version="1.0.1",
|
version="1.0.0",
|
||||||
)
|
)
|
||||||
class IntegerCollectionInvocation(BaseInvocation):
|
class IntegerCollectionInvocation(BaseInvocation):
|
||||||
"""A collection of integer primitive values"""
|
"""A collection of integer primitive values"""
|
||||||
|
|
||||||
collection: list[int] = InputField(default=[], description="The collection of integer values")
|
collection: list[int] = InputField(default_factory=list, description="The collection of integer values")
|
||||||
|
|
||||||
def invoke(self, context: InvocationContext) -> IntegerCollectionOutput:
|
def invoke(self, context: InvocationContext) -> IntegerCollectionOutput:
|
||||||
return IntegerCollectionOutput(collection=self.collection)
|
return IntegerCollectionOutput(collection=self.collection)
|
||||||
@@ -158,12 +157,12 @@ class FloatInvocation(BaseInvocation):
|
|||||||
title="Float Collection Primitive",
|
title="Float Collection Primitive",
|
||||||
tags=["primitives", "float", "collection"],
|
tags=["primitives", "float", "collection"],
|
||||||
category="primitives",
|
category="primitives",
|
||||||
version="1.0.1",
|
version="1.0.0",
|
||||||
)
|
)
|
||||||
class FloatCollectionInvocation(BaseInvocation):
|
class FloatCollectionInvocation(BaseInvocation):
|
||||||
"""A collection of float primitive values"""
|
"""A collection of float primitive values"""
|
||||||
|
|
||||||
collection: list[float] = InputField(default=[], description="The collection of float values")
|
collection: list[float] = InputField(default_factory=list, description="The collection of float values")
|
||||||
|
|
||||||
def invoke(self, context: InvocationContext) -> FloatCollectionOutput:
|
def invoke(self, context: InvocationContext) -> FloatCollectionOutput:
|
||||||
return FloatCollectionOutput(collection=self.collection)
|
return FloatCollectionOutput(collection=self.collection)
|
||||||
@@ -205,12 +204,12 @@ class StringInvocation(BaseInvocation):
|
|||||||
title="String Collection Primitive",
|
title="String Collection Primitive",
|
||||||
tags=["primitives", "string", "collection"],
|
tags=["primitives", "string", "collection"],
|
||||||
category="primitives",
|
category="primitives",
|
||||||
version="1.0.1",
|
version="1.0.0",
|
||||||
)
|
)
|
||||||
class StringCollectionInvocation(BaseInvocation):
|
class StringCollectionInvocation(BaseInvocation):
|
||||||
"""A collection of string primitive values"""
|
"""A collection of string primitive values"""
|
||||||
|
|
||||||
collection: list[str] = InputField(default=[], description="The collection of string values")
|
collection: list[str] = InputField(default_factory=list, description="The collection of string values")
|
||||||
|
|
||||||
def invoke(self, context: InvocationContext) -> StringCollectionOutput:
|
def invoke(self, context: InvocationContext) -> StringCollectionOutput:
|
||||||
return StringCollectionOutput(collection=self.collection)
|
return StringCollectionOutput(collection=self.collection)
|
||||||
@@ -252,9 +251,7 @@ class ImageCollectionOutput(BaseInvocationOutput):
|
|||||||
|
|
||||||
|
|
||||||
@invocation("image", title="Image Primitive", tags=["primitives", "image"], category="primitives", version="1.0.0")
|
@invocation("image", title="Image Primitive", tags=["primitives", "image"], category="primitives", version="1.0.0")
|
||||||
class ImageInvocation(
|
class ImageInvocation(BaseInvocation):
|
||||||
BaseInvocation,
|
|
||||||
):
|
|
||||||
"""An image primitive value"""
|
"""An image primitive value"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to load")
|
image: ImageField = InputField(description="The image to load")
|
||||||
@@ -294,7 +291,7 @@ class DenoiseMaskField(BaseModel):
|
|||||||
"""An inpaint mask field"""
|
"""An inpaint mask field"""
|
||||||
|
|
||||||
mask_name: str = Field(description="The name of the mask image")
|
mask_name: str = Field(description="The name of the mask image")
|
||||||
masked_latents_name: Optional[str] = Field(default=None, description="The name of the masked image latents")
|
masked_latents_name: Optional[str] = Field(description="The name of the masked image latents")
|
||||||
|
|
||||||
|
|
||||||
@invocation_output("denoise_mask_output")
|
@invocation_output("denoise_mask_output")
|
||||||
@@ -467,13 +464,13 @@ class ConditioningInvocation(BaseInvocation):
|
|||||||
title="Conditioning Collection Primitive",
|
title="Conditioning Collection Primitive",
|
||||||
tags=["primitives", "conditioning", "collection"],
|
tags=["primitives", "conditioning", "collection"],
|
||||||
category="primitives",
|
category="primitives",
|
||||||
version="1.0.1",
|
version="1.0.0",
|
||||||
)
|
)
|
||||||
class ConditioningCollectionInvocation(BaseInvocation):
|
class ConditioningCollectionInvocation(BaseInvocation):
|
||||||
"""A collection of conditioning tensor primitive values"""
|
"""A collection of conditioning tensor primitive values"""
|
||||||
|
|
||||||
collection: list[ConditioningField] = InputField(
|
collection: list[ConditioningField] = InputField(
|
||||||
default=[],
|
default_factory=list,
|
||||||
description="The collection of conditioning tensors",
|
description="The collection of conditioning tensors",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from typing import Optional, Union
|
|||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from dynamicprompts.generators import CombinatorialPromptGenerator, RandomPromptGenerator
|
from dynamicprompts.generators import CombinatorialPromptGenerator, RandomPromptGenerator
|
||||||
from pydantic import field_validator
|
from pydantic import validator
|
||||||
|
|
||||||
from invokeai.app.invocations.primitives import StringCollectionOutput
|
from invokeai.app.invocations.primitives import StringCollectionOutput
|
||||||
|
|
||||||
@@ -21,10 +21,7 @@ from .baseinvocation import BaseInvocation, InputField, InvocationContext, UICom
|
|||||||
class DynamicPromptInvocation(BaseInvocation):
|
class DynamicPromptInvocation(BaseInvocation):
|
||||||
"""Parses a prompt using adieyal/dynamicprompts' random or combinatorial generator"""
|
"""Parses a prompt using adieyal/dynamicprompts' random or combinatorial generator"""
|
||||||
|
|
||||||
prompt: str = InputField(
|
prompt: str = InputField(description="The prompt to parse with dynamicprompts", ui_component=UIComponent.Textarea)
|
||||||
description="The prompt to parse with dynamicprompts",
|
|
||||||
ui_component=UIComponent.Textarea,
|
|
||||||
)
|
|
||||||
max_prompts: int = InputField(default=1, description="The number of prompts to generate")
|
max_prompts: int = InputField(default=1, description="The number of prompts to generate")
|
||||||
combinatorial: bool = InputField(default=False, description="Whether to use the combinatorial generator")
|
combinatorial: bool = InputField(default=False, description="Whether to use the combinatorial generator")
|
||||||
|
|
||||||
@@ -39,31 +36,21 @@ class DynamicPromptInvocation(BaseInvocation):
|
|||||||
return StringCollectionOutput(collection=prompts)
|
return StringCollectionOutput(collection=prompts)
|
||||||
|
|
||||||
|
|
||||||
@invocation(
|
@invocation("prompt_from_file", title="Prompts from File", tags=["prompt", "file"], category="prompt", version="1.0.0")
|
||||||
"prompt_from_file",
|
|
||||||
title="Prompts from File",
|
|
||||||
tags=["prompt", "file"],
|
|
||||||
category="prompt",
|
|
||||||
version="1.0.1",
|
|
||||||
)
|
|
||||||
class PromptsFromFileInvocation(BaseInvocation):
|
class PromptsFromFileInvocation(BaseInvocation):
|
||||||
"""Loads prompts from a text file"""
|
"""Loads prompts from a text file"""
|
||||||
|
|
||||||
file_path: str = InputField(description="Path to prompt text file")
|
file_path: str = InputField(description="Path to prompt text file")
|
||||||
pre_prompt: Optional[str] = InputField(
|
pre_prompt: Optional[str] = InputField(
|
||||||
default=None,
|
default=None, description="String to prepend to each prompt", ui_component=UIComponent.Textarea
|
||||||
description="String to prepend to each prompt",
|
|
||||||
ui_component=UIComponent.Textarea,
|
|
||||||
)
|
)
|
||||||
post_prompt: Optional[str] = InputField(
|
post_prompt: Optional[str] = InputField(
|
||||||
default=None,
|
default=None, description="String to append to each prompt", ui_component=UIComponent.Textarea
|
||||||
description="String to append to each prompt",
|
|
||||||
ui_component=UIComponent.Textarea,
|
|
||||||
)
|
)
|
||||||
start_line: int = InputField(default=1, ge=1, description="Line in the file to start start from")
|
start_line: int = InputField(default=1, ge=1, description="Line in the file to start start from")
|
||||||
max_prompts: int = InputField(default=1, ge=0, description="Max lines to read from file (0=all)")
|
max_prompts: int = InputField(default=1, ge=0, description="Max lines to read from file (0=all)")
|
||||||
|
|
||||||
@field_validator("file_path")
|
@validator("file_path")
|
||||||
def file_path_exists(cls, v):
|
def file_path_exists(cls, v):
|
||||||
if not exists(v):
|
if not exists(v):
|
||||||
raise ValueError(FileNotFoundError)
|
raise ValueError(FileNotFoundError)
|
||||||
@@ -82,7 +69,7 @@ class PromptsFromFileInvocation(BaseInvocation):
|
|||||||
end_line = start_line + max_prompts
|
end_line = start_line + max_prompts
|
||||||
if max_prompts <= 0:
|
if max_prompts <= 0:
|
||||||
end_line = np.iinfo(np.int32).max
|
end_line = np.iinfo(np.int32).max
|
||||||
with open(file_path, encoding="utf-8") as f:
|
with open(file_path) as f:
|
||||||
for i, line in enumerate(f):
|
for i, line in enumerate(f):
|
||||||
if i >= start_line and i < end_line:
|
if i >= start_line and i < end_line:
|
||||||
prompts.append((pre_prompt or "") + line.strip() + (post_prompt or ""))
|
prompts.append((pre_prompt or "") + line.strip() + (post_prompt or ""))
|
||||||
@@ -92,10 +79,6 @@ class PromptsFromFileInvocation(BaseInvocation):
|
|||||||
|
|
||||||
def invoke(self, context: InvocationContext) -> StringCollectionOutput:
|
def invoke(self, context: InvocationContext) -> StringCollectionOutput:
|
||||||
prompts = self.promptsFromFile(
|
prompts = self.promptsFromFile(
|
||||||
self.file_path,
|
self.file_path, self.pre_prompt, self.post_prompt, self.start_line, self.max_prompts
|
||||||
self.pre_prompt,
|
|
||||||
self.post_prompt,
|
|
||||||
self.start_line,
|
|
||||||
self.max_prompts,
|
|
||||||
)
|
)
|
||||||
return StringCollectionOutput(collection=prompts)
|
return StringCollectionOutput(collection=prompts)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user