Merge branch 'main' into development
- this syncs documentation and code
@ -1,4 +1,4 @@
|
|||||||
name: Test Dream with Conda
|
name: Test Invoke with Conda
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
@ -9,7 +9,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ ubuntu-latest, macos-12 ]
|
os: [ ubuntu-latest, macos-12 ]
|
||||||
name: Test dream.py on ${{ matrix.os }} with conda
|
name: Test invoke.py on ${{ matrix.os }} with conda
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- run: |
|
- run: |
|
||||||
@ -85,9 +85,9 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
# Utterly hacky, but I don't know how else to do this
|
# Utterly hacky, but I don't know how else to do this
|
||||||
if [[ ${{ github.ref }} == 'refs/heads/master' ]]; then
|
if [[ ${{ github.ref }} == 'refs/heads/master' ]]; then
|
||||||
time ${{ steps.vars.outputs.PYTHON_BIN }} scripts/dream.py --from_file tests/preflight_prompts.txt
|
time ${{ steps.vars.outputs.PYTHON_BIN }} scripts/invoke.py --from_file tests/preflight_prompts.txt
|
||||||
elif [[ ${{ github.ref }} == 'refs/heads/development' ]]; then
|
elif [[ ${{ github.ref }} == 'refs/heads/development' ]]; then
|
||||||
time ${{ steps.vars.outputs.PYTHON_BIN }} scripts/dream.py --from_file tests/dev_prompts.txt
|
time ${{ steps.vars.outputs.PYTHON_BIN }} scripts/invoke.py --from_file tests/dev_prompts.txt
|
||||||
fi
|
fi
|
||||||
mkdir -p outputs/img-samples
|
mkdir -p outputs/img-samples
|
||||||
- name: Archive results
|
- name: Archive results
|
3
.gitignore
vendored
@ -196,3 +196,6 @@ checkpoints
|
|||||||
.vscode/
|
.vscode/
|
||||||
gfpgan/
|
gfpgan/
|
||||||
models/ldm/stable-diffusion-v1/model.sha256
|
models/ldm/stable-diffusion-v1/model.sha256
|
||||||
|
|
||||||
|
# GFPGAN model files
|
||||||
|
gfpgan/
|
||||||
|
32
README.md
@ -24,7 +24,7 @@ _This repository was formally known as lstein/stable-diffusion_
|
|||||||
[CI checks on dev badge]: https://flat.badgen.net/github/checks/invoke-ai/InvokeAI/development?label=CI%20status%20on%20dev&cache=900&icon=github
|
[CI checks on dev badge]: https://flat.badgen.net/github/checks/invoke-ai/InvokeAI/development?label=CI%20status%20on%20dev&cache=900&icon=github
|
||||||
[CI checks on dev link]: https://github.com/invoke-ai/InvokeAI/actions?query=branch%3Adevelopment
|
[CI checks on dev link]: https://github.com/invoke-ai/InvokeAI/actions?query=branch%3Adevelopment
|
||||||
[CI checks on main badge]: https://flat.badgen.net/github/checks/invoke-ai/InvokeAI/main?label=CI%20status%20on%20main&cache=900&icon=github
|
[CI checks on main badge]: https://flat.badgen.net/github/checks/invoke-ai/InvokeAI/main?label=CI%20status%20on%20main&cache=900&icon=github
|
||||||
[CI checks on main link]: https://github.com/invoke-ai/InvokeAI/actions/workflows/test-dream-conda.yml
|
[CI checks on main link]: https://github.com/invoke-ai/InvokeAI/actions/workflows/test-invoke-conda.yml
|
||||||
[discord badge]: https://flat.badgen.net/discord/members/ZmtBAhwWhy?icon=discord
|
[discord badge]: https://flat.badgen.net/discord/members/ZmtBAhwWhy?icon=discord
|
||||||
[discord link]: https://discord.gg/ZmtBAhwWhy
|
[discord link]: https://discord.gg/ZmtBAhwWhy
|
||||||
[github forks badge]: https://flat.badgen.net/github/forks/invoke-ai/InvokeAI?icon=github
|
[github forks badge]: https://flat.badgen.net/github/forks/invoke-ai/InvokeAI?icon=github
|
||||||
@ -41,10 +41,13 @@ _This repository was formally known as lstein/stable-diffusion_
|
|||||||
[latest release link]: https://github.com/invoke-ai/InvokeAI/releases
|
[latest release link]: https://github.com/invoke-ai/InvokeAI/releases
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
This is a fork of [CompVis/stable-diffusion](https://github.com/CompVis/stable-diffusion), the open
|
This is a fork of
|
||||||
source text-to-image generator. It provides a streamlined process with various new features and
|
[CompVis/stable-diffusion](https://github.com/CompVis/stable-diffusion),
|
||||||
options to aid the image generation process. It runs on Windows, Mac and Linux machines, and runs on
|
the open source text-to-image generator. It provides a streamlined
|
||||||
GPU cards with as little as 4 GB or RAM.
|
process with various new features and options to aid the image
|
||||||
|
generation process. It runs on Windows, Mac and Linux machines, with
|
||||||
|
GPU cards with as little as 4 GB or RAM. It provides both a polished
|
||||||
|
Web interface, and an easy-to-use command-line interface.
|
||||||
|
|
||||||
_Note: This fork is rapidly evolving. Please use the
|
_Note: This fork is rapidly evolving. Please use the
|
||||||
[Issues](https://github.com/invoke-ai/InvokeAI/issues) tab to report bugs and make feature
|
[Issues](https://github.com/invoke-ai/InvokeAI/issues) tab to report bugs and make feature
|
||||||
@ -90,20 +93,26 @@ You wil need one of the following:
|
|||||||
|
|
||||||
- At least 6 GB of free disk space for the machine learning model, Python, and all its dependencies.
|
- At least 6 GB of free disk space for the machine learning model, Python, and all its dependencies.
|
||||||
|
|
||||||
#### Note
|
**Note**
|
||||||
|
|
||||||
|
If you have a Nvidia 10xx series card (e.g. the 1080ti), please
|
||||||
|
run the dream script in full-precision mode as shown below.
|
||||||
|
|
||||||
|
Similarly, specify full-precision mode on Apple M1 hardware.
|
||||||
|
|
||||||
Precision is auto configured based on the device. If however you encounter
|
Precision is auto configured based on the device. If however you encounter
|
||||||
errors like 'expected type Float but found Half' or 'not implemented for Half'
|
errors like 'expected type Float but found Half' or 'not implemented for Half'
|
||||||
you can try starting `dream.py` with the `--precision=float32` flag:
|
you can try starting `invoke.py` with the `--precision=float32` flag:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
(ldm) ~/stable-diffusion$ python scripts/dream.py --precision=float32
|
(ldm) ~/stable-diffusion$ python scripts/invoke.py --precision=float32
|
||||||
```
|
```
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
#### Major Features
|
#### Major Features
|
||||||
|
|
||||||
|
- [Web Server](docs/features/WEB.md)
|
||||||
- [Interactive Command Line Interface](docs/features/CLI.md)
|
- [Interactive Command Line Interface](docs/features/CLI.md)
|
||||||
- [Image To Image](docs/features/IMG2IMG.md)
|
- [Image To Image](docs/features/IMG2IMG.md)
|
||||||
- [Inpainting Support](docs/features/INPAINTING.md)
|
- [Inpainting Support](docs/features/INPAINTING.md)
|
||||||
@ -111,10 +120,9 @@ you can try starting `dream.py` with the `--precision=float32` flag:
|
|||||||
- [Upscaling, face-restoration and outpainting](docs/features/POSTPROCESS.md)
|
- [Upscaling, face-restoration and outpainting](docs/features/POSTPROCESS.md)
|
||||||
- [Seamless Tiling](docs/features/OTHER.md#seamless-tiling)
|
- [Seamless Tiling](docs/features/OTHER.md#seamless-tiling)
|
||||||
- [Google Colab](docs/features/OTHER.md#google-colab)
|
- [Google Colab](docs/features/OTHER.md#google-colab)
|
||||||
- [Web Server](docs/features/WEB.md)
|
|
||||||
- [Reading Prompts From File](docs/features/PROMPTS.md#reading-prompts-from-a-file)
|
- [Reading Prompts From File](docs/features/PROMPTS.md#reading-prompts-from-a-file)
|
||||||
- [Shortcut: Reusing Seeds](docs/features/OTHER.md#shortcuts-reusing-seeds)
|
- [Shortcut: Reusing Seeds](docs/features/OTHER.md#shortcuts-reusing-seeds)
|
||||||
- [Weighted Prompts](docs/features/PROMPTS.md#weighted-prompts)
|
- [Prompt Blending](docs/features/PROMPTS.md#prompt-blending)
|
||||||
- [Thresholding and Perlin Noise Initialization Options](/docs/features/OTHER.md#thresholding-and-perlin-noise-initialization-options)
|
- [Thresholding and Perlin Noise Initialization Options](/docs/features/OTHER.md#thresholding-and-perlin-noise-initialization-options)
|
||||||
- [Negative/Unconditioned Prompts](docs/features/PROMPTS.md#negative-and-unconditioned-prompts)
|
- [Negative/Unconditioned Prompts](docs/features/PROMPTS.md#negative-and-unconditioned-prompts)
|
||||||
- [Variations](docs/features/VARIATIONS.md)
|
- [Variations](docs/features/VARIATIONS.md)
|
||||||
@ -130,7 +138,7 @@ you can try starting `dream.py` with the `--precision=float32` flag:
|
|||||||
|
|
||||||
- vNEXT (TODO 2022)
|
- vNEXT (TODO 2022)
|
||||||
|
|
||||||
- Deprecated `--full_precision` / `-F`. Simply omit it and `dream.py` will auto
|
- Deprecated `--full_precision` / `-F`. Simply omit it and `invoke.py` will auto
|
||||||
configure. To switch away from auto use the new flag like `--precision=float32`.
|
configure. To switch away from auto use the new flag like `--precision=float32`.
|
||||||
|
|
||||||
- v1.14 (11 September 2022)
|
- v1.14 (11 September 2022)
|
||||||
@ -156,7 +164,7 @@ you can try starting `dream.py` with the `--precision=float32` flag:
|
|||||||
- A new configuration file scheme that allows new models (including upcoming
|
- A new configuration file scheme that allows new models (including upcoming
|
||||||
stable-diffusion-v1.5) to be added without altering the code.
|
stable-diffusion-v1.5) to be added without altering the code.
|
||||||
([David Wager](https://github.com/maddavid12))
|
([David Wager](https://github.com/maddavid12))
|
||||||
- Can specify --grid on dream.py command line as the default.
|
- Can specify --grid on invoke.py command line as the default.
|
||||||
- Miscellaneous internal bug and stability fixes.
|
- Miscellaneous internal bug and stability fixes.
|
||||||
- Works on M1 Apple hardware.
|
- Works on M1 Apple hardware.
|
||||||
- Multiple bug fixes.
|
- Multiple bug fixes.
|
||||||
|
@ -12,9 +12,9 @@ from PIL import Image
|
|||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from threading import Event
|
from threading import Event
|
||||||
|
|
||||||
from ldm.dream.args import Args, APP_ID, APP_VERSION, calculate_init_img_hash
|
from ldm.invoke.args import Args, APP_ID, APP_VERSION, calculate_init_img_hash
|
||||||
from ldm.dream.pngwriter import PngWriter, retrieve_metadata
|
from ldm.invoke.pngwriter import PngWriter, retrieve_metadata
|
||||||
from ldm.dream.conditioning import split_weighted_subprompts
|
from ldm.invoke.conditioning import split_weighted_subprompts
|
||||||
|
|
||||||
from backend.modules.parameters import parameters_to_command
|
from backend.modules.parameters import parameters_to_command
|
||||||
|
|
||||||
@ -147,7 +147,7 @@ class InvokeAIWebServer:
|
|||||||
self.init_image_path = os.path.join(self.result_path, 'init-images/')
|
self.init_image_path = os.path.join(self.result_path, 'init-images/')
|
||||||
self.mask_image_path = os.path.join(self.result_path, 'mask-images/')
|
self.mask_image_path = os.path.join(self.result_path, 'mask-images/')
|
||||||
# txt log
|
# txt log
|
||||||
self.log_path = os.path.join(self.result_path, 'dream_log.txt')
|
self.log_path = os.path.join(self.result_path, 'invoke_log.txt')
|
||||||
# make all output paths
|
# make all output paths
|
||||||
[
|
[
|
||||||
os.makedirs(path, exist_ok=True)
|
os.makedirs(path, exist_ok=True)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
from ldm.dream.args import PRECISION_CHOICES
|
from ldm.invoke.args import PRECISION_CHOICES
|
||||||
|
|
||||||
|
|
||||||
def create_cmd_parser():
|
def create_cmd_parser():
|
||||||
|
@ -15,7 +15,7 @@ SAMPLER_CHOICES = [
|
|||||||
|
|
||||||
def parameters_to_command(params):
|
def parameters_to_command(params):
|
||||||
"""
|
"""
|
||||||
Converts dict of parameters into a `dream.py` REPL command.
|
Converts dict of parameters into a `invoke.py` REPL command.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
switches = list()
|
switches = list()
|
||||||
|
@ -30,10 +30,10 @@ from send2trash import send2trash
|
|||||||
|
|
||||||
|
|
||||||
from ldm.generate import Generate
|
from ldm.generate import Generate
|
||||||
from ldm.dream.restoration import Restoration
|
from ldm.invoke.restoration import Restoration
|
||||||
from ldm.dream.pngwriter import PngWriter, retrieve_metadata
|
from ldm.invoke.pngwriter import PngWriter, retrieve_metadata
|
||||||
from ldm.dream.args import APP_ID, APP_VERSION, calculate_init_img_hash
|
from ldm.invoke.args import APP_ID, APP_VERSION, calculate_init_img_hash
|
||||||
from ldm.dream.conditioning import split_weighted_subprompts
|
from ldm.invoke.conditioning import split_weighted_subprompts
|
||||||
|
|
||||||
from modules.parameters import parameters_to_command
|
from modules.parameters import parameters_to_command
|
||||||
|
|
||||||
@ -125,7 +125,7 @@ class CanceledException(Exception):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
gfpgan, codeformer, esrgan = None, None, None
|
gfpgan, codeformer, esrgan = None, None, None
|
||||||
from ldm.dream.restoration.base import Restoration
|
from ldm.invoke.restoration.base import Restoration
|
||||||
|
|
||||||
restoration = Restoration()
|
restoration = Restoration()
|
||||||
gfpgan, codeformer = restoration.load_face_restore_models()
|
gfpgan, codeformer = restoration.load_face_restore_models()
|
||||||
@ -164,7 +164,7 @@ init_image_path = os.path.join(result_path, "init-images/")
|
|||||||
mask_image_path = os.path.join(result_path, "mask-images/")
|
mask_image_path = os.path.join(result_path, "mask-images/")
|
||||||
|
|
||||||
# txt log
|
# txt log
|
||||||
log_path = os.path.join(result_path, "dream_log.txt")
|
log_path = os.path.join(result_path, "invoke_log.txt")
|
||||||
|
|
||||||
# make all output paths
|
# make all output paths
|
||||||
[
|
[
|
||||||
|
@ -5,9 +5,9 @@
|
|||||||
- Supports a Google Colab notebook for a standalone server running on Google hardware [Arturo Mendivil](https://github.com/artmen1516)
|
- Supports a Google Colab notebook for a standalone server running on Google hardware [Arturo Mendivil](https://github.com/artmen1516)
|
||||||
- WebUI supports GFPGAN/ESRGAN facial reconstruction and upscaling [Kevin Gibbons](https://github.com/bakkot)
|
- WebUI supports GFPGAN/ESRGAN facial reconstruction and upscaling [Kevin Gibbons](https://github.com/bakkot)
|
||||||
- WebUI supports incremental display of in-progress images during generation [Kevin Gibbons](https://github.com/bakkot)
|
- WebUI supports incremental display of in-progress images during generation [Kevin Gibbons](https://github.com/bakkot)
|
||||||
- Output directory can be specified on the dream> command line.
|
- Output directory can be specified on the invoke> command line.
|
||||||
- The grid was displaying duplicated images when not enough images to fill the final row [Muhammad Usama](https://github.com/SMUsamaShah)
|
- The grid was displaying duplicated images when not enough images to fill the final row [Muhammad Usama](https://github.com/SMUsamaShah)
|
||||||
- Can specify --grid on dream.py command line as the default.
|
- Can specify --grid on invoke.py command line as the default.
|
||||||
- Miscellaneous internal bug and stability fixes.
|
- Miscellaneous internal bug and stability fixes.
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -16,13 +16,13 @@
|
|||||||
|
|
||||||
- Improved file handling, including ability to read prompts from standard input.
|
- Improved file handling, including ability to read prompts from standard input.
|
||||||
(kudos to [Yunsaki](https://github.com/yunsaki)
|
(kudos to [Yunsaki](https://github.com/yunsaki)
|
||||||
- The web server is now integrated with the dream.py script. Invoke by adding --web to
|
- The web server is now integrated with the invoke.py script. Invoke by adding --web to
|
||||||
the dream.py command arguments.
|
the invoke.py command arguments.
|
||||||
- Face restoration and upscaling via GFPGAN and Real-ESGAN are now automatically
|
- Face restoration and upscaling via GFPGAN and Real-ESGAN are now automatically
|
||||||
enabled if the GFPGAN directory is located as a sibling to Stable Diffusion.
|
enabled if the GFPGAN directory is located as a sibling to Stable Diffusion.
|
||||||
VRAM requirements are modestly reduced. Thanks to both [Blessedcoolant](https://github.com/blessedcoolant) and
|
VRAM requirements are modestly reduced. Thanks to both [Blessedcoolant](https://github.com/blessedcoolant) and
|
||||||
[Oceanswave](https://github.com/oceanswave) for their work on this.
|
[Oceanswave](https://github.com/oceanswave) for their work on this.
|
||||||
- You can now swap samplers on the dream> command line. [Blessedcoolant](https://github.com/blessedcoolant)
|
- You can now swap samplers on the invoke> command line. [Blessedcoolant](https://github.com/blessedcoolant)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -32,7 +32,7 @@
|
|||||||
- You now can specify a seed of -1 to use the previous image's seed, -2 to use the seed for the image generated before that, etc.
|
- You now can specify a seed of -1 to use the previous image's seed, -2 to use the seed for the image generated before that, etc.
|
||||||
Seed memory only extends back to the previous command, but will work on all images generated with the -n# switch.
|
Seed memory only extends back to the previous command, but will work on all images generated with the -n# switch.
|
||||||
- Variant generation support temporarily disabled pending more general solution.
|
- Variant generation support temporarily disabled pending more general solution.
|
||||||
- Created a feature branch named **yunsaki-morphing-dream** which adds experimental support for
|
- Created a feature branch named **yunsaki-morphing-invoke** which adds experimental support for
|
||||||
iteratively modifying the prompt and its parameters. Please see[ Pull Request #86](https://github.com/lstein/stable-diffusion/pull/86)
|
iteratively modifying the prompt and its parameters. Please see[ Pull Request #86](https://github.com/lstein/stable-diffusion/pull/86)
|
||||||
for a synopsis of how this works. Note that when this feature is eventually added to the main branch, it will may be modified
|
for a synopsis of how this works. Note that when this feature is eventually added to the main branch, it will may be modified
|
||||||
significantly.
|
significantly.
|
||||||
@ -57,7 +57,7 @@
|
|||||||
|
|
||||||
## v1.08 (24 August 2022)
|
## v1.08 (24 August 2022)
|
||||||
|
|
||||||
- Escape single quotes on the dream> command before trying to parse. This avoids
|
- Escape single quotes on the invoke> command before trying to parse. This avoids
|
||||||
parse errors.
|
parse errors.
|
||||||
- Removed instruction to get Python3.8 as first step in Windows install.
|
- Removed instruction to get Python3.8 as first step in Windows install.
|
||||||
Anaconda3 does it for you.
|
Anaconda3 does it for you.
|
||||||
@ -94,7 +94,7 @@
|
|||||||
be regenerated with the indicated key
|
be regenerated with the indicated key
|
||||||
|
|
||||||
- It should no longer be possible for one image to overwrite another
|
- It should no longer be possible for one image to overwrite another
|
||||||
- You can use the "cd" and "pwd" commands at the dream> prompt to set and retrieve
|
- You can use the "cd" and "pwd" commands at the invoke> prompt to set and retrieve
|
||||||
the path of the output directory.
|
the path of the output directory.
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -128,7 +128,7 @@
|
|||||||
- added k_lms sampling.
|
- added k_lms sampling.
|
||||||
**Please run "conda env update" to load the k_lms dependencies!!**
|
**Please run "conda env update" to load the k_lms dependencies!!**
|
||||||
- use half precision arithmetic by default, resulting in faster execution and lower memory requirements
|
- use half precision arithmetic by default, resulting in faster execution and lower memory requirements
|
||||||
Pass argument --full_precision to dream.py to get slower but more accurate image generation
|
Pass argument --full_precision to invoke.py to get slower but more accurate image generation
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
BIN
docs/assets/Lincoln-and-Parrot-512-transparent.png
Executable file
After Width: | Height: | Size: 284 KiB |
BIN
docs/assets/Lincoln-and-Parrot-512.png
Normal file
After Width: | Height: | Size: 252 KiB |
BIN
docs/assets/invoke-web-server-1.png
Normal file
After Width: | Height: | Size: 983 KiB |
BIN
docs/assets/invoke-web-server-2.png
Normal file
After Width: | Height: | Size: 101 KiB |
BIN
docs/assets/invoke-web-server-3.png
Normal file
After Width: | Height: | Size: 546 KiB |
BIN
docs/assets/invoke-web-server-4.png
Normal file
After Width: | Height: | Size: 336 KiB |
BIN
docs/assets/invoke-web-server-5.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
docs/assets/invoke-web-server-6.png
Normal file
After Width: | Height: | Size: 148 KiB |
BIN
docs/assets/invoke-web-server-7.png
Normal file
After Width: | Height: | Size: 637 KiB |
BIN
docs/assets/invoke-web-server-8.png
Normal file
After Width: | Height: | Size: 529 KiB |
BIN
docs/assets/invoke_web_server.png
Normal file
After Width: | Height: | Size: 989 KiB |
After Width: | Height: | Size: 501 KiB |
After Width: | Height: | Size: 473 KiB |
BIN
docs/assets/prompt-blending/blue-sphere-0.5-red-cube-0.5.png
Normal file
After Width: | Height: | Size: 618 KiB |
After Width: | Height: | Size: 557 KiB |
BIN
docs/assets/prompt-blending/blue-sphere-red-cube-hybrid.png
Normal file
After Width: | Height: | Size: 340 KiB |
@ -4,7 +4,7 @@ title: Changelog
|
|||||||
|
|
||||||
# :octicons-log-16: Changelog
|
# :octicons-log-16: Changelog
|
||||||
|
|
||||||
## v1.13 <small>(in process)</small>
|
## v1.13
|
||||||
|
|
||||||
- Supports a Google Colab notebook for a standalone server running on Google
|
- Supports a Google Colab notebook for a standalone server running on Google
|
||||||
hardware [Arturo Mendivil](https://github.com/artmen1516)
|
hardware [Arturo Mendivil](https://github.com/artmen1516)
|
||||||
@ -12,10 +12,10 @@ title: Changelog
|
|||||||
[Kevin Gibbons](https://github.com/bakkot)
|
[Kevin Gibbons](https://github.com/bakkot)
|
||||||
- WebUI supports incremental display of in-progress images during generation
|
- WebUI supports incremental display of in-progress images during generation
|
||||||
[Kevin Gibbons](https://github.com/bakkot)
|
[Kevin Gibbons](https://github.com/bakkot)
|
||||||
- Output directory can be specified on the dream> command line.
|
- Output directory can be specified on the invoke> command line.
|
||||||
- The grid was displaying duplicated images when not enough images to fill the
|
- The grid was displaying duplicated images when not enough images to fill the
|
||||||
final row [Muhammad Usama](https://github.com/SMUsamaShah)
|
final row [Muhammad Usama](https://github.com/SMUsamaShah)
|
||||||
- Can specify --grid on dream.py command line as the default.
|
- Can specify --grid on invoke.py command line as the default.
|
||||||
- Miscellaneous internal bug and stability fixes.
|
- Miscellaneous internal bug and stability fixes.
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -24,14 +24,14 @@ title: Changelog
|
|||||||
|
|
||||||
- Improved file handling, including ability to read prompts from standard input.
|
- Improved file handling, including ability to read prompts from standard input.
|
||||||
(kudos to [Yunsaki](https://github.com/yunsaki)
|
(kudos to [Yunsaki](https://github.com/yunsaki)
|
||||||
- The web server is now integrated with the dream.py script. Invoke by adding
|
- The web server is now integrated with the invoke.py script. Invoke by adding
|
||||||
--web to the dream.py command arguments.
|
--web to the invoke.py command arguments.
|
||||||
- Face restoration and upscaling via GFPGAN and Real-ESGAN are now automatically
|
- Face restoration and upscaling via GFPGAN and Real-ESGAN are now automatically
|
||||||
enabled if the GFPGAN directory is located as a sibling to Stable Diffusion.
|
enabled if the GFPGAN directory is located as a sibling to Stable Diffusion.
|
||||||
VRAM requirements are modestly reduced. Thanks to both
|
VRAM requirements are modestly reduced. Thanks to both
|
||||||
[Blessedcoolant](https://github.com/blessedcoolant) and
|
[Blessedcoolant](https://github.com/blessedcoolant) and
|
||||||
[Oceanswave](https://github.com/oceanswave) for their work on this.
|
[Oceanswave](https://github.com/oceanswave) for their work on this.
|
||||||
- You can now swap samplers on the dream> command line.
|
- You can now swap samplers on the invoke> command line.
|
||||||
[Blessedcoolant](https://github.com/blessedcoolant)
|
[Blessedcoolant](https://github.com/blessedcoolant)
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -45,7 +45,7 @@ title: Changelog
|
|||||||
back to the previous command, but will work on all images generated with the
|
back to the previous command, but will work on all images generated with the
|
||||||
-n# switch.
|
-n# switch.
|
||||||
- Variant generation support temporarily disabled pending more general solution.
|
- Variant generation support temporarily disabled pending more general solution.
|
||||||
- Created a feature branch named **yunsaki-morphing-dream** which adds
|
- Created a feature branch named **yunsaki-morphing-invoke** which adds
|
||||||
experimental support for iteratively modifying the prompt and its parameters.
|
experimental support for iteratively modifying the prompt and its parameters.
|
||||||
Please
|
Please
|
||||||
see[ Pull Request #86](https://github.com/lstein/stable-diffusion/pull/86) for
|
see[ Pull Request #86](https://github.com/lstein/stable-diffusion/pull/86) for
|
||||||
@ -75,7 +75,7 @@ title: Changelog
|
|||||||
|
|
||||||
## v1.08 <small>(24 August 2022)</small>
|
## v1.08 <small>(24 August 2022)</small>
|
||||||
|
|
||||||
- Escape single quotes on the dream> command before trying to parse. This avoids
|
- Escape single quotes on the invoke> command before trying to parse. This avoids
|
||||||
parse errors.
|
parse errors.
|
||||||
- Removed instruction to get Python3.8 as first step in Windows install.
|
- Removed instruction to get Python3.8 as first step in Windows install.
|
||||||
Anaconda3 does it for you.
|
Anaconda3 does it for you.
|
||||||
@ -112,7 +112,7 @@ title: Changelog
|
|||||||
can be regenerated with the indicated key
|
can be regenerated with the indicated key
|
||||||
|
|
||||||
- It should no longer be possible for one image to overwrite another
|
- It should no longer be possible for one image to overwrite another
|
||||||
- You can use the "cd" and "pwd" commands at the dream> prompt to set and
|
- You can use the "cd" and "pwd" commands at the invoke> prompt to set and
|
||||||
retrieve the path of the output directory.
|
retrieve the path of the output directory.
|
||||||
|
|
||||||
## v1.04 <small>(22 August 2022 - after the drop)</small>
|
## v1.04 <small>(22 August 2022 - after the drop)</small>
|
||||||
@ -139,5 +139,5 @@ title: Changelog
|
|||||||
- added k_lms sampling. **Please run "conda env update -f environment.yaml" to
|
- added k_lms sampling. **Please run "conda env update -f environment.yaml" to
|
||||||
load the k_lms dependencies!!**
|
load the k_lms dependencies!!**
|
||||||
- use half precision arithmetic by default, resulting in faster execution and
|
- use half precision arithmetic by default, resulting in faster execution and
|
||||||
lower memory requirements Pass argument --full_precision to dream.py to get
|
lower memory requirements Pass argument --full_precision to invoke.py to get
|
||||||
slower but more accurate image generation
|
slower but more accurate image generation
|
||||||
|
@ -8,8 +8,8 @@ hide:
|
|||||||
|
|
||||||
## **Interactive Command Line Interface**
|
## **Interactive Command Line Interface**
|
||||||
|
|
||||||
The `dream.py` script, located in `scripts/dream.py`, provides an interactive
|
The `invoke.py` script, located in `scripts/dream.py`, provides an interactive
|
||||||
interface to image generation similar to the "dream mothership" bot that Stable
|
interface to image generation similar to the "invoke mothership" bot that Stable
|
||||||
AI provided on its Discord server.
|
AI provided on its Discord server.
|
||||||
|
|
||||||
Unlike the `txt2img.py` and `img2img.py` scripts provided in the original
|
Unlike the `txt2img.py` and `img2img.py` scripts provided in the original
|
||||||
@ -34,21 +34,21 @@ The script is confirmed to work on Linux, Windows and Mac systems.
|
|||||||
currently rudimentary, but a much better replacement is on its way.
|
currently rudimentary, but a much better replacement is on its way.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
(ldm) ~/stable-diffusion$ python3 ./scripts/dream.py
|
(ldm) ~/stable-diffusion$ python3 ./scripts/invoke.py
|
||||||
* Initializing, be patient...
|
* Initializing, be patient...
|
||||||
Loading model from models/ldm/text2img-large/model.ckpt
|
Loading model from models/ldm/text2img-large/model.ckpt
|
||||||
(...more initialization messages...)
|
(...more initialization messages...)
|
||||||
|
|
||||||
* Initialization done! Awaiting your command...
|
* Initialization done! Awaiting your command...
|
||||||
dream> ashley judd riding a camel -n2 -s150
|
invoke> ashley judd riding a camel -n2 -s150
|
||||||
Outputs:
|
Outputs:
|
||||||
outputs/img-samples/00009.png: "ashley judd riding a camel" -n2 -s150 -S 416354203
|
outputs/img-samples/00009.png: "ashley judd riding a camel" -n2 -s150 -S 416354203
|
||||||
outputs/img-samples/00010.png: "ashley judd riding a camel" -n2 -s150 -S 1362479620
|
outputs/img-samples/00010.png: "ashley judd riding a camel" -n2 -s150 -S 1362479620
|
||||||
|
|
||||||
dream> "there's a fly in my soup" -n6 -g
|
invoke> "there's a fly in my soup" -n6 -g
|
||||||
outputs/img-samples/00011.png: "there's a fly in my soup" -n6 -g -S 2685670268
|
outputs/img-samples/00011.png: "there's a fly in my soup" -n6 -g -S 2685670268
|
||||||
seeds for individual rows: [2685670268, 1216708065, 2335773498, 822223658, 714542046, 3395302430]
|
seeds for individual rows: [2685670268, 1216708065, 2335773498, 822223658, 714542046, 3395302430]
|
||||||
dream> q
|
invoke> q
|
||||||
|
|
||||||
# this shows how to retrieve the prompt stored in the saved image's metadata
|
# this shows how to retrieve the prompt stored in the saved image's metadata
|
||||||
(ldm) ~/stable-diffusion$ python ./scripts/images2prompt.py outputs/img_samples/*.png
|
(ldm) ~/stable-diffusion$ python ./scripts/images2prompt.py outputs/img_samples/*.png
|
||||||
@ -57,10 +57,10 @@ dream> q
|
|||||||
00011.png: "there's a fly in my soup" -n6 -g -S 2685670268
|
00011.png: "there's a fly in my soup" -n6 -g -S 2685670268
|
||||||
```
|
```
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The `dream>` prompt's arguments are pretty much identical to those used in the
|
The `invoke>` prompt's arguments are pretty much identical to those used in the
|
||||||
Discord bot, except you don't need to type "!dream" (it doesn't hurt if you do).
|
Discord bot, except you don't need to type "!invoke" (it doesn't hurt if you do).
|
||||||
A significant change is that creation of individual images is now the default
|
A significant change is that creation of individual images is now the default
|
||||||
unless `--grid` (`-g`) is given. A full list is given in
|
unless `--grid` (`-g`) is given. A full list is given in
|
||||||
[List of prompt arguments](#list-of-prompt-arguments).
|
[List of prompt arguments](#list-of-prompt-arguments).
|
||||||
@ -73,7 +73,7 @@ the location of the model weight files.
|
|||||||
|
|
||||||
### List of arguments recognized at the command line
|
### List of arguments recognized at the command line
|
||||||
|
|
||||||
These command-line arguments can be passed to `dream.py` when you first run it
|
These command-line arguments can be passed to `invoke.py` when you first run it
|
||||||
from the Windows, Mac or Linux command line. Some set defaults that can be
|
from the Windows, Mac or Linux command line. Some set defaults that can be
|
||||||
overridden on a per-prompt basis (see [List of prompt arguments]
|
overridden on a per-prompt basis (see [List of prompt arguments]
|
||||||
(#list-of-prompt-arguments). Others
|
(#list-of-prompt-arguments). Others
|
||||||
@ -112,15 +112,15 @@ These arguments are deprecated but still work:
|
|||||||
| --laion400m | -l | False | Use older LAION400m weights; use `--model=laion400m` instead |
|
| --laion400m | -l | False | Use older LAION400m weights; use `--model=laion400m` instead |
|
||||||
|
|
||||||
**A note on path names:** On Windows systems, you may run into
|
**A note on path names:** On Windows systems, you may run into
|
||||||
problems when passing the dream script standard backslashed path
|
problems when passing the invoke script standard backslashed path
|
||||||
names because the Python interpreter treats "\" as an escape.
|
names because the Python interpreter treats "\" as an escape.
|
||||||
You can either double your slashes (ick): C:\\\\path\\\\to\\\\my\\\\file, or
|
You can either double your slashes (ick): C:\\\\path\\\\to\\\\my\\\\file, or
|
||||||
use Linux/Mac style forward slashes (better): C:/path/to/my/file.
|
use Linux/Mac style forward slashes (better): C:/path/to/my/file.
|
||||||
|
|
||||||
## List of prompt arguments
|
## List of prompt arguments
|
||||||
|
|
||||||
After the dream.py script initializes, it will present you with a
|
After the invoke.py script initializes, it will present you with a
|
||||||
**dream>** prompt. Here you can enter information to generate images
|
**invoke>** prompt. Here you can enter information to generate images
|
||||||
from text (txt2img), to embellish an existing image or sketch
|
from text (txt2img), to embellish an existing image or sketch
|
||||||
(img2img), or to selectively alter chosen regions of the image
|
(img2img), or to selectively alter chosen regions of the image
|
||||||
(inpainting).
|
(inpainting).
|
||||||
@ -128,13 +128,13 @@ from text (txt2img), to embellish an existing image or sketch
|
|||||||
### This is an example of txt2img:
|
### This is an example of txt2img:
|
||||||
|
|
||||||
~~~~
|
~~~~
|
||||||
dream> waterfall and rainbow -W640 -H480
|
invoke> waterfall and rainbow -W640 -H480
|
||||||
~~~~
|
~~~~
|
||||||
|
|
||||||
This will create the requested image with the dimensions 640 (width)
|
This will create the requested image with the dimensions 640 (width)
|
||||||
and 480 (height).
|
and 480 (height).
|
||||||
|
|
||||||
Here are the dream> command that apply to txt2img:
|
Here are the invoke> command that apply to txt2img:
|
||||||
|
|
||||||
| Argument | Shortcut | Default | Description |
|
| Argument | Shortcut | Default | Description |
|
||||||
|--------------------|------------|---------------------|--------------|
|
|--------------------|------------|---------------------|--------------|
|
||||||
@ -167,7 +167,7 @@ the nearest multiple of 64.
|
|||||||
### This is an example of img2img:
|
### This is an example of img2img:
|
||||||
|
|
||||||
~~~~
|
~~~~
|
||||||
dream> waterfall and rainbow -I./vacation-photo.png -W640 -H480 --fit
|
invoke> waterfall and rainbow -I./vacation-photo.png -W640 -H480 --fit
|
||||||
~~~~
|
~~~~
|
||||||
|
|
||||||
This will modify the indicated vacation photograph by making it more
|
This will modify the indicated vacation photograph by making it more
|
||||||
@ -188,7 +188,7 @@ accepts additional options:
|
|||||||
### This is an example of inpainting:
|
### This is an example of inpainting:
|
||||||
|
|
||||||
~~~~
|
~~~~
|
||||||
dream> waterfall and rainbow -I./vacation-photo.png -M./vacation-mask.png -W640 -H480 --fit
|
invoke> waterfall and rainbow -I./vacation-photo.png -M./vacation-mask.png -W640 -H480 --fit
|
||||||
~~~~
|
~~~~
|
||||||
|
|
||||||
This will do the same thing as img2img, but image alterations will
|
This will do the same thing as img2img, but image alterations will
|
||||||
@ -224,20 +224,20 @@ Some examples:
|
|||||||
|
|
||||||
Upscale to 4X its original size and fix faces using codeformer:
|
Upscale to 4X its original size and fix faces using codeformer:
|
||||||
~~~
|
~~~
|
||||||
dream> !fix 0000045.4829112.png -G1 -U4 -ft codeformer
|
invoke> !fix 0000045.4829112.png -G1 -U4 -ft codeformer
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
Use the GFPGAN algorithm to fix faces, then upscale to 3X using --embiggen:
|
Use the GFPGAN algorithm to fix faces, then upscale to 3X using --embiggen:
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
dream> !fix 0000045.4829112.png -G0.8 -ft gfpgan
|
invoke> !fix 0000045.4829112.png -G0.8 -ft gfpgan
|
||||||
>> fixing outputs/img-samples/0000045.4829112.png
|
>> fixing outputs/img-samples/0000045.4829112.png
|
||||||
>> retrieved seed 4829112 and prompt "boy enjoying a banana split"
|
>> retrieved seed 4829112 and prompt "boy enjoying a banana split"
|
||||||
>> GFPGAN - Restoring Faces for image seed:4829112
|
>> GFPGAN - Restoring Faces for image seed:4829112
|
||||||
Outputs:
|
Outputs:
|
||||||
[1] outputs/img-samples/000017.4829112.gfpgan-00.png: !fix "outputs/img-samples/0000045.4829112.png" -s 50 -S -W 512 -H 512 -C 7.5 -A k_lms -G 0.8
|
[1] outputs/img-samples/000017.4829112.gfpgan-00.png: !fix "outputs/img-samples/0000045.4829112.png" -s 50 -S -W 512 -H 512 -C 7.5 -A k_lms -G 0.8
|
||||||
|
|
||||||
dream> !fix 000017.4829112.gfpgan-00.png --embiggen 3
|
invoke> !fix 000017.4829112.gfpgan-00.png --embiggen 3
|
||||||
...lots of text...
|
...lots of text...
|
||||||
Outputs:
|
Outputs:
|
||||||
[2] outputs/img-samples/000018.2273800735.embiggen-00.png: !fix "outputs/img-samples/000017.243781548.gfpgan-00.png" -s 50 -S 2273800735 -W 512 -H 512 -C 7.5 -A k_lms --embiggen 3.0 0.75 0.25
|
[2] outputs/img-samples/000018.2273800735.embiggen-00.png: !fix "outputs/img-samples/000017.243781548.gfpgan-00.png" -s 50 -S 2273800735 -W 512 -H 512 -C 7.5 -A k_lms --embiggen 3.0 0.75 0.25
|
||||||
@ -251,9 +251,9 @@ provide either the name of a file in the current output directory, or
|
|||||||
a full file path.
|
a full file path.
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
dream> !fetch 0000015.8929913.png
|
invoke> !fetch 0000015.8929913.png
|
||||||
# the script returns the next line, ready for editing and running:
|
# the script returns the next line, ready for editing and running:
|
||||||
dream> a fantastic alien landscape -W 576 -H 512 -s 60 -A plms -C 7.5
|
invoke> a fantastic alien landscape -W 576 -H 512 -s 60 -A plms -C 7.5
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
Note that this command may behave unexpectedly if given a PNG file that
|
Note that this command may behave unexpectedly if given a PNG file that
|
||||||
@ -261,7 +261,7 @@ was not generated by InvokeAI.
|
|||||||
|
|
||||||
## !history
|
## !history
|
||||||
|
|
||||||
The dream script keeps track of all the commands you issue during a
|
The invoke script keeps track of all the commands you issue during a
|
||||||
session, allowing you to re-run them. On Mac and Linux systems, it
|
session, allowing you to re-run them. On Mac and Linux systems, it
|
||||||
also writes the command-line history out to disk, giving you access to
|
also writes the command-line history out to disk, giving you access to
|
||||||
the most recent 1000 commands issued.
|
the most recent 1000 commands issued.
|
||||||
@ -272,7 +272,7 @@ issued during the session (Windows), or the most recent 1000 commands
|
|||||||
where "NNN" is the history line number. For example:
|
where "NNN" is the history line number. For example:
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
dream> !history
|
invoke> !history
|
||||||
...
|
...
|
||||||
[14] happy woman sitting under tree wearing broad hat and flowing garment
|
[14] happy woman sitting under tree wearing broad hat and flowing garment
|
||||||
[15] beautiful woman sitting under tree wearing broad hat and flowing garment
|
[15] beautiful woman sitting under tree wearing broad hat and flowing garment
|
||||||
@ -280,8 +280,8 @@ dream> !history
|
|||||||
[20] watercolor of beautiful woman sitting under tree wearing broad hat and flowing garment -v0.2 -n6 -S2878767194
|
[20] watercolor of beautiful woman sitting under tree wearing broad hat and flowing garment -v0.2 -n6 -S2878767194
|
||||||
[21] surrealist painting of beautiful woman sitting under tree wearing broad hat and flowing garment -v0.2 -n6 -S2878767194
|
[21] surrealist painting of beautiful woman sitting under tree wearing broad hat and flowing garment -v0.2 -n6 -S2878767194
|
||||||
...
|
...
|
||||||
dream> !20
|
invoke> !20
|
||||||
dream> watercolor of beautiful woman sitting under tree wearing broad hat and flowing garment -v0.2 -n6 -S2878767194
|
invoke> watercolor of beautiful woman sitting under tree wearing broad hat and flowing garment -v0.2 -n6 -S2878767194
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
## !search <search string>
|
## !search <search string>
|
||||||
@ -290,7 +290,7 @@ This is similar to !history but it only returns lines that contain
|
|||||||
`search string`. For example:
|
`search string`. For example:
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
dream> !search surreal
|
invoke> !search surreal
|
||||||
[21] surrealist painting of beautiful woman sitting under tree wearing broad hat and flowing garment -v0.2 -n6 -S2878767194
|
[21] surrealist painting of beautiful woman sitting under tree wearing broad hat and flowing garment -v0.2 -n6 -S2878767194
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
@ -312,16 +312,16 @@ command completion.
|
|||||||
- To paste a cut section back in, position the cursor where you want to paste, and type CTRL-Y
|
- To paste a cut section back in, position the cursor where you want to paste, and type CTRL-Y
|
||||||
|
|
||||||
Windows users can get similar, but more limited, functionality if they
|
Windows users can get similar, but more limited, functionality if they
|
||||||
launch dream.py with the "winpty" program and have the `pyreadline3`
|
launch invoke.py with the "winpty" program and have the `pyreadline3`
|
||||||
library installed:
|
library installed:
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
> winpty python scripts\dream.py
|
> winpty python scripts\invoke.py
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
On the Mac and Linux platforms, when you exit dream.py, the last 1000
|
On the Mac and Linux platforms, when you exit invoke.py, the last 1000
|
||||||
lines of your command-line history will be saved. When you restart
|
lines of your command-line history will be saved. When you restart
|
||||||
dream.py, you can access the saved history using the up-arrow key.
|
invoke.py, you can access the saved history using the up-arrow key.
|
||||||
|
|
||||||
In addition, limited command-line completion is installed. In various
|
In addition, limited command-line completion is installed. In various
|
||||||
contexts, you can start typing your command and press tab. A list of
|
contexts, you can start typing your command and press tab. A list of
|
||||||
@ -334,7 +334,7 @@ will attempt to complete pathnames for you. This is most handy for the
|
|||||||
the path with a slash ("/") or "./". For example:
|
the path with a slash ("/") or "./". For example:
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
dream> zebra with a mustache -I./test-pictures<TAB>
|
invoke> zebra with a mustache -I./test-pictures<TAB>
|
||||||
-I./test-pictures/Lincoln-and-Parrot.png -I./test-pictures/zebra.jpg -I./test-pictures/madonna.png
|
-I./test-pictures/Lincoln-and-Parrot.png -I./test-pictures/zebra.jpg -I./test-pictures/madonna.png
|
||||||
-I./test-pictures/bad-sketch.png -I./test-pictures/man_with_eagle/
|
-I./test-pictures/bad-sketch.png -I./test-pictures/man_with_eagle/
|
||||||
```
|
```
|
||||||
|
@ -106,8 +106,8 @@ Running Embiggen with 512x512 tiles on an existing image, scaling up by a factor
|
|||||||
and doing the same again (default ESRGAN strength is 0.75, default overlap between tiles is 0.25):
|
and doing the same again (default ESRGAN strength is 0.75, default overlap between tiles is 0.25):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dream > a photo of a forest at sunset -s 100 -W 512 -H 512 -I outputs/forest.png -f 0.4 -embiggen 2.5
|
invoke > a photo of a forest at sunset -s 100 -W 512 -H 512 -I outputs/forest.png -f 0.4 -embiggen 2.5
|
||||||
dream > a photo of a forest at sunset -s 100 -W 512 -H 512 -I outputs/forest.png -f 0.4 -embiggen 2.5 0.75 0.25
|
invoke > a photo of a forest at sunset -s 100 -W 512 -H 512 -I outputs/forest.png -f 0.4 -embiggen 2.5 0.75 0.25
|
||||||
```
|
```
|
||||||
|
|
||||||
If your starting image was also 512x512 this should have taken 9 tiles.
|
If your starting image was also 512x512 this should have taken 9 tiles.
|
||||||
@ -118,7 +118,7 @@ If there weren't enough clouds in the sky of that forest you just made
|
|||||||
tiles:
|
tiles:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dream> a photo of puffy clouds over a forest at sunset -s 100 -W 512 -H 512 -I outputs/000002.seed.png -f 0.5 -embiggen_tiles 1 2 3
|
invoke> a photo of puffy clouds over a forest at sunset -s 100 -W 512 -H 512 -I outputs/000002.seed.png -f 0.5 -embiggen_tiles 1 2 3
|
||||||
```
|
```
|
||||||
|
|
||||||
## Fixing Previously-Generated Images
|
## Fixing Previously-Generated Images
|
||||||
@ -129,7 +129,7 @@ syntax `!fix path/to/file.png <embiggen>`. For example, you can rewrite the
|
|||||||
previous command to look like this:
|
previous command to look like this:
|
||||||
|
|
||||||
~~~~
|
~~~~
|
||||||
dream> !fix ./outputs/000002.seed.png -embiggen_tiles 1 2 3
|
invoke> !fix ./outputs/000002.seed.png -embiggen_tiles 1 2 3
|
||||||
~~~~
|
~~~~
|
||||||
|
|
||||||
A new file named `000002.seed.fixed.png` will be created in the output directory. Note that
|
A new file named `000002.seed.fixed.png` will be created in the output directory. Note that
|
||||||
|
@ -10,18 +10,39 @@ top of the image you provide, preserving the original's basic shape and layout.
|
|||||||
the `--init_img` option as shown here:
|
the `--init_img` option as shown here:
|
||||||
|
|
||||||
```commandline
|
```commandline
|
||||||
dream> "waterfall and rainbow" --init_img=./init-images/crude_drawing.png --strength=0.5 -s100 -n4
|
tree on a hill with a river, nature photograph, national geographic -I./test-pictures/tree-and-river-sketch.png -f 0.85
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This will take the original image shown here:
|
||||||
|
|
||||||
|
<img src="https://user-images.githubusercontent.com/50542132/193946000-c42a96d8-5a74-4f8a-b4c3-5213e6cadcce.png" width=350>
|
||||||
|
|
||||||
|
and generate a new image based on it as shown here:
|
||||||
|
|
||||||
|
<img src="https://user-images.githubusercontent.com/111189/194135515-53d4c060-e994-4016-8121-7c685e281ac9.png" width=350>
|
||||||
|
|
||||||
The `--init_img (-I)` option gives the path to the seed picture. `--strength (-f)` controls how much
|
The `--init_img (-I)` option gives the path to the seed picture. `--strength (-f)` controls how much
|
||||||
the original will be modified, ranging from `0.0` (keep the original intact), to `1.0` (ignore the
|
the original will be modified, ranging from `0.0` (keep the original intact), to `1.0` (ignore the
|
||||||
original completely). The default is `0.75`, and ranges from `0.25-0.75` give interesting results.
|
original completely). The default is `0.75`, and ranges from `0.25-0.90` give interesting results.
|
||||||
|
Other relevant options include `-C` (classification free guidance scale), and `-s` (steps). Unlike `txt2img`,
|
||||||
|
adding steps will continuously change the resulting image and it will not converge.
|
||||||
|
|
||||||
You may also pass a `-v<variation_amount>` option to generate `-n<iterations>` count variants on
|
You may also pass a `-v<variation_amount>` option to generate `-n<iterations>` count variants on
|
||||||
the original image. This is done by passing the first generated image
|
the original image. This is done by passing the first generated image
|
||||||
back into img2img the requested number of times. It generates
|
back into img2img the requested number of times. It generates
|
||||||
interesting variants.
|
interesting variants.
|
||||||
|
|
||||||
|
Note that the prompt makes a big difference. For example, this slight variation on the prompt produces
|
||||||
|
a very different image:
|
||||||
|
|
||||||
|
`photograph of a tree on a hill with a river`
|
||||||
|
|
||||||
|
<img src="https://user-images.githubusercontent.com/111189/194135220-16b62181-b60c-4248-8989-4834a8fd7fbd.png" width=350>
|
||||||
|
|
||||||
|
(When designing prompts, think about how the images scraped from the internet were captioned. Very few photographs will
|
||||||
|
be labeled "photograph" or "photorealistic." They will, however, be captioned with the publication, photographer, camera
|
||||||
|
model, or film settings.)
|
||||||
|
|
||||||
If the initial image contains transparent regions, then Stable Diffusion will only draw within the
|
If the initial image contains transparent regions, then Stable Diffusion will only draw within the
|
||||||
transparent regions, a process called "inpainting". However, for this to work correctly, the color
|
transparent regions, a process called "inpainting". However, for this to work correctly, the color
|
||||||
information underneath the transparent needs to be preserved, not erased.
|
information underneath the transparent needs to be preserved, not erased.
|
||||||
@ -29,6 +50,17 @@ information underneath the transparent needs to be preserved, not erased.
|
|||||||
More details can be found here:
|
More details can be found here:
|
||||||
[Creating Transparent Images For Inpainting](./INPAINTING.md#creating-transparent-regions-for-inpainting)
|
[Creating Transparent Images For Inpainting](./INPAINTING.md#creating-transparent-regions-for-inpainting)
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
**IMPORTANT ISSUE** `img2img` does not work properly on initial images smaller than 512x512. Please scale your
|
||||||
|
image to at least 512x512 before using it. Larger images are not a problem, but may run out of VRAM on your
|
||||||
|
GPU card. To fix this, use the --fit option, which downscales the initial image to fit within the box specified
|
||||||
|
by width x height:
|
||||||
|
~~~
|
||||||
|
tree on a hill with a river, national geographic -I./test-pictures/big-sketch.png -H512 -W512 --fit
|
||||||
|
~~~
|
||||||
|
|
||||||
|
>>>>>>> main
|
||||||
## How does it actually work, though?
|
## How does it actually work, though?
|
||||||
|
|
||||||
The main difference between `img2img` and `prompt2img` is the starting point. While `prompt2img` always starts with pure
|
The main difference between `img2img` and `prompt2img` is the starting point. While `prompt2img` always starts with pure
|
||||||
@ -38,7 +70,11 @@ gaussian noise and progressively refines it over the requested number of steps,
|
|||||||
**Let's start** by thinking about vanilla `prompt2img`, just generating an image from a prompt. If the step count is 10, then the "latent space" (Stable Diffusion's internal representation of the image) for the prompt "fire" with seed `1592514025` develops something like this:
|
**Let's start** by thinking about vanilla `prompt2img`, just generating an image from a prompt. If the step count is 10, then the "latent space" (Stable Diffusion's internal representation of the image) for the prompt "fire" with seed `1592514025` develops something like this:
|
||||||
|
|
||||||
```commandline
|
```commandline
|
||||||
|
<<<<<<< HEAD
|
||||||
dream> "fire" -s10 -W384 -H384 -S1592514025
|
dream> "fire" -s10 -W384 -H384 -S1592514025
|
||||||
|
=======
|
||||||
|
invoke> "fire" -s10 -W384 -H384 -S1592514025
|
||||||
|
>>>>>>> main
|
||||||
```
|
```
|
||||||
|
|
||||||

|

|
||||||
@ -66,7 +102,11 @@ Notice how much more fuzzy the starting image is for strength `0.7` compared to
|
|||||||
| | strength = 0.7 | strength = 0.4 |
|
| | strength = 0.7 | strength = 0.4 |
|
||||||
| -- | -- | -- |
|
| -- | -- | -- |
|
||||||
| initial image that SD sees |  |  |
|
| initial image that SD sees |  |  |
|
||||||
|
<<<<<<< HEAD
|
||||||
| steps argument to `dream>` | `-S10` | `-S10` |
|
| steps argument to `dream>` | `-S10` | `-S10` |
|
||||||
|
=======
|
||||||
|
| steps argument to `invoke>` | `-S10` | `-S10` |
|
||||||
|
>>>>>>> main
|
||||||
| steps actually taken | 7 | 4 |
|
| steps actually taken | 7 | 4 |
|
||||||
| latent space at each step |  |  |
|
| latent space at each step |  |  |
|
||||||
| output |  |  |
|
| output |  |  |
|
||||||
@ -77,10 +117,17 @@ Both of the outputs look kind of like what I was thinking of. With the strength
|
|||||||
If you want to try this out yourself, all of these are using a seed of `1592514025` with a width/height of `384`, step count `10`, the default sampler (`k_lms`), and the single-word prompt `fire`:
|
If you want to try this out yourself, all of these are using a seed of `1592514025` with a width/height of `384`, step count `10`, the default sampler (`k_lms`), and the single-word prompt `fire`:
|
||||||
|
|
||||||
```commandline
|
```commandline
|
||||||
|
<<<<<<< HEAD
|
||||||
dream> "fire" -s10 -W384 -H384 -S1592514025 -I /tmp/fire-drawing.png --strength 0.7
|
dream> "fire" -s10 -W384 -H384 -S1592514025 -I /tmp/fire-drawing.png --strength 0.7
|
||||||
```
|
```
|
||||||
|
|
||||||
The code for rendering intermediates is on my (damian0815's) branch [document-img2img](https://github.com/damian0815/InvokeAI/tree/document-img2img) - run `dream.py` and check your `outputs/img-samples/intermediates` folder while generating an image.
|
The code for rendering intermediates is on my (damian0815's) branch [document-img2img](https://github.com/damian0815/InvokeAI/tree/document-img2img) - run `dream.py` and check your `outputs/img-samples/intermediates` folder while generating an image.
|
||||||
|
=======
|
||||||
|
invoke> "fire" -s10 -W384 -H384 -S1592514025 -I /tmp/fire-drawing.png --strength 0.7
|
||||||
|
```
|
||||||
|
|
||||||
|
The code for rendering intermediates is on my (damian0815's) branch [document-img2img](https://github.com/damian0815/InvokeAI/tree/document-img2img) - run `invoke.py` and check your `outputs/img-samples/intermediates` folder while generating an image.
|
||||||
|
>>>>>>> main
|
||||||
|
|
||||||
### Compensating for the reduced step count
|
### Compensating for the reduced step count
|
||||||
|
|
||||||
@ -89,7 +136,11 @@ After putting this guide together I was curious to see how the difference would
|
|||||||
Here's strength `0.4` (note step count `50`, which is `20 ÷ 0.4` to make sure SD does `20` steps from my image):
|
Here's strength `0.4` (note step count `50`, which is `20 ÷ 0.4` to make sure SD does `20` steps from my image):
|
||||||
|
|
||||||
```commandline
|
```commandline
|
||||||
|
<<<<<<< HEAD
|
||||||
dream> "fire" -s50 -W384 -H384 -S1592514025 -I /tmp/fire-drawing.png -f 0.4
|
dream> "fire" -s50 -W384 -H384 -S1592514025 -I /tmp/fire-drawing.png -f 0.4
|
||||||
|
=======
|
||||||
|
invoke> "fire" -s50 -W384 -H384 -S1592514025 -I /tmp/fire-drawing.png -f 0.4
|
||||||
|
>>>>>>> main
|
||||||
```
|
```
|
||||||
|
|
||||||

|

|
||||||
@ -97,7 +148,11 @@ dream> "fire" -s50 -W384 -H384 -S1592514025 -I /tmp/fire-drawing.png -f 0.4
|
|||||||
and strength `0.7` (note step count `30`, which is roughly `20 ÷ 0.7` to make sure SD does `20` steps from my image):
|
and strength `0.7` (note step count `30`, which is roughly `20 ÷ 0.7` to make sure SD does `20` steps from my image):
|
||||||
|
|
||||||
```commandline
|
```commandline
|
||||||
|
<<<<<<< HEAD
|
||||||
dream> "fire" -s30 -W384 -H384 -S1592514025 -I /tmp/fire-drawing.png -f 0.7
|
dream> "fire" -s30 -W384 -H384 -S1592514025 -I /tmp/fire-drawing.png -f 0.7
|
||||||
|
=======
|
||||||
|
invoke> "fire" -s30 -W384 -H384 -S1592514025 -I /tmp/fire-drawing.png -f 0.7
|
||||||
|
>>>>>>> main
|
||||||
```
|
```
|
||||||
|
|
||||||

|

|
||||||
|
@ -8,7 +8,7 @@ title: Inpainting
|
|||||||
|
|
||||||
Inpainting is really cool. To do it, you start with an initial image and use a photoeditor to make
|
Inpainting is really cool. To do it, you start with an initial image and use a photoeditor to make
|
||||||
one or more regions transparent (i.e. they have a "hole" in them). You then provide the path to this
|
one or more regions transparent (i.e. they have a "hole" in them). You then provide the path to this
|
||||||
image at the dream> command line using the `-I` switch. Stable Diffusion will only paint within the
|
image at the invoke> command line using the `-I` switch. Stable Diffusion will only paint within the
|
||||||
transparent region.
|
transparent region.
|
||||||
|
|
||||||
There's a catch. In the current implementation, you have to prepare the initial image correctly so
|
There's a catch. In the current implementation, you have to prepare the initial image correctly so
|
||||||
@ -17,13 +17,13 @@ applications will by default erase the color information under the transparent p
|
|||||||
them with white or black, which will lead to suboptimal inpainting. You also must take care to
|
them with white or black, which will lead to suboptimal inpainting. You also must take care to
|
||||||
export the PNG file in such a way that the color information is preserved.
|
export the PNG file in such a way that the color information is preserved.
|
||||||
|
|
||||||
If your photoeditor is erasing the underlying color information, `dream.py` will give you a big fat
|
If your photoeditor is erasing the underlying color information, `invoke.py` will give you a big fat
|
||||||
warning. If you can't find a way to coax your photoeditor to retain color values under transparent
|
warning. If you can't find a way to coax your photoeditor to retain color values under transparent
|
||||||
areas, then you can combine the `-I` and `-M` switches to provide both the original unedited image
|
areas, then you can combine the `-I` and `-M` switches to provide both the original unedited image
|
||||||
and the masked (partially transparent) image:
|
and the masked (partially transparent) image:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dream> "man with cat on shoulder" -I./images/man.png -M./images/man-transparent.png
|
invoke> "man with cat on shoulder" -I./images/man.png -M./images/man-transparent.png
|
||||||
```
|
```
|
||||||
|
|
||||||
We are hoping to get rid of the need for this workaround in an upcoming release.
|
We are hoping to get rid of the need for this workaround in an upcoming release.
|
||||||
@ -38,8 +38,8 @@ We are hoping to get rid of the need for this workaround in an upcoming release.
|
|||||||
2. Layer->Transparency->Add Alpha Channel
|
2. Layer->Transparency->Add Alpha Channel
|
||||||
3. Use lasoo tool to select region to mask
|
3. Use lasoo tool to select region to mask
|
||||||
4. Choose Select -> Float to create a floating selection
|
4. Choose Select -> Float to create a floating selection
|
||||||
5. Open the Layers toolbar (++ctrl+l++) and select "Floating Selection"
|
5. Open the Layers toolbar (^L) and select "Floating Selection"
|
||||||
6. Set opacity to 0%
|
6. Set opacity to a value between 0% and 99%
|
||||||
7. Export as PNG
|
7. Export as PNG
|
||||||
8. In the export dialogue, Make sure the "Save colour values from
|
8. In the export dialogue, Make sure the "Save colour values from
|
||||||
transparent pixels" checkbox is selected.
|
transparent pixels" checkbox is selected.
|
||||||
@ -69,7 +69,7 @@ We are hoping to get rid of the need for this workaround in an upcoming release.
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
7. After following the inpainting instructions above (either through the CLI or the Web UI), marvel at your newfound ability to selectively dream. Lookin' good!
|
7. After following the inpainting instructions above (either through the CLI or the Web UI), marvel at your newfound ability to selectively invoke. Lookin' good!
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
@ -22,10 +22,10 @@ Output Example: 
|
|||||||
|
|
||||||
The seamless tiling mode causes generated images to seamlessly tile with itself. To use it, add the
|
The seamless tiling mode causes generated images to seamlessly tile with itself. To use it, add the
|
||||||
`--seamless` option when starting the script which will result in all generated images to tile, or
|
`--seamless` option when starting the script which will result in all generated images to tile, or
|
||||||
for each `dream>` prompt as shown here:
|
for each `invoke>` prompt as shown here:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
dream> "pond garden with lotus by claude monet" --seamless -s100 -n4
|
invoke> "pond garden with lotus by claude monet" --seamless -s100 -n4
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -42,12 +42,12 @@ Here's an example of using this to do a quick refinement. It also illustrates us
|
|||||||
switch to turn on upscaling and face enhancement (see previous section):
|
switch to turn on upscaling and face enhancement (see previous section):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dream> a cute child playing hopscotch -G0.5
|
invoke> a cute child playing hopscotch -G0.5
|
||||||
[...]
|
[...]
|
||||||
outputs/img-samples/000039.3498014304.png: "a cute child playing hopscotch" -s50 -W512 -H512 -C7.5 -mk_lms -S3498014304
|
outputs/img-samples/000039.3498014304.png: "a cute child playing hopscotch" -s50 -W512 -H512 -C7.5 -mk_lms -S3498014304
|
||||||
|
|
||||||
# I wonder what it will look like if I bump up the steps and set facial enhancement to full strength?
|
# I wonder what it will look like if I bump up the steps and set facial enhancement to full strength?
|
||||||
dream> a cute child playing hopscotch -G1.0 -s100 -S -1
|
invoke> a cute child playing hopscotch -G1.0 -s100 -S -1
|
||||||
reusing previous seed 3498014304
|
reusing previous seed 3498014304
|
||||||
[...]
|
[...]
|
||||||
outputs/img-samples/000040.3498014304.png: "a cute child playing hopscotch" -G1.0 -s100 -W512 -H512 -C7.5 -mk_lms -S3498014304
|
outputs/img-samples/000040.3498014304.png: "a cute child playing hopscotch" -G1.0 -s100 -W512 -H512 -C7.5 -mk_lms -S3498014304
|
||||||
|
@ -31,7 +31,7 @@ Pretty nice, but it's annoying that the top of her head is cut
|
|||||||
off. She's also a bit off center. Let's fix that!
|
off. She's also a bit off center. Let's fix that!
|
||||||
|
|
||||||
~~~~
|
~~~~
|
||||||
dream> !fix images/curly.png --outcrop top 64 right 64
|
invoke> !fix images/curly.png --outcrop top 64 right 64
|
||||||
~~~~
|
~~~~
|
||||||
|
|
||||||
This is saying to apply the `outcrop` extension by extending the top
|
This is saying to apply the `outcrop` extension by extending the top
|
||||||
@ -67,7 +67,7 @@ differences. Starting with the same image, here is how we would add an
|
|||||||
additional 64 pixels to the top of the image:
|
additional 64 pixels to the top of the image:
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
dream> !fix images/curly.png --out_direction top 64
|
invoke> !fix images/curly.png --out_direction top 64
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
(you can abbreviate ``--out_direction` as `-D`.
|
(you can abbreviate ``--out_direction` as `-D`.
|
||||||
|
@ -20,39 +20,33 @@ The default face restoration module is GFPGAN. The default upscale is
|
|||||||
Real-ESRGAN. For an alternative face restoration module, see [CodeFormer
|
Real-ESRGAN. For an alternative face restoration module, see [CodeFormer
|
||||||
Support] below.
|
Support] below.
|
||||||
|
|
||||||
As of version 1.14, environment.yaml will install the Real-ESRGAN package into
|
As of version 1.14, environment.yaml will install the Real-ESRGAN
|
||||||
the standard install location for python packages, and will put GFPGAN into a
|
package into the standard install location for python packages, and
|
||||||
subdirectory of "src" in the InvokeAI directory. (The reason for this is
|
will put GFPGAN into a subdirectory of "src" in the InvokeAI
|
||||||
that the standard GFPGAN distribution has a minor bug that adversely affects
|
directory. Upscaling with Real-ESRGAN should "just work" without
|
||||||
image color.) Upscaling with Real-ESRGAN should "just work" without further
|
further intervention. Simply pass the --upscale (-U) option on the
|
||||||
intervention. Simply pass the --upscale (-U) option on the dream> command line,
|
invoke> command line, or indicate the desired scale on the popup in
|
||||||
or indicate the desired scale on the popup in the Web GUI.
|
the Web GUI.
|
||||||
|
|
||||||
For **GFPGAN** to work, there is one additional step needed. You will need to
|
**GFPGAN** requires a series of downloadable model files to
|
||||||
download and copy the GFPGAN
|
work. These are loaded when you run `scripts/preload_models.py`. If
|
||||||
[models file](https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.4.pth)
|
GFPAN is failing with an error, please run the following from the
|
||||||
into **src/gfpgan/experiments/pretrained_models**. On Mac and Linux systems,
|
InvokeAI directory:
|
||||||
here's how you'd do it using **wget**:
|
|
||||||
|
|
||||||
```bash
|
~~~~
|
||||||
wget https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.4.pth -P src/gfpgan/experiments/pretrained_models/
|
python scripts/preload_models.py
|
||||||
```
|
~~~~
|
||||||
|
|
||||||
Make sure that you're in the InvokeAI directory when you do this.
|
If you do not run this script in advance, the GFPGAN module will attempt
|
||||||
|
to download the models files the first time you try to perform facial
|
||||||
|
reconstruction.
|
||||||
|
|
||||||
Alternatively, if you have GFPGAN installed elsewhere, or if you are using an
|
Alternatively, if you have GFPGAN installed elsewhere, or if you are
|
||||||
earlier version of this package which asked you to install GFPGAN in a sibling
|
using an earlier version of this package which asked you to install
|
||||||
directory, you may use the `--gfpgan_dir` argument with `dream.py` to set a
|
GFPGAN in a sibling directory, you may use the `--gfpgan_dir` argument
|
||||||
custom path to your GFPGAN directory. _There are other GFPGAN related boot
|
with `invoke.py` to set a custom path to your GFPGAN directory. _There
|
||||||
arguments if you wish to customize further._
|
are other GFPGAN related boot arguments if you wish to customize
|
||||||
|
further._
|
||||||
!!! warning "Internet connection needed"
|
|
||||||
|
|
||||||
Users whose GPU machines are isolated from the Internet (e.g.
|
|
||||||
on a University cluster) should be aware that the first time you run dream.py with GFPGAN and
|
|
||||||
Real-ESRGAN turned on, it will try to download model files from the Internet. To rectify this, you
|
|
||||||
may run `python3 scripts/preload_models.py` after you have installed GFPGAN and all its
|
|
||||||
dependencies.
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@ -94,13 +88,13 @@ too.
|
|||||||
### Example Usage
|
### Example Usage
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dream> superman dancing with a panda bear -U 2 0.6 -G 0.4
|
invoke> superman dancing with a panda bear -U 2 0.6 -G 0.4
|
||||||
```
|
```
|
||||||
|
|
||||||
This also works with img2img:
|
This also works with img2img:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dream> a man wearing a pineapple hat -I path/to/your/file.png -U 2 0.5 -G 0.6
|
invoke> a man wearing a pineapple hat -I path/to/your/file.png -U 2 0.5 -G 0.6
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
@ -124,15 +118,15 @@ actions.
|
|||||||
This repo also allows you to perform face restoration using
|
This repo also allows you to perform face restoration using
|
||||||
[CodeFormer](https://github.com/sczhou/CodeFormer).
|
[CodeFormer](https://github.com/sczhou/CodeFormer).
|
||||||
|
|
||||||
In order to setup CodeFormer to work, you need to download the models like with
|
In order to setup CodeFormer to work, you need to download the models
|
||||||
GFPGAN. You can do this either by running `preload_models.py` or by manually
|
like with GFPGAN. You can do this either by running
|
||||||
downloading the
|
`preload_models.py` or by manually downloading the [model
|
||||||
[model file](https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/codeformer.pth)
|
file](https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/codeformer.pth)
|
||||||
and saving it to `ldm/restoration/codeformer/weights` folder.
|
and saving it to `ldm/restoration/codeformer/weights` folder.
|
||||||
|
|
||||||
You can use `-ft` prompt argument to swap between CodeFormer and the default
|
You can use `-ft` prompt argument to swap between CodeFormer and the
|
||||||
GFPGAN. The above mentioned `-G` prompt argument will allow you to control the
|
default GFPGAN. The above mentioned `-G` prompt argument will allow
|
||||||
strength of the restoration effect.
|
you to control the strength of the restoration effect.
|
||||||
|
|
||||||
### Usage:
|
### Usage:
|
||||||
|
|
||||||
@ -168,7 +162,7 @@ previously-generated file. Just use the syntax `!fix path/to/file.png
|
|||||||
just run:
|
just run:
|
||||||
|
|
||||||
```
|
```
|
||||||
dream> !fix ./outputs/img-samples/000044.2945021133.png -G 0.8 -U 2
|
invoke> !fix ./outputs/img-samples/000044.2945021133.png -G 0.8 -U 2
|
||||||
```
|
```
|
||||||
|
|
||||||
A new file named `000044.2945021133.fixed.png` will be created in the output
|
A new file named `000044.2945021133.fixed.png` will be created in the output
|
||||||
@ -178,5 +172,5 @@ unlike the behavior at generate time.
|
|||||||
### Disabling:
|
### Disabling:
|
||||||
|
|
||||||
If, for some reason, you do not wish to load the GFPGAN and/or ESRGAN libraries,
|
If, for some reason, you do not wish to load the GFPGAN and/or ESRGAN libraries,
|
||||||
you can disable them on the dream.py command line with the `--no_restore` and
|
you can disable them on the invoke.py command line with the `--no_restore` and
|
||||||
`--no_upscale` options, respectively.
|
`--no_upscale` options, respectively.
|
||||||
|
@ -6,9 +6,9 @@ title: Prompting Features
|
|||||||
|
|
||||||
## **Reading Prompts from a File**
|
## **Reading Prompts from a File**
|
||||||
|
|
||||||
You can automate `dream.py` by providing a text file with the prompts you want to run, one line per
|
You can automate `invoke.py` by providing a text file with the prompts you want to run, one line per
|
||||||
prompt. The text file must be composed with a text editor (e.g. Notepad) and not a word processor.
|
prompt. The text file must be composed with a text editor (e.g. Notepad) and not a word processor.
|
||||||
Each line should look like what you would type at the dream> prompt:
|
Each line should look like what you would type at the invoke> prompt:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
a beautiful sunny day in the park, children playing -n4 -C10
|
a beautiful sunny day in the park, children playing -n4 -C10
|
||||||
@ -16,39 +16,23 @@ stormy weather on a mountain top, goats grazing -s100
|
|||||||
innovative packaging for a squid's dinner -S137038382
|
innovative packaging for a squid's dinner -S137038382
|
||||||
```
|
```
|
||||||
|
|
||||||
Then pass this file's name to `dream.py` when you invoke it:
|
Then pass this file's name to `invoke.py` when you invoke it:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
(ldm) ~/stable-diffusion$ python3 scripts/dream.py --from_file "path/to/prompts.txt"
|
(ldm) ~/stable-diffusion$ python3 scripts/invoke.py --from_file "path/to/prompts.txt"
|
||||||
```
|
```
|
||||||
|
|
||||||
You may read a series of prompts from standard input by providing a filename of `-`:
|
You may read a series of prompts from standard input by providing a filename of `-`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
(ldm) ~/stable-diffusion$ echo "a beautiful day" | python3 scripts/dream.py --from_file -
|
(ldm) ~/stable-diffusion$ echo "a beautiful day" | python3 scripts/invoke.py --from_file -
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## **Weighted Prompts**
|
|
||||||
|
|
||||||
You may weight different sections of the prompt to tell the sampler to attach different levels of
|
|
||||||
priority to them, by adding `:(number)` to the end of the section you wish to up- or downweight. For
|
|
||||||
example consider this prompt:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
tabby cat:0.25 white duck:0.75 hybrid
|
|
||||||
```
|
|
||||||
|
|
||||||
This will tell the sampler to invest 25% of its effort on the tabby cat aspect of the image and 75%
|
|
||||||
on the white duck aspect (surprisingly, this example actually works). The prompt weights can use any
|
|
||||||
combination of integers and floating point numbers, and they do not need to add up to 1.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## **Negative and Unconditioned Prompts**
|
## **Negative and Unconditioned Prompts**
|
||||||
|
|
||||||
Any words between a pair of square brackets will try and be ignored by Stable Diffusion's model during generation of images.
|
Any words between a pair of square brackets will instruct Stable
|
||||||
|
Diffusion to attempt to ban the concept from the generated image.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
this is a test prompt [not really] to make you understand [cool] how this works.
|
this is a test prompt [not really] to make you understand [cool] how this works.
|
||||||
@ -88,3 +72,78 @@ Getting close - but there's no sense in having a saddle when our horse doesn't h
|
|||||||
* You can provide multiple words within the same bracket.
|
* You can provide multiple words within the same bracket.
|
||||||
* You can provide multiple brackets with multiple words in different places of your prompt. That works just fine.
|
* You can provide multiple brackets with multiple words in different places of your prompt. That works just fine.
|
||||||
* To improve typical anatomy problems, you can add negative prompts like `[bad anatomy, extra legs, extra arms, extra fingers, poorly drawn hands, poorly drawn feet, disfigured, out of frame, tiling, bad art, deformed, mutated]`.
|
* To improve typical anatomy problems, you can add negative prompts like `[bad anatomy, extra legs, extra arms, extra fingers, poorly drawn hands, poorly drawn feet, disfigured, out of frame, tiling, bad art, deformed, mutated]`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## **Prompt Blending**
|
||||||
|
|
||||||
|
You may blend together different sections of the prompt to explore the
|
||||||
|
AI's latent semantic space and generate interesting (and often
|
||||||
|
surprising!) variations. The syntax is:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
blue sphere:0.25 red cube:0.75 hybrid
|
||||||
|
```
|
||||||
|
|
||||||
|
This will tell the sampler to blend 25% of the concept of a blue
|
||||||
|
sphere with 75% of the concept of a red cube. The blend weights can
|
||||||
|
use any combination of integers and floating point numbers, and they
|
||||||
|
do not need to add up to 1. Everything to the left of the `:XX` up to
|
||||||
|
the previous `:XX` is used for merging, so the overall effect is:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
0.25 * "blue sphere" + 0.75 * "white duck" + hybrid
|
||||||
|
```
|
||||||
|
|
||||||
|
Because you are exploring the "mind" of the AI, the AI's way of mixing
|
||||||
|
two concepts may not match yours, leading to surprising effects. To
|
||||||
|
illustrate, here are three images generated using various combinations
|
||||||
|
of blend weights. As usual, unless you fix the seed, the prompts will give you
|
||||||
|
different results each time you run them.
|
||||||
|
|
||||||
|
### "blue sphere, red cube, hybrid"
|
||||||
|
|
||||||
|
This example doesn't use melding at all and represents the default way
|
||||||
|
of mixing concepts.
|
||||||
|
|
||||||
|
<img src="../assets/prompt-blending/blue-sphere-red-cube-hybrid.png" width=256>
|
||||||
|
|
||||||
|
It's interesting to see how the AI expressed the concept of "cube" as
|
||||||
|
the four quadrants of the enclosing frame. If you look closely, there
|
||||||
|
is depth there, so the enclosing frame is actually a cube.
|
||||||
|
|
||||||
|
### "blue sphere:0.25 red cube:0.75 hybrid"
|
||||||
|
|
||||||
|
<img src="../assets/prompt-blending/blue-sphere-0.25-red-cube-0.75-hybrid.png" width=256>
|
||||||
|
|
||||||
|
Now that's interesting. We get neither a blue sphere nor a red cube,
|
||||||
|
but a red sphere embedded in a brick wall, which represents a melding
|
||||||
|
of concepts within the AI's "latent space" of semantic
|
||||||
|
representations. Where is Ludwig Wittgenstein when you need him?
|
||||||
|
|
||||||
|
### "blue sphere:0.75 red cube:0.25 hybrid"
|
||||||
|
|
||||||
|
<img src="../assets/prompt-blending/blue-sphere-0.75-red-cube-0.25-hybrid.png" width=256>
|
||||||
|
|
||||||
|
Definitely more blue-spherey. The cube is gone entirely, but it's
|
||||||
|
really cool abstract art.
|
||||||
|
|
||||||
|
### "blue sphere:0.5 red cube:0.5 hybrid"
|
||||||
|
|
||||||
|
<img src="../assets/prompt-blending/blue-sphere-0.5-red-cube-0.5-hybrid.png" width=256>
|
||||||
|
|
||||||
|
Whoa...! I see blue and red, but no spheres or cubes. Is the word
|
||||||
|
"hybrid" summoning up the concept of some sort of scifi creature?
|
||||||
|
Let's find out.
|
||||||
|
|
||||||
|
### "blue sphere:0.5 red cube:0.5"
|
||||||
|
|
||||||
|
<img src="../assets/prompt-blending/blue-sphere-0.5-red-cube-0.5.png" width=256>
|
||||||
|
|
||||||
|
Indeed, removing the word "hybrid" produces an image that is more like
|
||||||
|
what we'd expect.
|
||||||
|
|
||||||
|
In conclusion, prompt blending is great for exploring creative space,
|
||||||
|
but can be difficult to direct. A forthcoming release of InvokeAI will
|
||||||
|
feature more deterministic prompt weighting.
|
||||||
|
|
||||||
|
@ -56,22 +56,22 @@ configs/stable_diffusion/v1-finetune.yaml (currently set to 4000000)
|
|||||||
## **Run the Model**
|
## **Run the Model**
|
||||||
|
|
||||||
Once the model is trained, specify the trained .pt or .bin file when starting
|
Once the model is trained, specify the trained .pt or .bin file when starting
|
||||||
dream using
|
invoke using
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 ./scripts/dream.py --embedding_path /path/to/embedding.pt
|
python3 ./scripts/invoke.py --embedding_path /path/to/embedding.pt
|
||||||
```
|
```
|
||||||
|
|
||||||
Then, to utilize your subject at the dream prompt
|
Then, to utilize your subject at the invoke prompt
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dream> "a photo of *"
|
invoke> "a photo of *"
|
||||||
```
|
```
|
||||||
|
|
||||||
This also works with image2image
|
This also works with image2image
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dream> "waterfall and rainbow in the style of *" --init_img=./init-images/crude_drawing.png --strength=0.5 -s100 -n4
|
invoke> "waterfall and rainbow in the style of *" --init_img=./init-images/crude_drawing.png --strength=0.5 -s100 -n4
|
||||||
```
|
```
|
||||||
|
|
||||||
For .pt files it's also possible to train multiple tokens (modify the
|
For .pt files it's also possible to train multiple tokens (modify the
|
||||||
|
@ -34,7 +34,7 @@ First we let SD create a series of images in the usual way, in this case
|
|||||||
requesting six iterations:
|
requesting six iterations:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dream> lucy lawless as xena, warrior princess, character portrait, high resolution -n6
|
invoke> lucy lawless as xena, warrior princess, character portrait, high resolution -n6
|
||||||
...
|
...
|
||||||
Outputs:
|
Outputs:
|
||||||
./outputs/Xena/000001.1579445059.png: "prompt" -s50 -W512 -H512 -C7.5 -Ak_lms -S1579445059
|
./outputs/Xena/000001.1579445059.png: "prompt" -s50 -W512 -H512 -C7.5 -Ak_lms -S1579445059
|
||||||
@ -57,7 +57,7 @@ differing by a variation amount of 0.2. This number ranges from `0` to `1.0`,
|
|||||||
with higher numbers being larger amounts of variation.
|
with higher numbers being larger amounts of variation.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dream> "prompt" -n6 -S3357757885 -v0.2
|
invoke> "prompt" -n6 -S3357757885 -v0.2
|
||||||
...
|
...
|
||||||
Outputs:
|
Outputs:
|
||||||
./outputs/Xena/000002.784039624.png: "prompt" -s50 -W512 -H512 -C7.5 -Ak_lms -V 784039624:0.2 -S3357757885
|
./outputs/Xena/000002.784039624.png: "prompt" -s50 -W512 -H512 -C7.5 -Ak_lms -V 784039624:0.2 -S3357757885
|
||||||
@ -89,7 +89,7 @@ We combine the two variations using `-V` (`--with_variations`). Again, we must
|
|||||||
provide the seed for the originally-chosen image in order for this to work.
|
provide the seed for the originally-chosen image in order for this to work.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dream> "prompt" -S3357757885 -V3647897225,0.1,1614299449,0.1
|
invoke> "prompt" -S3357757885 -V3647897225,0.1,1614299449,0.1
|
||||||
Outputs:
|
Outputs:
|
||||||
./outputs/Xena/000003.1614299449.png: "prompt" -s50 -W512 -H512 -C7.5 -Ak_lms -V 3647897225:0.1,1614299449:0.1 -S3357757885
|
./outputs/Xena/000003.1614299449.png: "prompt" -s50 -W512 -H512 -C7.5 -Ak_lms -V 3647897225:0.1,1614299449:0.1 -S3357757885
|
||||||
```
|
```
|
||||||
@ -105,7 +105,7 @@ latter, using both the `-V` (combining) and `-v` (variation strength) options.
|
|||||||
Note that we use `-n6` to generate 6 variations:
|
Note that we use `-n6` to generate 6 variations:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dream> "prompt" -S3357757885 -V3647897225,0.1,1614299449,0.1 -v0.05 -n6
|
invoke> "prompt" -S3357757885 -V3647897225,0.1,1614299449,0.1 -v0.05 -n6
|
||||||
Outputs:
|
Outputs:
|
||||||
./outputs/Xena/000004.3279757577.png: "prompt" -s50 -W512 -H512 -C7.5 -Ak_lms -V 3647897225:0.1,1614299449:0.1,3279757577:0.05 -S3357757885
|
./outputs/Xena/000004.3279757577.png: "prompt" -s50 -W512 -H512 -C7.5 -Ak_lms -V 3647897225:0.1,1614299449:0.1,3279757577:0.05 -S3357757885
|
||||||
./outputs/Xena/000004.2853129515.png: "prompt" -s50 -W512 -H512 -C7.5 -Ak_lms -V 3647897225:0.1,1614299449:0.1,2853129515:0.05 -S3357757885
|
./outputs/Xena/000004.2853129515.png: "prompt" -s50 -W512 -H512 -C7.5 -Ak_lms -V 3647897225:0.1,1614299449:0.1,2853129515:0.05 -S3357757885
|
||||||
|
@ -1,24 +1,290 @@
|
|||||||
---
|
---
|
||||||
title: InvokeAI Web UI & Server
|
title: InvokeAI Web Server
|
||||||
---
|
---
|
||||||
|
|
||||||
# :material-web: InvokeAI Web Server
|
As of version 2.0.0, this distribution comes with a full-featured web
|
||||||
|
server (see screenshot). To use it, run the `invoke.py` script by
|
||||||
As of version 2.0, this distribution's web server has been updated to include
|
adding the `--web` option:
|
||||||
an all-new UI, with optimizations to improve common workflows for image generation.
|
|
||||||
|
|
||||||
## Getting Started & Initialization Commands
|
|
||||||
|
|
||||||
To start the web server, run the `dream.py` script by adding the `--web` parameter.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
(ldm) ~/stable-diffusion$ python3 scripts/dream.py --web
|
(ldm) ~/InvokeAI$ python3 scripts/invoke.py --web
|
||||||
```
|
```
|
||||||
|
|
||||||
You can then connect to the server by pointing your web browser at
|
You can then connect to the server by pointing your web browser at
|
||||||
http://localhost:9090, or to the network name or IP address of the server.
|
http://localhost:9090. To reach the server from a different machine on
|
||||||
|
your LAN, you may launch the web server with the `--host` argument and
|
||||||
|
either the IP address of the host you are running it on, or the
|
||||||
|
wildcard `0.0.0.0`. For example:
|
||||||
|
|
||||||
### Additional Options
|
```bash
|
||||||
|
(ldm) ~/InvokeAI$ python3 scripts/invoke.py --web --host 0.0.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
# Quick guided walkthrough of the WebGUI's features
|
||||||
|
|
||||||
|
While most of the WebGUI's features are intuitive, here is a guided
|
||||||
|
walkthrough through its various components.
|
||||||
|
|
||||||
|
<img src="../assets/invoke-web-server-1.png" width=640>
|
||||||
|
|
||||||
|
The screenshot above shows the Text to Image tab of the WebGUI. There
|
||||||
|
are three main sections:
|
||||||
|
|
||||||
|
1. A **control panel** on the left, which contains various settings
|
||||||
|
for text to image generation. The most important part is the text
|
||||||
|
field (currently showing `strawberry sushi`) for entering the text
|
||||||
|
prompt, and the camera icon directly underneath that will render the
|
||||||
|
image. We'll call this the *Invoke* button from now on.
|
||||||
|
|
||||||
|
2. The **current image** section in the middle, which shows a large
|
||||||
|
format version of the image you are currently working on. A series of
|
||||||
|
buttons at the top ("image to image", "Use All", "Use Seed", etc) lets
|
||||||
|
you modify the image in various ways.
|
||||||
|
|
||||||
|
3. A **gallery* section on the left that contains a history of the
|
||||||
|
images you have generated. These images are read and written to the
|
||||||
|
directory specified at launch time in `--outdir`.
|
||||||
|
|
||||||
|
In addition to these three elements, there are a series of icons for
|
||||||
|
changing global settings, reporting bugs, and changing the theme on
|
||||||
|
the upper right.
|
||||||
|
|
||||||
|
There are also a series of icons to the left of the control panel (see
|
||||||
|
highlighted area in the screenshot below) which select among a series
|
||||||
|
of tabs for performing different types of operations.
|
||||||
|
|
||||||
|
<img src="../assets/invoke-web-server-2.png" width=512>
|
||||||
|
|
||||||
|
From top to bottom, these are:
|
||||||
|
|
||||||
|
1. Text to Image - generate images from text
|
||||||
|
2. Image to Image - from an uploaded starting image (drawing or photograph) generate a new one, modified by the text prompt
|
||||||
|
3. Inpainting (pending) - Interactively erase portions of a starting image and have the AI fill in the erased region from a text prompt.
|
||||||
|
4. Outpainting (pending) - Interactively add blank space to the borders of a starting image and fill in the background from a text prompt.
|
||||||
|
5. Postprocessing (pending) - Interactively postprocess generated images using a variety of filters.
|
||||||
|
|
||||||
|
The inpainting, outpainting and postprocessing tabs are currently in
|
||||||
|
development. However, limited versions of their features can already
|
||||||
|
be accessed through the Text to Image and Image to Image tabs.
|
||||||
|
|
||||||
|
## Walkthrough
|
||||||
|
|
||||||
|
The following walkthrough will exercise most (but not all) of the
|
||||||
|
WebGUI's feature set.
|
||||||
|
|
||||||
|
### Text to Image
|
||||||
|
|
||||||
|
1. Launch the WebGUI using `python scripts/invoke.py --web` and
|
||||||
|
connect to it with your browser by accessing
|
||||||
|
`http://localhost:9090`. If the browser and server are running on
|
||||||
|
different machines on your LAN, add the option `--host 0.0.0.0` to the
|
||||||
|
launch command line and connect to the machine hosting the web server
|
||||||
|
using its IP address or domain name.
|
||||||
|
|
||||||
|
2. If all goes well, the WebGUI should come up and you'll see a green
|
||||||
|
`connected` message on the upper right.
|
||||||
|
|
||||||
|
#### Basics
|
||||||
|
|
||||||
|
3. Generate an image by typing *strawberry sushi* into the large
|
||||||
|
prompt field on the upper left and then clicking on the Invoke button
|
||||||
|
(the one with the Camera icon). After a short wait, you'll see a large
|
||||||
|
image of sushi in the image panel, and a new thumbnail in the gallery
|
||||||
|
on the right.
|
||||||
|
|
||||||
|
If you need more room on the screen, you can turn the gallery off
|
||||||
|
by clicking on the **x** to the right of "Your Invocations". You can
|
||||||
|
turn it back on later by clicking the image icon that appears in the
|
||||||
|
gallery's place.
|
||||||
|
|
||||||
|
The images are written into the directory indicated by the `--outdir`
|
||||||
|
option provided at script launch time. By default, this is
|
||||||
|
`outputs/img-samples` under the InvokeAI directory.
|
||||||
|
|
||||||
|
4. Generate a bunch of strawberry sushi images by increasing the
|
||||||
|
number of requested images by adjusting the Images counter just below
|
||||||
|
the Camera button. As each is generated, it will be added to the
|
||||||
|
gallery. You can switch the active image by clicking on the gallery
|
||||||
|
thumbnails.
|
||||||
|
|
||||||
|
5. Try playing with different settings, including image width and
|
||||||
|
height, the Sampler, the Steps and the CFG scale.
|
||||||
|
|
||||||
|
Image *Width* and *Height* do what you'd expect. However, be aware that
|
||||||
|
larger images consume more VRAM memory and take longer to generate.
|
||||||
|
|
||||||
|
The *Sampler* controls how the AI selects the image to display. Some
|
||||||
|
samplers are more "creative" than others and will produce a wider
|
||||||
|
range of variations (see next section). Some samplers run faster than
|
||||||
|
others.
|
||||||
|
|
||||||
|
*Steps* controls how many noising/denoising/sampling steps the AI will
|
||||||
|
take. The higher this value, the more refined the image will be, but
|
||||||
|
the longer the image will take to generate. A typical strategy is to
|
||||||
|
generate images with a low number of steps in order to select one to
|
||||||
|
work on further, and then regenerate it using a higher number of
|
||||||
|
steps.
|
||||||
|
|
||||||
|
The *CFG Scale* controls how hard the AI tries to match the generated
|
||||||
|
image to the input prompt. You can go as high or low as you like, but
|
||||||
|
generally values greater than 20 won't improve things much, and values
|
||||||
|
lower than 5 will produce unexpected images. There are complex
|
||||||
|
interactions between *Steps*, *CFG Scale* and the *Sampler*, so
|
||||||
|
experiment to find out what works for you.
|
||||||
|
|
||||||
|
6. To regenerate a previously-generated image, select the image you
|
||||||
|
want and click *Use All*. This loads the text prompt and other
|
||||||
|
original settings into the control panel. If you then press *Invoke*
|
||||||
|
it will regenerate the image exactly. You can also selectively modify
|
||||||
|
the prompt or other settings to tweak the image.
|
||||||
|
|
||||||
|
Alternatively, you may click on *Use Seed* to load just the image's
|
||||||
|
seed, and leave other settings unchanged.
|
||||||
|
|
||||||
|
7. To regenerate a Stable Diffusion image that was generated by
|
||||||
|
another SD package, you need to know its text prompt and its
|
||||||
|
*Seed*. Copy-paste the prompt into the prompt box, unset the
|
||||||
|
*Randomize Seed* control in the control panel, and copy-paste the
|
||||||
|
desired *Seed* into its text field. When you Invoke, you will get
|
||||||
|
something similar to the original image. It will not be exact unless
|
||||||
|
you also set the correct values for the original sampler, CFG,
|
||||||
|
steps and dimensions, but it will (usually) be close.
|
||||||
|
|
||||||
|
#### Variations on a theme
|
||||||
|
|
||||||
|
5. Let's try generating some variations. Select your favorite sushi
|
||||||
|
image from the gallery to load it. Then select "Use All" from the list
|
||||||
|
of buttons above. This will load up all the settings used to generate
|
||||||
|
this image, including its unique seed.
|
||||||
|
|
||||||
|
Go down to the Variations section of the Control Panel and set the
|
||||||
|
button to On. Set Variation Amount to 0.2 to generate a modest
|
||||||
|
number of variations on the image, and also set the Image counter to
|
||||||
|
4. Press the `invoke` button. This will generate a series of related
|
||||||
|
images. To obtain smaller variations, just lower the Variation
|
||||||
|
Amount. You may also experiment with changing the Sampler. Some
|
||||||
|
samplers generate more variability than others. *k_euler_a* is
|
||||||
|
particularly creative, while *ddim* is pretty conservative.
|
||||||
|
|
||||||
|
6. For even more variations, experiment with increasing the setting
|
||||||
|
for *Perlin*. This adds a bit of noise to the image generation
|
||||||
|
process. Note that values of Perlin noise greater than 0.15 produce
|
||||||
|
poor images for several of the samplers.
|
||||||
|
|
||||||
|
#### Facial reconstruction and upscaling
|
||||||
|
|
||||||
|
Stable Diffusion frequently produces mangled faces, particularly when
|
||||||
|
there are multiple figures in the same scene. Stable Diffusion has
|
||||||
|
particular issues with generating reallistic eyes. InvokeAI provides
|
||||||
|
the ability to reconstruct faces using either the GFPGAN or CodeFormer
|
||||||
|
libraries. For more information see [POSTPROCESS](POSTPROCESS.md).
|
||||||
|
|
||||||
|
7. Invoke a prompt that generates a mangled face. A prompt that often
|
||||||
|
gives this is "portrait of a lawyer, 3/4 shot" (this is not intended
|
||||||
|
as a slur against lawyers!) Once you have an image that needs some
|
||||||
|
touching up, load it into the Image panel, and press the button with
|
||||||
|
the face icon (highlighted in the first screenshot below). A dialog
|
||||||
|
box will appear. Leave *Strength* at 0.8 and press *Restore Faces". If
|
||||||
|
all goes well, the eyes and other aspects of the face will be improved
|
||||||
|
(see the second screenshot)
|
||||||
|
|
||||||
|
<img src="../assets/invoke-web-server-3.png">
|
||||||
|
<img src="../assets/invoke-web-server-4.png">
|
||||||
|
|
||||||
|
The facial reconstruction *Strength* field adjusts how aggressively
|
||||||
|
the face library will try to alter the face. It can be as high as 1.0,
|
||||||
|
but be aware that this often softens the face airbrush style, losing
|
||||||
|
some details. The default 0.8 is usually sufficient.
|
||||||
|
|
||||||
|
8. "Upscaling" is the process of increasing the size of an image while
|
||||||
|
retaining the sharpness. InvokeAI uses an external library called
|
||||||
|
"ESRGAN" to do this. To invoke upscaling, simply select an image and
|
||||||
|
press the *HD* button above it. You can select between 2X and 4X
|
||||||
|
upscaling, and adjust the upscaling strength, which has much the same
|
||||||
|
meaning as in facial reconstruction. Try running this on one of your
|
||||||
|
previously-generated images.
|
||||||
|
|
||||||
|
9. Finally, you can run facial reconstruction and/or upscaling
|
||||||
|
automatically after each Invocation. Go to the Advanced Options
|
||||||
|
section of the Control Panel and turn on *Restore Face* and/or
|
||||||
|
*Upscale*.
|
||||||
|
|
||||||
|
### Image to Image
|
||||||
|
|
||||||
|
InvokeAI lets you take an existing image and use it as the basis for a
|
||||||
|
new creation. You can use any sort of image, including a photograph, a
|
||||||
|
scanned sketch, or a digital drawing, as long as it is in PNG or JPEG
|
||||||
|
format.
|
||||||
|
|
||||||
|
For this tutorial, we'll use files named
|
||||||
|
[Lincoln-and-Parrot-512.png](../assets/Lincoln-and-Parrot-512.png),
|
||||||
|
and
|
||||||
|
[Lincoln-and-Parrot-512-transparent.png](../assets/Lincoln-and-Parrot-512-transparent.png).
|
||||||
|
Download these images to your local machine now to continue with the walkthrough.
|
||||||
|
|
||||||
|
10. Click on the *Image to Image* tab icon, which is the second icon
|
||||||
|
from the top on the left-hand side of the screen:
|
||||||
|
|
||||||
|
<img src="../assets/invoke-web-server-5.png">
|
||||||
|
|
||||||
|
This will bring you to a screen similar to the one shown here:
|
||||||
|
|
||||||
|
<img src="../assets/invoke-web-server-6.png" width=640>
|
||||||
|
|
||||||
|
Drag-and-drop the Lincoln-and-Parrot image into the Image panel, or
|
||||||
|
click the blank area to get an upload dialog. The image will load into
|
||||||
|
an area marked *Initial Image*. (The WebGUI will also load the most
|
||||||
|
recently-generated image from the gallery into a section on the left,
|
||||||
|
but this image will be replaced in the next step.)
|
||||||
|
|
||||||
|
11. Go to the prompt box and type *old sea captain with raven on
|
||||||
|
shoulder* and press Invoke. A derived image will appear to the right
|
||||||
|
of the original one:
|
||||||
|
|
||||||
|
<img src="../assets/invoke-web-server-7.png" width=640>
|
||||||
|
|
||||||
|
12. Experiment with the different settings. The most influential one
|
||||||
|
in Image to Image is *Image to Image Strength* located about midway
|
||||||
|
down the control panel. By default it is set to 0.75, but can range
|
||||||
|
from 0.0 to 0.99. The higher the value, the more of the original image
|
||||||
|
the AI will replace. A value of 0 will leave the initial image
|
||||||
|
completely unchanged, while 0.99 will replace it completely. However,
|
||||||
|
the Sampler and CFG Scale also influence the final result. You can
|
||||||
|
also generate variations in the same way as described in Text to
|
||||||
|
Image.
|
||||||
|
|
||||||
|
13. What if we only want to change certain part(s) of the image and
|
||||||
|
leave the rest intact? This is called Inpainting, and a future version
|
||||||
|
of the InvokeAI web server will provide an interactive painting canvas
|
||||||
|
on which you can directly draw the areas you wish to Inpaint into. For
|
||||||
|
now, you can achieve this effect by using an external photoeditor tool
|
||||||
|
to make one or more regions of the image transparent as described in
|
||||||
|
[INPAINTING.md] and uploading that.
|
||||||
|
|
||||||
|
The file
|
||||||
|
[Lincoln-and-Parrot-512-transparent.png](../assets/Lincoln-and-Parrot-512-transparent.png)
|
||||||
|
is a version of the earlier image in which the area around the parrot
|
||||||
|
has been replaced with transparency. Click on the "x" in the upper
|
||||||
|
right of the Initial Image and upload the transparent version. Using
|
||||||
|
the same prompt "old sea captain with raven on shoulder" try Invoking
|
||||||
|
an image. This time, only the parrot will be replaced, leaving the
|
||||||
|
rest of the original image intact:
|
||||||
|
|
||||||
|
<img src="../assets/invoke-web-server-8.png" width=640>
|
||||||
|
|
||||||
|
## Parting remarks
|
||||||
|
|
||||||
|
This concludes the walkthrough, but there are several more features that you
|
||||||
|
can explore. Please check out the [Command Line Interface](CLI.md)
|
||||||
|
documentation for further explanation of the advanced features that
|
||||||
|
were not covered here.
|
||||||
|
|
||||||
|
The WebGUI is only rapid development. Check back regularly for
|
||||||
|
updates!
|
||||||
|
|
||||||
|
# Reference
|
||||||
|
|
||||||
|
## Additional Options
|
||||||
`--web_develop` - Starts the web server in development mode.
|
`--web_develop` - Starts the web server in development mode.
|
||||||
|
|
||||||
`--web_verbose` - Enables verbose logging
|
`--web_verbose` - Enables verbose logging
|
||||||
@ -72,4 +338,3 @@ When an image from the Invocation Gallery is selected, or is generated, the imag
|
|||||||
## Acknowledgements
|
## Acknowledgements
|
||||||
|
|
||||||
A huge shout-out to the core team working to make this vision a reality, including [psychedelicious](https://github.com/psychedelicious), [Kyle0654](https://github.com/Kyle0654) and [blessedcoolant](https://github.com/blessedcoolant). [hipsterusername](https://github.com/hipsterusername) was the team's unofficial cheerleader and added tooltips/docs.
|
A huge shout-out to the core team working to make this vision a reality, including [psychedelicious](https://github.com/psychedelicious), [Kyle0654](https://github.com/Kyle0654) and [blessedcoolant](https://github.com/blessedcoolant). [hipsterusername](https://github.com/hipsterusername) was the team's unofficial cheerleader and added tooltips/docs.
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ rm ${PIP_LOG}
|
|||||||
|
|
||||||
### **QUESTION**
|
### **QUESTION**
|
||||||
|
|
||||||
`dream.py` crashes with the complaint that it can't find `ldm.simplet2i.py`. Or it complains that
|
`invoke.py` crashes with the complaint that it can't find `ldm.simplet2i.py`. Or it complains that
|
||||||
function is being passed incorrect parameters.
|
function is being passed incorrect parameters.
|
||||||
|
|
||||||
### **SOLUTION**
|
### **SOLUTION**
|
||||||
@ -63,7 +63,7 @@ Reinstall the stable diffusion modules. Enter the `stable-diffusion` directory a
|
|||||||
|
|
||||||
### **QUESTION**
|
### **QUESTION**
|
||||||
|
|
||||||
`dream.py` dies, complaining of various missing modules, none of which starts with `ldm``.
|
`invoke.py` dies, complaining of various missing modules, none of which starts with `ldm``.
|
||||||
|
|
||||||
### **SOLUTION**
|
### **SOLUTION**
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ template: main.html
|
|||||||
[CI checks on dev badge]: https://flat.badgen.net/github/checks/lstein/stable-diffusion/development?label=CI%20status%20on%20dev&cache=900&icon=github
|
[CI checks on dev badge]: https://flat.badgen.net/github/checks/lstein/stable-diffusion/development?label=CI%20status%20on%20dev&cache=900&icon=github
|
||||||
[CI checks on dev link]: https://github.com/lstein/stable-diffusion/actions?query=branch%3Adevelopment
|
[CI checks on dev link]: https://github.com/lstein/stable-diffusion/actions?query=branch%3Adevelopment
|
||||||
[CI checks on main badge]: https://flat.badgen.net/github/checks/lstein/stable-diffusion/main?label=CI%20status%20on%20main&cache=900&icon=github
|
[CI checks on main badge]: https://flat.badgen.net/github/checks/lstein/stable-diffusion/main?label=CI%20status%20on%20main&cache=900&icon=github
|
||||||
[CI checks on main link]: https://github.com/lstein/stable-diffusion/actions/workflows/test-dream-conda.yml
|
[CI checks on main link]: https://github.com/lstein/stable-diffusion/actions/workflows/test-invoke-conda.yml
|
||||||
[discord badge]: https://flat.badgen.net/discord/members/htRgbc7e?icon=discord
|
[discord badge]: https://flat.badgen.net/discord/members/htRgbc7e?icon=discord
|
||||||
[discord link]: https://discord.com/invite/htRgbc7e
|
[discord link]: https://discord.com/invite/htRgbc7e
|
||||||
[github forks badge]: https://flat.badgen.net/github/forks/lstein/stable-diffusion?icon=github
|
[github forks badge]: https://flat.badgen.net/github/forks/lstein/stable-diffusion?icon=github
|
||||||
@ -85,21 +85,21 @@ You wil need one of the following:
|
|||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
|
|
||||||
If you are have a Nvidia 10xx series card (e.g. the 1080ti), please run the dream script in
|
If you are have a Nvidia 10xx series card (e.g. the 1080ti), please run the invoke script in
|
||||||
full-precision mode as shown below.
|
full-precision mode as shown below.
|
||||||
|
|
||||||
Similarly, specify full-precision mode on Apple M1 hardware.
|
Similarly, specify full-precision mode on Apple M1 hardware.
|
||||||
|
|
||||||
To run in full-precision mode, start `dream.py` with the `--full_precision` flag:
|
To run in full-precision mode, start `invoke.py` with the `--full_precision` flag:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
(ldm) ~/stable-diffusion$ python scripts/dream.py --full_precision
|
(ldm) ~/stable-diffusion$ python scripts/invoke.py --full_precision
|
||||||
```
|
```
|
||||||
## :octicons-log-16: Latest Changes
|
## :octicons-log-16: Latest Changes
|
||||||
|
|
||||||
### vNEXT <small>(TODO 2022)</small>
|
### vNEXT <small>(TODO 2022)</small>
|
||||||
|
|
||||||
- Deprecated `--full_precision` / `-F`. Simply omit it and `dream.py` will auto
|
- Deprecated `--full_precision` / `-F`. Simply omit it and `invoke.py` will auto
|
||||||
configure. To switch away from auto use the new flag like `--precision=float32`.
|
configure. To switch away from auto use the new flag like `--precision=float32`.
|
||||||
|
|
||||||
### v1.14 <small>(11 September 2022)</small>
|
### v1.14 <small>(11 September 2022)</small>
|
||||||
@ -124,7 +124,7 @@ You wil need one of the following:
|
|||||||
[Kevin Gibbons](https://github.com/bakkot)
|
[Kevin Gibbons](https://github.com/bakkot)
|
||||||
- A new configuration file scheme that allows new models (including upcoming stable-diffusion-v1.5)
|
- A new configuration file scheme that allows new models (including upcoming stable-diffusion-v1.5)
|
||||||
to be added without altering the code. ([David Wager](https://github.com/maddavid12))
|
to be added without altering the code. ([David Wager](https://github.com/maddavid12))
|
||||||
- Can specify --grid on dream.py command line as the default.
|
- Can specify --grid on invoke.py command line as the default.
|
||||||
- Miscellaneous internal bug and stability fixes.
|
- Miscellaneous internal bug and stability fixes.
|
||||||
- Works on M1 Apple hardware.
|
- Works on M1 Apple hardware.
|
||||||
- Multiple bug fixes.
|
- Multiple bug fixes.
|
||||||
|
@ -136,7 +136,7 @@ $TAG_STABLE_DIFFUSION
|
|||||||
|
|
||||||
## Startup
|
## Startup
|
||||||
|
|
||||||
If you're on a **Linux container** the `dream` script is **automatically
|
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.
|
started** and the output dir set to the Docker volume you created earlier.
|
||||||
|
|
||||||
If you're **directly on macOS follow these startup instructions**.
|
If you're **directly on macOS follow these startup instructions**.
|
||||||
@ -148,14 +148,14 @@ half-precision requires autocast and won't work.
|
|||||||
By default the images are saved in `outputs/img-samples/`.
|
By default the images are saved in `outputs/img-samples/`.
|
||||||
|
|
||||||
```Shell
|
```Shell
|
||||||
python3 scripts/dream.py --full_precision
|
python3 scripts/invoke.py --full_precision
|
||||||
```
|
```
|
||||||
|
|
||||||
You'll get the script's prompt. You can see available options or quit.
|
You'll get the script's prompt. You can see available options or quit.
|
||||||
|
|
||||||
```Shell
|
```Shell
|
||||||
dream> -h
|
invoke> -h
|
||||||
dream> q
|
invoke> q
|
||||||
```
|
```
|
||||||
|
|
||||||
## Text to Image
|
## Text to Image
|
||||||
@ -166,10 +166,10 @@ Then increase steps to 100 or more for good (but slower) results.
|
|||||||
The prompt can be in quotes or not.
|
The prompt can be in quotes or not.
|
||||||
|
|
||||||
```Shell
|
```Shell
|
||||||
dream> The hulk fighting with sheldon cooper -s5 -n1
|
invoke> The hulk fighting with sheldon cooper -s5 -n1
|
||||||
dream> "woman closeup highly detailed" -s 150
|
invoke> "woman closeup highly detailed" -s 150
|
||||||
# Reuse previous seed and apply face restoration
|
# Reuse previous seed and apply face restoration
|
||||||
dream> "woman closeup highly detailed" --steps 150 --seed -1 -G 0.75
|
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
|
You'll need to experiment to see if face restoration is making it better or
|
||||||
@ -210,28 +210,28 @@ If you're on a Docker container, copy your input image into the Docker volume
|
|||||||
docker cp /Users/<your-user>/Pictures/sketch-mountains-input.jpg dummy:/data/
|
docker cp /Users/<your-user>/Pictures/sketch-mountains-input.jpg dummy:/data/
|
||||||
```
|
```
|
||||||
|
|
||||||
Try it out generating an image (or more). The `dream` script needs absolute
|
Try it out generating an image (or more). The `invoke` script needs absolute
|
||||||
paths to find the image so don't use `~`.
|
paths to find the image so don't use `~`.
|
||||||
|
|
||||||
If you're on your Mac
|
If you're on your Mac
|
||||||
|
|
||||||
```Shell
|
```Shell
|
||||||
dream> "A fantasy landscape, trending on artstation" -I /Users/<your-user>/Pictures/sketch-mountains-input.jpg --strength 0.75 --steps 100 -n4
|
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
|
If you're on a Linux container on your Mac
|
||||||
|
|
||||||
```Shell
|
```Shell
|
||||||
dream> "A fantasy landscape, trending on artstation" -I /data/sketch-mountains-input.jpg --strength 0.75 --steps 50 -n1
|
invoke> "A fantasy landscape, trending on artstation" -I /data/sketch-mountains-input.jpg --strength 0.75 --steps 50 -n1
|
||||||
```
|
```
|
||||||
|
|
||||||
## Web Interface
|
## Web Interface
|
||||||
|
|
||||||
You can use the `dream` script with a graphical web interface. Start the web
|
You can use the `invoke` script with a graphical web interface. Start the web
|
||||||
server with:
|
server with:
|
||||||
|
|
||||||
```Shell
|
```Shell
|
||||||
python3 scripts/dream.py --full_precision --web
|
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
|
If it's running on your Mac point your Mac web browser to http://127.0.0.1:9090
|
||||||
|
@ -89,16 +89,16 @@ This will create InvokeAI folder where you will follow the rest of the steps.
|
|||||||
|
|
||||||
```
|
```
|
||||||
# for the pre-release weights use the -l or --liaon400m switch
|
# for the pre-release weights use the -l or --liaon400m switch
|
||||||
(ldm) ~/InvokeAI$ python3 scripts/dream.py -l
|
(ldm) ~/InvokeAI$ python3 scripts/invoke.py -l
|
||||||
|
|
||||||
# for the post-release weights do not use the switch
|
# for the post-release weights do not use the switch
|
||||||
(ldm) ~/InvokeAI$ python3 scripts/dream.py
|
(ldm) ~/InvokeAI$ python3 scripts/invoke.py
|
||||||
|
|
||||||
# for additional configuration switches and arguments, use -h or --help
|
# for additional configuration switches and arguments, use -h or --help
|
||||||
(ldm) ~/InvokeAI$ python3 scripts/dream.py -h
|
(ldm) ~/InvokeAI$ python3 scripts/invoke.py -h
|
||||||
```
|
```
|
||||||
|
|
||||||
9. Subsequently, to relaunch the script, be sure to run "conda activate ldm" (step 5, second command), enter the `InvokeAI` directory, and then launch the dream script (step 8). If you forget to activate the ldm environment, the script will fail with multiple `ModuleNotFound` errors.
|
9. Subsequently, to relaunch the script, be sure to run "conda activate ldm" (step 5, second command), enter the `InvokeAI` directory, and then launch the invoke script (step 8). If you forget to activate the ldm environment, the script will fail with multiple `ModuleNotFound` errors.
|
||||||
|
|
||||||
## Updating to newer versions of the script
|
## Updating to newer versions of the script
|
||||||
|
|
||||||
|
@ -2,145 +2,113 @@
|
|||||||
title: macOS
|
title: macOS
|
||||||
---
|
---
|
||||||
|
|
||||||
# :fontawesome-brands-apple: macOS
|
Invoke AI runs quite well on M1 Macs and we have a number of M1 users
|
||||||
|
in the community.
|
||||||
|
|
||||||
|
While the repo does run on Intel Macs, we only have a couple
|
||||||
|
reports. If you have an Intel Mac and run into issues, please create
|
||||||
|
an issue on Github and we will do our best to help.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- macOS 12.3 Monterey or later
|
- macOS 12.3 Monterey or later
|
||||||
- Python
|
- About 10GB of storage (and 10GB of data if your internet connection has data caps)
|
||||||
- Patience
|
- Any M1 Macs or an Intel Macs with 4GB+ of VRAM (ideally more)
|
||||||
- Apple Silicon or Intel Mac
|
|
||||||
|
|
||||||
Things have moved really fast and so these instructions change often which makes
|
## Installation
|
||||||
them outdated pretty fast. One of the problems is that there are so many
|
|
||||||
different ways to run this.
|
|
||||||
|
|
||||||
We are trying to build a testing setup so that when we make changes it doesn't
|
First you need to download a large checkpoint file.
|
||||||
always break.
|
|
||||||
|
|
||||||
## How to
|
1. Sign up at https://huggingface.co
|
||||||
|
2. Go to the [Stable diffusion diffusion model page](https://huggingface.co/CompVis/stable-diffusion-v-1-4-original)
|
||||||
|
3. Accept the terms and click Access Repository
|
||||||
|
4. Download [sd-v1-4.ckpt (4.27 GB)](https://huggingface.co/CompVis/stable-diffusion-v-1-4-original/blob/main/sd-v1-4.ckpt) and note where you have saved it (probably the Downloads folder). You may want to move it somewhere else for longer term storage - SD needs this file to run.
|
||||||
|
|
||||||
(this hasn't been 100% tested yet)
|
While that is downloading, open Terminal and run the following
|
||||||
|
commands one at a time, reading the comments and taking care to run
|
||||||
|
the appropriate command for your Mac's architecture (Intel or M1).
|
||||||
|
|
||||||
First get the weights checkpoint download started since it's big and will take
|
Do not just copy and paste the whole thing into your terminal!
|
||||||
some time:
|
|
||||||
|
|
||||||
1. Sign up at [huggingface.co](https://huggingface.co)
|
```bash
|
||||||
2. Go to the
|
# Install brew (and Xcode command line tools):
|
||||||
[Stable diffusion diffusion model page](https://huggingface.co/CompVis/stable-diffusion-v-1-4-original)
|
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||||
3. Accept the terms and click Access Repository:
|
|
||||||
4. Download
|
|
||||||
[sd-v1-4.ckpt (4.27 GB)](https://huggingface.co/CompVis/stable-diffusion-v-1-4-original/blob/main/sd-v1-4.ckpt)
|
|
||||||
and note where you have saved it (probably the Downloads folder)
|
|
||||||
|
|
||||||
While that is downloading, open a Terminal and run the following commands:
|
# Now there are two options to get the Python (miniconda) environment up and running:
|
||||||
|
# 1. Alongside pyenv
|
||||||
|
# 2. Standalone
|
||||||
|
#
|
||||||
|
# If you don't know what we are talking about, choose 2.
|
||||||
|
#
|
||||||
|
# If you are familiar with python environments, you'll know there are other options
|
||||||
|
# for setting up the environment - you are on your own if you go one of those routes.
|
||||||
|
##### BEGIN TWO DIFFERENT OPTIONS #####
|
||||||
|
|
||||||
!!! todo "Homebrew"
|
### BEGIN OPTION 1: Installing alongside pyenv ###
|
||||||
|
brew install pyenv-virtualenv # you might have this from before, no problem
|
||||||
|
pyenv install anaconda3-2022.05
|
||||||
|
pyenv virtualenv anaconda3-2022.05
|
||||||
|
eval "$(pyenv init -)"
|
||||||
|
pyenv activate anaconda3-2022.05
|
||||||
|
### END OPTION 1 ###
|
||||||
|
|
||||||
=== "no brew installation yet"
|
|
||||||
|
|
||||||
```bash title="install brew (and Xcode command line tools)"
|
### BEGIN OPTION 2: Installing standalone ###
|
||||||
/bin/bash -c \
|
# Install cmake, protobuf, and rust:
|
||||||
"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
brew install cmake protobuf rust
|
||||||
```
|
|
||||||
|
|
||||||
=== "brew is already installed"
|
# BEGIN ARCHITECTURE-DEPENDENT STEP #
|
||||||
|
# For M1: install miniconda (M1 arm64 version):
|
||||||
Only if you installed protobuf in a previous version of this tutorial, otherwise skip
|
curl https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-arm64.sh -o Miniconda3-latest-MacOSX-arm64.sh
|
||||||
|
/bin/bash Miniconda3-latest-MacOSX-arm64.sh
|
||||||
|
|
||||||
`#!bash brew uninstall protobuf`
|
# For Intel: install miniconda (Intel x86-64 version):
|
||||||
|
curl https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-x86_64.sh -o Miniconda3-latest-MacOSX-x86_64.sh
|
||||||
|
/bin/bash Miniconda3-latest-MacOSX-x86_64.sh
|
||||||
|
# END ARCHITECTURE-DEPENDENT STEP #
|
||||||
|
|
||||||
!!! todo "Conda Installation"
|
### END OPTION 2 ###
|
||||||
|
|
||||||
Now there are two different ways to set up the Python (miniconda) environment:
|
##### END TWO DIFFERENT OPTIONS #####
|
||||||
1. Standalone
|
|
||||||
2. with pyenv
|
|
||||||
If you don't know what we are talking about, choose Standalone
|
|
||||||
|
|
||||||
=== "Standalone"
|
# Clone the Invoke AI repo
|
||||||
|
|
||||||
```bash
|
|
||||||
# install cmake and rust:
|
|
||||||
brew install cmake rust
|
|
||||||
```
|
|
||||||
|
|
||||||
=== "M1 arm64"
|
|
||||||
|
|
||||||
```bash title="Install miniconda for M1 arm64"
|
|
||||||
curl https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-arm64.sh \
|
|
||||||
-o Miniconda3-latest-MacOSX-arm64.sh
|
|
||||||
/bin/bash Miniconda3-latest-MacOSX-arm64.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
=== "Intel x86_64"
|
|
||||||
|
|
||||||
```bash title="Install miniconda for Intel"
|
|
||||||
curl https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-x86_64.sh \
|
|
||||||
-o Miniconda3-latest-MacOSX-x86_64.sh
|
|
||||||
/bin/bash Miniconda3-latest-MacOSX-x86_64.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
=== "with pyenv"
|
|
||||||
|
|
||||||
```{.bash .annotate}
|
|
||||||
brew install rust pyenv-virtualenv # (1)!
|
|
||||||
pyenv install anaconda3-2022.05
|
|
||||||
pyenv virtualenv anaconda3-2022.05
|
|
||||||
eval "$(pyenv init -)"
|
|
||||||
pyenv activate anaconda3-2022.05
|
|
||||||
```
|
|
||||||
|
|
||||||
1. You might already have this installed, if that is the case just continue.
|
|
||||||
|
|
||||||
```{.bash .annotate title="local repo setup"}
|
|
||||||
# clone the repo
|
|
||||||
git clone https://github.com/invoke-ai/InvokeAI.git
|
git clone https://github.com/invoke-ai/InvokeAI.git
|
||||||
|
|
||||||
cd InvokeAI
|
cd InvokeAI
|
||||||
|
<<<<<<< HEAD
|
||||||
|
|
||||||
# wait until the checkpoint file has downloaded, then proceed
|
### WAIT FOR THE CHECKPOINT FILE TO DOWNLOAD, THEN PROCEED ###
|
||||||
|
# We will leave the big checkpoint wherever you stashed it for long-term storage,
|
||||||
|
# and make a link to it from the repo's folder. This allows you to use it for
|
||||||
|
# other repos, and if you need to delete Invoke AI, you won't have to download it again.
|
||||||
|
|
||||||
# create symlink to checkpoint
|
# Make the directory in the repo for the symlink
|
||||||
mkdir -p models/ldm/stable-diffusion-v1/
|
mkdir -p models/ldm/stable-diffusion-v1/
|
||||||
|
|
||||||
PATH_TO_CKPT="$HOME/Downloads" # (1)!
|
# This is the folder where you put the checkpoint file `sd-v1-4.ckpt`
|
||||||
|
PATH_TO_CKPT="$HOME/Downloads"
|
||||||
|
|
||||||
ln -s "$PATH_TO_CKPT/sd-v1-4.ckpt" \
|
# Create a link to the checkpoint
|
||||||
models/ldm/stable-diffusion-v1/model.ckpt
|
ln -s "$PATH_TO_CKPT/sd-v1-4.ckpt" models/ldm/stable-diffusion-v1/model.ckpt
|
||||||
```
|
|
||||||
|
|
||||||
1. or wherever you saved sd-v1-4.ckpt
|
# BEGIN ARCHITECTURE-DEPENDENT STEP #
|
||||||
|
# For M1: Create the environment & install packages
|
||||||
|
PIP_EXISTS_ACTION=w CONDA_SUBDIR=osx-arm64 conda env create -f environment-mac.yaml
|
||||||
|
|
||||||
!!! todo "create Conda Environment"
|
# For Intel: Create the environment & install packages
|
||||||
|
PIP_EXISTS_ACTION=w CONDA_SUBDIR=osx-64 conda env create -f environment-mac.yaml
|
||||||
|
# END ARCHITECTURE-DEPENDENT STEP #
|
||||||
|
|
||||||
=== "M1 arm64"
|
# Activate the environment (you need to do this every time you want to run SD)
|
||||||
|
conda activate ldm
|
||||||
|
|
||||||
```bash
|
# This will download some bits and pieces and make take a while
|
||||||
PIP_EXISTS_ACTION=w CONDA_SUBDIR=osx-arm64 \
|
|
||||||
conda env create \
|
|
||||||
-f environment-mac.yml \
|
|
||||||
&& conda activate ldm
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
=== "Intel x86_64"
|
|
||||||
|
|
||||||
```bash
|
|
||||||
PIP_EXISTS_ACTION=w CONDA_SUBDIR=osx-64 \
|
|
||||||
conda env create \
|
|
||||||
-f environment-mac.yml \
|
|
||||||
&& conda activate ldm
|
|
||||||
```
|
|
||||||
|
|
||||||
```{.bash .annotate title="preload models and run script"}
|
|
||||||
# only need to do this once
|
|
||||||
python scripts/preload_models.py
|
python scripts/preload_models.py
|
||||||
|
|
||||||
# now you can run SD in CLI mode
|
# Run SD!
|
||||||
python scripts/dream.py --full_precision # (1)!
|
python scripts/dream.py
|
||||||
|
```
|
||||||
# or run the web interface!
|
# or run the web interface!
|
||||||
python scripts/dream.py --web
|
python scripts/invoke.py --web
|
||||||
|
|
||||||
# The original scripts should work as well.
|
# The original scripts should work as well.
|
||||||
python scripts/orig_scripts/txt2img.py \
|
python scripts/orig_scripts/txt2img.py \
|
||||||
@ -155,7 +123,7 @@ it isn't required but wont hurt.
|
|||||||
|
|
||||||
## Common problems
|
## Common problems
|
||||||
|
|
||||||
After you followed all the instructions and try to run dream.py, you might
|
After you followed all the instructions and try to run invoke.py, you might
|
||||||
get several errors. Here's the errors I've seen and found solutions for.
|
get several errors. Here's the errors I've seen and found solutions for.
|
||||||
|
|
||||||
### Is it slow?
|
### Is it slow?
|
||||||
@ -172,13 +140,13 @@ python ./scripts/orig_scripts/txt2img.py \
|
|||||||
|
|
||||||
### Doesn't work anymore?
|
### Doesn't work anymore?
|
||||||
|
|
||||||
PyTorch nightly includes support for MPS. Because of this, this setup is
|
PyTorch nightly includes support for MPS. Because of this, this setup
|
||||||
inherently unstable. One morning I woke up and it no longer worked no matter
|
is inherently unstable. One morning I woke up and it no longer worked
|
||||||
what I did until I switched to miniforge. However, I have another Mac that works
|
no matter what I did until I switched to miniforge. However, I have
|
||||||
just fine with Anaconda. If you can't get it to work, please search a little
|
another Mac that works just fine with Anaconda. If you can't get it to
|
||||||
first because many of the errors will get posted and solved. If you can't find a
|
work, please search a little first because many of the errors will get
|
||||||
solution please
|
posted and solved. If you can't find a solution please [create an
|
||||||
[create an issue](https://github.com/invoke-ai/InvokeAI/issues).
|
issue](https://github.com/invoke-ai/InvokeAI/issues).
|
||||||
|
|
||||||
One debugging step is to update to the latest version of PyTorch nightly.
|
One debugging step is to update to the latest version of PyTorch nightly.
|
||||||
|
|
||||||
@ -220,9 +188,9 @@ There are several causes of these errors:
|
|||||||
"(ldm)" then you activated it. If it begins with "(base)" or something else
|
"(ldm)" then you activated it. If it begins with "(base)" or something else
|
||||||
you haven't.
|
you haven't.
|
||||||
|
|
||||||
2. You might've run `./scripts/preload_models.py` or `./scripts/dream.py`
|
2. You might've run `./scripts/preload_models.py` or `./scripts/invoke.py`
|
||||||
instead of `python ./scripts/preload_models.py` or
|
instead of `python ./scripts/preload_models.py` or
|
||||||
`python ./scripts/dream.py`. The cause of this error is long so it's below.
|
`python ./scripts/invoke.py`. The cause of this error is long so it's below.
|
||||||
|
|
||||||
<!-- I could not find out where the error is, otherwise would have marked it as a footnote -->
|
<!-- I could not find out where the error is, otherwise would have marked it as a footnote -->
|
||||||
|
|
||||||
@ -378,8 +346,8 @@ python scripts/preload_models.py
|
|||||||
WARNING: this will be slower than running natively on MPS.
|
WARNING: this will be slower than running natively on MPS.
|
||||||
```
|
```
|
||||||
|
|
||||||
This fork already includes a fix for this in
|
The InvokeAI version includes this fix in
|
||||||
[environment-mac.yml](https://github.com/invoke-ai/InvokeAI/blob/main/environment-mac.yml).
|
[environment-mac.yaml](https://github.com/invoke-ai/InvokeAI/blob/main/environment-mac.yaml).
|
||||||
|
|
||||||
### "Could not build wheels for tokenizers"
|
### "Could not build wheels for tokenizers"
|
||||||
|
|
||||||
@ -463,13 +431,10 @@ C.
|
|||||||
|
|
||||||
You don't have a virus. It's part of the project. Here's
|
You don't have a virus. It's part of the project. Here's
|
||||||
[Rick](https://github.com/invoke-ai/InvokeAI/blob/main/assets/rick.jpeg)
|
[Rick](https://github.com/invoke-ai/InvokeAI/blob/main/assets/rick.jpeg)
|
||||||
and here's
|
and here's [the
|
||||||
[the code](https://github.com/invoke-ai/InvokeAI/blob/69ae4b35e0a0f6ee1af8bb9a5d0016ccb27e36dc/scripts/txt2img.py#L79)
|
code](https://github.com/invoke-ai/InvokeAI/blob/69ae4b35e0a0f6ee1af8bb9a5d0016ccb27e36dc/scripts/txt2img.py#L79)
|
||||||
that swaps him in. It's a NSFW filter, which IMO, doesn't work very good (and we
|
that swaps him in. It's a NSFW filter, which IMO, doesn't work very
|
||||||
call this "computer vision", sheesh).
|
good (and we call this "computer vision", sheesh).
|
||||||
|
|
||||||
Actually, this could be happening because there's not enough RAM. You could try
|
|
||||||
the `model.half()` suggestion or specify smaller output images.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -492,11 +457,9 @@ return torch.layer_norm(input, normalized_shape, weight, bias, eps, torch.backen
|
|||||||
RuntimeError: view size is not compatible with input tensor's size and stride (at least one dimension spans across two contiguous subspaces). Use .reshape(...) instead.
|
RuntimeError: view size is not compatible with input tensor's size and stride (at least one dimension spans across two contiguous subspaces). Use .reshape(...) instead.
|
||||||
```
|
```
|
||||||
|
|
||||||
Update to the latest version of invoke-ai/InvokeAI. We were patching
|
Update to the latest version of invoke-ai/InvokeAI. We were
|
||||||
pytorch but we found a file in stable-diffusion that we could change instead.
|
patching pytorch but we found a file in stable-diffusion that we could
|
||||||
This is a 32-bit vs 16-bit problem.
|
change instead. This is a 32-bit vs 16-bit problem.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### The processor must support the Intel bla bla bla
|
### The processor must support the Intel bla bla bla
|
||||||
|
|
||||||
@ -519,7 +482,7 @@ use ARM packages, and use `nomkl` as described above.
|
|||||||
May appear when just starting to generate, e.g.:
|
May appear when just starting to generate, e.g.:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dream> clouds
|
invoke> clouds
|
||||||
Generating: 0%| | 0/1 [00:00<?, ?it/s]/Users/[...]/dev/stable-diffusion/ldm/modules/embedding_manager.py:152: UserWarning: The operator 'aten::nonzero' is not currently supported on the MPS backend and will fall back to run on the CPU. This may have performance implications. (Triggered internally at /Users/runner/work/_temp/anaconda/conda-bld/pytorch_1662016319283/work/aten/src/ATen/mps/MPSFallback.mm:11.)
|
Generating: 0%| | 0/1 [00:00<?, ?it/s]/Users/[...]/dev/stable-diffusion/ldm/modules/embedding_manager.py:152: UserWarning: The operator 'aten::nonzero' is not currently supported on the MPS backend and will fall back to run on the CPU. This may have performance implications. (Triggered internally at /Users/runner/work/_temp/anaconda/conda-bld/pytorch_1662016319283/work/aten/src/ATen/mps/MPSFallback.mm:11.)
|
||||||
placeholder_idx = torch.where(
|
placeholder_idx = torch.where(
|
||||||
loc("mps_add"("(mpsFileLoc): /AppleInternal/Library/BuildRoots/20d6c351-ee94-11ec-bcaf-7247572f23b4/Library/Caches/com.apple.xbs/Sources/MetalPerformanceShadersGraph/mpsgraph/MetalPerformanceShadersGraph/Core/Files/MPSGraphUtilities.mm":219:0)): error: input types 'tensor<2x1280xf32>' and 'tensor<*xf16>' are not broadcast compatible
|
loc("mps_add"("(mpsFileLoc): /AppleInternal/Library/BuildRoots/20d6c351-ee94-11ec-bcaf-7247572f23b4/Library/Caches/com.apple.xbs/Sources/MetalPerformanceShadersGraph/mpsgraph/MetalPerformanceShadersGraph/Core/Files/MPSGraphUtilities.mm":219:0)): error: input types 'tensor<2x1280xf32>' and 'tensor<*xf16>' are not broadcast compatible
|
||||||
|
@ -101,13 +101,13 @@ you may instead create a shortcut to it from within `models\ldm\stable-diffusion
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# for the pre-release weights
|
# for the pre-release weights
|
||||||
python scripts\dream.py -l
|
python scripts\invoke.py -l
|
||||||
|
|
||||||
# for the post-release weights
|
# for the post-release weights
|
||||||
python scripts\dream.py
|
python scripts\invoke.py
|
||||||
```
|
```
|
||||||
|
|
||||||
10. Subsequently, to relaunch the script, first activate the Anaconda command window (step 3),enter the InvokeAI directory (step 5, `cd \path\to\InvokeAI`), run `conda activate ldm` (step 6b), and then launch the dream script (step 9).
|
10. Subsequently, to relaunch the script, first activate the Anaconda command window (step 3),enter the InvokeAI directory (step 5, `cd \path\to\InvokeAI`), run `conda activate ldm` (step 6b), and then launch the invoke script (step 9).
|
||||||
|
|
||||||
**Note:** Tildebyte has written an alternative
|
**Note:** Tildebyte has written an alternative
|
||||||
["Easy peasy Windows install"](https://github.com/invoke-ai/InvokeAI/wiki/Easy-peasy-Windows-install)
|
["Easy peasy Windows install"](https://github.com/invoke-ai/InvokeAI/wiki/Easy-peasy-Windows-install)
|
||||||
|
@ -3,12 +3,12 @@ channels:
|
|||||||
- pytorch
|
- pytorch
|
||||||
- conda-forge
|
- conda-forge
|
||||||
dependencies:
|
dependencies:
|
||||||
- python==3.10.5
|
- python==3.9.13
|
||||||
- pip==22.2.2
|
- pip==22.2.2
|
||||||
|
|
||||||
# pytorch left unpinned
|
# pytorch left unpinned
|
||||||
- pytorch
|
- pytorch==1.12.1
|
||||||
- torchvision
|
- torchvision==0.13.1
|
||||||
|
|
||||||
# I suggest to keep the other deps sorted for convenience.
|
# I suggest to keep the other deps sorted for convenience.
|
||||||
# To determine what the latest versions should be, run:
|
# To determine what the latest versions should be, run:
|
||||||
@ -27,13 +27,12 @@ dependencies:
|
|||||||
- imgaug==0.4.0
|
- imgaug==0.4.0
|
||||||
- kornia==0.6.7
|
- kornia==0.6.7
|
||||||
- mpmath==1.2.1
|
- mpmath==1.2.1
|
||||||
- nomkl
|
- nomkl=1.0
|
||||||
- numpy==1.23.2
|
- numpy==1.23.2
|
||||||
- omegaconf==2.1.1
|
- omegaconf==2.1.1
|
||||||
- openh264==2.3.0
|
- openh264==2.3.0
|
||||||
- onnx==1.12.0
|
- onnx==1.12.0
|
||||||
- onnxruntime==1.12.1
|
- onnxruntime==1.12.1
|
||||||
- protobuf==3.19.4
|
|
||||||
- pudb==2022.1
|
- pudb==2022.1
|
||||||
- pytorch-lightning==1.7.5
|
- pytorch-lightning==1.7.5
|
||||||
- scipy==1.9.1
|
- scipy==1.9.1
|
||||||
@ -42,22 +41,22 @@ dependencies:
|
|||||||
- tensorboard==2.10.0
|
- tensorboard==2.10.0
|
||||||
- torchmetrics==0.9.3
|
- torchmetrics==0.9.3
|
||||||
- pip:
|
- pip:
|
||||||
- flask==2.1.3
|
- flask==2.1.3
|
||||||
- flask_socketio==5.3.0
|
- flask_socketio==5.3.0
|
||||||
- flask_cors==3.0.10
|
- flask_cors==3.0.10
|
||||||
- dependency_injector==4.40.0
|
- dependency_injector==4.40.0
|
||||||
- eventlet
|
- eventlet==0.33.1
|
||||||
- opencv-python==4.6.0
|
- opencv-python==4.6.0
|
||||||
- protobuf==3.20.1
|
- protobuf==3.19.5
|
||||||
- realesrgan==0.2.5.0
|
- realesrgan==0.2.5.0
|
||||||
- send2trash==1.8.0
|
- send2trash==1.8.0
|
||||||
- test-tube==0.7.5
|
- test-tube==0.7.5
|
||||||
- transformers==4.21.2
|
- transformers==4.21.2
|
||||||
- torch-fidelity==0.3.0
|
- torch-fidelity==0.3.0
|
||||||
- -e git+https://github.com/CompVis/taming-transformers.git@master#egg=taming-transformers
|
- -e git+https://github.com/CompVis/taming-transformers.git@master#egg=taming-transformers
|
||||||
- -e git+https://github.com/openai/CLIP.git@main#egg=clip
|
- -e git+https://github.com/openai/CLIP.git@main#egg=clip
|
||||||
- -e git+https://github.com/Birch-san/k-diffusion.git@mps#egg=k_diffusion
|
- -e git+https://github.com/Birch-san/k-diffusion.git@mps#egg=k_diffusion
|
||||||
- -e git+https://github.com/lstein/GFPGAN@fix-dark-cast-images#egg=gfpgan
|
- -e git+https://github.com/TencentARC/GFPGAN.git#egg=gfpgan
|
||||||
- -e .
|
- -e .
|
||||||
variables:
|
variables:
|
||||||
PYTORCH_ENABLE_MPS_FALLBACK: 1
|
PYTORCH_ENABLE_MPS_FALLBACK: 1
|
||||||
|
@ -36,5 +36,5 @@ dependencies:
|
|||||||
- -e git+https://github.com/openai/CLIP.git@main#egg=clip
|
- -e git+https://github.com/openai/CLIP.git@main#egg=clip
|
||||||
- -e git+https://github.com/CompVis/taming-transformers.git@master#egg=taming-transformers
|
- -e git+https://github.com/CompVis/taming-transformers.git@master#egg=taming-transformers
|
||||||
- -e git+https://github.com/Birch-san/k-diffusion.git@mps#egg=k_diffusion
|
- -e git+https://github.com/Birch-san/k-diffusion.git@mps#egg=k_diffusion
|
||||||
- -e git+https://github.com/lstein/GFPGAN@fix-dark-cast-images#egg=gfpgan
|
- -e git+https://github.com/TencentARC/GFPGAN.git#egg=gfpgan
|
||||||
- -e .
|
- -e .
|
||||||
|
1
frontend/dist/assets/index.4ceb640a.css
vendored
Normal file
483
frontend/dist/assets/index.6cdae835.js
vendored
Normal file
6
frontend/dist/index.html
vendored
@ -6,8 +6,8 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>InvokeAI - A Stable Diffusion Toolkit</title>
|
<title>InvokeAI - A Stable Diffusion Toolkit</title>
|
||||||
<link rel="shortcut icon" type="icon" href="/assets/favicon.0d253ced.ico" />
|
<link rel="shortcut icon" type="icon" href="/assets/favicon.0d253ced.ico" />
|
||||||
<script type="module" crossorigin src="/assets/index.27eefde8.js"></script>
|
<script type="module" crossorigin src="/assets/index.6cdae835.js"></script>
|
||||||
<link rel="stylesheet" href="/assets/index.c04b2fd8.css">
|
<link rel="stylesheet" href="/assets/index.4ceb640a.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@ -15,4 +15,4 @@
|
|||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
@ -71,7 +71,7 @@
|
|||||||
.next-prev-button-trigger-area {
|
.next-prev-button-trigger-area {
|
||||||
width: 7rem;
|
width: 7rem;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 15%;
|
||||||
display: grid;
|
display: grid;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
|
@ -16,8 +16,12 @@ const SeedOptions = () => {
|
|||||||
<Seed />
|
<Seed />
|
||||||
<ShuffleSeed />
|
<ShuffleSeed />
|
||||||
</Flex>
|
</Flex>
|
||||||
<Threshold />
|
<Flex gap={2}>
|
||||||
<Perlin />
|
<Threshold />
|
||||||
|
</Flex>
|
||||||
|
<Flex gap={2}>
|
||||||
|
<Perlin />
|
||||||
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -252,7 +252,9 @@ export const optionsSlice = createSlice({
|
|||||||
if (steps) state.steps = steps;
|
if (steps) state.steps = steps;
|
||||||
if (cfg_scale) state.cfgScale = cfg_scale;
|
if (cfg_scale) state.cfgScale = cfg_scale;
|
||||||
if (threshold) state.threshold = threshold;
|
if (threshold) state.threshold = threshold;
|
||||||
|
if (typeof threshold === 'undefined') state.threshold = 0;
|
||||||
if (perlin) state.perlin = perlin;
|
if (perlin) state.perlin = perlin;
|
||||||
|
if (typeof perlin === 'undefined') state.perlin = 0;
|
||||||
if (typeof seamless === 'boolean') state.seamless = seamless;
|
if (typeof seamless === 'boolean') state.seamless = seamless;
|
||||||
if (width) state.width = width;
|
if (width) state.width = width;
|
||||||
if (height) state.height = height;
|
if (height) state.height = height;
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
.site-header-right-side {
|
.site-header-right-side {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(6, max-content);
|
grid-template-columns: repeat(7, max-content);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
column-gap: 0.5rem;
|
column-gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { IconButton, Link, Tooltip, useColorMode } from '@chakra-ui/react';
|
import { IconButton, Link, Tooltip, useColorMode } from '@chakra-ui/react';
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
|
|
||||||
import { FaSun, FaMoon, FaGithub } from 'react-icons/fa';
|
import { FaSun, FaMoon, FaGithub, FaDiscord } from 'react-icons/fa';
|
||||||
import { MdHelp, MdKeyboard, MdSettings } from 'react-icons/md';
|
import { MdHelp, MdKeyboard, MdSettings } from 'react-icons/md';
|
||||||
|
|
||||||
import InvokeAILogo from '../../assets/images/logo.png';
|
import InvokeAILogo from '../../assets/images/logo.png';
|
||||||
@ -70,7 +70,7 @@ const SiteHeader = () => {
|
|||||||
icon={
|
icon={
|
||||||
<Link
|
<Link
|
||||||
isExternal
|
isExternal
|
||||||
href="http://github.com/lstein/stable-diffusion/issues"
|
href="http://github.com/invoke-ai/InvokeAI/issues"
|
||||||
>
|
>
|
||||||
<MdHelp />
|
<MdHelp />
|
||||||
</Link>
|
</Link>
|
||||||
@ -85,13 +85,27 @@ const SiteHeader = () => {
|
|||||||
fontSize={20}
|
fontSize={20}
|
||||||
size={'sm'}
|
size={'sm'}
|
||||||
icon={
|
icon={
|
||||||
<Link isExternal href="http://github.com/lstein/stable-diffusion">
|
<Link isExternal href="http://github.com/invoke-ai/InvokeAI">
|
||||||
<FaGithub />
|
<FaGithub />
|
||||||
</Link>
|
</Link>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
|
<Tooltip hasArrow label="Discord" placement={'bottom'}>
|
||||||
|
<IconButton
|
||||||
|
aria-label="Link to Discord Server"
|
||||||
|
variant="link"
|
||||||
|
fontSize={20}
|
||||||
|
size={'sm'}
|
||||||
|
icon={
|
||||||
|
<Link isExternal href="https://discord.gg/ZmtBAhwWhy">
|
||||||
|
<FaDiscord />
|
||||||
|
</Link>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
<Tooltip hasArrow label="Theme" placement={'bottom'}>
|
<Tooltip hasArrow label="Theme" placement={'bottom'}>
|
||||||
<IconButton
|
<IconButton
|
||||||
aria-label="Toggle Dark Mode"
|
aria-label="Toggle Dark Mode"
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
'''
|
|
||||||
Initialization file for the ldm.dream.generator package
|
|
||||||
'''
|
|
||||||
from .base import Generator
|
|
@ -1,4 +0,0 @@
|
|||||||
'''
|
|
||||||
Initialization file for the ldm.dream.restoration package
|
|
||||||
'''
|
|
||||||
from .base import Restoration
|
|
@ -19,7 +19,7 @@ import cv2
|
|||||||
import skimage
|
import skimage
|
||||||
|
|
||||||
from omegaconf import OmegaConf
|
from omegaconf import OmegaConf
|
||||||
from ldm.dream.generator.base import downsampling
|
from ldm.invoke.generator.base import downsampling
|
||||||
from PIL import Image, ImageOps
|
from PIL import Image, ImageOps
|
||||||
from torch import nn
|
from torch import nn
|
||||||
from pytorch_lightning import seed_everything, logging
|
from pytorch_lightning import seed_everything, logging
|
||||||
@ -28,33 +28,11 @@ from ldm.util import instantiate_from_config
|
|||||||
from ldm.models.diffusion.ddim import DDIMSampler
|
from ldm.models.diffusion.ddim import DDIMSampler
|
||||||
from ldm.models.diffusion.plms import PLMSSampler
|
from ldm.models.diffusion.plms import PLMSSampler
|
||||||
from ldm.models.diffusion.ksampler import KSampler
|
from ldm.models.diffusion.ksampler import KSampler
|
||||||
from ldm.dream.pngwriter import PngWriter
|
from ldm.invoke.pngwriter import PngWriter
|
||||||
from ldm.dream.args import metadata_from_png
|
from ldm.invoke.args import metadata_from_png
|
||||||
from ldm.dream.image_util import InitImageResizer
|
from ldm.invoke.image_util import InitImageResizer
|
||||||
from ldm.dream.devices import choose_torch_device, choose_precision
|
from ldm.invoke.devices import choose_torch_device, choose_precision
|
||||||
from ldm.dream.conditioning import get_uc_and_c
|
from ldm.invoke.conditioning import get_uc_and_c
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def fix_func(orig):
|
|
||||||
if hasattr(torch.backends, 'mps') and torch.backends.mps.is_available():
|
|
||||||
def new_func(*args, **kw):
|
|
||||||
device = kw.get("device", "mps")
|
|
||||||
kw["device"]="cpu"
|
|
||||||
return orig(*args, **kw).to(device)
|
|
||||||
return new_func
|
|
||||||
return orig
|
|
||||||
|
|
||||||
torch.rand = fix_func(torch.rand)
|
|
||||||
torch.rand_like = fix_func(torch.rand_like)
|
|
||||||
torch.randn = fix_func(torch.randn)
|
|
||||||
torch.randn_like = fix_func(torch.randn_like)
|
|
||||||
torch.randint = fix_func(torch.randint)
|
|
||||||
torch.randint_like = fix_func(torch.randint_like)
|
|
||||||
torch.bernoulli = fix_func(torch.bernoulli)
|
|
||||||
torch.multinomial = fix_func(torch.multinomial)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"""Simplified text to image API for stable diffusion/latent diffusion
|
"""Simplified text to image API for stable diffusion/latent diffusion
|
||||||
|
|
||||||
@ -142,7 +120,8 @@ class Generate:
|
|||||||
config = None,
|
config = None,
|
||||||
gfpgan=None,
|
gfpgan=None,
|
||||||
codeformer=None,
|
codeformer=None,
|
||||||
esrgan=None
|
esrgan=None,
|
||||||
|
free_gpu_mem=False,
|
||||||
):
|
):
|
||||||
models = OmegaConf.load(conf)
|
models = OmegaConf.load(conf)
|
||||||
mconfig = models[model]
|
mconfig = models[model]
|
||||||
@ -169,6 +148,7 @@ class Generate:
|
|||||||
self.gfpgan = gfpgan
|
self.gfpgan = gfpgan
|
||||||
self.codeformer = codeformer
|
self.codeformer = codeformer
|
||||||
self.esrgan = esrgan
|
self.esrgan = esrgan
|
||||||
|
self.free_gpu_mem = free_gpu_mem
|
||||||
|
|
||||||
# Note that in previous versions, there was an option to pass the
|
# Note that in previous versions, there was an option to pass the
|
||||||
# device to Generate(). However the device was then ignored, so
|
# device to Generate(). However the device was then ignored, so
|
||||||
@ -295,9 +275,9 @@ class Generate:
|
|||||||
def process_image(image,seed):
|
def process_image(image,seed):
|
||||||
image.save(f{'images/seed.png'})
|
image.save(f{'images/seed.png'})
|
||||||
|
|
||||||
The callback used by the prompt2png() can be found in ldm/dream_util.py. It contains code
|
The code used to save images to a directory can be found in ldm/invoke/pngwriter.py.
|
||||||
to create the requested output directory, select a unique informative name for each image, and
|
It contains code to create the requested output directory, select a unique informative
|
||||||
write the prompt into the PNG metadata.
|
name for each image, and write the prompt into the PNG metadata.
|
||||||
"""
|
"""
|
||||||
# TODO: convert this into a getattr() loop
|
# TODO: convert this into a getattr() loop
|
||||||
steps = steps or self.steps
|
steps = steps or self.steps
|
||||||
@ -385,7 +365,8 @@ class Generate:
|
|||||||
generator = self._make_txt2img()
|
generator = self._make_txt2img()
|
||||||
|
|
||||||
generator.set_variation(
|
generator.set_variation(
|
||||||
self.seed, variation_amount, with_variations)
|
self.seed, variation_amount, with_variations
|
||||||
|
)
|
||||||
results = generator.generate(
|
results = generator.generate(
|
||||||
prompt,
|
prompt,
|
||||||
iterations=iterations,
|
iterations=iterations,
|
||||||
@ -521,7 +502,7 @@ class Generate:
|
|||||||
)
|
)
|
||||||
|
|
||||||
elif tool == 'outcrop':
|
elif tool == 'outcrop':
|
||||||
from ldm.dream.restoration.outcrop import Outcrop
|
from ldm.invoke.restoration.outcrop import Outcrop
|
||||||
extend_instructions = {}
|
extend_instructions = {}
|
||||||
for direction,pixels in _pairwise(opt.outcrop):
|
for direction,pixels in _pairwise(opt.outcrop):
|
||||||
extend_instructions[direction]=int(pixels)
|
extend_instructions[direction]=int(pixels)
|
||||||
@ -558,7 +539,7 @@ class Generate:
|
|||||||
image_callback = callback,
|
image_callback = callback,
|
||||||
)
|
)
|
||||||
elif tool == 'outpaint':
|
elif tool == 'outpaint':
|
||||||
from ldm.dream.restoration.outpaint import Outpaint
|
from ldm.invoke.restoration.outpaint import Outpaint
|
||||||
restorer = Outpaint(image,self)
|
restorer = Outpaint(image,self)
|
||||||
return restorer.process(
|
return restorer.process(
|
||||||
opt,
|
opt,
|
||||||
@ -594,18 +575,14 @@ class Generate:
|
|||||||
height,
|
height,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if image.width < self.width and image.height < self.height:
|
||||||
|
print(f'>> WARNING: img2img and inpainting may produce unexpected results with initial images smaller than {self.width}x{self.height} in both dimensions')
|
||||||
|
|
||||||
# if image has a transparent area and no mask was provided, then try to generate mask
|
# if image has a transparent area and no mask was provided, then try to generate mask
|
||||||
if self._has_transparency(image) and not mask:
|
if self._has_transparency(image):
|
||||||
print(
|
self._transparency_check_and_warning(image, mask)
|
||||||
'>> Initial image has transparent areas. Will inpaint in these regions.')
|
|
||||||
if self._check_for_erasure(image):
|
|
||||||
print(
|
|
||||||
'>> WARNING: Colors underneath the transparent region seem to have been erased.\n',
|
|
||||||
'>> Inpainting will be suboptimal. Please preserve the colors when making\n',
|
|
||||||
'>> a transparency mask, or provide mask explicitly using --init_mask (-M).'
|
|
||||||
)
|
|
||||||
# this returns a torch tensor
|
# this returns a torch tensor
|
||||||
init_mask = self._create_init_mask(image,width,height,fit=fit)
|
init_mask = self._create_init_mask(image, width, height, fit=fit)
|
||||||
|
|
||||||
if (image.width * image.height) > (self.width * self.height):
|
if (image.width * image.height) > (self.width * self.height):
|
||||||
print(">> This input is larger than your defaults. If you run out of memory, please use a smaller image.")
|
print(">> This input is larger than your defaults. If you run out of memory, please use a smaller image.")
|
||||||
@ -621,39 +598,39 @@ class Generate:
|
|||||||
|
|
||||||
def _make_base(self):
|
def _make_base(self):
|
||||||
if not self.generators.get('base'):
|
if not self.generators.get('base'):
|
||||||
from ldm.dream.generator import Generator
|
from ldm.invoke.generator import Generator
|
||||||
self.generators['base'] = Generator(self.model, self.precision)
|
self.generators['base'] = Generator(self.model, self.precision)
|
||||||
return self.generators['base']
|
return self.generators['base']
|
||||||
|
|
||||||
def _make_img2img(self):
|
def _make_img2img(self):
|
||||||
if not self.generators.get('img2img'):
|
if not self.generators.get('img2img'):
|
||||||
from ldm.dream.generator.img2img import Img2Img
|
from ldm.invoke.generator.img2img import Img2Img
|
||||||
self.generators['img2img'] = Img2Img(self.model, self.precision)
|
self.generators['img2img'] = Img2Img(self.model, self.precision)
|
||||||
return self.generators['img2img']
|
return self.generators['img2img']
|
||||||
|
|
||||||
def _make_embiggen(self):
|
def _make_embiggen(self):
|
||||||
if not self.generators.get('embiggen'):
|
if not self.generators.get('embiggen'):
|
||||||
from ldm.dream.generator.embiggen import Embiggen
|
from ldm.invoke.generator.embiggen import Embiggen
|
||||||
self.generators['embiggen'] = Embiggen(self.model, self.precision)
|
self.generators['embiggen'] = Embiggen(self.model, self.precision)
|
||||||
return self.generators['embiggen']
|
return self.generators['embiggen']
|
||||||
|
|
||||||
def _make_txt2img(self):
|
def _make_txt2img(self):
|
||||||
if not self.generators.get('txt2img'):
|
if not self.generators.get('txt2img'):
|
||||||
from ldm.dream.generator.txt2img import Txt2Img
|
from ldm.invoke.generator.txt2img import Txt2Img
|
||||||
self.generators['txt2img'] = Txt2Img(self.model, self.precision)
|
self.generators['txt2img'] = Txt2Img(self.model, self.precision)
|
||||||
self.generators['txt2img'].free_gpu_mem = self.free_gpu_mem
|
self.generators['txt2img'].free_gpu_mem = self.free_gpu_mem
|
||||||
return self.generators['txt2img']
|
return self.generators['txt2img']
|
||||||
|
|
||||||
def _make_txt2img2img(self):
|
def _make_txt2img2img(self):
|
||||||
if not self.generators.get('txt2img2'):
|
if not self.generators.get('txt2img2'):
|
||||||
from ldm.dream.generator.txt2img2img import Txt2Img2Img
|
from ldm.invoke.generator.txt2img2img import Txt2Img2Img
|
||||||
self.generators['txt2img2'] = Txt2Img2Img(self.model, self.precision)
|
self.generators['txt2img2'] = Txt2Img2Img(self.model, self.precision)
|
||||||
self.generators['txt2img2'].free_gpu_mem = self.free_gpu_mem
|
self.generators['txt2img2'].free_gpu_mem = self.free_gpu_mem
|
||||||
return self.generators['txt2img2']
|
return self.generators['txt2img2']
|
||||||
|
|
||||||
def _make_inpaint(self):
|
def _make_inpaint(self):
|
||||||
if not self.generators.get('inpaint'):
|
if not self.generators.get('inpaint'):
|
||||||
from ldm.dream.generator.inpaint import Inpaint
|
from ldm.invoke.generator.inpaint import Inpaint
|
||||||
self.generators['inpaint'] = Inpaint(self.model, self.precision)
|
self.generators['inpaint'] = Inpaint(self.model, self.precision)
|
||||||
return self.generators['inpaint']
|
return self.generators['inpaint']
|
||||||
|
|
||||||
@ -784,7 +761,7 @@ class Generate:
|
|||||||
|
|
||||||
print(msg)
|
print(msg)
|
||||||
|
|
||||||
# Be warned: config is the path to the model config file, not the dream conf file!
|
# Be warned: config is the path to the model config file, not the invoke conf file!
|
||||||
# Also note that we can get config and weights from self, so why do we need to
|
# Also note that we can get config and weights from self, so why do we need to
|
||||||
# pass them as args?
|
# pass them as args?
|
||||||
def _load_model_from_config(self, config, weights):
|
def _load_model_from_config(self, config, weights):
|
||||||
@ -920,6 +897,17 @@ class Generate:
|
|||||||
colored += 1
|
colored += 1
|
||||||
return colored == 0
|
return colored == 0
|
||||||
|
|
||||||
|
def _transparency_check_and_warning(self,image, mask):
|
||||||
|
if not mask:
|
||||||
|
print(
|
||||||
|
'>> Initial image has transparent areas. Will inpaint in these regions.')
|
||||||
|
if self._check_for_erasure(image):
|
||||||
|
print(
|
||||||
|
'>> WARNING: Colors underneath the transparent region seem to have been erased.\n',
|
||||||
|
'>> Inpainting will be suboptimal. Please preserve the colors when making\n',
|
||||||
|
'>> a transparency mask, or provide mask explicitly using --init_mask (-M).'
|
||||||
|
)
|
||||||
|
|
||||||
def _squeeze_image(self, image):
|
def _squeeze_image(self, image):
|
||||||
x, y, resize_needed = self._resolution_check(image.width, image.height)
|
x, y, resize_needed = self._resolution_check(image.width, image.height)
|
||||||
if resize_needed:
|
if resize_needed:
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""Helper class for dealing with image generation arguments.
|
"""Helper class for dealing with image generation arguments.
|
||||||
|
|
||||||
The Args class parses both the command line (shell) arguments, as well as the
|
The Args class parses both the command line (shell) arguments, as well as the
|
||||||
command string passed at the dream> prompt. It serves as the definitive repository
|
command string passed at the invoke> prompt. It serves as the definitive repository
|
||||||
of all the arguments used by Generate and their default values, and implements the
|
of all the arguments used by Generate and their default values, and implements the
|
||||||
preliminary metadata standards discussed here:
|
preliminary metadata standards discussed here:
|
||||||
|
|
||||||
@ -19,7 +19,7 @@ To use:
|
|||||||
print('oops')
|
print('oops')
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
# read in a command passed to the dream> prompt:
|
# read in a command passed to the invoke> prompt:
|
||||||
opts = opt.parse_cmd('do androids dream of electric sheep? -H256 -W1024 -n4')
|
opts = opt.parse_cmd('do androids dream of electric sheep? -H256 -W1024 -n4')
|
||||||
|
|
||||||
# The Args object acts like a namespace object
|
# The Args object acts like a namespace object
|
||||||
@ -64,7 +64,7 @@ To generate a dict representing RFC266 metadata:
|
|||||||
This will generate an RFC266 dictionary that can then be turned into a JSON
|
This will generate an RFC266 dictionary that can then be turned into a JSON
|
||||||
and written to the PNG file. The optional seeds, weights, model_hash and
|
and written to the PNG file. The optional seeds, weights, model_hash and
|
||||||
postprocesser arguments are not available to the opt object and so must be
|
postprocesser arguments are not available to the opt object and so must be
|
||||||
provided externally. See how dream.py does it.
|
provided externally. See how invoke.py does it.
|
||||||
|
|
||||||
Note that this function was originally called format_metadata() and a wrapper
|
Note that this function was originally called format_metadata() and a wrapper
|
||||||
is provided that issues a deprecation notice.
|
is provided that issues a deprecation notice.
|
||||||
@ -91,8 +91,8 @@ import re
|
|||||||
import copy
|
import copy
|
||||||
import base64
|
import base64
|
||||||
import functools
|
import functools
|
||||||
import ldm.dream.pngwriter
|
import ldm.invoke.pngwriter
|
||||||
from ldm.dream.conditioning import split_weighted_subprompts
|
from ldm.invoke.conditioning import split_weighted_subprompts
|
||||||
|
|
||||||
SAMPLER_CHOICES = [
|
SAMPLER_CHOICES = [
|
||||||
'ddim',
|
'ddim',
|
||||||
@ -151,7 +151,7 @@ class Args(object):
|
|||||||
'''
|
'''
|
||||||
Initialize new Args class. It takes two optional arguments, an argparse
|
Initialize new Args class. It takes two optional arguments, an argparse
|
||||||
parser for switches given on the shell command line, and an argparse
|
parser for switches given on the shell command line, and an argparse
|
||||||
parser for switches given on the dream> CLI line. If one or both are
|
parser for switches given on the invoke> CLI line. If one or both are
|
||||||
missing, it creates appropriate parsers internally.
|
missing, it creates appropriate parsers internally.
|
||||||
'''
|
'''
|
||||||
self._arg_parser = arg_parser or self._create_arg_parser()
|
self._arg_parser = arg_parser or self._create_arg_parser()
|
||||||
@ -168,7 +168,7 @@ class Args(object):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def parse_cmd(self,cmd_string):
|
def parse_cmd(self,cmd_string):
|
||||||
'''Parse a dream>-style command string '''
|
'''Parse a invoke>-style command string '''
|
||||||
command = cmd_string.replace("'", "\\'")
|
command = cmd_string.replace("'", "\\'")
|
||||||
try:
|
try:
|
||||||
elements = shlex.split(command)
|
elements = shlex.split(command)
|
||||||
@ -269,7 +269,7 @@ class Args(object):
|
|||||||
if a['with_variations']:
|
if a['with_variations']:
|
||||||
formatted_variations = ','.join(f'{seed}:{weight}' for seed, weight in (a["with_variations"]))
|
formatted_variations = ','.join(f'{seed}:{weight}' for seed, weight in (a["with_variations"]))
|
||||||
switches.append(f'-V {formatted_variations}')
|
switches.append(f'-V {formatted_variations}')
|
||||||
if 'variations' in a:
|
if 'variations' in a and len(a['variations'])>0:
|
||||||
switches.append(f'-V {a["variations"]}')
|
switches.append(f'-V {a["variations"]}')
|
||||||
return ' '.join(switches)
|
return ' '.join(switches)
|
||||||
|
|
||||||
@ -509,23 +509,23 @@ class Args(object):
|
|||||||
)
|
)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
# This creates the parser that processes commands on the dream> command line
|
# This creates the parser that processes commands on the invoke> command line
|
||||||
def _create_dream_cmd_parser(self):
|
def _create_dream_cmd_parser(self):
|
||||||
parser = PagingArgumentParser(
|
parser = PagingArgumentParser(
|
||||||
formatter_class=ArgFormatter,
|
formatter_class=ArgFormatter,
|
||||||
description=
|
description=
|
||||||
"""
|
"""
|
||||||
*Image generation:*
|
*Image generation:*
|
||||||
dream> a fantastic alien landscape -W576 -H512 -s60 -n4
|
invoke> a fantastic alien landscape -W576 -H512 -s60 -n4
|
||||||
|
|
||||||
*postprocessing*
|
*postprocessing*
|
||||||
!fix applies upscaling/facefixing to a previously-generated image.
|
!fix applies upscaling/facefixing to a previously-generated image.
|
||||||
dream> !fix 0000045.4829112.png -G1 -U4 -ft codeformer
|
invoke> !fix 0000045.4829112.png -G1 -U4 -ft codeformer
|
||||||
|
|
||||||
*History manipulation*
|
*History manipulation*
|
||||||
!fetch retrieves the command used to generate an earlier image.
|
!fetch retrieves the command used to generate an earlier image.
|
||||||
dream> !fetch 0000015.8929913.png
|
invoke> !fetch 0000015.8929913.png
|
||||||
dream> a fantastic alien landscape -W 576 -H 512 -s 60 -A plms -C 7.5
|
invoke> a fantastic alien landscape -W 576 -H 512 -s 60 -A plms -C 7.5
|
||||||
|
|
||||||
!history lists all the commands issued during the current session.
|
!history lists all the commands issued during the current session.
|
||||||
|
|
||||||
@ -842,7 +842,7 @@ def metadata_from_png(png_file_path) -> Args:
|
|||||||
an Args object containing the image metadata. Note that this
|
an Args object containing the image metadata. Note that this
|
||||||
returns a single Args object, not multiple.
|
returns a single Args object, not multiple.
|
||||||
'''
|
'''
|
||||||
meta = ldm.dream.pngwriter.retrieve_metadata(png_file_path)
|
meta = ldm.invoke.pngwriter.retrieve_metadata(png_file_path)
|
||||||
if 'sd-metadata' in meta and len(meta['sd-metadata'])>0 :
|
if 'sd-metadata' in meta and len(meta['sd-metadata'])>0 :
|
||||||
return metadata_loads(meta)[0]
|
return metadata_loads(meta)[0]
|
||||||
else:
|
else:
|
4
ldm/invoke/generator/__init__.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
'''
|
||||||
|
Initialization file for the ldm.invoke.generator package
|
||||||
|
'''
|
||||||
|
from .base import Generator
|
@ -1,5 +1,5 @@
|
|||||||
'''
|
'''
|
||||||
Base class for ldm.dream.generator.*
|
Base class for ldm.invoke.generator.*
|
||||||
including img2img, txt2img, and inpaint
|
including img2img, txt2img, and inpaint
|
||||||
'''
|
'''
|
||||||
import torch
|
import torch
|
||||||
@ -9,7 +9,7 @@ from tqdm import tqdm, trange
|
|||||||
from PIL import Image
|
from PIL import Image
|
||||||
from einops import rearrange, repeat
|
from einops import rearrange, repeat
|
||||||
from pytorch_lightning import seed_everything
|
from pytorch_lightning import seed_everything
|
||||||
from ldm.dream.devices import choose_autocast
|
from ldm.invoke.devices import choose_autocast
|
||||||
from ldm.util import rand_perlin_2d
|
from ldm.util import rand_perlin_2d
|
||||||
|
|
||||||
downsampling = 8
|
downsampling = 8
|
||||||
@ -21,6 +21,8 @@ class Generator():
|
|||||||
self.seed = None
|
self.seed = None
|
||||||
self.latent_channels = model.channels
|
self.latent_channels = model.channels
|
||||||
self.downsampling_factor = downsampling # BUG: should come from model or config
|
self.downsampling_factor = downsampling # BUG: should come from model or config
|
||||||
|
self.perlin = 0.0
|
||||||
|
self.threshold = 0
|
||||||
self.variation_amount = 0
|
self.variation_amount = 0
|
||||||
self.with_variations = []
|
self.with_variations = []
|
||||||
|
|
@ -1,15 +1,15 @@
|
|||||||
'''
|
'''
|
||||||
ldm.dream.generator.embiggen descends from ldm.dream.generator
|
ldm.invoke.generator.embiggen descends from ldm.invoke.generator
|
||||||
and generates with ldm.dream.generator.img2img
|
and generates with ldm.invoke.generator.img2img
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from tqdm import trange
|
from tqdm import trange
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from ldm.dream.generator.base import Generator
|
from ldm.invoke.generator.base import Generator
|
||||||
from ldm.dream.generator.img2img import Img2Img
|
from ldm.invoke.generator.img2img import Img2Img
|
||||||
from ldm.dream.devices import choose_autocast
|
from ldm.invoke.devices import choose_autocast
|
||||||
from ldm.models.diffusion.ddim import DDIMSampler
|
from ldm.models.diffusion.ddim import DDIMSampler
|
||||||
|
|
||||||
class Embiggen(Generator):
|
class Embiggen(Generator):
|
||||||
@ -107,7 +107,7 @@ class Embiggen(Generator):
|
|||||||
initsuperwidth = round(initsuperwidth*embiggen[0])
|
initsuperwidth = round(initsuperwidth*embiggen[0])
|
||||||
initsuperheight = round(initsuperheight*embiggen[0])
|
initsuperheight = round(initsuperheight*embiggen[0])
|
||||||
if embiggen[1] > 0: # No point in ESRGAN upscaling if strength is set zero
|
if embiggen[1] > 0: # No point in ESRGAN upscaling if strength is set zero
|
||||||
from ldm.dream.restoration.realesrgan import ESRGAN
|
from ldm.invoke.restoration.realesrgan import ESRGAN
|
||||||
esrgan = ESRGAN()
|
esrgan = ESRGAN()
|
||||||
print(
|
print(
|
||||||
f'>> ESRGAN upscaling init image prior to cutting with Embiggen with strength {embiggen[1]}')
|
f'>> ESRGAN upscaling init image prior to cutting with Embiggen with strength {embiggen[1]}')
|
@ -1,11 +1,11 @@
|
|||||||
'''
|
'''
|
||||||
ldm.dream.generator.img2img descends from ldm.dream.generator
|
ldm.invoke.generator.img2img descends from ldm.invoke.generator
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from ldm.dream.devices import choose_autocast
|
from ldm.invoke.devices import choose_autocast
|
||||||
from ldm.dream.generator.base import Generator
|
from ldm.invoke.generator.base import Generator
|
||||||
from ldm.models.diffusion.ddim import DDIMSampler
|
from ldm.models.diffusion.ddim import DDIMSampler
|
||||||
|
|
||||||
class Img2Img(Generator):
|
class Img2Img(Generator):
|
@ -1,12 +1,12 @@
|
|||||||
'''
|
'''
|
||||||
ldm.dream.generator.inpaint descends from ldm.dream.generator
|
ldm.invoke.generator.inpaint descends from ldm.invoke.generator
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from einops import rearrange, repeat
|
from einops import rearrange, repeat
|
||||||
from ldm.dream.devices import choose_autocast
|
from ldm.invoke.devices import choose_autocast
|
||||||
from ldm.dream.generator.img2img import Img2Img
|
from ldm.invoke.generator.img2img import Img2Img
|
||||||
from ldm.models.diffusion.ddim import DDIMSampler
|
from ldm.models.diffusion.ddim import DDIMSampler
|
||||||
from ldm.models.diffusion.ksampler import KSampler
|
from ldm.models.diffusion.ksampler import KSampler
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ class Inpaint(Img2Img):
|
|||||||
# klms samplers not supported yet, so ignore previous sampler
|
# klms samplers not supported yet, so ignore previous sampler
|
||||||
if isinstance(sampler,KSampler):
|
if isinstance(sampler,KSampler):
|
||||||
print(
|
print(
|
||||||
f">> sampler '{sampler.__class__.__name__}' is not yet supported for inpainting, using DDIMSampler instead."
|
f">> Using recommended DDIM sampler for inpainting."
|
||||||
)
|
)
|
||||||
sampler = DDIMSampler(self.model, device=self.model.device)
|
sampler = DDIMSampler(self.model, device=self.model.device)
|
||||||
|
|
@ -1,10 +1,10 @@
|
|||||||
'''
|
'''
|
||||||
ldm.dream.generator.txt2img inherits from ldm.dream.generator
|
ldm.invoke.generator.txt2img inherits from ldm.invoke.generator
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from ldm.dream.generator.base import Generator
|
from ldm.invoke.generator.base import Generator
|
||||||
|
|
||||||
class Txt2Img(Generator):
|
class Txt2Img(Generator):
|
||||||
def __init__(self, model, precision):
|
def __init__(self, model, precision):
|
@ -1,11 +1,11 @@
|
|||||||
'''
|
'''
|
||||||
ldm.dream.generator.txt2img inherits from ldm.dream.generator
|
ldm.invoke.generator.txt2img inherits from ldm.invoke.generator
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import math
|
import math
|
||||||
from ldm.dream.generator.base import Generator
|
from ldm.invoke.generator.base import Generator
|
||||||
from ldm.models.diffusion.ddim import DDIMSampler
|
from ldm.models.diffusion.ddim import DDIMSampler
|
||||||
|
|
||||||
|
|
||||||
@ -64,7 +64,7 @@ class Txt2Img2Img(Generator):
|
|||||||
)
|
)
|
||||||
|
|
||||||
print(
|
print(
|
||||||
f"\n>> Interpolating from {init_width}x{init_height} to {width}x{height}"
|
f"\n>> Interpolating from {init_width}x{init_height} to {width}x{height} using DDIM sampling"
|
||||||
)
|
)
|
||||||
|
|
||||||
# resizing
|
# resizing
|
||||||
@ -75,17 +75,19 @@ class Txt2Img2Img(Generator):
|
|||||||
)
|
)
|
||||||
|
|
||||||
t_enc = int(strength * steps)
|
t_enc = int(strength * steps)
|
||||||
|
ddim_sampler = DDIMSampler(self.model, device=self.model.device)
|
||||||
|
ddim_sampler.make_schedule(
|
||||||
|
ddim_num_steps=steps, ddim_eta=ddim_eta, verbose=False
|
||||||
|
)
|
||||||
|
|
||||||
x = self.get_noise(width,height,False)
|
z_enc = ddim_sampler.stochastic_encode(
|
||||||
|
|
||||||
z_enc = sampler.stochastic_encode(
|
|
||||||
samples,
|
samples,
|
||||||
torch.tensor([t_enc]).to(self.model.device),
|
torch.tensor([t_enc]).to(self.model.device),
|
||||||
noise=x
|
noise=self.get_noise(width,height,False)
|
||||||
)
|
)
|
||||||
|
|
||||||
# decode it
|
# decode it
|
||||||
samples = sampler.decode(
|
samples = ddim_sampler.decode(
|
||||||
z_enc,
|
z_enc,
|
||||||
c,
|
c,
|
||||||
t_enc,
|
t_enc,
|
@ -1,17 +1,17 @@
|
|||||||
"""
|
"""
|
||||||
Readline helper functions for dream.py (linux and mac only).
|
Readline helper functions for invoke.py.
|
||||||
You may import the global singleton `completer` to get access to the
|
You may import the global singleton `completer` to get access to the
|
||||||
completer object itself. This is useful when you want to autocomplete
|
completer object itself. This is useful when you want to autocomplete
|
||||||
seeds:
|
seeds:
|
||||||
|
|
||||||
from ldm.dream.readline import completer
|
from ldm.invoke.readline import completer
|
||||||
completer.add_seed(18247566)
|
completer.add_seed(18247566)
|
||||||
completer.add_seed(9281839)
|
completer.add_seed(9281839)
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import atexit
|
import atexit
|
||||||
from ldm.dream.args import Args
|
from ldm.invoke.args import Args
|
||||||
|
|
||||||
# ---------------readline utilities---------------------
|
# ---------------readline utilities---------------------
|
||||||
try:
|
try:
|
||||||
@ -20,7 +20,7 @@ try:
|
|||||||
except (ImportError,ModuleNotFoundError):
|
except (ImportError,ModuleNotFoundError):
|
||||||
readline_available = False
|
readline_available = False
|
||||||
|
|
||||||
IMG_EXTENSIONS = ('.png','.jpg','.jpeg')
|
IMG_EXTENSIONS = ('.png','.jpg','.jpeg','.PNG','.JPG','.JPEG','.gif','.GIF')
|
||||||
COMMANDS = (
|
COMMANDS = (
|
||||||
'--steps','-s',
|
'--steps','-s',
|
||||||
'--seed','-S',
|
'--seed','-S',
|
||||||
@ -74,7 +74,7 @@ class Completer(object):
|
|||||||
|
|
||||||
def complete(self, text, state):
|
def complete(self, text, state):
|
||||||
'''
|
'''
|
||||||
Completes dream command line.
|
Completes invoke command line.
|
||||||
BUG: it doesn't correctly complete files that have spaces in the name.
|
BUG: it doesn't correctly complete files that have spaces in the name.
|
||||||
'''
|
'''
|
||||||
buffer = readline.get_line_buffer()
|
buffer = readline.get_line_buffer()
|
||||||
@ -287,7 +287,7 @@ def get_completer(opt:Args)->Completer:
|
|||||||
readline.parse_and_bind('set skip-completed-text on')
|
readline.parse_and_bind('set skip-completed-text on')
|
||||||
readline.parse_and_bind('set show-all-if-ambiguous on')
|
readline.parse_and_bind('set show-all-if-ambiguous on')
|
||||||
|
|
||||||
histfile = os.path.join(os.path.expanduser(opt.outdir), '.dream_history')
|
histfile = os.path.join(os.path.expanduser(opt.outdir), '.invoke_history')
|
||||||
try:
|
try:
|
||||||
readline.read_history_file(histfile)
|
readline.read_history_file(histfile)
|
||||||
readline.set_history_length(1000)
|
readline.set_history_length(1000)
|
4
ldm/invoke/restoration/__init__.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
'''
|
||||||
|
Initialization file for the ldm.invoke.restoration package
|
||||||
|
'''
|
||||||
|
from .base import Restoration
|
@ -23,16 +23,16 @@ class Restoration():
|
|||||||
|
|
||||||
# Face Restore Models
|
# Face Restore Models
|
||||||
def load_gfpgan(self, gfpgan_dir, gfpgan_model_path):
|
def load_gfpgan(self, gfpgan_dir, gfpgan_model_path):
|
||||||
from ldm.dream.restoration.gfpgan import GFPGAN
|
from ldm.invoke.restoration.gfpgan import GFPGAN
|
||||||
return GFPGAN(gfpgan_dir, gfpgan_model_path)
|
return GFPGAN(gfpgan_dir, gfpgan_model_path)
|
||||||
|
|
||||||
def load_codeformer(self):
|
def load_codeformer(self):
|
||||||
from ldm.dream.restoration.codeformer import CodeFormerRestoration
|
from ldm.invoke.restoration.codeformer import CodeFormerRestoration
|
||||||
return CodeFormerRestoration()
|
return CodeFormerRestoration()
|
||||||
|
|
||||||
# Upscale Models
|
# Upscale Models
|
||||||
def load_esrgan(self, esrgan_bg_tile=400):
|
def load_esrgan(self, esrgan_bg_tile=400):
|
||||||
from ldm.dream.restoration.realesrgan import ESRGAN
|
from ldm.invoke.restoration.realesrgan import ESRGAN
|
||||||
esrgan = ESRGAN(esrgan_bg_tile)
|
esrgan = ESRGAN(esrgan_bg_tile)
|
||||||
print('>> ESRGAN Initialized')
|
print('>> ESRGAN Initialized')
|
||||||
return esrgan;
|
return esrgan;
|
@ -8,7 +8,7 @@ pretrained_model_url = 'https://github.com/sczhou/CodeFormer/releases/download/v
|
|||||||
|
|
||||||
class CodeFormerRestoration():
|
class CodeFormerRestoration():
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
codeformer_dir='ldm/dream/restoration/codeformer',
|
codeformer_dir='ldm/invoke/restoration/codeformer',
|
||||||
codeformer_model_path='weights/codeformer.pth') -> None:
|
codeformer_model_path='weights/codeformer.pth') -> None:
|
||||||
self.model_path = os.path.join(codeformer_dir, codeformer_model_path)
|
self.model_path = os.path.join(codeformer_dir, codeformer_model_path)
|
||||||
self.codeformer_model_exists = os.path.isfile(self.model_path)
|
self.codeformer_model_exists = os.path.isfile(self.model_path)
|
||||||
@ -27,7 +27,7 @@ class CodeFormerRestoration():
|
|||||||
from basicsr.utils.download_util import load_file_from_url
|
from basicsr.utils.download_util import load_file_from_url
|
||||||
from basicsr.utils import img2tensor, tensor2img
|
from basicsr.utils import img2tensor, tensor2img
|
||||||
from facexlib.utils.face_restoration_helper import FaceRestoreHelper
|
from facexlib.utils.face_restoration_helper import FaceRestoreHelper
|
||||||
from ldm.dream.restoration.codeformer_arch import CodeFormer
|
from ldm.invoke.restoration.codeformer_arch import CodeFormer
|
||||||
from torchvision.transforms.functional import normalize
|
from torchvision.transforms.functional import normalize
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ class CodeFormerRestoration():
|
|||||||
|
|
||||||
cf = cf_class(dim_embd=512, codebook_size=1024, n_head=8, n_layers=9, connect_list=['32', '64', '128', '256']).to(device)
|
cf = cf_class(dim_embd=512, codebook_size=1024, n_head=8, n_layers=9, connect_list=['32', '64', '128', '256']).to(device)
|
||||||
|
|
||||||
checkpoint_path = load_file_from_url(url=pretrained_model_url, model_dir=os.path.abspath('ldm/dream/restoration/codeformer/weights'), progress=True)
|
checkpoint_path = load_file_from_url(url=pretrained_model_url, model_dir=os.path.abspath('ldm/invoke/restoration/codeformer/weights'), progress=True)
|
||||||
checkpoint = torch.load(checkpoint_path)['params_ema']
|
checkpoint = torch.load(checkpoint_path)['params_ema']
|
||||||
cf.load_state_dict(checkpoint)
|
cf.load_state_dict(checkpoint)
|
||||||
cf.eval()
|
cf.eval()
|
@ -5,7 +5,7 @@ from torch import nn, Tensor
|
|||||||
import torch.nn.functional as F
|
import torch.nn.functional as F
|
||||||
from typing import Optional, List
|
from typing import Optional, List
|
||||||
|
|
||||||
from ldm.dream.restoration.vqgan_arch import *
|
from ldm.invoke.restoration.vqgan_arch import *
|
||||||
from basicsr.utils import get_root_logger
|
from basicsr.utils import get_root_logger
|
||||||
from basicsr.utils.registry import ARCH_REGISTRY
|
from basicsr.utils.registry import ARCH_REGISTRY
|
||||||
|
|
@ -13,8 +13,6 @@ class Outpaint(object):
|
|||||||
seed = old_opt.seed
|
seed = old_opt.seed
|
||||||
prompt = old_opt.prompt
|
prompt = old_opt.prompt
|
||||||
|
|
||||||
print(f'DEBUG: old seed={seed}, old prompt = {prompt}')
|
|
||||||
|
|
||||||
def wrapped_callback(img,seed,**kwargs):
|
def wrapped_callback(img,seed,**kwargs):
|
||||||
image_callback(img,seed,use_prefix=prefix,**kwargs)
|
image_callback(img,seed,use_prefix=prefix,**kwargs)
|
||||||
|
|
@ -4,9 +4,9 @@ import copy
|
|||||||
import base64
|
import base64
|
||||||
import mimetypes
|
import mimetypes
|
||||||
import os
|
import os
|
||||||
from ldm.dream.args import Args, metadata_dumps
|
from ldm.invoke.args import Args, metadata_dumps
|
||||||
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
||||||
from ldm.dream.pngwriter import PngWriter
|
from ldm.invoke.pngwriter import PngWriter
|
||||||
from threading import Event
|
from threading import Event
|
||||||
|
|
||||||
def build_opt(post_data, seed, gfpgan_model_exists):
|
def build_opt(post_data, seed, gfpgan_model_exists):
|
@ -4,7 +4,7 @@ import torch
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from ldm.dream.devices import choose_torch_device
|
from ldm.invoke.devices import choose_torch_device
|
||||||
from ldm.models.diffusion.sampler import Sampler
|
from ldm.models.diffusion.sampler import Sampler
|
||||||
from ldm.modules.diffusionmodules.util import noise_like
|
from ldm.modules.diffusionmodules.util import noise_like
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
import k_diffusion as K
|
import k_diffusion as K
|
||||||
import torch
|
import torch
|
||||||
import torch.nn as nn
|
import torch.nn as nn
|
||||||
from ldm.dream.devices import choose_torch_device
|
from ldm.invoke.devices import choose_torch_device
|
||||||
from ldm.models.diffusion.sampler import Sampler
|
from ldm.models.diffusion.sampler import Sampler
|
||||||
from ldm.util import rand_perlin_2d
|
from ldm.util import rand_perlin_2d
|
||||||
from ldm.modules.diffusionmodules.util import (
|
from ldm.modules.diffusionmodules.util import (
|
||||||
@ -57,8 +57,9 @@ class KSampler(Sampler):
|
|||||||
schedule,
|
schedule,
|
||||||
steps=model.num_timesteps,
|
steps=model.num_timesteps,
|
||||||
)
|
)
|
||||||
self.ds = None
|
self.sigmas = None
|
||||||
self.s_in = None
|
self.ds = None
|
||||||
|
self.s_in = None
|
||||||
|
|
||||||
def forward(self, x, sigma, uncond, cond, cond_scale):
|
def forward(self, x, sigma, uncond, cond, cond_scale):
|
||||||
x_in = torch.cat([x] * 2)
|
x_in = torch.cat([x] * 2)
|
||||||
@ -190,7 +191,7 @@ class KSampler(Sampler):
|
|||||||
'uncond': unconditional_conditioning,
|
'uncond': unconditional_conditioning,
|
||||||
'cond_scale': unconditional_guidance_scale,
|
'cond_scale': unconditional_guidance_scale,
|
||||||
}
|
}
|
||||||
print(f'>> Sampling with k_{self.schedule}')
|
print(f'>> Sampling with k_{self.schedule} starting at step {len(self.sigmas)-S-1} of {len(self.sigmas)-1} ({S} new sampling steps)')
|
||||||
return (
|
return (
|
||||||
K.sampling.__dict__[f'sample_{self.schedule}'](
|
K.sampling.__dict__[f'sample_{self.schedule}'](
|
||||||
model_wrap_cfg, x, sigmas, extra_args=extra_args,
|
model_wrap_cfg, x, sigmas, extra_args=extra_args,
|
||||||
@ -199,6 +200,8 @@ class KSampler(Sampler):
|
|||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# this code will support inpainting if and when ksampler API modified or
|
||||||
|
# a workaround is found.
|
||||||
@torch.no_grad()
|
@torch.no_grad()
|
||||||
def p_sample(
|
def p_sample(
|
||||||
self,
|
self,
|
||||||
|
@ -4,7 +4,7 @@ import torch
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from ldm.dream.devices import choose_torch_device
|
from ldm.invoke.devices import choose_torch_device
|
||||||
from ldm.models.diffusion.sampler import Sampler
|
from ldm.models.diffusion.sampler import Sampler
|
||||||
from ldm.modules.diffusionmodules.util import noise_like
|
from ldm.modules.diffusionmodules.util import noise_like
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import torch
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from ldm.dream.devices import choose_torch_device
|
from ldm.invoke.devices import choose_torch_device
|
||||||
|
|
||||||
from ldm.modules.diffusionmodules.util import (
|
from ldm.modules.diffusionmodules.util import (
|
||||||
make_ddim_sampling_parameters,
|
make_ddim_sampling_parameters,
|
||||||
|
@ -5,7 +5,7 @@ import clip
|
|||||||
from einops import rearrange, repeat
|
from einops import rearrange, repeat
|
||||||
from transformers import CLIPTokenizer, CLIPTextModel
|
from transformers import CLIPTokenizer, CLIPTextModel
|
||||||
import kornia
|
import kornia
|
||||||
from ldm.dream.devices import choose_torch_device
|
from ldm.invoke.devices import choose_torch_device
|
||||||
|
|
||||||
from ldm.modules.x_transformer import (
|
from ldm.modules.x_transformer import (
|
||||||
Encoder,
|
Encoder,
|
||||||
|
@ -14,7 +14,7 @@ from ldm.models.diffusion.ddim import DDIMSampler
|
|||||||
from ldm.util import ismap
|
from ldm.util import ismap
|
||||||
import time
|
import time
|
||||||
from omegaconf import OmegaConf
|
from omegaconf import OmegaConf
|
||||||
from ldm.dream.devices import choose_torch_device
|
from ldm.invoke.devices import choose_torch_device
|
||||||
|
|
||||||
def download_models(mode):
|
def download_models(mode):
|
||||||
|
|
||||||
|
@ -20,6 +20,6 @@ torchmetrics==0.6.0
|
|||||||
transformers==4.19.2
|
transformers==4.19.2
|
||||||
-e git+https://github.com/openai/CLIP.git@main#egg=clip
|
-e git+https://github.com/openai/CLIP.git@main#egg=clip
|
||||||
-e git+https://github.com/CompVis/taming-transformers.git@master#egg=taming-transformers
|
-e git+https://github.com/CompVis/taming-transformers.git@master#egg=taming-transformers
|
||||||
git+https://github.com/lstein/k-diffusion.git@master#egg=k-diffusion
|
-e git+https://github.com/lstein/k-diffusion.git@master#egg=k-diffusion
|
||||||
-e git+https://github.com/lstein/GFPGAN@fix-dark-cast-images#egg=gfpgan
|
-e git+https://github.com/TencentARC/GFPGAN.git#egg=gfpgan
|
||||||
-e .
|
-e .
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
-r requirements.txt
|
-r requirements.txt
|
||||||
|
|
||||||
--pre
|
|
||||||
--extra-index-url https://download.pytorch.org/whl/nightly/cpu --trusted-host https://download.pytorch.org
|
|
||||||
|
|
||||||
protobuf==3.19.4
|
protobuf==3.19.4
|
||||||
torch
|
torch
|
||||||
torchvision
|
torchvision
|
||||||
|
@ -31,6 +31,7 @@ flaskwebgui==0.3.7
|
|||||||
send2trash
|
send2trash
|
||||||
dependency_injector==4.40.0
|
dependency_injector==4.40.0
|
||||||
eventlet
|
eventlet
|
||||||
|
realesrgan
|
||||||
git+https://github.com/openai/CLIP.git@main#egg=clip
|
git+https://github.com/openai/CLIP.git@main#egg=clip
|
||||||
git+https://github.com/Birch-san/k-diffusion.git@mps#egg=k-diffusion
|
git+https://github.com/Birch-san/k-diffusion.git@mps#egg=k-diffusion
|
||||||
git+https://github.com/lstein/GFPGAN@fix-dark-cast-images#egg=gfpgan
|
git+https://github.com/TencentARC/GFPGAN.git#egg=gfpgan
|
||||||
|
571
scripts/dream.py
@ -1,571 +1,12 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# Copyright (c) 2022 Lincoln D. Stein (https://github.com/lstein)
|
# Copyright (c) 2022 Lincoln D. Stein (https://github.com/lstein)
|
||||||
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import sys
|
import sys
|
||||||
import shlex
|
import os.path
|
||||||
import copy
|
|
||||||
import warnings
|
|
||||||
import time
|
|
||||||
import traceback
|
|
||||||
sys.path.append('.') # corrects a weird problem on Macs
|
|
||||||
from ldm.dream.readline import get_completer
|
|
||||||
from ldm.dream.args import Args, metadata_dumps, metadata_from_png, dream_cmd_from_png
|
|
||||||
from ldm.dream.pngwriter import PngWriter, retrieve_metadata, write_metadata
|
|
||||||
from ldm.dream.image_util import make_grid
|
|
||||||
from ldm.dream.log import write_log
|
|
||||||
from omegaconf import OmegaConf
|
|
||||||
from backend.invoke_ai_web_server import InvokeAIWebServer
|
|
||||||
|
|
||||||
|
script_path = sys.argv[0]
|
||||||
|
script_args = sys.argv[1:]
|
||||||
|
script_dir,script_name = os.path.split(script_path)
|
||||||
|
script_dest = os.path.join(script_dir,'invoke.py')
|
||||||
|
os.execlp('python3','python3',script_dest,*script_args)
|
||||||
|
|
||||||
def main():
|
|
||||||
"""Initialize command-line parsers and the diffusion model"""
|
|
||||||
opt = Args()
|
|
||||||
args = opt.parse_args()
|
|
||||||
if not args:
|
|
||||||
sys.exit(-1)
|
|
||||||
|
|
||||||
if args.laion400m:
|
|
||||||
print('--laion400m flag has been deprecated. Please use --model laion400m instead.')
|
|
||||||
sys.exit(-1)
|
|
||||||
if args.weights:
|
|
||||||
print('--weights argument has been deprecated. Please edit ./configs/models.yaml, and select the weights using --model instead.')
|
|
||||||
sys.exit(-1)
|
|
||||||
|
|
||||||
print('* Initializing, be patient...\n')
|
|
||||||
from ldm.generate import Generate
|
|
||||||
|
|
||||||
# these two lines prevent a horrible warning message from appearing
|
|
||||||
# when the frozen CLIP tokenizer is imported
|
|
||||||
import transformers
|
|
||||||
transformers.logging.set_verbosity_error()
|
|
||||||
|
|
||||||
# Loading Face Restoration and ESRGAN Modules
|
|
||||||
try:
|
|
||||||
gfpgan, codeformer, esrgan = None, None, None
|
|
||||||
if opt.restore or opt.esrgan:
|
|
||||||
from ldm.dream.restoration import Restoration
|
|
||||||
restoration = Restoration()
|
|
||||||
if opt.restore:
|
|
||||||
gfpgan, codeformer = restoration.load_face_restore_models(opt.gfpgan_dir, opt.gfpgan_model_path)
|
|
||||||
else:
|
|
||||||
print('>> Face restoration disabled')
|
|
||||||
if opt.esrgan:
|
|
||||||
esrgan = restoration.load_esrgan(opt.esrgan_bg_tile)
|
|
||||||
else:
|
|
||||||
print('>> Upscaling disabled')
|
|
||||||
else:
|
|
||||||
print('>> Face restoration and upscaling disabled')
|
|
||||||
except (ModuleNotFoundError, ImportError):
|
|
||||||
print(traceback.format_exc(), file=sys.stderr)
|
|
||||||
print('>> You may need to install the ESRGAN and/or GFPGAN modules')
|
|
||||||
|
|
||||||
# creating a simple text2image object with a handful of
|
|
||||||
# defaults passed on the command line.
|
|
||||||
# additional parameters will be added (or overriden) during
|
|
||||||
# the user input loop
|
|
||||||
try:
|
|
||||||
gen = Generate(
|
|
||||||
conf = opt.conf,
|
|
||||||
model = opt.model,
|
|
||||||
sampler_name = opt.sampler_name,
|
|
||||||
embedding_path = opt.embedding_path,
|
|
||||||
full_precision = opt.full_precision,
|
|
||||||
precision = opt.precision,
|
|
||||||
gfpgan=gfpgan,
|
|
||||||
codeformer=codeformer,
|
|
||||||
esrgan=esrgan
|
|
||||||
)
|
|
||||||
except (FileNotFoundError, IOError, KeyError) as e:
|
|
||||||
print(f'{e}. Aborting.')
|
|
||||||
sys.exit(-1)
|
|
||||||
|
|
||||||
# make sure the output directory exists
|
|
||||||
if not os.path.exists(opt.outdir):
|
|
||||||
os.makedirs(opt.outdir)
|
|
||||||
|
|
||||||
# load the infile as a list of lines
|
|
||||||
infile = None
|
|
||||||
if opt.infile:
|
|
||||||
try:
|
|
||||||
if os.path.isfile(opt.infile):
|
|
||||||
infile = open(opt.infile, 'r', encoding='utf-8')
|
|
||||||
elif opt.infile == '-': # stdin
|
|
||||||
infile = sys.stdin
|
|
||||||
else:
|
|
||||||
raise FileNotFoundError(f'{opt.infile} not found.')
|
|
||||||
except (FileNotFoundError, IOError) as e:
|
|
||||||
print(f'{e}. Aborting.')
|
|
||||||
sys.exit(-1)
|
|
||||||
|
|
||||||
if opt.seamless:
|
|
||||||
print(">> changed to seamless tiling mode")
|
|
||||||
|
|
||||||
# preload the model
|
|
||||||
gen.load_model()
|
|
||||||
#set additional option
|
|
||||||
gen.free_gpu_mem = opt.free_gpu_mem
|
|
||||||
|
|
||||||
# web server loops forever
|
|
||||||
if opt.web or opt.gui:
|
|
||||||
invoke_ai_web_server_loop(gen, gfpgan, codeformer, esrgan)
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
if not infile:
|
|
||||||
print(
|
|
||||||
"\n* Initialization done! Awaiting your command (-h for help, 'q' to quit)"
|
|
||||||
)
|
|
||||||
|
|
||||||
main_loop(gen, opt, infile)
|
|
||||||
|
|
||||||
# TODO: main_loop() has gotten busy. Needs to be refactored.
|
|
||||||
def main_loop(gen, opt, infile):
|
|
||||||
"""prompt/read/execute loop"""
|
|
||||||
done = False
|
|
||||||
path_filter = re.compile(r'[<>:"/\\|?*]')
|
|
||||||
last_results = list()
|
|
||||||
model_config = OmegaConf.load(opt.conf)[opt.model]
|
|
||||||
|
|
||||||
# The readline completer reads history from the .dream_history file located in the
|
|
||||||
# output directory specified at the time of script launch. We do not currently support
|
|
||||||
# changing the history file midstream when the output directory is changed.
|
|
||||||
completer = get_completer(opt)
|
|
||||||
output_cntr = completer.get_current_history_length()+1
|
|
||||||
|
|
||||||
# os.pathconf is not available on Windows
|
|
||||||
if hasattr(os, 'pathconf'):
|
|
||||||
path_max = os.pathconf(opt.outdir, 'PC_PATH_MAX')
|
|
||||||
name_max = os.pathconf(opt.outdir, 'PC_NAME_MAX')
|
|
||||||
else:
|
|
||||||
path_max = 260
|
|
||||||
name_max = 255
|
|
||||||
|
|
||||||
while not done:
|
|
||||||
operation = 'generate' # default operation, alternative is 'postprocess'
|
|
||||||
|
|
||||||
if completer:
|
|
||||||
completer.set_default_dir(opt.outdir)
|
|
||||||
|
|
||||||
try:
|
|
||||||
command = get_next_command(infile)
|
|
||||||
except EOFError:
|
|
||||||
done = True
|
|
||||||
continue
|
|
||||||
|
|
||||||
# skip empty lines
|
|
||||||
if not command.strip():
|
|
||||||
continue
|
|
||||||
|
|
||||||
if command.startswith(('#', '//')):
|
|
||||||
continue
|
|
||||||
|
|
||||||
if len(command.strip()) == 1 and command.startswith('q'):
|
|
||||||
done = True
|
|
||||||
break
|
|
||||||
|
|
||||||
if command.startswith('!'):
|
|
||||||
subcommand = command[1:]
|
|
||||||
|
|
||||||
if subcommand.startswith('dream'): # in case a stored prompt still contains the !dream command
|
|
||||||
command = command.replace('!dream ','',1)
|
|
||||||
|
|
||||||
elif subcommand.startswith('fix'):
|
|
||||||
command = command.replace('!fix ','',1)
|
|
||||||
operation = 'postprocess'
|
|
||||||
|
|
||||||
elif subcommand.startswith('fetch'):
|
|
||||||
file_path = command.replace('!fetch ','',1)
|
|
||||||
retrieve_dream_command(opt,file_path,completer)
|
|
||||||
continue
|
|
||||||
|
|
||||||
elif subcommand.startswith('history'):
|
|
||||||
completer.show_history()
|
|
||||||
continue
|
|
||||||
|
|
||||||
elif subcommand.startswith('search'):
|
|
||||||
search_str = command.replace('!search ','',1)
|
|
||||||
completer.show_history(search_str)
|
|
||||||
continue
|
|
||||||
|
|
||||||
elif subcommand.startswith('clear'):
|
|
||||||
completer.clear_history()
|
|
||||||
continue
|
|
||||||
|
|
||||||
elif re.match('^(\d+)',subcommand):
|
|
||||||
command_no = re.match('^(\d+)',subcommand).groups()[0]
|
|
||||||
command = completer.get_line(int(command_no))
|
|
||||||
completer.set_line(command)
|
|
||||||
continue
|
|
||||||
|
|
||||||
else: # not a recognized subcommand, so give the --help text
|
|
||||||
command = '-h'
|
|
||||||
|
|
||||||
if opt.parse_cmd(command) is None:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if opt.init_img:
|
|
||||||
try:
|
|
||||||
if not opt.prompt:
|
|
||||||
oldargs = metadata_from_png(opt.init_img)
|
|
||||||
opt.prompt = oldargs.prompt
|
|
||||||
print(f'>> Retrieved old prompt "{opt.prompt}" from {opt.init_img}')
|
|
||||||
except (OSError, AttributeError, KeyError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
if len(opt.prompt) == 0:
|
|
||||||
print('\nTry again with a prompt!')
|
|
||||||
continue
|
|
||||||
|
|
||||||
# width and height are set by model if not specified
|
|
||||||
if not opt.width:
|
|
||||||
opt.width = model_config.width
|
|
||||||
if not opt.height:
|
|
||||||
opt.height = model_config.height
|
|
||||||
|
|
||||||
# retrieve previous value of init image if requested
|
|
||||||
if opt.init_img is not None and re.match('^-\\d+$', opt.init_img):
|
|
||||||
try:
|
|
||||||
opt.init_img = last_results[int(opt.init_img)][0]
|
|
||||||
print(f'>> Reusing previous image {opt.init_img}')
|
|
||||||
except IndexError:
|
|
||||||
print(
|
|
||||||
f'>> No previous initial image at position {opt.init_img} found')
|
|
||||||
opt.init_img = None
|
|
||||||
continue
|
|
||||||
|
|
||||||
# try to relativize pathnames
|
|
||||||
for attr in ('init_img','init_mask','init_color','embedding_path'):
|
|
||||||
if getattr(opt,attr) and not os.path.exists(getattr(opt,attr)):
|
|
||||||
basename = getattr(opt,attr)
|
|
||||||
path = os.path.join(opt.outdir,basename)
|
|
||||||
setattr(opt,attr,path)
|
|
||||||
|
|
||||||
# retrieve previous value of seed if requested
|
|
||||||
if opt.seed is not None and opt.seed < 0:
|
|
||||||
try:
|
|
||||||
opt.seed = last_results[opt.seed][1]
|
|
||||||
print(f'>> Reusing previous seed {opt.seed}')
|
|
||||||
except IndexError:
|
|
||||||
print(f'>> No previous seed at position {opt.seed} found')
|
|
||||||
opt.seed = None
|
|
||||||
continue
|
|
||||||
|
|
||||||
if opt.strength is None:
|
|
||||||
opt.strength = 0.75 if opt.out_direction is None else 0.83
|
|
||||||
|
|
||||||
if opt.with_variations is not None:
|
|
||||||
opt.with_variations = split_variations(opt.with_variations)
|
|
||||||
|
|
||||||
if opt.prompt_as_dir:
|
|
||||||
# sanitize the prompt to a valid folder name
|
|
||||||
subdir = path_filter.sub('_', opt.prompt)[:name_max].rstrip(' .')
|
|
||||||
|
|
||||||
# truncate path to maximum allowed length
|
|
||||||
# 39 is the length of '######.##########.##########-##.png', plus two separators and a NUL
|
|
||||||
subdir = subdir[:(path_max - 39 - len(os.path.abspath(opt.outdir)))]
|
|
||||||
current_outdir = os.path.join(opt.outdir, subdir)
|
|
||||||
|
|
||||||
print('Writing files to directory: "' + current_outdir + '"')
|
|
||||||
|
|
||||||
# make sure the output directory exists
|
|
||||||
if not os.path.exists(current_outdir):
|
|
||||||
os.makedirs(current_outdir)
|
|
||||||
else:
|
|
||||||
if not os.path.exists(opt.outdir):
|
|
||||||
os.makedirs(opt.outdir)
|
|
||||||
current_outdir = opt.outdir
|
|
||||||
|
|
||||||
# Here is where the images are actually generated!
|
|
||||||
last_results = []
|
|
||||||
try:
|
|
||||||
file_writer = PngWriter(current_outdir)
|
|
||||||
results = [] # list of filename, prompt pairs
|
|
||||||
grid_images = dict() # seed -> Image, only used if `opt.grid`
|
|
||||||
prior_variations = opt.with_variations or []
|
|
||||||
prefix = file_writer.unique_prefix()
|
|
||||||
|
|
||||||
def image_writer(image, seed, upscaled=False, first_seed=None, use_prefix=None):
|
|
||||||
# note the seed is the seed of the current image
|
|
||||||
# the first_seed is the original seed that noise is added to
|
|
||||||
# when the -v switch is used to generate variations
|
|
||||||
nonlocal prior_variations
|
|
||||||
nonlocal prefix
|
|
||||||
if use_prefix is not None:
|
|
||||||
prefix = use_prefix
|
|
||||||
|
|
||||||
path = None
|
|
||||||
if opt.grid:
|
|
||||||
grid_images[seed] = image
|
|
||||||
else:
|
|
||||||
postprocessed = upscaled if upscaled else operation=='postprocess'
|
|
||||||
filename, formatted_dream_prompt = prepare_image_metadata(
|
|
||||||
opt,
|
|
||||||
prefix,
|
|
||||||
seed,
|
|
||||||
operation,
|
|
||||||
prior_variations,
|
|
||||||
postprocessed,
|
|
||||||
first_seed
|
|
||||||
)
|
|
||||||
path = file_writer.save_image_and_prompt_to_png(
|
|
||||||
image = image,
|
|
||||||
dream_prompt = formatted_dream_prompt,
|
|
||||||
metadata = metadata_dumps(
|
|
||||||
opt,
|
|
||||||
seeds = [seed if opt.variation_amount==0 and len(prior_variations)==0 else first_seed],
|
|
||||||
model_hash = gen.model_hash,
|
|
||||||
),
|
|
||||||
name = filename,
|
|
||||||
)
|
|
||||||
|
|
||||||
# update rfc metadata
|
|
||||||
if operation == 'postprocess':
|
|
||||||
tool = re.match('postprocess:(\w+)',opt.last_operation).groups()[0]
|
|
||||||
add_postprocessing_to_metadata(
|
|
||||||
opt,
|
|
||||||
opt.prompt,
|
|
||||||
filename,
|
|
||||||
tool,
|
|
||||||
formatted_dream_prompt,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (not postprocessed) or opt.save_original:
|
|
||||||
# only append to results if we didn't overwrite an earlier output
|
|
||||||
results.append([path, formatted_dream_prompt])
|
|
||||||
|
|
||||||
# so that the seed autocompletes (on linux|mac when -S or --seed specified
|
|
||||||
if completer:
|
|
||||||
completer.add_seed(seed)
|
|
||||||
completer.add_seed(first_seed)
|
|
||||||
last_results.append([path, seed])
|
|
||||||
|
|
||||||
if operation == 'generate':
|
|
||||||
catch_ctrl_c = infile is None # if running interactively, we catch keyboard interrupts
|
|
||||||
opt.last_operation='generate'
|
|
||||||
gen.prompt2image(
|
|
||||||
image_callback=image_writer,
|
|
||||||
catch_interrupts=catch_ctrl_c,
|
|
||||||
**vars(opt)
|
|
||||||
)
|
|
||||||
elif operation == 'postprocess':
|
|
||||||
print(f'>> fixing {opt.prompt}')
|
|
||||||
opt.last_operation = do_postprocess(gen,opt,image_writer)
|
|
||||||
|
|
||||||
if opt.grid and len(grid_images) > 0:
|
|
||||||
grid_img = make_grid(list(grid_images.values()))
|
|
||||||
grid_seeds = list(grid_images.keys())
|
|
||||||
first_seed = last_results[0][1]
|
|
||||||
filename = f'{prefix}.{first_seed}.png'
|
|
||||||
formatted_dream_prompt = opt.dream_prompt_str(seed=first_seed,grid=True,iterations=len(grid_images))
|
|
||||||
formatted_dream_prompt += f' # {grid_seeds}'
|
|
||||||
metadata = metadata_dumps(
|
|
||||||
opt,
|
|
||||||
seeds = grid_seeds,
|
|
||||||
model_hash = gen.model_hash
|
|
||||||
)
|
|
||||||
path = file_writer.save_image_and_prompt_to_png(
|
|
||||||
image = grid_img,
|
|
||||||
dream_prompt = formatted_dream_prompt,
|
|
||||||
metadata = metadata,
|
|
||||||
name = filename
|
|
||||||
)
|
|
||||||
results = [[path, formatted_dream_prompt]]
|
|
||||||
|
|
||||||
except AssertionError as e:
|
|
||||||
print(e)
|
|
||||||
continue
|
|
||||||
|
|
||||||
except OSError as e:
|
|
||||||
print(e)
|
|
||||||
continue
|
|
||||||
|
|
||||||
print('Outputs:')
|
|
||||||
log_path = os.path.join(current_outdir, 'dream_log')
|
|
||||||
output_cntr = write_log(results, log_path ,('txt', 'md'), output_cntr)
|
|
||||||
print()
|
|
||||||
if operation == 'postprocess':
|
|
||||||
completer.add_history(f'!fix {command}')
|
|
||||||
else:
|
|
||||||
completer.add_history(command)
|
|
||||||
|
|
||||||
print('goodbye!')
|
|
||||||
|
|
||||||
def do_postprocess (gen, opt, callback):
|
|
||||||
file_path = opt.prompt # treat the prompt as the file pathname
|
|
||||||
if os.path.dirname(file_path) == '': #basename given
|
|
||||||
file_path = os.path.join(opt.outdir,file_path)
|
|
||||||
|
|
||||||
tool=None
|
|
||||||
if opt.gfpgan_strength > 0:
|
|
||||||
tool = opt.facetool
|
|
||||||
elif opt.embiggen:
|
|
||||||
tool = 'embiggen'
|
|
||||||
elif opt.upscale:
|
|
||||||
tool = 'upscale'
|
|
||||||
elif opt.out_direction:
|
|
||||||
tool = 'outpaint'
|
|
||||||
elif opt.outcrop:
|
|
||||||
tool = 'outcrop'
|
|
||||||
opt.save_original = True # do not overwrite old image!
|
|
||||||
opt.last_operation = f'postprocess:{tool}'
|
|
||||||
try:
|
|
||||||
gen.apply_postprocessor(
|
|
||||||
image_path = file_path,
|
|
||||||
tool = tool,
|
|
||||||
gfpgan_strength = opt.gfpgan_strength,
|
|
||||||
codeformer_fidelity = opt.codeformer_fidelity,
|
|
||||||
save_original = opt.save_original,
|
|
||||||
upscale = opt.upscale,
|
|
||||||
out_direction = opt.out_direction,
|
|
||||||
outcrop = opt.outcrop,
|
|
||||||
callback = callback,
|
|
||||||
opt = opt,
|
|
||||||
)
|
|
||||||
except OSError:
|
|
||||||
print(f'** {file_path}: file could not be read')
|
|
||||||
return
|
|
||||||
except (KeyError, AttributeError):
|
|
||||||
print(traceback.format_exc(), file=sys.stderr)
|
|
||||||
return
|
|
||||||
return opt.last_operation
|
|
||||||
|
|
||||||
def add_postprocessing_to_metadata(opt,original_file,new_file,tool,command):
|
|
||||||
original_file = original_file if os.path.exists(original_file) else os.path.join(opt.outdir,original_file)
|
|
||||||
new_file = new_file if os.path.exists(new_file) else os.path.join(opt.outdir,new_file)
|
|
||||||
meta = retrieve_metadata(original_file)['sd-metadata']
|
|
||||||
img_data = meta['image']
|
|
||||||
pp = img_data.get('postprocessing',[]) or []
|
|
||||||
pp.append(
|
|
||||||
{
|
|
||||||
'tool':tool,
|
|
||||||
'dream_command':command,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
meta['image']['postprocessing'] = pp
|
|
||||||
write_metadata(new_file,meta)
|
|
||||||
|
|
||||||
def prepare_image_metadata(
|
|
||||||
opt,
|
|
||||||
prefix,
|
|
||||||
seed,
|
|
||||||
operation='generate',
|
|
||||||
prior_variations=[],
|
|
||||||
postprocessed=False,
|
|
||||||
first_seed=None
|
|
||||||
):
|
|
||||||
|
|
||||||
if postprocessed and opt.save_original:
|
|
||||||
filename = choose_postprocess_name(opt,prefix,seed)
|
|
||||||
else:
|
|
||||||
filename = f'{prefix}.{seed}.png'
|
|
||||||
|
|
||||||
if opt.variation_amount > 0:
|
|
||||||
first_seed = first_seed or seed
|
|
||||||
this_variation = [[seed, opt.variation_amount]]
|
|
||||||
opt.with_variations = prior_variations + this_variation
|
|
||||||
formatted_dream_prompt = opt.dream_prompt_str(seed=first_seed)
|
|
||||||
elif len(prior_variations) > 0:
|
|
||||||
formatted_dream_prompt = opt.dream_prompt_str(seed=first_seed)
|
|
||||||
elif operation == 'postprocess':
|
|
||||||
formatted_dream_prompt = '!fix '+opt.dream_prompt_str(seed=seed)
|
|
||||||
else:
|
|
||||||
formatted_dream_prompt = opt.dream_prompt_str(seed=seed)
|
|
||||||
return filename,formatted_dream_prompt
|
|
||||||
|
|
||||||
def choose_postprocess_name(opt,prefix,seed) -> str:
|
|
||||||
match = re.search('postprocess:(\w+)',opt.last_operation)
|
|
||||||
if match:
|
|
||||||
modifier = match.group(1) # will look like "gfpgan", "upscale", "outpaint" or "embiggen"
|
|
||||||
else:
|
|
||||||
modifier = 'postprocessed'
|
|
||||||
|
|
||||||
counter = 0
|
|
||||||
filename = None
|
|
||||||
available = False
|
|
||||||
while not available:
|
|
||||||
if counter == 0:
|
|
||||||
filename = f'{prefix}.{seed}.{modifier}.png'
|
|
||||||
else:
|
|
||||||
filename = f'{prefix}.{seed}.{modifier}-{counter:02d}.png'
|
|
||||||
available = not os.path.exists(os.path.join(opt.outdir,filename))
|
|
||||||
counter += 1
|
|
||||||
return filename
|
|
||||||
|
|
||||||
def get_next_command(infile=None) -> str: # command string
|
|
||||||
if infile is None:
|
|
||||||
command = input('dream> ')
|
|
||||||
else:
|
|
||||||
command = infile.readline()
|
|
||||||
if not command:
|
|
||||||
raise EOFError
|
|
||||||
else:
|
|
||||||
command = command.strip()
|
|
||||||
if len(command)>0:
|
|
||||||
print(f'#{command}')
|
|
||||||
return command
|
|
||||||
|
|
||||||
def invoke_ai_web_server_loop(gen, gfpgan, codeformer, esrgan):
|
|
||||||
print('\n* --web was specified, starting web server...')
|
|
||||||
# Change working directory to the stable-diffusion directory
|
|
||||||
os.chdir(
|
|
||||||
os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
|
||||||
)
|
|
||||||
|
|
||||||
invoke_ai_web_server = InvokeAIWebServer(generate=gen, gfpgan=gfpgan, codeformer=codeformer, esrgan=esrgan)
|
|
||||||
|
|
||||||
try:
|
|
||||||
invoke_ai_web_server.run()
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def split_variations(variations_string) -> list:
|
|
||||||
# shotgun parsing, woo
|
|
||||||
parts = []
|
|
||||||
broken = False # python doesn't have labeled loops...
|
|
||||||
for part in variations_string.split(','):
|
|
||||||
seed_and_weight = part.split(':')
|
|
||||||
if len(seed_and_weight) != 2:
|
|
||||||
print(f'** Could not parse with_variation part "{part}"')
|
|
||||||
broken = True
|
|
||||||
break
|
|
||||||
try:
|
|
||||||
seed = int(seed_and_weight[0])
|
|
||||||
weight = float(seed_and_weight[1])
|
|
||||||
except ValueError:
|
|
||||||
print(f'** Could not parse with_variation part "{part}"')
|
|
||||||
broken = True
|
|
||||||
break
|
|
||||||
parts.append([seed, weight])
|
|
||||||
if broken:
|
|
||||||
return None
|
|
||||||
elif len(parts) == 0:
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
return parts
|
|
||||||
|
|
||||||
def retrieve_dream_command(opt,file_path,completer):
|
|
||||||
'''
|
|
||||||
Given a full or partial path to a previously-generated image file,
|
|
||||||
will retrieve and format the dream command used to generate the image,
|
|
||||||
and pop it into the readline buffer (linux, Mac), or print out a comment
|
|
||||||
for cut-and-paste (windows)
|
|
||||||
'''
|
|
||||||
dir,basename = os.path.split(file_path)
|
|
||||||
if len(dir) == 0:
|
|
||||||
path = os.path.join(opt.outdir,basename)
|
|
||||||
else:
|
|
||||||
path = file_path
|
|
||||||
try:
|
|
||||||
cmd = dream_cmd_from_png(path)
|
|
||||||
except OSError:
|
|
||||||
print(f'** {path}: file could not be read')
|
|
||||||
return
|
|
||||||
except (KeyError, AttributeError):
|
|
||||||
print(f'** {path}: file has no metadata')
|
|
||||||
return
|
|
||||||
completer.set_line(cmd)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
'''This script reads the "Dream" Stable Diffusion prompt embedded in files generated by dream.py'''
|
'''This script reads the "Invoke" Stable Diffusion prompt embedded in files generated by invoke.py'''
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from PIL import Image,PngImagePlugin
|
from PIL import Image,PngImagePlugin
|
||||||
|
|
||||||
if len(sys.argv) < 2:
|
if len(sys.argv) < 2:
|
||||||
print("Usage: file2prompt.py <file1.png> <file2.png> <file3.png>...")
|
print("Usage: file2prompt.py <file1.png> <file2.png> <file3.png>...")
|
||||||
print("This script opens up the indicated dream.py-generated PNG file(s) and prints out the prompt used to generate them.")
|
print("This script opens up the indicated invoke.py-generated PNG file(s) and prints out the prompt used to generate them.")
|
||||||
exit(-1)
|
exit(-1)
|
||||||
|
|
||||||
filenames = sys.argv[1:]
|
filenames = sys.argv[1:]
|
||||||
|
573
scripts/invoke.py
Normal file
@ -0,0 +1,573 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# Copyright (c) 2022 Lincoln D. Stein (https://github.com/lstein)
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import shlex
|
||||||
|
import copy
|
||||||
|
import warnings
|
||||||
|
import time
|
||||||
|
import traceback
|
||||||
|
sys.path.append('.') # corrects a weird problem on Macs
|
||||||
|
from ldm.invoke.readline import get_completer
|
||||||
|
from ldm.invoke.args import Args, metadata_dumps, metadata_from_png, dream_cmd_from_png
|
||||||
|
from ldm.invoke.pngwriter import PngWriter, retrieve_metadata, write_metadata
|
||||||
|
from ldm.invoke.image_util import make_grid
|
||||||
|
from ldm.invoke.log import write_log
|
||||||
|
from omegaconf import OmegaConf
|
||||||
|
from backend.invoke_ai_web_server import InvokeAIWebServer
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Initialize command-line parsers and the diffusion model"""
|
||||||
|
opt = Args()
|
||||||
|
args = opt.parse_args()
|
||||||
|
if not args:
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
|
if args.laion400m:
|
||||||
|
print('--laion400m flag has been deprecated. Please use --model laion400m instead.')
|
||||||
|
sys.exit(-1)
|
||||||
|
if args.weights:
|
||||||
|
print('--weights argument has been deprecated. Please edit ./configs/models.yaml, and select the weights using --model instead.')
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
|
print('* Initializing, be patient...\n')
|
||||||
|
from ldm.generate import Generate
|
||||||
|
|
||||||
|
# these two lines prevent a horrible warning message from appearing
|
||||||
|
# when the frozen CLIP tokenizer is imported
|
||||||
|
import transformers
|
||||||
|
transformers.logging.set_verbosity_error()
|
||||||
|
|
||||||
|
# Loading Face Restoration and ESRGAN Modules
|
||||||
|
try:
|
||||||
|
gfpgan, codeformer, esrgan = None, None, None
|
||||||
|
if opt.restore or opt.esrgan:
|
||||||
|
from ldm.invoke.restoration import Restoration
|
||||||
|
restoration = Restoration()
|
||||||
|
if opt.restore:
|
||||||
|
gfpgan, codeformer = restoration.load_face_restore_models(opt.gfpgan_dir, opt.gfpgan_model_path)
|
||||||
|
else:
|
||||||
|
print('>> Face restoration disabled')
|
||||||
|
if opt.esrgan:
|
||||||
|
esrgan = restoration.load_esrgan(opt.esrgan_bg_tile)
|
||||||
|
else:
|
||||||
|
print('>> Upscaling disabled')
|
||||||
|
else:
|
||||||
|
print('>> Face restoration and upscaling disabled')
|
||||||
|
except (ModuleNotFoundError, ImportError):
|
||||||
|
print(traceback.format_exc(), file=sys.stderr)
|
||||||
|
print('>> You may need to install the ESRGAN and/or GFPGAN modules')
|
||||||
|
|
||||||
|
# creating a simple text2image object with a handful of
|
||||||
|
# defaults passed on the command line.
|
||||||
|
# additional parameters will be added (or overriden) during
|
||||||
|
# the user input loop
|
||||||
|
try:
|
||||||
|
gen = Generate(
|
||||||
|
conf = opt.conf,
|
||||||
|
model = opt.model,
|
||||||
|
sampler_name = opt.sampler_name,
|
||||||
|
embedding_path = opt.embedding_path,
|
||||||
|
full_precision = opt.full_precision,
|
||||||
|
precision = opt.precision,
|
||||||
|
gfpgan=gfpgan,
|
||||||
|
codeformer=codeformer,
|
||||||
|
esrgan=esrgan,
|
||||||
|
free_gpu_mem=opt.free_gpu_mem,
|
||||||
|
)
|
||||||
|
except (FileNotFoundError, IOError, KeyError) as e:
|
||||||
|
print(f'{e}. Aborting.')
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
|
# make sure the output directory exists
|
||||||
|
if not os.path.exists(opt.outdir):
|
||||||
|
os.makedirs(opt.outdir)
|
||||||
|
|
||||||
|
# load the infile as a list of lines
|
||||||
|
infile = None
|
||||||
|
if opt.infile:
|
||||||
|
try:
|
||||||
|
if os.path.isfile(opt.infile):
|
||||||
|
infile = open(opt.infile, 'r', encoding='utf-8')
|
||||||
|
elif opt.infile == '-': # stdin
|
||||||
|
infile = sys.stdin
|
||||||
|
else:
|
||||||
|
raise FileNotFoundError(f'{opt.infile} not found.')
|
||||||
|
except (FileNotFoundError, IOError) as e:
|
||||||
|
print(f'{e}. Aborting.')
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
|
if opt.seamless:
|
||||||
|
print(">> changed to seamless tiling mode")
|
||||||
|
|
||||||
|
# preload the model
|
||||||
|
gen.load_model()
|
||||||
|
|
||||||
|
# web server loops forever
|
||||||
|
if opt.web or opt.gui:
|
||||||
|
invoke_ai_web_server_loop(gen, gfpgan, codeformer, esrgan)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
if not infile:
|
||||||
|
print(
|
||||||
|
"\n* Initialization done! Awaiting your command (-h for help, 'q' to quit)"
|
||||||
|
)
|
||||||
|
|
||||||
|
main_loop(gen, opt, infile)
|
||||||
|
|
||||||
|
# TODO: main_loop() has gotten busy. Needs to be refactored.
|
||||||
|
def main_loop(gen, opt, infile):
|
||||||
|
"""prompt/read/execute loop"""
|
||||||
|
done = False
|
||||||
|
path_filter = re.compile(r'[<>:"/\\|?*]')
|
||||||
|
last_results = list()
|
||||||
|
model_config = OmegaConf.load(opt.conf)[opt.model]
|
||||||
|
|
||||||
|
# The readline completer reads history from the .dream_history file located in the
|
||||||
|
# output directory specified at the time of script launch. We do not currently support
|
||||||
|
# changing the history file midstream when the output directory is changed.
|
||||||
|
completer = get_completer(opt)
|
||||||
|
output_cntr = completer.get_current_history_length()+1
|
||||||
|
|
||||||
|
# os.pathconf is not available on Windows
|
||||||
|
if hasattr(os, 'pathconf'):
|
||||||
|
path_max = os.pathconf(opt.outdir, 'PC_PATH_MAX')
|
||||||
|
name_max = os.pathconf(opt.outdir, 'PC_NAME_MAX')
|
||||||
|
else:
|
||||||
|
path_max = 260
|
||||||
|
name_max = 255
|
||||||
|
|
||||||
|
while not done:
|
||||||
|
operation = 'generate' # default operation, alternative is 'postprocess'
|
||||||
|
|
||||||
|
if completer:
|
||||||
|
completer.set_default_dir(opt.outdir)
|
||||||
|
|
||||||
|
try:
|
||||||
|
command = get_next_command(infile)
|
||||||
|
except EOFError:
|
||||||
|
done = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
# skip empty lines
|
||||||
|
if not command.strip():
|
||||||
|
continue
|
||||||
|
|
||||||
|
if command.startswith(('#', '//')):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if len(command.strip()) == 1 and command.startswith('q'):
|
||||||
|
done = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if command.startswith('!'):
|
||||||
|
subcommand = command[1:]
|
||||||
|
|
||||||
|
if subcommand.startswith('dream'): # in case a stored prompt still contains the !dream command
|
||||||
|
command = command.replace('!dream ','',1)
|
||||||
|
|
||||||
|
elif subcommand.startswith('fix'):
|
||||||
|
command = command.replace('!fix ','',1)
|
||||||
|
operation = 'postprocess'
|
||||||
|
|
||||||
|
elif subcommand.startswith('fetch'):
|
||||||
|
file_path = command.replace('!fetch ','',1)
|
||||||
|
retrieve_dream_command(opt,file_path,completer)
|
||||||
|
continue
|
||||||
|
|
||||||
|
elif subcommand.startswith('history'):
|
||||||
|
completer.show_history()
|
||||||
|
continue
|
||||||
|
|
||||||
|
elif subcommand.startswith('search'):
|
||||||
|
search_str = command.replace('!search ','',1)
|
||||||
|
completer.show_history(search_str)
|
||||||
|
continue
|
||||||
|
|
||||||
|
elif subcommand.startswith('clear'):
|
||||||
|
completer.clear_history()
|
||||||
|
continue
|
||||||
|
|
||||||
|
elif re.match('^(\d+)',subcommand):
|
||||||
|
command_no = re.match('^(\d+)',subcommand).groups()[0]
|
||||||
|
command = completer.get_line(int(command_no))
|
||||||
|
completer.set_line(command)
|
||||||
|
continue
|
||||||
|
|
||||||
|
else: # not a recognized subcommand, so give the --help text
|
||||||
|
command = '-h'
|
||||||
|
|
||||||
|
if opt.parse_cmd(command) is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if opt.init_img:
|
||||||
|
try:
|
||||||
|
if not opt.prompt:
|
||||||
|
oldargs = metadata_from_png(opt.init_img)
|
||||||
|
opt.prompt = oldargs.prompt
|
||||||
|
print(f'>> Retrieved old prompt "{opt.prompt}" from {opt.init_img}')
|
||||||
|
except (OSError, AttributeError, KeyError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
if len(opt.prompt) == 0:
|
||||||
|
print('\nTry again with a prompt!')
|
||||||
|
continue
|
||||||
|
|
||||||
|
# width and height are set by model if not specified
|
||||||
|
if not opt.width:
|
||||||
|
opt.width = model_config.width
|
||||||
|
if not opt.height:
|
||||||
|
opt.height = model_config.height
|
||||||
|
|
||||||
|
# retrieve previous value of init image if requested
|
||||||
|
if opt.init_img is not None and re.match('^-\\d+$', opt.init_img):
|
||||||
|
try:
|
||||||
|
opt.init_img = last_results[int(opt.init_img)][0]
|
||||||
|
print(f'>> Reusing previous image {opt.init_img}')
|
||||||
|
except IndexError:
|
||||||
|
print(
|
||||||
|
f'>> No previous initial image at position {opt.init_img} found')
|
||||||
|
opt.init_img = None
|
||||||
|
continue
|
||||||
|
|
||||||
|
# try to relativize pathnames
|
||||||
|
for attr in ('init_img','init_mask','init_color','embedding_path'):
|
||||||
|
if getattr(opt,attr) and not os.path.exists(getattr(opt,attr)):
|
||||||
|
basename = getattr(opt,attr)
|
||||||
|
path = os.path.join(opt.outdir,basename)
|
||||||
|
setattr(opt,attr,path)
|
||||||
|
|
||||||
|
# retrieve previous value of seed if requested
|
||||||
|
if opt.seed is not None and opt.seed < 0:
|
||||||
|
try:
|
||||||
|
opt.seed = last_results[opt.seed][1]
|
||||||
|
print(f'>> Reusing previous seed {opt.seed}')
|
||||||
|
except IndexError:
|
||||||
|
print(f'>> No previous seed at position {opt.seed} found')
|
||||||
|
opt.seed = None
|
||||||
|
continue
|
||||||
|
|
||||||
|
if opt.strength is None:
|
||||||
|
opt.strength = 0.75 if opt.out_direction is None else 0.83
|
||||||
|
|
||||||
|
if opt.with_variations is not None:
|
||||||
|
opt.with_variations = split_variations(opt.with_variations)
|
||||||
|
|
||||||
|
if opt.prompt_as_dir and operation == 'generate':
|
||||||
|
# sanitize the prompt to a valid folder name
|
||||||
|
subdir = path_filter.sub('_', opt.prompt)[:name_max].rstrip(' .')
|
||||||
|
|
||||||
|
# truncate path to maximum allowed length
|
||||||
|
# 39 is the length of '######.##########.##########-##.png', plus two separators and a NUL
|
||||||
|
subdir = subdir[:(path_max - 39 - len(os.path.abspath(opt.outdir)))]
|
||||||
|
current_outdir = os.path.join(opt.outdir, subdir)
|
||||||
|
|
||||||
|
print('Writing files to directory: "' + current_outdir + '"')
|
||||||
|
|
||||||
|
# make sure the output directory exists
|
||||||
|
if not os.path.exists(current_outdir):
|
||||||
|
os.makedirs(current_outdir)
|
||||||
|
else:
|
||||||
|
if not os.path.exists(opt.outdir):
|
||||||
|
os.makedirs(opt.outdir)
|
||||||
|
current_outdir = opt.outdir
|
||||||
|
|
||||||
|
# write out the history at this point
|
||||||
|
if operation == 'postprocess':
|
||||||
|
completer.add_history(f'!fix {command}')
|
||||||
|
else:
|
||||||
|
completer.add_history(command)
|
||||||
|
|
||||||
|
# Here is where the images are actually generated!
|
||||||
|
last_results = []
|
||||||
|
try:
|
||||||
|
file_writer = PngWriter(current_outdir)
|
||||||
|
results = [] # list of filename, prompt pairs
|
||||||
|
grid_images = dict() # seed -> Image, only used if `opt.grid`
|
||||||
|
prior_variations = opt.with_variations or []
|
||||||
|
prefix = file_writer.unique_prefix()
|
||||||
|
|
||||||
|
def image_writer(image, seed, upscaled=False, first_seed=None, use_prefix=None):
|
||||||
|
# note the seed is the seed of the current image
|
||||||
|
# the first_seed is the original seed that noise is added to
|
||||||
|
# when the -v switch is used to generate variations
|
||||||
|
nonlocal prior_variations
|
||||||
|
nonlocal prefix
|
||||||
|
if use_prefix is not None:
|
||||||
|
prefix = use_prefix
|
||||||
|
|
||||||
|
path = None
|
||||||
|
if opt.grid:
|
||||||
|
grid_images[seed] = image
|
||||||
|
else:
|
||||||
|
postprocessed = upscaled if upscaled else operation=='postprocess'
|
||||||
|
filename, formatted_dream_prompt = prepare_image_metadata(
|
||||||
|
opt,
|
||||||
|
prefix,
|
||||||
|
seed,
|
||||||
|
operation,
|
||||||
|
prior_variations,
|
||||||
|
postprocessed,
|
||||||
|
first_seed
|
||||||
|
)
|
||||||
|
path = file_writer.save_image_and_prompt_to_png(
|
||||||
|
image = image,
|
||||||
|
dream_prompt = formatted_dream_prompt,
|
||||||
|
metadata = metadata_dumps(
|
||||||
|
opt,
|
||||||
|
seeds = [seed if opt.variation_amount==0 and len(prior_variations)==0 else first_seed],
|
||||||
|
model_hash = gen.model_hash,
|
||||||
|
),
|
||||||
|
name = filename,
|
||||||
|
)
|
||||||
|
|
||||||
|
# update rfc metadata
|
||||||
|
if operation == 'postprocess':
|
||||||
|
tool = re.match('postprocess:(\w+)',opt.last_operation).groups()[0]
|
||||||
|
add_postprocessing_to_metadata(
|
||||||
|
opt,
|
||||||
|
opt.prompt,
|
||||||
|
filename,
|
||||||
|
tool,
|
||||||
|
formatted_dream_prompt,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (not postprocessed) or opt.save_original:
|
||||||
|
# only append to results if we didn't overwrite an earlier output
|
||||||
|
results.append([path, formatted_dream_prompt])
|
||||||
|
|
||||||
|
# so that the seed autocompletes (on linux|mac when -S or --seed specified
|
||||||
|
if completer:
|
||||||
|
completer.add_seed(seed)
|
||||||
|
completer.add_seed(first_seed)
|
||||||
|
last_results.append([path, seed])
|
||||||
|
|
||||||
|
if operation == 'generate':
|
||||||
|
catch_ctrl_c = infile is None # if running interactively, we catch keyboard interrupts
|
||||||
|
opt.last_operation='generate'
|
||||||
|
gen.prompt2image(
|
||||||
|
image_callback=image_writer,
|
||||||
|
catch_interrupts=catch_ctrl_c,
|
||||||
|
**vars(opt)
|
||||||
|
)
|
||||||
|
elif operation == 'postprocess':
|
||||||
|
print(f'>> fixing {opt.prompt}')
|
||||||
|
opt.last_operation = do_postprocess(gen,opt,image_writer)
|
||||||
|
|
||||||
|
if opt.grid and len(grid_images) > 0:
|
||||||
|
grid_img = make_grid(list(grid_images.values()))
|
||||||
|
grid_seeds = list(grid_images.keys())
|
||||||
|
first_seed = last_results[0][1]
|
||||||
|
filename = f'{prefix}.{first_seed}.png'
|
||||||
|
formatted_dream_prompt = opt.dream_prompt_str(seed=first_seed,grid=True,iterations=len(grid_images))
|
||||||
|
formatted_dream_prompt += f' # {grid_seeds}'
|
||||||
|
metadata = metadata_dumps(
|
||||||
|
opt,
|
||||||
|
seeds = grid_seeds,
|
||||||
|
model_hash = gen.model_hash
|
||||||
|
)
|
||||||
|
path = file_writer.save_image_and_prompt_to_png(
|
||||||
|
image = grid_img,
|
||||||
|
dream_prompt = formatted_dream_prompt,
|
||||||
|
metadata = metadata,
|
||||||
|
name = filename
|
||||||
|
)
|
||||||
|
results = [[path, formatted_dream_prompt]]
|
||||||
|
|
||||||
|
except AssertionError as e:
|
||||||
|
print(e)
|
||||||
|
continue
|
||||||
|
|
||||||
|
except OSError as e:
|
||||||
|
print(e)
|
||||||
|
continue
|
||||||
|
|
||||||
|
print('Outputs:')
|
||||||
|
log_path = os.path.join(current_outdir, 'invoke_log')
|
||||||
|
output_cntr = write_log(results, log_path ,('txt', 'md'), output_cntr)
|
||||||
|
print()
|
||||||
|
|
||||||
|
print('goodbye!')
|
||||||
|
|
||||||
|
def do_postprocess (gen, opt, callback):
|
||||||
|
file_path = opt.prompt # treat the prompt as the file pathname
|
||||||
|
if os.path.dirname(file_path) == '': #basename given
|
||||||
|
file_path = os.path.join(opt.outdir,file_path)
|
||||||
|
|
||||||
|
tool=None
|
||||||
|
if opt.gfpgan_strength > 0:
|
||||||
|
tool = opt.facetool
|
||||||
|
elif opt.embiggen:
|
||||||
|
tool = 'embiggen'
|
||||||
|
elif opt.upscale:
|
||||||
|
tool = 'upscale'
|
||||||
|
elif opt.out_direction:
|
||||||
|
tool = 'outpaint'
|
||||||
|
elif opt.outcrop:
|
||||||
|
tool = 'outcrop'
|
||||||
|
opt.save_original = True # do not overwrite old image!
|
||||||
|
opt.last_operation = f'postprocess:{tool}'
|
||||||
|
try:
|
||||||
|
gen.apply_postprocessor(
|
||||||
|
image_path = file_path,
|
||||||
|
tool = tool,
|
||||||
|
gfpgan_strength = opt.gfpgan_strength,
|
||||||
|
codeformer_fidelity = opt.codeformer_fidelity,
|
||||||
|
save_original = opt.save_original,
|
||||||
|
upscale = opt.upscale,
|
||||||
|
out_direction = opt.out_direction,
|
||||||
|
outcrop = opt.outcrop,
|
||||||
|
callback = callback,
|
||||||
|
opt = opt,
|
||||||
|
)
|
||||||
|
except OSError:
|
||||||
|
print(traceback.format_exc(), file=sys.stderr)
|
||||||
|
print(f'** {file_path}: file could not be read')
|
||||||
|
return
|
||||||
|
except (KeyError, AttributeError):
|
||||||
|
print(traceback.format_exc(), file=sys.stderr)
|
||||||
|
return
|
||||||
|
return opt.last_operation
|
||||||
|
|
||||||
|
def add_postprocessing_to_metadata(opt,original_file,new_file,tool,command):
|
||||||
|
original_file = original_file if os.path.exists(original_file) else os.path.join(opt.outdir,original_file)
|
||||||
|
new_file = new_file if os.path.exists(new_file) else os.path.join(opt.outdir,new_file)
|
||||||
|
meta = retrieve_metadata(original_file)['sd-metadata']
|
||||||
|
img_data = meta['image']
|
||||||
|
pp = img_data.get('postprocessing',[]) or []
|
||||||
|
pp.append(
|
||||||
|
{
|
||||||
|
'tool':tool,
|
||||||
|
'dream_command':command,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
meta['image']['postprocessing'] = pp
|
||||||
|
write_metadata(new_file,meta)
|
||||||
|
|
||||||
|
def prepare_image_metadata(
|
||||||
|
opt,
|
||||||
|
prefix,
|
||||||
|
seed,
|
||||||
|
operation='generate',
|
||||||
|
prior_variations=[],
|
||||||
|
postprocessed=False,
|
||||||
|
first_seed=None
|
||||||
|
):
|
||||||
|
|
||||||
|
if postprocessed and opt.save_original:
|
||||||
|
filename = choose_postprocess_name(opt,prefix,seed)
|
||||||
|
else:
|
||||||
|
filename = f'{prefix}.{seed}.png'
|
||||||
|
|
||||||
|
if opt.variation_amount > 0:
|
||||||
|
first_seed = first_seed or seed
|
||||||
|
this_variation = [[seed, opt.variation_amount]]
|
||||||
|
opt.with_variations = prior_variations + this_variation
|
||||||
|
formatted_dream_prompt = opt.dream_prompt_str(seed=first_seed)
|
||||||
|
elif len(prior_variations) > 0:
|
||||||
|
formatted_dream_prompt = opt.dream_prompt_str(seed=first_seed)
|
||||||
|
elif operation == 'postprocess':
|
||||||
|
formatted_dream_prompt = '!fix '+opt.dream_prompt_str(seed=seed)
|
||||||
|
else:
|
||||||
|
formatted_dream_prompt = opt.dream_prompt_str(seed=seed)
|
||||||
|
return filename,formatted_dream_prompt
|
||||||
|
|
||||||
|
def choose_postprocess_name(opt,prefix,seed) -> str:
|
||||||
|
match = re.search('postprocess:(\w+)',opt.last_operation)
|
||||||
|
if match:
|
||||||
|
modifier = match.group(1) # will look like "gfpgan", "upscale", "outpaint" or "embiggen"
|
||||||
|
else:
|
||||||
|
modifier = 'postprocessed'
|
||||||
|
|
||||||
|
counter = 0
|
||||||
|
filename = None
|
||||||
|
available = False
|
||||||
|
while not available:
|
||||||
|
if counter == 0:
|
||||||
|
filename = f'{prefix}.{seed}.{modifier}.png'
|
||||||
|
else:
|
||||||
|
filename = f'{prefix}.{seed}.{modifier}-{counter:02d}.png'
|
||||||
|
available = not os.path.exists(os.path.join(opt.outdir,filename))
|
||||||
|
counter += 1
|
||||||
|
return filename
|
||||||
|
|
||||||
|
def get_next_command(infile=None) -> str: # command string
|
||||||
|
if infile is None:
|
||||||
|
command = input('invoke> ')
|
||||||
|
else:
|
||||||
|
command = infile.readline()
|
||||||
|
if not command:
|
||||||
|
raise EOFError
|
||||||
|
else:
|
||||||
|
command = command.strip()
|
||||||
|
if len(command)>0:
|
||||||
|
print(f'#{command}')
|
||||||
|
return command
|
||||||
|
|
||||||
|
def invoke_ai_web_server_loop(gen, gfpgan, codeformer, esrgan):
|
||||||
|
print('\n* --web was specified, starting web server...')
|
||||||
|
# Change working directory to the stable-diffusion directory
|
||||||
|
os.chdir(
|
||||||
|
os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
||||||
|
)
|
||||||
|
|
||||||
|
invoke_ai_web_server = InvokeAIWebServer(generate=gen, gfpgan=gfpgan, codeformer=codeformer, esrgan=esrgan)
|
||||||
|
|
||||||
|
try:
|
||||||
|
invoke_ai_web_server.run()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def split_variations(variations_string) -> list:
|
||||||
|
# shotgun parsing, woo
|
||||||
|
parts = []
|
||||||
|
broken = False # python doesn't have labeled loops...
|
||||||
|
for part in variations_string.split(','):
|
||||||
|
seed_and_weight = part.split(':')
|
||||||
|
if len(seed_and_weight) != 2:
|
||||||
|
print(f'** Could not parse with_variation part "{part}"')
|
||||||
|
broken = True
|
||||||
|
break
|
||||||
|
try:
|
||||||
|
seed = int(seed_and_weight[0])
|
||||||
|
weight = float(seed_and_weight[1])
|
||||||
|
except ValueError:
|
||||||
|
print(f'** Could not parse with_variation part "{part}"')
|
||||||
|
broken = True
|
||||||
|
break
|
||||||
|
parts.append([seed, weight])
|
||||||
|
if broken:
|
||||||
|
return None
|
||||||
|
elif len(parts) == 0:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return parts
|
||||||
|
|
||||||
|
def retrieve_dream_command(opt,file_path,completer):
|
||||||
|
'''
|
||||||
|
Given a full or partial path to a previously-generated image file,
|
||||||
|
will retrieve and format the dream command used to generate the image,
|
||||||
|
and pop it into the readline buffer (linux, Mac), or print out a comment
|
||||||
|
for cut-and-paste (windows)
|
||||||
|
'''
|
||||||
|
dir,basename = os.path.split(file_path)
|
||||||
|
if len(dir) == 0:
|
||||||
|
path = os.path.join(opt.outdir,basename)
|
||||||
|
else:
|
||||||
|
path = file_path
|
||||||
|
try:
|
||||||
|
cmd = dream_cmd_from_png(path)
|
||||||
|
except OSError:
|
||||||
|
print(f'** {path}: file could not be read')
|
||||||
|
return
|
||||||
|
except (KeyError, AttributeError):
|
||||||
|
print(f'** {path}: file has no metadata')
|
||||||
|
return
|
||||||
|
completer.set_line(cmd)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
@ -18,7 +18,7 @@ from pytorch_lightning import seed_everything
|
|||||||
from ldm.util import instantiate_from_config
|
from ldm.util import instantiate_from_config
|
||||||
from ldm.models.diffusion.ddim import DDIMSampler
|
from ldm.models.diffusion.ddim import DDIMSampler
|
||||||
from ldm.models.diffusion.plms import PLMSSampler
|
from ldm.models.diffusion.plms import PLMSSampler
|
||||||
from ldm.dream.devices import choose_torch_device
|
from ldm.invoke.devices import choose_torch_device
|
||||||
|
|
||||||
|
|
||||||
def chunk(it, size):
|
def chunk(it, size):
|
||||||
|
@ -6,7 +6,7 @@ import numpy as np
|
|||||||
import torch
|
import torch
|
||||||
from main import instantiate_from_config
|
from main import instantiate_from_config
|
||||||
from ldm.models.diffusion.ddim import DDIMSampler
|
from ldm.models.diffusion.ddim import DDIMSampler
|
||||||
from ldm.dream.devices import choose_torch_device
|
from ldm.invoke.devices import choose_torch_device
|
||||||
|
|
||||||
def make_batch(image, mask, device):
|
def make_batch(image, mask, device):
|
||||||
image = np.array(Image.open(image).convert("RGB"))
|
image = np.array(Image.open(image).convert("RGB"))
|
||||||
|
@ -18,7 +18,7 @@ import torch.nn as nn
|
|||||||
from ldm.util import instantiate_from_config
|
from ldm.util import instantiate_from_config
|
||||||
from ldm.models.diffusion.ddim import DDIMSampler
|
from ldm.models.diffusion.ddim import DDIMSampler
|
||||||
from ldm.models.diffusion.plms import PLMSSampler
|
from ldm.models.diffusion.plms import PLMSSampler
|
||||||
from ldm.dream.devices import choose_torch_device
|
from ldm.invoke.devices import choose_torch_device
|
||||||
|
|
||||||
def chunk(it, size):
|
def chunk(it, size):
|
||||||
it = iter(it)
|
it = iter(it)
|
||||||
|
@ -15,13 +15,13 @@ import urllib.request
|
|||||||
transformers.logging.set_verbosity_error()
|
transformers.logging.set_verbosity_error()
|
||||||
|
|
||||||
# this will preload the Bert tokenizer fles
|
# this will preload the Bert tokenizer fles
|
||||||
print('preloading bert tokenizer...')
|
print('preloading bert tokenizer...', end='')
|
||||||
|
|
||||||
tokenizer = BertTokenizerFast.from_pretrained('bert-base-uncased')
|
tokenizer = BertTokenizerFast.from_pretrained('bert-base-uncased')
|
||||||
print('...success')
|
print('...success')
|
||||||
|
|
||||||
# this will download requirements for Kornia
|
# this will download requirements for Kornia
|
||||||
print('preloading Kornia requirements (ignore the deprecation warnings)...')
|
print('preloading Kornia requirements...', end='')
|
||||||
with warnings.catch_warnings():
|
with warnings.catch_warnings():
|
||||||
warnings.filterwarnings('ignore', category=DeprecationWarning)
|
warnings.filterwarnings('ignore', category=DeprecationWarning)
|
||||||
import kornia
|
import kornia
|
||||||
@ -29,12 +29,12 @@ print('...success')
|
|||||||
|
|
||||||
version = 'openai/clip-vit-large-patch14'
|
version = 'openai/clip-vit-large-patch14'
|
||||||
|
|
||||||
print('preloading CLIP model (Ignore the deprecation warnings)...')
|
print('preloading CLIP model...',end='')
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
|
||||||
tokenizer = CLIPTokenizer.from_pretrained(version)
|
tokenizer = CLIPTokenizer.from_pretrained(version)
|
||||||
transformer = CLIPTextModel.from_pretrained(version)
|
transformer = CLIPTextModel.from_pretrained(version)
|
||||||
print('\n\n...success')
|
print('...success')
|
||||||
|
|
||||||
# In the event that the user has installed GFPGAN and also elected to use
|
# In the event that the user has installed GFPGAN and also elected to use
|
||||||
# RealESRGAN, this will attempt to download the model needed by RealESRGANer
|
# RealESRGAN, this will attempt to download the model needed by RealESRGANer
|
||||||
@ -47,7 +47,7 @@ except ModuleNotFoundError:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
if gfpgan:
|
if gfpgan:
|
||||||
print('Loading models from RealESRGAN and facexlib')
|
print('Loading models from RealESRGAN and facexlib...',end='')
|
||||||
try:
|
try:
|
||||||
from realesrgan.archs.srvgg_arch import SRVGGNetCompact
|
from realesrgan.archs.srvgg_arch import SRVGGNetCompact
|
||||||
from facexlib.utils.face_restoration_helper import FaceRestoreHelper
|
from facexlib.utils.face_restoration_helper import FaceRestoreHelper
|
||||||
@ -65,27 +65,41 @@ if gfpgan:
|
|||||||
print('Error loading ESRGAN:')
|
print('Error loading ESRGAN:')
|
||||||
print(traceback.format_exc())
|
print(traceback.format_exc())
|
||||||
|
|
||||||
try:
|
print('Loading models from GFPGAN')
|
||||||
import urllib.request
|
import urllib.request
|
||||||
model_url = 'https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.4.pth'
|
for model in (
|
||||||
model_dest = 'src/gfpgan/experiments/pretrained_models/GFPGANv1.4.pth'
|
[
|
||||||
|
'https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.4.pth',
|
||||||
|
'src/gfpgan/experiments/pretrained_models/GFPGANv1.4.pth'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'https://github.com/xinntao/facexlib/releases/download/v0.1.0/detection_Resnet50_Final.pth',
|
||||||
|
'./gfpgan/weights/detection_Resnet50_Final.pth'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'https://github.com/xinntao/facexlib/releases/download/v0.2.2/parsing_parsenet.pth',
|
||||||
|
'./gfpgan/weights/parsing_parsenet.pth'
|
||||||
|
],
|
||||||
|
):
|
||||||
|
model_url,model_dest = model
|
||||||
|
try:
|
||||||
|
if not os.path.exists(model_dest):
|
||||||
|
print(f'Downloading gfpgan model file {model_url}...',end='')
|
||||||
|
os.makedirs(os.path.dirname(model_dest), exist_ok=True)
|
||||||
|
urllib.request.urlretrieve(model_url,model_dest)
|
||||||
|
print('...success')
|
||||||
|
except Exception:
|
||||||
|
import traceback
|
||||||
|
print('Error loading GFPGAN:')
|
||||||
|
print(traceback.format_exc())
|
||||||
|
|
||||||
if not os.path.exists(model_dest):
|
print('preloading CodeFormer model file...',end='')
|
||||||
print('downloading gfpgan model file...')
|
|
||||||
urllib.request.urlretrieve(model_url,model_dest)
|
|
||||||
except Exception:
|
|
||||||
import traceback
|
|
||||||
print('Error loading GFPGAN:')
|
|
||||||
print(traceback.format_exc())
|
|
||||||
print('...success')
|
|
||||||
|
|
||||||
print('preloading CodeFormer model file...')
|
|
||||||
try:
|
try:
|
||||||
import urllib.request
|
import urllib.request
|
||||||
model_url = 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/codeformer.pth'
|
model_url = 'https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/codeformer.pth'
|
||||||
model_dest = 'ldm/dream/restoration/codeformer/weights/codeformer.pth'
|
model_dest = 'ldm/invoke/restoration/codeformer/weights/codeformer.pth'
|
||||||
if not os.path.exists(model_dest):
|
if not os.path.exists(model_dest):
|
||||||
print('downloading codeformer model file...')
|
print('Downloading codeformer model file...')
|
||||||
os.makedirs(os.path.dirname(model_dest), exist_ok=True)
|
os.makedirs(os.path.dirname(model_dest), exist_ok=True)
|
||||||
urllib.request.urlretrieve(model_url,model_dest)
|
urllib.request.urlretrieve(model_url,model_dest)
|
||||||
except Exception:
|
except Exception:
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
from ldm.dream.pngwriter import retrieve_metadata
|
from ldm.invoke.pngwriter import retrieve_metadata
|
||||||
|
|
||||||
if len(sys.argv) < 2:
|
if len(sys.argv) < 2:
|
||||||
print("Usage: file2prompt.py <file1.png> <file2.png> <file3.png>...")
|
print("Usage: file2prompt.py <file1.png> <file2.png> <file3.png>...")
|
||||||
print("This script opens up the indicated dream.py-generated PNG file(s) and prints out their metadata.")
|
print("This script opens up the indicated invoke.py-generated PNG file(s) and prints out their metadata.")
|
||||||
exit(-1)
|
exit(-1)
|
||||||
|
|
||||||
filenames = sys.argv[1:]
|
filenames = sys.argv[1:]
|
||||||
|
@ -10,7 +10,7 @@ from flask_cors import CORS
|
|||||||
from flask_socketio import SocketIO
|
from flask_socketio import SocketIO
|
||||||
from omegaconf import OmegaConf
|
from omegaconf import OmegaConf
|
||||||
from dependency_injector.wiring import inject, Provide
|
from dependency_injector.wiring import inject, Provide
|
||||||
from ldm.dream.args import Args
|
from ldm.invoke.args import Args
|
||||||
from server import views
|
from server import views
|
||||||
from server.containers import Container
|
from server.containers import Container
|
||||||
from server.services import GeneratorService, SignalService
|
from server.services import GeneratorService, SignalService
|
||||||
|
@ -12,12 +12,12 @@ import shlex
|
|||||||
from threading import Thread
|
from threading import Thread
|
||||||
import time
|
import time
|
||||||
from flask_socketio import SocketIO, join_room, leave_room
|
from flask_socketio import SocketIO, join_room, leave_room
|
||||||
from ldm.dream.args import Args
|
from ldm.invoke.args import Args
|
||||||
from ldm.dream.generator import embiggen
|
from ldm.invoke.generator import embiggen
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from ldm.dream.pngwriter import PngWriter
|
from ldm.invoke.pngwriter import PngWriter
|
||||||
from ldm.dream.server import CanceledException
|
from ldm.invoke.server import CanceledException
|
||||||
from ldm.generate import Generate
|
from ldm.generate import Generate
|
||||||
from server.models import DreamResult, JobRequest, PaginatedItems, ProgressType, Signal
|
from server.models import DreamResult, JobRequest, PaginatedItems, ProgressType, Signal
|
||||||
|
|
||||||
|
6
setup.py
@ -1,8 +1,8 @@
|
|||||||
from setuptools import setup, find_packages
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='stable-diffusion',
|
name='invoke-ai',
|
||||||
version='1.15.0-dev',
|
version='2.0.0',
|
||||||
description='',
|
description='',
|
||||||
packages=find_packages(),
|
packages=find_packages(),
|
||||||
install_requires=[
|
install_requires=[
|
||||||
@ -10,4 +10,4 @@ setup(
|
|||||||
'numpy',
|
'numpy',
|
||||||
'tqdm',
|
'tqdm',
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|