Compare commits
44 Commits
Author | SHA1 | Date | |
---|---|---|---|
cc14ac0bac | |||
2e0d7fdbb8 | |||
2284eea2d8 | |||
867b5b2ee4 | |||
85e29fffc9 | |||
fa84d812f1 | |||
4ffa8420dd | |||
7dc1c54578 | |||
05434d3575 | |||
ddea9b9f38 | |||
9445ee41cf | |||
8325b4e5aa | |||
1ff9db3714 | |||
12ff102a21 | |||
f660111751 | |||
bac08306fb | |||
b860dbd9a6 | |||
c7713f559d | |||
7c60189f29 | |||
35dc13ffcf | |||
6b55f385c7 | |||
75a9466232 | |||
2ffe7cc843 | |||
d111911c18 | |||
d55935f8c0 | |||
4def55368c | |||
661eff90fc | |||
66d809e9ed | |||
1bd812a2cf | |||
96e4a4ae03 | |||
f526b2df94 | |||
a2a4b32f7b | |||
cb914ea77f | |||
e1bbc2876d | |||
3bcde36821 | |||
716ba93787 | |||
34e6fee4e7 | |||
3de2383505 | |||
5c8c9d3bac | |||
e45d1d42a8 | |||
252900356f | |||
8edce9fe36 | |||
e8fd6845c8 | |||
946505cc0e |
@ -10,6 +10,6 @@ RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 8000
|
||||
EXPOSE 9000
|
||||
|
||||
ENTRYPOINT ["uvicorn", "main:app", "--reload", "--host", "0.0.0.0"]
|
||||
ENTRYPOINT ["uvicorn", "main:app", "--reload", "--host", "0.0.0.0", "--port", "9000"]
|
15
OldPortDockerfile
Normal file
@ -0,0 +1,15 @@
|
||||
FROM python:3.9
|
||||
|
||||
RUN mkdir /app
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt update
|
||||
|
||||
COPY requirements.txt ./
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
ENTRYPOINT ["uvicorn", "old_port_main:app", "--host", "0.0.0.0", "--port", "8000"]
|
63
README.md
@ -1,15 +1,19 @@
|
||||
A simple way to install and use [Stable Diffusion](https://replicate.com/stability-ai/stable-diffusion) on your local computer. Provides a browser UI for generating images from text prompts. Just enter your text prompt, and see the generated image.
|
||||
# Stable Diffusion UI
|
||||
### A simple way to install and use [Stable Diffusion](https://replicate.com/stability-ai/stable-diffusion) on your own computer
|
||||
|
||||
🎉 **New!** `img2img` is now supported! You can provide an image to generate new images based on it (and an optional text prompt). You can also use the generated image as the new input image in 1-click, to refine it further.
|
||||
[](https://discord.com/invite/u9yhsFmEkB) (for support, and development discussion)
|
||||
|
||||
---
|
||||
|
||||
# What does this do?
|
||||
Two things:
|
||||
1. Automatically downloads and installs Stable Diffusion on your local computer (no need to mess with conda or environments)
|
||||
1. Automatically downloads and installs Stable Diffusion on your own computer (no need to mess with conda or environments)
|
||||
2. Gives you a simple browser-based UI to talk to your local Stable Diffusion. Enter text prompts and view the generated image. No API keys required.
|
||||
|
||||
All the processing will happen on your local computer, it does not transmit your prompts or process on any remote server.
|
||||
All the processing will happen on your computer locally, it does not transmit your prompts or process on any remote server.
|
||||
|
||||

|
||||
|
||||
<img src="https://github.com/cmdr2/stable-diffusion-ui/raw/main/media/shot-v4.jpg" height="500" alt="Screenshot of tool">
|
||||
|
||||
# System Requirements
|
||||
1. Computer capable of running Stable Diffusion.
|
||||
@ -21,46 +25,43 @@ All the processing will happen on your local computer, it does not transmit your
|
||||
# Installation
|
||||
1. Clone this repository: `git clone https://github.com/cmdr2/stable-diffusion-ui.git` or [download the zip file](https://github.com/cmdr2/stable-diffusion-ui/archive/refs/heads/main.zip) and unzip.
|
||||
2. Open your terminal, and in the project directory run: `docker-compose up &` (warning: this will take some time during the first run, since it'll download Stable Diffusion's [docker image](https://replicate.com/stability-ai/stable-diffusion), nearly 17 GiB)
|
||||
3. Open http://localhost:8000 in your browser. That's it!
|
||||
3. Open http://localhost:9000 in your browser. That's it!
|
||||
|
||||
If you're getting errors, please check the [Troubleshooting](#troubleshooting) section below.
|
||||
If you're getting errors, please check the [Troubleshooting](https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting) page.
|
||||
|
||||
To stop the server, please run `docker-compose down`
|
||||
|
||||
# Usage
|
||||
1. Open http://localhost:8000 in your browser (after running `docker-compose up &` from step 2 previously).
|
||||
2. Enter a text prompt, like `a photograph of an astronaut riding a horse` in the textbox.
|
||||
3. Press `Make Image`. This will take some time, depending on your system's processing power.
|
||||
4. See the image generated using your prompt.
|
||||
5. **New!** img2img: You can also choose an `initial image`, to generate an image based on that. An optional text prompt can help you refine this image.
|
||||
Open http://localhost:9000 in your browser (after running `docker-compose up &` from step 2 previously).
|
||||
|
||||
## With a text description
|
||||
1. Enter a text prompt, like `a photograph of an astronaut riding a horse` in the textbox.
|
||||
2. Press `Make Image`. This will take some time, depending on your system's processing power.
|
||||
3. See the image generated using your prompt.
|
||||
|
||||
## With an image
|
||||
1. Click `Browse..` next to `Initial Image`. Select your desired image.
|
||||
2. An optional text prompt can help you further describe the kind of image you want to generate.
|
||||
3. Press `Make Image`. See the image generated using your prompt.
|
||||
|
||||
You can also set an `Image Mask` for telling Stable Diffusion to draw in only the black areas in your image mask. White areas in your mask will be ignored.
|
||||
|
||||
**Pro tip:** You can also click `Use as Input` on a generated image, to use it as the input image for your next generation. This can be useful for sequentially refining the generated image with a single click.
|
||||
|
||||
Please [file an issue](https://github.com/cmdr2/stable-diffusion-ui/issues) if this did not work for you (after trying the common [troubleshooting](#troubleshooting) steps)!
|
||||
**Another tip:** Images with the same aspect ratio of your generated image work best. E.g. 1:1 if you're generating images sized 512x512.
|
||||
|
||||
To stop the server, please run `docker-compose down`
|
||||
## Problems?
|
||||
Please [file an issue](https://github.com/cmdr2/stable-diffusion-ui/issues) if this did not work for you (after trying the common [troubleshooting](#troubleshooting) steps)!
|
||||
|
||||
# Advanced Settings
|
||||
You can also set the configuration like `seed`, `width`, `height`, `num_outputs`, `num_inference_steps` and `guidance_scale` using the 'show' button next to 'Advanced settings'.
|
||||
|
||||
Use the same `seed` number to get the same image for a certain prompt. This is useful for refining a prompt without losing the basic image design. Enable the `random images` checkbox to get random images.
|
||||
|
||||

|
||||

|
||||
|
||||
# Troubleshooting
|
||||
## './docker-compose.yml' is invalid:
|
||||
> ERROR: The Compose file './docker-compose.yml' is invalid because:
|
||||
> services.stability-ai.deploy.resources.reservations value Additional properties are not allowed ('devices' was unexpected)
|
||||
|
||||
Please ensure you have `docker-compose` version 1.29 or higher. Check `docker-compose --version`, and if required [update it to 1.29](https://docs.docker.com/compose/install/). (Thanks [HVRyan](https://github.com/HVRyan))
|
||||
|
||||
## RuntimeError: Found no NVIDIA driver on your system:
|
||||
If you have an NVIDIA GPU and the latest [NVIDIA driver](http://www.nvidia.com/Download/index.aspx), please ensure that you've installed [nvidia-container-toolkit](https://stackoverflow.com/a/58432877). (Thanks [u/exintrovert420](https://www.reddit.com/user/exintrovert420/))
|
||||
|
||||
## Some other process is already running at port 8000 / port 8000 could not be bound
|
||||
You can override the port used. Please change `docker-compose.yml` inside the project directory, and update the line `8000:8000` to `1337:8000` (or where 1337 is whichever port number you want).
|
||||
|
||||
After doing this, please restart your server, by running `docker-compose down` and then `docker-compose up &`.
|
||||
|
||||
After this, you can access the server at `http://localhost:1337` (where 1337 is the new port you specified earlier).
|
||||
The [Troubleshooting wiki page](https://github.com/cmdr2/stable-diffusion-ui/wiki/Troubleshooting) contains some common errors and their solutions. Please check that, and if it doesn't work, feel free to [file an issue](https://github.com/cmdr2/stable-diffusion-ui/issues).
|
||||
|
||||
# Behind the scenes
|
||||
This project is a quick way to get started with Stable Diffusion. You do not need to have Stable Diffusion already installed, and do not need any API keys. This project will automatically download Stable Diffusion's docker image, the first time it is run.
|
||||
@ -70,7 +71,7 @@ This project runs Stable Diffusion in a docker container behind the scenes, usin
|
||||
# Bugs reports and code contributions welcome
|
||||
If there are any problems or suggestions, please feel free to [file an issue](https://github.com/cmdr2/stable-diffusion-ui/issues).
|
||||
|
||||
Also, please feel free to submit a pull request, if you have any code contributions in mind.
|
||||
Also, please feel free to submit a pull request, if you have any code contributions in mind. Join the [discord server](https://discord.com/invite/u9yhsFmEkB) for development-related discussions, and for helping other users.
|
||||
|
||||
# Disclaimer
|
||||
The authors of this project are not responsible for any content generated using this interface.
|
||||
|
@ -5,7 +5,7 @@ services:
|
||||
container_name: sd
|
||||
ports:
|
||||
- '5000:5000'
|
||||
image: 'r8.im/stability-ai/stable-diffusion@sha256:3080f37ef32771c9984d65033cbe71caa96c69680008bae64cf691724a6df04c'
|
||||
image: 'r8.im/stability-ai/stable-diffusion@sha256:be04660a5b93ef2aff61e3668dedb4cbeb14941e62a3fd5998364a32d613e35e'
|
||||
deploy:
|
||||
resources:
|
||||
reservations:
|
||||
@ -15,7 +15,7 @@ services:
|
||||
stable-diffusion-ui:
|
||||
container_name: sd-ui
|
||||
ports:
|
||||
- '8000:8000'
|
||||
- '9000:9000'
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
@ -25,4 +25,4 @@ services:
|
||||
- stability-ai
|
||||
|
||||
networks:
|
||||
default:
|
||||
default:
|
||||
|
728
index.html
@ -5,6 +5,8 @@
|
||||
body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-size: 11pt;
|
||||
background-color: rgb(32, 33, 36);
|
||||
color: #eee;
|
||||
}
|
||||
a {
|
||||
color: rgb(0, 102, 204);
|
||||
@ -12,17 +14,11 @@
|
||||
a:visited {
|
||||
color: rgb(0, 102, 204);
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: rgb(32, 33, 36);
|
||||
color: #eee;
|
||||
}
|
||||
}
|
||||
label {
|
||||
font-size: 10pt;
|
||||
}
|
||||
#prompt {
|
||||
width: 50vw;
|
||||
width: 100%;
|
||||
height: 50pt;
|
||||
}
|
||||
@media screen and (max-width: 600px) {
|
||||
@ -30,12 +26,13 @@
|
||||
width: 95%;
|
||||
}
|
||||
}
|
||||
#init_image_preview_container {
|
||||
.image_preview_container {
|
||||
display: none;
|
||||
margin-top: 10pt;
|
||||
}
|
||||
#init_image_clear {
|
||||
.image_clear_btn {
|
||||
position: absolute;
|
||||
transform: translateX(-50%);
|
||||
transform: translateX(-50%) translateY(-35%);
|
||||
background: black;
|
||||
color: white;
|
||||
border: 2pt solid #ccc;
|
||||
@ -45,77 +42,305 @@
|
||||
border-radius: 8pt;
|
||||
width: 16pt;
|
||||
height: 16pt;
|
||||
font-size: 10pt;
|
||||
font-family: Verdana;
|
||||
font-size: 8pt;
|
||||
}
|
||||
#configHeader {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 10pt;
|
||||
}
|
||||
#config {
|
||||
#editor-settings-entries {
|
||||
font-size: 9pt;
|
||||
margin-bottom: 5px;
|
||||
padding-left: 10px;
|
||||
list-style-type: none;
|
||||
}
|
||||
#editor-settings-entries li {
|
||||
padding-bottom: 3pt;
|
||||
}
|
||||
#guidance_scale {
|
||||
transform: translateY(30%);
|
||||
}
|
||||
#outputMsg {
|
||||
font-size: small;
|
||||
}
|
||||
#footer {
|
||||
border-top: 1px solid #999;
|
||||
margin-top: 10px;
|
||||
padding-top: 10px;
|
||||
font-size: small;
|
||||
padding-left: 10pt;
|
||||
background: none;
|
||||
}
|
||||
#footer-legal {
|
||||
font-size: 8pt;
|
||||
}
|
||||
.imgSeedLabel {
|
||||
position: absolute;
|
||||
transform: translateX(-100%);
|
||||
margin-top: 5pt;
|
||||
margin-left: -5pt;
|
||||
font-size: 10pt;
|
||||
|
||||
background-color: #333;
|
||||
opacity: 0.8;
|
||||
color: #ddd;
|
||||
border-radius: 3pt;
|
||||
padding: 1pt 3pt;
|
||||
}
|
||||
.imgUseBtn {
|
||||
position: absolute;
|
||||
transform: translateX(-100%);
|
||||
margin-top: 5pt;
|
||||
margin-top: 30pt;
|
||||
margin-left: -5pt;
|
||||
}
|
||||
.imgSaveBtn {
|
||||
position: absolute;
|
||||
transform: translateX(-100%);
|
||||
margin-top: 55pt;
|
||||
margin-left: -5pt;
|
||||
}
|
||||
.imgItem {
|
||||
display: inline;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.imgItemInfo {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
#container {
|
||||
width: 75%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
@media screen and (max-width: 1400px) {
|
||||
#container {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
#meta small {
|
||||
font-size: 11pt;
|
||||
}
|
||||
#editor {
|
||||
padding: 5px;
|
||||
}
|
||||
#editor label {
|
||||
font-weight: bold;
|
||||
}
|
||||
#preview {
|
||||
padding: 5px;
|
||||
}
|
||||
#editor-inputs {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
#editor-inputs-prompt {
|
||||
flex: 1;
|
||||
}
|
||||
#editor-inputs .row {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
#makeImage {
|
||||
border-radius: 6px;
|
||||
}
|
||||
#editor-modifiers h5 {
|
||||
padding: 5pt 0;
|
||||
margin: 0;
|
||||
}
|
||||
#makeImage {
|
||||
flex: 0 0 70px;
|
||||
background: rgb(80, 0, 185);
|
||||
border: 2px solid rgb(40, 0, 78);
|
||||
color: rgb(255, 221, 255);
|
||||
width: 100%;
|
||||
height: 30pt;
|
||||
}
|
||||
#makeImage:hover {
|
||||
background: rgb(93, 0, 214);
|
||||
}
|
||||
.flex-container {
|
||||
display: flex;
|
||||
}
|
||||
.col-50 {
|
||||
flex: 50%;
|
||||
}
|
||||
.col-free {
|
||||
flex: 1;
|
||||
}
|
||||
.collapsible {
|
||||
cursor: pointer;
|
||||
}
|
||||
.collapsible-content {
|
||||
display: none;
|
||||
padding-left: 15px;
|
||||
}
|
||||
.collapsible-content h5 {
|
||||
padding: 5pt 0pt;
|
||||
margin: 0;
|
||||
font-size: 10pt;
|
||||
}
|
||||
.collapsible-handle {
|
||||
color: white;
|
||||
padding-right: 5px;
|
||||
}
|
||||
.panel-box {
|
||||
background: rgb(44, 45, 48);
|
||||
border: 1px solid rgb(47, 49, 53);
|
||||
border-radius: 7px;
|
||||
padding: 5px;
|
||||
margin-bottom: 15px;
|
||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.15), 0 6px 20px 0 rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
.panel-box h4 {
|
||||
margin: 0;
|
||||
padding: 2px 0;
|
||||
}
|
||||
.prompt-modifier-tag {
|
||||
border: 1px solid rgb(10, 0, 24);
|
||||
border-radius: 4px;
|
||||
padding: 0pt 3pt;
|
||||
margin-right: 2pt;
|
||||
cursor: pointer;
|
||||
display: inline;
|
||||
background: rgb(163, 163, 163);
|
||||
color: black;
|
||||
line-height: 25pt;
|
||||
float: left;
|
||||
font-size: 9pt;
|
||||
}
|
||||
.prompt-modifier-tag:hover {
|
||||
background: black;
|
||||
color: white;
|
||||
}
|
||||
#editor-modifiers-entries .prompt-modifier-tag {
|
||||
background: #110f0f;
|
||||
color: rgb(212, 212, 212);
|
||||
margin-bottom: 4pt;
|
||||
font-size: 10pt;
|
||||
}
|
||||
#editor-modifiers-entries .prompt-modifier-tag:hover {
|
||||
background: rgb(163, 163, 163);
|
||||
color: black;
|
||||
}
|
||||
#editor-modifiers .editor-modifiers-leaf {
|
||||
padding-top: 10pt;
|
||||
padding-bottom: 10pt;
|
||||
}
|
||||
#preview {
|
||||
margin-left: 20pt;
|
||||
}
|
||||
img {
|
||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.15), 0 6px 20px 0 rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
.line-separator {
|
||||
background: rgb(56, 56, 56);
|
||||
height: 1pt;
|
||||
margin: 15pt 0;
|
||||
}
|
||||
#editor-inputs-tags-container {
|
||||
margin-top: 5pt;
|
||||
display: none;
|
||||
}
|
||||
#server-status {
|
||||
float: right;
|
||||
}
|
||||
#server-status-color {
|
||||
width: 10pt;
|
||||
height: 10pt;
|
||||
border-radius: 5pt;
|
||||
background-color: rgb(128, 87, 0);
|
||||
/* background-color: rgb(197, 1, 1); */
|
||||
float: left;
|
||||
transform: translateY(15%);
|
||||
}
|
||||
#server-status-msg {
|
||||
color: rgb(128, 87, 0);
|
||||
padding-left: 2pt;
|
||||
}
|
||||
#preview-prompt {
|
||||
font-size: 16pt;
|
||||
margin-bottom: 10pt;
|
||||
}
|
||||
</style>
|
||||
</html>
|
||||
<body>
|
||||
<div id="status">Server status: <span id="serverStatus">checking..</span> | Request status: <span id="reqStatus">n/a</span></div>
|
||||
<div id="container">
|
||||
<div class="flex-container">
|
||||
<div id="editor" class="col-50">
|
||||
<div id="meta">
|
||||
<div id="server-status">
|
||||
<div id="server-status-color"> </div>
|
||||
<span id="server-status-msg">server starting..</span>
|
||||
</div>
|
||||
<h1>Stable Diffusion UI <small>v1</small></h1>
|
||||
</div>
|
||||
<div id="editor-inputs">
|
||||
<div id="editor-inputs-prompt" class="row">
|
||||
<label for="prompt">Prompt</label>
|
||||
<textarea id="prompt" class="col-free">a photograph of an astronaut riding a horse</textarea>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<div id="editor-inputs-init-image" class="row">
|
||||
<label for="init_image"><b>Initial Image:</b> (optional) </label> <input id="init_image" name="init_image" type="file" /> </button><br/>
|
||||
<div id="init_image_preview_container" class="image_preview_container">
|
||||
<img id="init_image_preview" src="" width="100" height="100" />
|
||||
<button id="init_image_clear" class="image_clear_btn">X</button>
|
||||
</div>
|
||||
|
||||
<b>Prompt:</b><br/>
|
||||
<textarea id="prompt">a photograph of an astronaut riding a horse</textarea><br/>
|
||||
<div id="mask_setting">
|
||||
<label for="mask"><b>Image Mask:</b> (optional) </label> <input id="mask" name="mask" type="file" /> </button><br/>
|
||||
<div id="mask_preview_container" class="image_preview_container">
|
||||
<img id="mask_preview" src="" width="100" height="100" />
|
||||
<button id="mask_clear" class="image_clear_btn">X</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label for="init_image"><b>Initial Image:</b> (optional) </label> <input id="init_image" name="init_image" type="file" /> </button><br/>
|
||||
<div id="init_image_preview_container">
|
||||
<img id="init_image_preview" src="" width="100" height="100" />
|
||||
<button id="init_image_clear">X</button>
|
||||
<div id="editor-inputs-tags-container" class="row">
|
||||
<label>Tags: <small>(click a tag to remove it)</small></label>
|
||||
<div id="editor-inputs-tags-list">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button id="makeImage">Make Image</button>
|
||||
</div>
|
||||
|
||||
<div class="line-separator"> </div>
|
||||
|
||||
<div id="editor-settings" class="panel-box">
|
||||
<h4 class="collapsible">Advanced Settings</h4>
|
||||
<ul id="editor-settings-entries" class="collapsible-content">
|
||||
<li><label for="seed">Seed:</label> <input id="seed" name="seed" size="10" value="30000"> <input id="random_seed" name="random_seed" type="checkbox" checked> <label for="random_seed">Random Image</label></li>
|
||||
<li><label for="num_outputs_total">Number of outputs:</label> <input id="num_outputs_total" name="num_outputs_total" value="1" size="4"> <label for="num_outputs_parallel">Generate in parallel:</label> <select id="num_outputs_parallel" name="num_outputs_parallel" value="1"><option value="1" selected>1 image at a time</option><option value="4">4 images at a time</option></select></li>
|
||||
<li><label for="width">Width:</label> <select id="width" name="width" value="512"><option value="128">128</option><option value="256">256</option><option value="512" selected>512</option><option value="768">768</option><option value="1024">1024</option></select></li>
|
||||
<li><label for="height">Height:</label> <select id="height" name="height" value="512"><option value="128">128</option><option value="256">256</option><option value="512" selected>512</option><option value="768">768</option></select></li>
|
||||
<li><label for="num_inference_steps">Number of inference steps:</label> <input id="num_inference_steps" name="num_inference_steps" size="4" value="50"></li>
|
||||
<li><label for="guidance_scale">Guidance Scale:</label> <input id="guidance_scale" name="guidance_scale" value="75" type="range" min="10" max="200"> <span id="guidance_scale_value"></span></li>
|
||||
<li><span id="prompt_strength_container"><label for="prompt_strength">Prompt Strength:</label> <input id="prompt_strength" name="prompt_strength" value="8" type="range" min="0" max="10"> <span id="prompt_strength_value"></span><br/></span></li>
|
||||
<li> </li>
|
||||
<li><input id="sound_toggle" name="sound_toggle" type="checkbox" checked> <label for="sound_toggle">Play sound on task completion</label></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="editor-modifiers" class="panel-box">
|
||||
<h4 class="collapsible">Image Modifiers (art styles, tags etc)</h4>
|
||||
<div id="editor-modifiers-entries" class="collapsible-content">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="preview" class="col-50">
|
||||
<div id="preview-prompt">Type a prompt and press the "Make Image" button.<br/><br/>You can also add modifiers like "Realistic", "Pencil Sketch", "ArtStation" etc by browsing through the "Image Modifiers" section and selecting the desired modifiers.<br/><br/>Click "Advanced Settings" for additional settings like seed, image size, number of images to generate etc.<br/><br/>Enjoy! :)</div>
|
||||
|
||||
<div id="outputMsg"></div>
|
||||
<div id="current-images" class="img-preview">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="configHeader"><b>Advanced settings:</b> [<a id="configToggleBtn" href="#">show</a>]</div>
|
||||
<div id="config">
|
||||
<label for="seed">Seed:</label> <input id="seed" name="seed" value="30000"> <input id="random_seed" name="random_seed" type="checkbox" checked> <label for="random_seed">Random Image</label> <br/>
|
||||
<label for="num_outputs">Number of outputs:</label> <select id="num_outputs" name="num_outputs" value="1"><option value="1" selected>1</option><option value="4">4</option></select><br/>
|
||||
<label for="width">Width:</label> <select id="width" name="width" value="512"><option value="128">128</option><option value="256">256</option><option value="512" selected>512</option><option value="768">768</option><option value="1024">1024</option></select><br/>
|
||||
<label for="height">Height:</label> <select id="height" name="height" value="512"><option value="128">128</option><option value="256">256</option><option value="512" selected>512</option><option value="768">768</option></select><br/>
|
||||
<label for="num_inference_steps">Number of inference steps:</label> <input id="num_inference_steps" name="num_inference_steps" value="50"><br/>
|
||||
<label for="guidance_scale">Guidance Scale:</label> <input id="guidance_scale" name="guidance_scale" value="75" type="range" min="10" max="200"> <span id="guidance_scale_value"></span><br/>
|
||||
<span id="prompt_strength_container"><label for="prompt_strength">Prompt Strength:</label> <input id="prompt_strength" name="prompt_strength" value="8" type="range" min="0" max="10"> <span id="prompt_strength_value"></span><br/></span><br/>
|
||||
<input id="sound_toggle" name="sound_toggle" type="checkbox" checked> <label for="sound_toggle">Play sound on task completion</label><br/>
|
||||
</div>
|
||||
<div class="line-separator"> </div>
|
||||
|
||||
<button id="makeImage">Make Image</button> <br/><br/>
|
||||
|
||||
<div id="outputMsg"></div>
|
||||
|
||||
<div id="images"></div>
|
||||
|
||||
<div id="footer">
|
||||
<div id="footer" class="panel-box">
|
||||
<p>Please feel free to <a href="https://github.com/cmdr2/stable-diffusion-ui/issues" target="_blank">file an issue</a> if you have any problems or suggestions in using this interface.</p>
|
||||
<p><b>Disclaimer:</b> The authors of this project are not responsible for any content generated using this interface.</p>
|
||||
<p>This license of this software forbids you from sharing any content that violates any laws, produce any harm to a person, disseminate any personal information that would be meant for harm, <br/>spread misinformation and target vulnerable groups. For the full list of restrictions please read <a href="https://github.com/cmdr2/stable-diffusion-ui/blob/main/LICENSE" target="_blank">the license</a>.</p>
|
||||
<p>By using this software, you consent to the terms and conditions of the license.</p>
|
||||
<div id="footer-legal">
|
||||
<p><b>Disclaimer:</b> The authors of this project are not responsible for any content generated using this interface.</p>
|
||||
<p>This license of this software forbids you from sharing any content that violates any laws, produce any harm to a person, disseminate any personal information that would be meant for harm, <br/>spread misinformation and target vulnerable groups. For the full list of restrictions please read <a href="https://github.com/cmdr2/stable-diffusion-ui/blob/main/LICENSE" target="_blank">the license</a>.</p>
|
||||
<p>By using this software, you consent to the terms and conditions of the license.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
@ -123,7 +348,8 @@ const SOUND_ENABLED_KEY = "soundEnabled"
|
||||
const HEALTH_PING_INTERVAL = 5 // seconds
|
||||
|
||||
let promptField = document.querySelector('#prompt')
|
||||
let numOutputsField = document.querySelector('#num_outputs')
|
||||
let numOutputsTotalField = document.querySelector('#num_outputs_total')
|
||||
let numOutputsParallelField = document.querySelector('#num_outputs_parallel')
|
||||
let numInferenceStepsField = document.querySelector('#num_inference_steps')
|
||||
let guidanceScaleField = document.querySelector('#guidance_scale')
|
||||
let guidanceScaleValueLabel = document.querySelector('#guidance_scale_value')
|
||||
@ -133,23 +359,39 @@ let widthField = document.querySelector('#width')
|
||||
let heightField = document.querySelector('#height')
|
||||
let initImageSelector = document.querySelector("#init_image")
|
||||
let initImagePreview = document.querySelector("#init_image_preview")
|
||||
let maskImageSelector = document.querySelector("#mask")
|
||||
let maskImagePreview = document.querySelector("#mask_preview")
|
||||
let promptStrengthField = document.querySelector('#prompt_strength')
|
||||
let promptStrengthValueLabel = document.querySelector('#prompt_strength_value')
|
||||
|
||||
let makeImageBtn = document.querySelector('#makeImage')
|
||||
|
||||
let imagesContainer = document.querySelector('#images')
|
||||
let imagesContainer = document.querySelector('#current-images')
|
||||
let initImagePreviewContainer = document.querySelector('#init_image_preview_container')
|
||||
let initImageClearBtn = document.querySelector('#init_image_clear')
|
||||
let promptStrengthContainer = document.querySelector('#prompt_strength_container')
|
||||
|
||||
let maskSetting = document.querySelector('#mask_setting')
|
||||
let maskImagePreviewContainer = document.querySelector('#mask_preview_container')
|
||||
let maskImageClearBtn = document.querySelector('#mask_clear')
|
||||
|
||||
let editorModifierEntries = document.querySelector('#editor-modifiers-entries')
|
||||
let editorModifierTagsList = document.querySelector('#editor-inputs-tags-list')
|
||||
let editorTagsContainer = document.querySelector('#editor-inputs-tags-container')
|
||||
|
||||
let previewPrompt = document.querySelector('#preview-prompt')
|
||||
|
||||
let showConfigToggle = document.querySelector('#configToggleBtn')
|
||||
let configBox = document.querySelector('#config')
|
||||
// let configBox = document.querySelector('#config')
|
||||
let outputMsg = document.querySelector('#outputMsg')
|
||||
|
||||
let soundToggle = document.querySelector('#sound_toggle')
|
||||
|
||||
let serverStatusColor = document.querySelector('#server-status-color')
|
||||
let serverStatusMsg = document.querySelector('#server-status-msg')
|
||||
|
||||
let serverStatus = 'offline'
|
||||
let activeTags = []
|
||||
|
||||
function isSoundEnabled() {
|
||||
if (localStorage.getItem(SOUND_ENABLED_KEY) === 'false') {
|
||||
@ -159,24 +401,28 @@ function isSoundEnabled() {
|
||||
}
|
||||
|
||||
function setStatus(statusType, msg, msgType) {
|
||||
let el = ''
|
||||
|
||||
if (statusType === 'server') {
|
||||
el = '#serverStatus'
|
||||
serverStatus = msg
|
||||
} else if (statusType === 'request') {
|
||||
el = '#reqStatus'
|
||||
if (statusType !== 'server') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (msgType == 'error') {
|
||||
msg = '<span style="color: red">' + msg + '<span>'
|
||||
// msg = '<span style="color: red">' + msg + '<span>'
|
||||
serverStatusColor.style.backgroundColor = 'red'
|
||||
serverStatusMsg.style.color = 'red'
|
||||
serverStatusMsg.innerHTML = 'server offline'
|
||||
} else if (msgType == 'success') {
|
||||
msg = '<span style="color: green">' + msg + '<span>'
|
||||
// msg = '<span style="color: green">' + msg + '<span>'
|
||||
serverStatusColor.style.backgroundColor = 'green'
|
||||
serverStatusMsg.style.color = 'green'
|
||||
serverStatusMsg.innerHTML = 'server online'
|
||||
serverStatus = 'online'
|
||||
}
|
||||
}
|
||||
|
||||
if (el) {
|
||||
document.querySelector(el).innerHTML = msg
|
||||
}
|
||||
function logError(msg, res) {
|
||||
outputMsg.innerHTML = '<span style="color: red">Error: ' + msg + '</span>'
|
||||
console.log('request error', res)
|
||||
setStatus('request', 'error', 'error')
|
||||
}
|
||||
|
||||
function playSound() {
|
||||
@ -200,39 +446,10 @@ async function healthCheck() {
|
||||
}
|
||||
}
|
||||
|
||||
async function makeImage() {
|
||||
setStatus('request', 'fetching..')
|
||||
|
||||
makeImageBtn.innerHTML = 'Processing..'
|
||||
makeImageBtn.disabled = true
|
||||
|
||||
outputMsg.innerHTML = 'Fetching..'
|
||||
|
||||
function logError(msg, res) {
|
||||
outputMsg.innerHTML = '<span style="color: red">Error: ' + msg + '</span>'
|
||||
console.log('request error', res)
|
||||
setStatus('request', 'error', 'error')
|
||||
}
|
||||
|
||||
let seed = (randomSeedField.checked ? Math.floor(Math.random() * 10000) : seedField.value)
|
||||
|
||||
let reqBody = {
|
||||
prompt: promptField.value,
|
||||
num_outputs: numOutputsField.value,
|
||||
num_inference_steps: numInferenceStepsField.value,
|
||||
guidance_scale: guidanceScaleField.value / 10,
|
||||
width: widthField.value,
|
||||
height: heightField.value,
|
||||
seed: seed,
|
||||
}
|
||||
|
||||
if (initImagePreview.src.indexOf('data:image/png;base64') !== -1) {
|
||||
reqBody['init_image'] = initImagePreview.src
|
||||
reqBody['prompt_strength'] = promptStrengthField.value / 10
|
||||
}
|
||||
|
||||
// makes a single image. don't call this directly, use makeImage() instead
|
||||
async function doMakeImage(reqBody) {
|
||||
let res = ''
|
||||
let time = new Date().getTime()
|
||||
let seed = reqBody['seed']
|
||||
|
||||
try {
|
||||
res = await fetch('/image', {
|
||||
@ -269,24 +486,10 @@ async function makeImage() {
|
||||
setStatus('request', 'error', 'error')
|
||||
}
|
||||
|
||||
makeImageBtn.innerHTML = 'Make Image'
|
||||
makeImageBtn.disabled = false
|
||||
|
||||
if (isSoundEnabled()) {
|
||||
playSound()
|
||||
}
|
||||
|
||||
if (!res) {
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
time = new Date().getTime() - time
|
||||
time /= 1000
|
||||
|
||||
outputMsg.innerHTML = 'Processed in ' + time + ' seconds. Seed: ' + seed
|
||||
|
||||
imagesContainer.innerHTML = ''
|
||||
|
||||
for (let idx in res.output) {
|
||||
let imgBody = ''
|
||||
|
||||
@ -295,7 +498,7 @@ async function makeImage() {
|
||||
} catch (e) {
|
||||
console.log(imgBody)
|
||||
setStatus('request', 'invalid image', 'error')
|
||||
return
|
||||
continue
|
||||
}
|
||||
|
||||
let imgItem = document.createElement('div')
|
||||
@ -306,12 +509,26 @@ async function makeImage() {
|
||||
img.height = parseInt(reqBody.height)
|
||||
img.src = imgBody
|
||||
|
||||
let imgItemInfo = document.createElement('span')
|
||||
imgItemInfo.className = 'imgItemInfo'
|
||||
|
||||
let imgSeedLabel = document.createElement('span')
|
||||
imgSeedLabel.className = 'imgSeedLabel'
|
||||
imgSeedLabel.innerHTML = 'Seed: ' + seed
|
||||
|
||||
let imgUseBtn = document.createElement('button')
|
||||
imgUseBtn.className = 'imgUseBtn'
|
||||
imgUseBtn.innerHTML = 'Use as Input'
|
||||
|
||||
let imgSaveBtn = document.createElement('button')
|
||||
imgSaveBtn.className = 'imgSaveBtn'
|
||||
imgSaveBtn.innerHTML = 'Download'
|
||||
|
||||
imgItem.appendChild(img)
|
||||
imgItem.appendChild(imgUseBtn)
|
||||
imgItem.appendChild(imgItemInfo)
|
||||
imgItemInfo.appendChild(imgSeedLabel)
|
||||
imgItemInfo.appendChild(imgUseBtn)
|
||||
imgItemInfo.appendChild(imgSaveBtn)
|
||||
imagesContainer.appendChild(imgItem)
|
||||
|
||||
imgUseBtn.addEventListener('click', function() {
|
||||
@ -321,19 +538,131 @@ async function makeImage() {
|
||||
initImagePreviewContainer.style.display = 'block'
|
||||
promptStrengthContainer.style.display = 'block'
|
||||
|
||||
maskSetting.style.display = 'block'
|
||||
|
||||
randomSeedField.checked = false
|
||||
seedField.value = seed
|
||||
seedField.disabled = false
|
||||
})
|
||||
|
||||
imgSaveBtn.addEventListener('click', function() {
|
||||
let imgDownload = document.createElement('a')
|
||||
imgDownload.download = generateUUID() + '.png'
|
||||
imgDownload.href = imgBody
|
||||
imgDownload.click()
|
||||
})
|
||||
|
||||
imgItem.addEventListener('mouseenter', function() {
|
||||
imgItemInfo.style.opacity = 1
|
||||
})
|
||||
|
||||
imgItem.addEventListener('mouseleave', function() {
|
||||
imgItemInfo.style.opacity = 0.5
|
||||
})
|
||||
}
|
||||
|
||||
setStatus('request', 'done', 'success')
|
||||
return true
|
||||
}
|
||||
|
||||
async function makeImage() {
|
||||
if (serverStatus !== 'online') {
|
||||
logError('The server is still starting up..')
|
||||
return
|
||||
}
|
||||
|
||||
setStatus('request', 'fetching..')
|
||||
|
||||
makeImageBtn.innerHTML = 'Processing..'
|
||||
makeImageBtn.disabled = true
|
||||
|
||||
outputMsg.innerHTML = 'Fetching..'
|
||||
|
||||
const imageRegex = new RegExp('data:image/[A-Za-z]+;base64')
|
||||
let seed = (randomSeedField.checked ? Math.floor(Math.random() * 10000) : parseInt(seedField.value))
|
||||
let numOutputsTotal = parseInt(numOutputsTotalField.value)
|
||||
let numOutputsParallel = parseInt(numOutputsParallelField.value)
|
||||
let batchCount = Math.ceil(numOutputsTotal / numOutputsParallel)
|
||||
let batchSize = numOutputsParallel
|
||||
|
||||
let prompt = promptField.value
|
||||
if (activeTags.length > 0) {
|
||||
let promptTags = activeTags.join(", ")
|
||||
prompt += ", " + promptTags
|
||||
}
|
||||
|
||||
previewPrompt.innerHTML = prompt
|
||||
|
||||
let reqBody = {
|
||||
prompt: prompt,
|
||||
num_outputs: batchSize,
|
||||
num_inference_steps: numInferenceStepsField.value,
|
||||
guidance_scale: parseInt(guidanceScaleField.value) / 10,
|
||||
width: widthField.value,
|
||||
height: heightField.value
|
||||
}
|
||||
|
||||
if (imageRegex.test(initImagePreview.src)) {
|
||||
reqBody['init_image'] = initImagePreview.src
|
||||
reqBody['prompt_strength'] = parseInt(promptStrengthField.value) / 10
|
||||
|
||||
if (imageRegex.test(maskImagePreview.src)) {
|
||||
reqBody['mask'] = maskImagePreview.src
|
||||
}
|
||||
}
|
||||
|
||||
let time = new Date().getTime()
|
||||
imagesContainer.innerHTML = ''
|
||||
|
||||
let successCount = 0
|
||||
|
||||
for (let i = 0; i < batchCount; i++) {
|
||||
reqBody['seed'] = seed + i
|
||||
|
||||
let success = await doMakeImage(reqBody)
|
||||
|
||||
if (success) {
|
||||
outputMsg.innerHTML = 'Processed batch ' + (i+1) + '/' + batchCount
|
||||
successCount++
|
||||
}
|
||||
}
|
||||
|
||||
makeImageBtn.innerHTML = 'Make Image'
|
||||
makeImageBtn.disabled = false
|
||||
|
||||
if (isSoundEnabled()) {
|
||||
playSound()
|
||||
}
|
||||
|
||||
time = new Date().getTime() - time
|
||||
time /= 1000
|
||||
|
||||
if (successCount === batchCount) {
|
||||
outputMsg.innerHTML = 'Processed ' + numOutputsTotal + ' images in ' + time + ' seconds'
|
||||
|
||||
setStatus('request', 'done', 'success')
|
||||
}
|
||||
|
||||
if (randomSeedField.checked) {
|
||||
seedField.value = seed
|
||||
}
|
||||
}
|
||||
|
||||
function generateUUID() { // Public Domain/MIT
|
||||
var d = new Date().getTime();//Timestamp
|
||||
var d2 = ((typeof performance !== 'undefined') && performance.now && (performance.now()*1000)) || 0;//Time in microseconds since page-load or 0 if unsupported
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||
var r = Math.random() * 16;//random number between 0 and 16
|
||||
if(d > 0){//Use timestamp until depleted
|
||||
r = (d + r)%16 | 0;
|
||||
d = Math.floor(d/16);
|
||||
} else {//Use microseconds since page-load if supported
|
||||
r = (d2 + r)%16 | 0;
|
||||
d2 = Math.floor(d2/16);
|
||||
}
|
||||
return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
|
||||
});
|
||||
}
|
||||
|
||||
function handleAudioEnabledChange(e) {
|
||||
localStorage.setItem(SOUND_ENABLED_KEY, e.target.checked.toString())
|
||||
}
|
||||
@ -343,13 +672,13 @@ soundToggle.checked = isSoundEnabled();
|
||||
|
||||
makeImageBtn.addEventListener('click', makeImage)
|
||||
|
||||
configBox.style.display = 'none'
|
||||
// configBox.style.display = 'none'
|
||||
|
||||
showConfigToggle.addEventListener('click', function() {
|
||||
configBox.style.display = (configBox.style.display === 'none' ? 'block' : 'none')
|
||||
showConfigToggle.innerHTML = (configBox.style.display === 'none' ? 'show' : 'hide')
|
||||
return false
|
||||
})
|
||||
// showConfigToggle.addEventListener('click', function() {
|
||||
// configBox.style.display = (configBox.style.display === 'none' ? 'block' : 'none')
|
||||
// showConfigToggle.innerHTML = (configBox.style.display === 'none' ? 'show' : 'hide')
|
||||
// return false
|
||||
// })
|
||||
|
||||
function updateGuidanceScale() {
|
||||
guidanceScaleValueLabel.innerHTML = guidanceScaleField.value / 10
|
||||
@ -380,6 +709,7 @@ function showInitImagePreview() {
|
||||
if (initImageSelector.files.length === 0) {
|
||||
initImagePreviewContainer.style.display = 'none'
|
||||
promptStrengthContainer.style.display = 'none'
|
||||
maskSetting.style.display = 'none'
|
||||
return
|
||||
}
|
||||
|
||||
@ -391,6 +721,8 @@ function showInitImagePreview() {
|
||||
initImagePreview.src = reader.result
|
||||
initImagePreviewContainer.style.display = 'block'
|
||||
promptStrengthContainer.style.display = 'block'
|
||||
|
||||
maskSetting.style.display = 'block'
|
||||
})
|
||||
|
||||
if (file) {
|
||||
@ -402,12 +734,164 @@ showInitImagePreview()
|
||||
|
||||
initImageClearBtn.addEventListener('click', function() {
|
||||
initImageSelector.value = null
|
||||
maskImageSelector.value = null
|
||||
|
||||
initImagePreview.src = ''
|
||||
maskImagePreview.src = ''
|
||||
|
||||
initImagePreviewContainer.style.display = 'none'
|
||||
maskImagePreviewContainer.style.display = 'none'
|
||||
|
||||
maskSetting.style.display = 'none'
|
||||
|
||||
promptStrengthContainer.style.display = 'none'
|
||||
})
|
||||
|
||||
setInterval(healthCheck, HEALTH_PING_INTERVAL * 1000)
|
||||
function showMaskImagePreview() {
|
||||
if (maskImageSelector.files.length === 0) {
|
||||
maskImagePreviewContainer.style.display = 'none'
|
||||
return
|
||||
}
|
||||
|
||||
let reader = new FileReader()
|
||||
let file = maskImageSelector.files[0]
|
||||
|
||||
reader.addEventListener('load', function() {
|
||||
maskImagePreview.src = reader.result
|
||||
maskImagePreviewContainer.style.display = 'block'
|
||||
})
|
||||
|
||||
if (file) {
|
||||
reader.readAsDataURL(file)
|
||||
}
|
||||
}
|
||||
maskImageSelector.addEventListener('change', showMaskImagePreview)
|
||||
showMaskImagePreview()
|
||||
|
||||
maskImageClearBtn.addEventListener('click', function() {
|
||||
maskImageSelector.value = null
|
||||
maskImagePreview.src = ''
|
||||
maskImagePreviewContainer.style.display = 'none'
|
||||
})
|
||||
</script>
|
||||
<script>
|
||||
function createCollapsibles(node) {
|
||||
if (!node) {
|
||||
node = document
|
||||
}
|
||||
|
||||
let collapsibles = node.querySelectorAll(".collapsible")
|
||||
collapsibles.forEach(function(c) {
|
||||
let handle = document.createElement('span')
|
||||
handle.className = 'collapsible-handle'
|
||||
handle.innerHTML = '➕'
|
||||
c.insertBefore(handle, c.firstChild)
|
||||
|
||||
c.addEventListener('click', function() {
|
||||
this.classList.toggle("active")
|
||||
let content = this.nextElementSibling
|
||||
if (content.style.display === "block") {
|
||||
content.style.display = "none"
|
||||
handle.innerHTML = '➕' // plus
|
||||
} else {
|
||||
content.style.display = "block"
|
||||
handle.innerHTML = '➖' // minus
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
createCollapsibles()
|
||||
|
||||
function refreshTagsList() {
|
||||
editorModifierTagsList.innerHTML = ''
|
||||
|
||||
if (activeTags.length == 0) {
|
||||
editorTagsContainer.style.display = 'none'
|
||||
return
|
||||
} else {
|
||||
editorTagsContainer.style.display = 'block'
|
||||
}
|
||||
|
||||
activeTags.forEach(function(tag) {
|
||||
let el = document.createElement('div')
|
||||
el.className = 'prompt-modifier-tag'
|
||||
el.innerHTML = tag
|
||||
|
||||
editorModifierTagsList.appendChild(el)
|
||||
|
||||
el.addEventListener('click', function() {
|
||||
let idx = activeTags.indexOf(tag)
|
||||
if (idx !== -1) {
|
||||
activeTags.splice(idx, 1)
|
||||
refreshTagsList()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
let brk = document.createElement('br')
|
||||
brk.style.clear = 'both'
|
||||
editorModifierTagsList.appendChild(brk)
|
||||
}
|
||||
|
||||
async function loadModifiers() {
|
||||
try {
|
||||
let res = await fetch('/modifiers.json')
|
||||
if (res.status === 200) {
|
||||
res = await res.json()
|
||||
|
||||
res.forEach(function(m) {
|
||||
let title = m[0]
|
||||
let modifiers = m[1]
|
||||
|
||||
let titleEl = document.createElement('h5')
|
||||
titleEl.className = 'collapsible'
|
||||
titleEl.innerHTML = title
|
||||
|
||||
let modifiersEl = document.createElement('div')
|
||||
modifiersEl.classList.add('collapsible-content', 'editor-modifiers-leaf')
|
||||
|
||||
modifiers.forEach(function(modifier) {
|
||||
let tagEl = document.createElement('div')
|
||||
tagEl.className = 'prompt-modifier-tag'
|
||||
tagEl.innerHTML = modifier
|
||||
|
||||
modifiersEl.appendChild(tagEl)
|
||||
|
||||
tagEl.addEventListener('click', function() {
|
||||
if (activeTags.includes(modifier)) {
|
||||
return
|
||||
}
|
||||
|
||||
activeTags.push(modifier)
|
||||
refreshTagsList()
|
||||
})
|
||||
})
|
||||
let brk = document.createElement('br')
|
||||
brk.style.clear = 'both'
|
||||
modifiersEl.appendChild(brk)
|
||||
|
||||
let e = document.createElement('div')
|
||||
e.appendChild(titleEl)
|
||||
e.appendChild(modifiersEl)
|
||||
|
||||
editorModifierEntries.appendChild(e)
|
||||
})
|
||||
|
||||
createCollapsibles(editorModifierEntries)
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('error fetching modifiers', e)
|
||||
}
|
||||
}
|
||||
|
||||
async function init() {
|
||||
await loadModifiers()
|
||||
|
||||
setInterval(healthCheck, HEALTH_PING_INTERVAL * 1000)
|
||||
healthCheck()
|
||||
}
|
||||
|
||||
init()
|
||||
</script>
|
||||
|
||||
</html>
|
||||
|
8
main.py
@ -13,6 +13,7 @@ app = FastAPI()
|
||||
class ImageRequest(BaseModel):
|
||||
prompt: str
|
||||
init_image: str = None # base64
|
||||
mask: str = None # base64
|
||||
num_outputs: str = "1"
|
||||
num_inference_steps: str = "50"
|
||||
guidance_scale: str = "7.5"
|
||||
@ -51,6 +52,9 @@ async def image(req : ImageRequest):
|
||||
data['input']['init_image'] = req.init_image
|
||||
data['input']['prompt_strength'] = req.prompt_strength
|
||||
|
||||
if req.mask is not None:
|
||||
data['input']['mask'] = req.mask
|
||||
|
||||
if req.seed == "-1":
|
||||
del data['input']['seed']
|
||||
|
||||
@ -63,3 +67,7 @@ async def image(req : ImageRequest):
|
||||
@app.get('/media/ding.mp3')
|
||||
def read_root():
|
||||
return FileResponse('media/ding.mp3')
|
||||
|
||||
@app.get('/modifiers.json')
|
||||
def read_modifiers():
|
||||
return FileResponse('modifiers.json')
|
||||
|
Before Width: | Height: | Size: 18 KiB |
BIN
media/config-v3.jpg
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
media/config-v4.jpg
Normal file
After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 51 KiB |
BIN
media/shot-v3a.jpg
Normal file
After Width: | Height: | Size: 122 KiB |
Before Width: | Height: | Size: 182 KiB |
BIN
media/shot-v6a.jpg
Normal file
After Width: | Height: | Size: 67 KiB |
BIN
media/shot-v8.jpg
Normal file
After Width: | Height: | Size: 244 KiB |
92
modifiers.json
Normal file
@ -0,0 +1,92 @@
|
||||
[
|
||||
["Drawing Style", [
|
||||
"Sketch",
|
||||
"Doodle",
|
||||
"Children's Drawing",
|
||||
"Line Art",
|
||||
"Dot Art",
|
||||
"Crosshatch",
|
||||
"Detailed and Intricate",
|
||||
"Cel Shading"
|
||||
]],
|
||||
["Visual Style", [
|
||||
"2D",
|
||||
"Cartoon",
|
||||
"8-bit",
|
||||
"16-bit",
|
||||
"Graphic Novel",
|
||||
"Visual Novel",
|
||||
"Street Art",
|
||||
"Fantasy",
|
||||
"Realistic",
|
||||
"Photo",
|
||||
"Hard Edge Painting",
|
||||
"Mural",
|
||||
"Mosaic",
|
||||
"Hydrodipped",
|
||||
"Modern Art",
|
||||
"Concept Art",
|
||||
"Digital Art",
|
||||
"CGI",
|
||||
"Anaglyph",
|
||||
"Comic Book",
|
||||
"Lithography"
|
||||
]],
|
||||
["Pen", [
|
||||
"Graphite",
|
||||
"Colored Pencil",
|
||||
"Ink",
|
||||
"Chalk",
|
||||
"Pastel Art",
|
||||
"Oil Paint"
|
||||
]],
|
||||
["Carving and Etching", [
|
||||
"Etching",
|
||||
"Wood-Carving",
|
||||
"Papercutting",
|
||||
"Paper-Mache",
|
||||
"Paper Model",
|
||||
"Linocut",
|
||||
"Pyrography"
|
||||
]],
|
||||
["Camera", [
|
||||
"HD",
|
||||
"Color Grading",
|
||||
"Film Grain",
|
||||
"White Balance",
|
||||
"Golden Hour",
|
||||
"Glamor Shot",
|
||||
"War Photography",
|
||||
"Lens Flare",
|
||||
"Polaroid",
|
||||
"Vintage"
|
||||
]],
|
||||
["Color", [
|
||||
"Colorful",
|
||||
"Electric Colors",
|
||||
"Warm Color Palette",
|
||||
"Infrared",
|
||||
"Beautiful Lighting"
|
||||
]],
|
||||
["Emotions", [
|
||||
"Happy",
|
||||
"Excited",
|
||||
"Sad",
|
||||
"Lonely",
|
||||
"Angry",
|
||||
"Good",
|
||||
"Evil"
|
||||
]],
|
||||
["Style of an artist or community", [
|
||||
"by Andy Warhol",
|
||||
"Artstation",
|
||||
"by Asaf Hanuka",
|
||||
"by Aubrey Beardsley",
|
||||
"by H.R. Giger",
|
||||
"by Hayao Mizaki",
|
||||
"by Salvador Dali",
|
||||
"by Tivadar Csontváry Kosztka",
|
||||
"by Lisa Frank",
|
||||
"by Pablo Piccaso"
|
||||
]]
|
||||
]
|
36
old_port_main.py
Normal file
@ -0,0 +1,36 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.responses import HTMLResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@app.get('/', response_class=HTMLResponse)
|
||||
def read_root():
|
||||
return '''
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial;
|
||||
font-size: 11pt;
|
||||
}
|
||||
pre {
|
||||
display: inline;
|
||||
background: #aaa;
|
||||
padding: 2px;
|
||||
border: 1px solid #777;
|
||||
border-radius: 3px;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: rgb(32, 33, 36);
|
||||
color: #eee;
|
||||
}
|
||||
pre {
|
||||
background: #444;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<h4>The UI has moved to <a href="http://localhost:9000">http://localhost:9000</a>. The current address that you used (ending with :8000) will be removed in the future, so please use <a href="http://localhost:9000">http://localhost:9000</a> going ahead (and in any bookmarks you've saved).</h4>
|
||||
|
||||
<h3>Why has the address changed?</h3>
|
||||
<p>The previously used port (8000) is often used by other servers, which results in port conflicts. So the project's port number has been changed, while the project is still young. Otherwise port-conflicts with 8000 will be a common source of new-user issues in the future.</p>
|
||||
<p>Sorry about this, and apologies for the inconvenience :)</p>
|
||||
'''
|