Merge pull request #258 from cmdr2/main

Merge main
This commit is contained in:
cmdr2 2022-09-28 18:39:37 +05:30 committed by GitHub
commit 4f5b4f387a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 379 additions and 134 deletions

View File

@ -8,7 +8,7 @@
[![Discord Server](https://img.shields.io/discord/1014774730907209781?label=Discord)](https://discord.com/invite/u9yhsFmEkB) (for support, and development discussion) | [Troubleshooting guide for common problems](Troubleshooting.md)
️‍🔥🎉 **New!** Live Preview, More Samplers, In-Painting, Face Correction (GFPGAN) and Upscaling (RealESRGAN) have been added!
️‍🔥🎉 **New!** Task Queue, Negative Prompt, Live Preview, More Samplers, In-Painting, Face Correction (GFPGAN) and Upscaling (RealESRGAN) have been added!
This distribution currently uses Stable Diffusion 1.4. Once the model for 1.5 becomes publicly available, the model in this distribution will be updated.
@ -17,6 +17,8 @@ This distribution currently uses Stable Diffusion 1.4. Once the model for 1.5 be
- **Face Correction (GFPGAN) and Upscaling (RealESRGAN)**
- **In-Painting**
- **Live Preview**: See the image as the AI is drawing it
- **Task Queue**: Queue up all your ideas, without waiting for the current task to finish
- **Negative Prompt**: Specify aspects of the image to *remove*.
- **Lots of Samplers:** ddim, plms, heun, euler, euler_a, dpm2, dpm2_a, lms
- **Image Modifiers**: A library of *modifier tags* like *"Realistic"*, *"Pencil Sketch"*, *"ArtStation"* etc. Experiment with various styles quickly.
- **New UI**: with cleaner design
@ -40,7 +42,7 @@ This distribution currently uses Stable Diffusion 1.4. Once the model for 1.5 be
You do not need anything else. You do not need WSL, Docker or Conda. The installer will take care of it.
# Installation
1. **Download** [for Windows](https://github.com/cmdr2/stable-diffusion-ui/releases/download/v2.05/stable-diffusion-ui-win64.zip) or [for Linux](https://github.com/cmdr2/stable-diffusion-ui/releases/download/v2.05/stable-diffusion-ui-linux.tar.xz).
1. **Download** [for Windows](https://github.com/cmdr2/stable-diffusion-ui/releases/download/v2.16/stable-diffusion-ui-win64.zip) or [for Linux](https://github.com/cmdr2/stable-diffusion-ui/releases/download/v2.16/stable-diffusion-ui-linux.tar.xz).
2. **Extract**:
- For Windows: After unzipping the file, please move the `stable-diffusion-ui` folder to your `C:` (or any drive like D:, at the top root level), e.g. `C:\stable-diffusion-ui`. This will avoid a common problem with Windows (file path length limits).
@ -77,12 +79,17 @@ You can use Face Correction or Upscaling to improve the image further.
## Problems? Troubleshooting
Please try the common [troubleshooting](Troubleshooting.md) steps. If that doesn't fix it, please ask on the [discord server](https://discord.com/invite/u9yhsFmEkB), or [file an issue](https://github.com/cmdr2/stable-diffusion-ui/issues).
# 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'.
# Image 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 'Image 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.
![Screenshot of advanced settings](media/config-v6.jpg?raw=true)
# System Settings
The system settings are reachable via the cogwheel symbol on the top right. It can be used to configure whether all generated images should
saved be automically, or to tune the Stable Diffusion image generation.
![Screenshot of advanced settings](media/system-settings-v2.jpg?raw=true)
# Image Modifiers

View File

@ -9,6 +9,11 @@ Additionally, a common reason for this error is that you're using an initial ima
Also try generating smaller sized images.
## basicsr module not found
For Windows: Please download and extract basicsr from [here](https://github.com/cmdr2/stable-diffusion-ui/releases/download/v2.16/basicsr-win64.zip), and place the `basicsr` folder inside the `stable-diffusion-ui\stable-diffusion\env\lib\site-packages` folder. Then run the `Start Stable Diffusion UI.cmd` file again.
For Linux: Please contact on the [discord server](https://discord.com/invite/u9yhsFmEkB).
## No ldm found, or antlr4 or any other missing module, or ClobberError: This transaction has incompatible packages due to a shared path
On Windows, please ensure that you had placed the `stable-diffusion-ui` folder after unzipping to the root of C: or D: (or any drive). For e.g. `C:\stable-diffusion-ui`. **Note:** This has to be done **before** you start the installation process. If you have already installed (and are facing this error), please delete the installed folder, and start fresh by unzipping and placing the folder at the top of your drive.
@ -44,3 +49,27 @@ After this, you can access the server at `http://localhost:1337` (where 1337 is
Please ensure that you have an NVIDIA GPU and the latest [NVIDIA driver](http://www.nvidia.com/Download/index.aspx), and that you've installed [nvidia-container-toolkit](https://stackoverflow.com/a/58432877).
Also, if you are using WSL (Windows), please ensure you have the latest WSL kernel by running `wsl --shutdown` and then `wsl --update`. (Thanks [AndrWeisR](https://github.com/AndrWeisR))
# For support queries
## Entering a conda environment in an existing installation
This will give you an activated conda environment in the terminal, so you can run commands and force-install any packages, if required.
Users don't need to have the Anaconda Prompt installed to do this anymore, since the installer bundles a portable version of conda inside it. Just follow these steps.
**Windows:**
1. Open the terminal: Press Win+R, type "cmd", and press "Run"
2. Type `cd C:\stable-diffusion-ui` and press enter (or wherever you've installed it)
3. Type `installer\Scripts\activate.bat` and press enter
4. Type `cd stable-diffusion` and press enter
5. Type `conda activate .\env` and press enter
6. Type `python --version` and press enter. You should see 3.8.5.
**Windows:**
1. Open the terminal
2. Type `cd /path/to/stable-diffusion-ui` and press enter
3. Type `installer/bin/activate` and press enter
4. Type `cd stable-diffusion` and press enter
5. Type `conda activate ./env` and press enter
6. Type `python --version` and press enter. You should see 3.8.5.
This will give you an activated conda environment. To confirm, type `python --version` and press enter. You should see 3.8.5.

View File

@ -8,6 +8,8 @@
set /p answer=Are you a developer of this project (Y/N)?
if /i "%answer:~,1%" NEQ "Y" exit /b
@set PYTHONNOUSERSITE=1
@mkdir dist\stable-diffusion-ui
@echo "Downloading components for the installer.."
@ -15,11 +17,6 @@ if /i "%answer:~,1%" NEQ "Y" exit /b
@call conda env create --prefix installer -f environment.yaml
@call conda activate .\installer
@echo "Setting up startup scripts.."
@mkdir installer\etc\conda\activate.d
@copy scripts\post_activate.bat installer\etc\conda\activate.d\
@echo "Creating a distributable package.."
@call conda install -c conda-forge -y conda-pack
@ -37,6 +34,7 @@ if /i "%answer:~,1%" NEQ "Y" exit /b
@copy ..\..\LICENSE .
@copy "..\..\CreativeML Open RAIL-M License" .
@copy "..\..\How to install and run.txt" .
@echo. > scripts\install_status.txt
@echo "Build ready. Zip the 'dist\stable-diffusion-ui' folder."

View File

@ -11,6 +11,8 @@ case $yn in
* ) exit;;
esac
export PYTHONNOUSERSITE=1
mkdir -p dist/stable-diffusion-ui
echo "Downloading components for the installer.."
@ -38,6 +40,7 @@ cp ../../scripts/start.sh .
cp ../../LICENSE .
cp "../../CreativeML Open RAIL-M License" .
cp "../../How to install and run.txt" .
echo "" > scripts/install_status.txt
chmod u+x start.sh

View File

@ -1 +1,19 @@
installer\Scripts\activate.bat
@echo off
@REM Delete the post-activate hook from the old installer
if exist "installer\etc\conda\activate.d\post_activate.bat" (
echo. > installer\etc\conda\activate.d\post_activate.bat
)
@call installer\Scripts\activate.bat
@call conda-unpack
@call conda --version
@call git --version
@cd installer
@call ..\scripts\on_env_start.bat
@pause

View File

@ -165,13 +165,13 @@ call WHERE uvicorn > .tmp
@if exist "sd-v1-4.ckpt" (
for %%I in ("sd-v1-4.ckpt") do if "%%~zI" EQU "4265380512" (
echo "Data files (weights) necessary for Stable Diffusion were already downloaded"
echo "Data files (weights) necessary for Stable Diffusion were already downloaded. Using the HuggingFace 4 GB Model."
) else (
for %%J in ("sd-v1-4.ckpt") do if "%%~zJ" EQU "7703807346" (
echo "Data files (weights) necessary for Stable Diffusion were already downloaded"
echo "Data files (weights) necessary for Stable Diffusion were already downloaded. Using the HuggingFace 7 GB Model."
) else (
for %%K in ("sd-v1-4.ckpt") do if "%%~zK" EQU "7703810927" (
echo "Data files (weights) necessary for Stable Diffusion were already downloaded"
echo "Data files (weights) necessary for Stable Diffusion were already downloaded. Using the Waifu Model."
) else (
echo. & echo "The model file present at %cd%\sd-v1-4.ckpt is invalid. It is only %%~zK bytes in size. Re-downloading.." & echo.
del "sd-v1-4.ckpt"

View File

@ -4,4 +4,7 @@ source installer/bin/activate
conda-unpack
conda --version
git --version
scripts/on_env_start.sh

View File

@ -8,7 +8,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/png" href="/media/favicon-16x16.png" sizes="16x16">
<link rel="icon" type="image/png" href="/media/favicon-32x32.png" sizes="32x32">
<link rel="stylesheet" href="/media/main.css?v=3">
<link rel="stylesheet" href="/media/main.css?v=10">
<link rel="stylesheet" href="/media/modifier-thumbnails.css?v=1">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css">
<link rel="stylesheet" href="/media/drawingboard.min.css">
@ -19,7 +19,7 @@
<div id="container">
<div id="top-nav">
<div id="logo">
<h1>Stable Diffusion UI <small>v2.17 <span id="updateBranchLabel"></span></small></h1>
<h1>Stable Diffusion UI <small>v2.195 <span id="updateBranchLabel"></span></small></h1>
</div>
<ul id="top-nav-items">
<li class="dropdown">
@ -27,6 +27,7 @@
<ul id="community-links" class="dropdown-content">
<li><a href="https://github.com/cmdr2/stable-diffusion-ui/blob/main/Troubleshooting.md" target="_blank"><i class="fa-solid fa-circle-question fa-fw"></i> Usual problems and solutions</a></li>
<li><a href="https://discord.com/invite/u9yhsFmEkB" target="_blank"><i class="fa-brands fa-discord fa-fw"></i> Discord user community</a></li>
<li><a href="https://www.reddit.com/r/StableDiffusionUI/" target="_blank"><i class="fa-brands fa-reddit fa-fw"></i> Reddit community</a></li>
<li><a href="https://github.com/cmdr2/stable-diffusion-ui" target="_blank"><i class="fa-brands fa-github fa-fw"></i> Source code on GitHub</a></li>
</ul>
</li>
@ -81,7 +82,7 @@
</div>
<button id="makeImage">Make Image</button>
<button id="stopImage">Stop</button>
<button id="stopImage" class="secondaryButton">Stop All</button>
</div>
<div class="line-separator">&nbsp;</div>
@ -125,7 +126,8 @@
<option value="1536">1536</option>
<option value="1792">1792</option>
<option value="2048">2048</option>
</select> <label for="width"><small>(width)</small></label>
</select>
<label for="width"><small>(width)</small></label>
<select id="height" name="height" value="512">
<option value="128">128 (*)</option>
<option value="192">192</option>
@ -155,8 +157,13 @@
<br/>
<li><b class="settings-subheader">Prompt Settings</b></li>
<li class="pl-5"><label for="negative_prompt">Negative Prompt:</label> <input id="negative_prompt" name="negative_prompt" size="55"></li>
<br/>
<li><b class="settings-subheader">Render Settings</b></li>
<li class="pl-5"><input id="stream_image_progress" name="stream_image_progress" type="checkbox"> <label for="stream_image_progress">Show a live preview of the image <small>(consumes more VRAM, slightly slower image generation)</small></label></li>
<li class="pl-5"><input id="stream_image_progress" name="stream_image_progress" type="checkbox"> <label for="stream_image_progress">Show a live preview of the image <small>(uses more VRAM, slightly slower image creation)</small></label></li>
<li class="pl-5"><input id="use_face_correction" name="use_face_correction" type="checkbox" checked> <label for="use_face_correction">Fix incorrect faces and eyes <small>(uses GFPGAN)</small></label></li>
<li class="pl-5">
<input id="use_upscale" name="use_upscale" type="checkbox"> <label for="use_upscale">Upscale the image to 4x resolution using </label>
@ -174,7 +181,7 @@
<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">
<label for="preview-image">Image Style:</label>
<label for="preview-image">Image Style:</label>
<select id="preview-image" name="preview-image" value="portrait">
<option value="portrait" selected="">Face</option>
<option value="landscape">Landscape</option>
@ -187,15 +194,11 @@
</div>
<div id="preview" class="col-free">
<div id="preview-prompt">
<div id="initial-text">
Type a prompt and press the "Make Image" button.<br/><br/>You can set an "Initial Image" if you want to guide the AI.<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="initial-text">
Type a prompt and press the "Make Image" button.<br/><br/>You can set an "Initial Image" if you want to guide the AI.<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="progressBar"></div>
<div id="current-images" class="img-preview">
<div id="preview-tools">
<button id="clear-all-previews" class="secondaryButton"><i class="fa-solid fa-trash-can"></i> Clear All</button>
</div>
</div>
</div>
@ -214,7 +217,7 @@
</div>
</body>
<script src="media/main.js?v=4"></script>
<script src="media/main.js?v=14"></script>
<script>
async function init() {
await loadModifiers()

View File

@ -54,8 +54,9 @@ label {
.editor-slider {
vertical-align: middle;
}
#outputMsg {
.outputMsg {
font-size: small;
padding-bottom: 3pt;
}
#progressBar {
font-size: small;
@ -164,7 +165,7 @@ label {
display: none;
}
#stopImage:hover {
background: rgb(214, 32, 0);
background: rgb(177, 27, 0);
}
.flex-container {
display: flex;
@ -173,7 +174,7 @@ label {
flex: 50%;
}
.col-fixed-10 {
flex: 0 0 400pt;
flex: 0 0 380pt;
}
.col-free {
flex: 1;
@ -211,7 +212,7 @@ label {
padding-bottom: 10pt;
}
#preview {
margin-left: 20pt;
margin-left: 10pt;
}
img {
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.15), 0 6px 20px 0 rgba(0, 0, 0, 0.15);
@ -245,7 +246,7 @@ img {
padding-left: 2pt;
font-size: 10pt;
}
#preview-prompt {
.preview-prompt {
font-size: 16pt;
margin-bottom: 10pt;
}
@ -364,4 +365,48 @@ img {
}
.dropdown:hover .dropdown-content {
display: block;
}
.imageTaskContainer {
border: 1px solid #333;
margin-bottom: 10pt;
padding: 5pt;
border-radius: 5pt;
box-shadow: 0 20px 28px 0 rgba(0, 0, 0, 0.15), 0 6px 20px 0 rgba(0, 0, 0, 0.15);
}
.taskStatusLabel {
float: left;
font-size: 8pt;
background:rgb(44, 45, 48);
border: 1px solid rgb(61, 62, 66);
padding: 2pt 4pt;
border-radius: 2pt;
margin-right: 5pt;
}
.activeTaskLabel {
background:rgb(0, 90, 30);
border: 1px solid rgb(0, 75, 19);
color:rgb(204, 255, 217)
}
.secondaryButton {
background: rgb(132, 8, 0);
border: 1px solid rgb(122, 29, 0);
color: rgb(255, 221, 255);
padding: 3pt 6pt;
border-radius: 5px;
}
.secondaryButton:hover {
background: rgb(177, 27, 0);
}
.stopTask {
float: right;
}
#preview-tools {
display: none;
padding: 4pt;
}
.taskConfig {
font-size: 10pt;
color: #aaa;
margin-bottom: 5pt;
}

View File

@ -18,6 +18,7 @@ const IMAGE_REGEX = new RegExp('data:image/[A-Za-z]+;base64')
let sessionId = new Date().getTime()
let promptField = document.querySelector('#prompt')
let negativePromptField = document.querySelector('#negative_prompt')
let numOutputsTotalField = document.querySelector('#num_outputs_total')
let numOutputsParallelField = document.querySelector('#num_outputs_parallel')
let numInferenceStepsField = document.querySelector('#num_inference_steps')
@ -57,6 +58,10 @@ let initImagePreviewContainer = document.querySelector('#init_image_preview_cont
let initImageClearBtn = document.querySelector('.init_image_clear')
let promptStrengthContainer = document.querySelector('#prompt_strength_container')
let initialText = document.querySelector("#initial-text")
let previewTools = document.querySelector("#preview-tools")
let clearAllPreviewsBtn = document.querySelector("#clear-all-previews")
// let maskSetting = document.querySelector('#editor-inputs-mask_setting')
// let maskImagePreviewContainer = document.querySelector('#mask_preview_container')
// let maskImageClearBtn = document.querySelector('#mask_clear')
@ -66,18 +71,19 @@ let editorModifierEntries = document.querySelector('#editor-modifiers-entries')
let editorModifierTagsList = document.querySelector('#editor-inputs-tags-list')
let editorTagsContainer = document.querySelector('#editor-inputs-tags-container')
let imagePreview = document.querySelector("#preview")
let previewImageField = document.querySelector('#preview-image')
previewImageField.onchange = () => changePreviewImages(previewImageField.value);
let modifierCardSizeSlider = document.querySelector('#modifier-card-size-slider')
modifierCardSizeSlider.onchange = () => resizeModifierCards(modifierCardSizeSlider.value);
let previewPrompt = document.querySelector('#preview-prompt')
// let previewPrompt = document.querySelector('#preview-prompt')
let showConfigToggle = document.querySelector('#configToggleBtn')
// let configBox = document.querySelector('#config')
let outputMsg = document.querySelector('#outputMsg')
let progressBar = document.querySelector("#progressBar")
// let outputMsg = document.querySelector('#outputMsg')
// let progressBar = document.querySelector("#progressBar")
let soundToggle = document.querySelector('#sound_toggle')
@ -108,8 +114,10 @@ let serverStatus = 'offline'
let activeTags = []
let modifiers = []
let lastPromptUsed = ''
let taskStopped = true
let batchesDone = 0
let bellPending = false
let taskQueue = []
let currentTask = null
const modifierThumbnailPath = 'media/modifier-thumbnails';
const activeCardClass = 'modifier-card-active';
@ -211,7 +219,7 @@ function setStatus(statusType, msg, msgType) {
}
}
function logMsg(msg, level) {
function logMsg(msg, level, outputMsg) {
if (level === 'error') {
outputMsg.innerHTML = '<span style="color: red">Error: ' + msg + '</span>'
} else if (level === 'warn') {
@ -223,8 +231,8 @@ function logMsg(msg, level) {
console.log(level, msg)
}
function logError(msg, res) {
logMsg(msg, 'error')
function logError(msg, res, outputMsg) {
logMsg(msg, 'error', outputMsg)
console.log('request error', res)
setStatus('request', 'error', 'error')
@ -251,7 +259,7 @@ async function healthCheck() {
}
}
function makeImageElement(width, height) {
function makeImageElement(width, height, outputContainer) {
let imgItem = document.createElement('div')
imgItem.className = 'imgItem'
@ -260,17 +268,25 @@ function makeImageElement(width, height) {
img.height = parseInt(height)
imgItem.appendChild(img)
imagesContainer.appendChild(imgItem)
outputContainer.insertBefore(imgItem, outputContainer.firstChild)
return imgItem
}
// makes a single image. don't call this directly, use makeImage() instead
async function doMakeImage(reqBody, batchCount) {
if (taskStopped) {
async function doMakeImage(task) {
if (task.stopped) {
return
}
const reqBody = task.reqBody
const batchCount = task.batchCount
const outputContainer = task.outputContainer
const outputMsg = task['outputMsg']
const previewPrompt = task['previewPrompt']
const progressBar = task['progressBar']
let res = ''
let seed = reqBody['seed']
let numOutputs = parseInt(reqBody['num_outputs'])
@ -279,7 +295,7 @@ async function doMakeImage(reqBody, batchCount) {
function makeImageContainers(numImages) {
for (let i = images.length; i < numImages; i++) {
images.push(makeImageElement(reqBody.width, reqBody.height))
images.push(makeImageElement(reqBody.width, reqBody.height, outputContainer))
}
}
@ -316,7 +332,7 @@ async function doMakeImage(reqBody, batchCount) {
finalJSON += jsonStr
} else {
let batchSize = stepUpdate.total_steps
let overallStepCount = stepUpdate.step + batchesDone * batchSize
let overallStepCount = stepUpdate.step + task.batchesDone * batchSize
let totalSteps = batchCount * batchSize
let percent = 100 * (overallStepCount / totalSteps)
percent = (percent > 100 ? 100 : percent)
@ -326,13 +342,13 @@ async function doMakeImage(reqBody, batchCount) {
stepsRemaining = (stepsRemaining < 0 ? 0 : stepsRemaining)
timeRemaining = (timeTaken === -1 ? '' : stepsRemaining * timeTaken) // ms
outputMsg.innerHTML = `Batch ${batchesDone+1} of ${batchCount}`
progressBar.innerHTML = `Generating image(s): ${percent}%`
outputMsg.innerHTML = `Batch ${task.batchesDone+1} of ${batchCount}`
outputMsg.innerHTML += `. Generating image(s): ${percent}%`
if (timeTaken !== -1) {
progressBar.innerHTML += `<br>Time remaining (approx): ${millisecondsToStr(timeRemaining)}`
}
progressBar.style.display = 'block'
timeRemaining = (timeTaken !== -1 ? millisecondsToStr(timeRemaining) : '')
outputMsg.innerHTML += `. Time remaining (approx): ${timeRemaining}`
outputMsg.style.display = 'block'
if (stepUpdate.output !== undefined) {
makeImageContainers(numOutputs)
@ -351,7 +367,7 @@ async function doMakeImage(reqBody, batchCount) {
prevTime = t
} catch (e) {
logError('Stable Diffusion had an error. Please check the logs in the command-line window.', res)
logError('Stable Diffusion had an error. Please check the logs in the command-line window.', res, outputMsg)
res = undefined
throw e
}
@ -359,9 +375,9 @@ async function doMakeImage(reqBody, batchCount) {
if (res.status != 200) {
if (serverStatus === 'online') {
logError('Stable Diffusion had an error: ' + await res.text(), res)
logError('Stable Diffusion had an error: ' + await res.text(), res, outputMsg)
} else {
logError("Stable Diffusion is still starting up, please wait. If this goes on beyond a few minutes, Stable Diffusion has probably crashed. Please check the error message in the command-line window.", res)
logError("Stable Diffusion is still starting up, please wait. If this goes on beyond a few minutes, Stable Diffusion has probably crashed. Please check the error message in the command-line window.", res, outputMsg)
}
res = undefined
progressBar.style.display = 'none'
@ -381,7 +397,6 @@ async function doMakeImage(reqBody, batchCount) {
}
res = JSON.parse(finalJSON)
progressBar.style.display = 'none'
if (res.status !== 'succeeded') {
let msg = ''
@ -399,13 +414,13 @@ async function doMakeImage(reqBody, batchCount) {
} else {
msg = res
}
logError(msg, res)
logError(msg, res, outputMsg)
res = undefined
}
}
} catch (e) {
console.log('request error', e)
logError('Stable Diffusion had an error. Please check the logs in the command-line window. <br/><br/>' + e + '<br/><pre>' + e.stack + '</pre>', res)
logError('Stable Diffusion had an error. Please check the logs in the command-line window. <br/><br/>' + e + '<br/><pre>' + e.stack + '</pre>', res, outputMsg)
setStatus('request', 'error', 'error')
progressBar.style.display = 'none'
res = undefined
@ -440,6 +455,7 @@ async function doMakeImage(reqBody, batchCount) {
let imgItemInfo = document.createElement('span')
imgItemInfo.className = 'imgItemInfo'
imgItemInfo.style.opacity = 0
let imgSeedLabel = document.createElement('span')
imgSeedLabel.className = 'imgSeedLabel'
@ -486,55 +502,103 @@ async function doMakeImage(reqBody, batchCount) {
})
imgItem.addEventListener('mouseleave', function() {
imgItemInfo.style.opacity = 0.5
imgItemInfo.style.opacity = 0
})
}
return true
}
function validateInput() {
let width = parseInt(widthField.value)
let height = parseInt(heightField.value)
async function checkTasks() {
if (taskQueue.length === 0) {
setStatus('request', 'done', 'success')
setTimeout(checkTasks, 500)
stopImageBtn.style.display = 'none'
makeImageBtn.innerHTML = 'Make Image'
if (IMAGE_REGEX.test(initImagePreview.src)) {
if (initImagePreview.naturalWidth > MAX_INIT_IMAGE_DIMENSION || initImagePreview.naturalHeight > MAX_INIT_IMAGE_DIMENSION) {
return {'isValid': false, 'warning': `The dimensions of your initial image are very large, and can cause 'Out of Memory' errors! Please ensure that its dimensions are equal (or smaller) than the desired output image.
<br/><br/>
Your initial image size is ${initImagePreview.naturalWidth}x${initImagePreview.naturalHeight} pixels. Please try to keep it smaller than ${MAX_INIT_IMAGE_DIMENSION}x${MAX_INIT_IMAGE_DIMENSION}.`}
currentTask = null
if (bellPending) {
if (isSoundEnabled()) {
playSound()
}
bellPending = false
}
}
return {'isValid': true}
}
async function makeImage() {
if (serverStatus !== 'online') {
logError('The server is still starting up..')
return
}
let validation = validateInput()
if (validation['isValid']) {
outputMsg.innerHTML = 'Starting..'
} else {
if (validation['error']) {
logError(validation['error'])
return
} else if (validation['warning']) {
logMsg(validation['warning'], 'warn')
}
}
setStatus('request', 'fetching..')
makeImageBtn.innerHTML = 'Processing..'
makeImageBtn.disabled = true
makeImageBtn.style.display = 'none'
stopImageBtn.style.display = 'block'
makeImageBtn.innerHTML = 'Enqueue Next Image'
bellPending = true
taskStopped = false
batchesDone = 0
previewTools.style.display = 'block'
let task = taskQueue.pop()
currentTask = task
let time = new Date().getTime()
let successCount = 0
task.isProcessing = true
task['stopTask'].innerHTML = '<i class="fa-solid fa-circle-stop"></i> Stop'
task['taskStatusLabel'].innerText = "Processing"
task['taskStatusLabel'].className += " activeTaskLabel"
console.log(task['taskStatusLabel'].className)
for (let i = 0; i < task.batchCount; i++) {
task.reqBody['seed'] = task.seed + (i * task.reqBody['num_outputs'])
let success = await doMakeImage(task)
task.batchesDone++
if (!task.isProcessing) {
break
}
if (success) {
successCount++
}
}
task.isProcessing = false
task['stopTask'].innerHTML = '<i class="fa-solid fa-trash-can"></i> Remove'
task['taskStatusLabel'].style.display = 'none'
time = new Date().getTime() - time
time /= 1000
if (successCount === task.batchCount) {
task.outputMsg.innerText = 'Processed ' + task.numOutputsTotal + ' images in ' + time + ' seconds'
// setStatus('request', 'done', 'success')
} else {
task.outputMsg.innerText = 'Task ended after ' + time + ' seconds'
}
if (randomSeedField.checked) {
seedField.value = task.seed
}
currentTask = null
setTimeout(checkTasks, 10)
}
setTimeout(checkTasks, 0)
async function makeImage() {
if (serverStatus !== 'online') {
alert('The server is still starting up..')
return
}
let task = {
stopped: false,
batchesDone: 0
}
let seed = (randomSeedField.checked ? Math.floor(Math.random() * 10000000) : parseInt(seedField.value))
let numOutputsTotal = parseInt(numOutputsTotalField.value)
@ -550,11 +614,10 @@ async function makeImage() {
prompt += ", " + promptTags;
}
previewPrompt.innerText = prompt
let reqBody = {
session_id: sessionId,
prompt: prompt,
negative_prompt: negativePromptField.value.trim(),
num_outputs: batchSize,
num_inference_steps: numInferenceStepsField.value,
guidance_scale: guidanceScaleField.value,
@ -597,44 +660,76 @@ async function makeImage() {
reqBody['use_upscale'] = upscaleModelField.value
}
let time = new Date().getTime()
imagesContainer.innerHTML = ''
let taskConfig = `Seed: ${seed}, Sampler: ${reqBody['sampler']}, Inference Steps: ${numInferenceStepsField.value}, Guidance Scale: ${guidanceScaleField.value}`
let successCount = 0
if (negativePromptField.value.trim() !== '') {
taskConfig += `, Negative Prompt: ${negativePromptField.value.trim()}`
}
for (let i = 0; i < batchCount; i++) {
reqBody['seed'] = seed + (i * batchSize)
if (reqBody['init_image'] !== undefined) {
taskConfig += `, Prompt Strength: ${promptStrengthField.value}`
}
let success = await doMakeImage(reqBody, batchCount)
batchesDone++
if (useFaceCorrectionField.checked) {
taskConfig += `, Fix Faces: ${reqBody['use_face_correction']}`
}
if (success) {
outputMsg.innerText = 'Processed batch ' + (i+1) + '/' + batchCount
successCount++
if (useUpscalingField.checked) {
taskConfig += `, Upscale: ${reqBody['use_upscale']}`
}
task['reqBody'] = reqBody
task['seed'] = seed
task['batchCount'] = batchCount
task['isProcessing'] = false
let taskEntry = document.createElement('div')
taskEntry.className = 'imageTaskContainer'
taskEntry.innerHTML = ` <div class="taskStatusLabel">Enqueued</div>
<button class="secondaryButton stopTask"><i class="fa-solid fa-trash-can"></i> Remove</button>
<div class="preview-prompt collapsible active"></div>
<div class="taskConfig">${taskConfig}</div>
<div class="collapsible-content" style="display: block">
<div class="outputMsg"></div>
<div class="progressBar"></div>
<div class="img-preview">
</div>`
createCollapsibles(taskEntry)
task['numOutputsTotal'] = numOutputsTotal
task['taskStatusLabel'] = taskEntry.querySelector('.taskStatusLabel')
task['outputContainer'] = taskEntry.querySelector('.img-preview')
task['outputMsg'] = taskEntry.querySelector('.outputMsg')
task['previewPrompt'] = taskEntry.querySelector('.preview-prompt')
task['progressBar'] = taskEntry.querySelector('.progressBar')
task['stopTask'] = taskEntry.querySelector('.stopTask')
task['stopTask'].addEventListener('click', async function() {
if (task['isProcessing']) {
task.isProcessing = false
try {
let res = await fetch('/image/stop')
} catch (e) {
console.log(e)
}
} else {
let idx = taskQueue.indexOf(task)
if (idx >= 0) {
taskQueue.splice(idx, 1)
}
taskEntry.remove()
}
}
})
makeImageBtn.innerText = 'Make Image'
makeImageBtn.disabled = false
makeImageBtn.style.display = 'block'
stopImageBtn.style.display = 'none'
imagePreview.insertBefore(taskEntry, previewTools.nextSibling)
if (isSoundEnabled()) {
playSound()
}
task['previewPrompt'].innerText = prompt
time = new Date().getTime() - time
time /= 1000
taskQueue.unshift(task)
if (successCount === batchCount) {
outputMsg.innerText = 'Processed ' + numOutputsTotal + ' images in ' + time + ' seconds'
setStatus('request', 'done', 'success')
}
if (randomSeedField.checked) {
seedField.value = seed
}
initialText.style.display = 'none'
}
// create a file name with embedded prompt and metadata
@ -673,17 +768,37 @@ function createFileName() {
return fileName
}
stopImageBtn.addEventListener('click', async function() {
async function stopAllTasks() {
taskQueue.forEach(task => {
task.isProcessing = false
})
taskQueue = []
if (currentTask !== null) {
currentTask.isProcessing = false
}
try {
let res = await fetch('/image/stop')
} catch (e) {
console.log(e)
}
}
stopImageBtn.style.display = 'none'
makeImageBtn.style.display = 'block'
clearAllPreviewsBtn.addEventListener('click', async function() {
await stopAllTasks()
taskStopped = true
let taskEntries = document.querySelectorAll('.imageTaskContainer')
taskEntries.forEach(task => {
task.remove()
})
previewTools.style.display = 'none'
initialText.style.display = 'block'
})
stopImageBtn.addEventListener('click', async function() {
await stopAllTasks()
})
soundToggle.addEventListener('click', handleBoolSettingChange(SOUND_ENABLED_KEY))
@ -780,7 +895,7 @@ updatePromptStrength()
useBetaChannelField.addEventListener('click', async function(e) {
if (serverStatus !== 'online') {
logError('The server is still starting up..')
// logError('The server is still starting up..')
alert('The server is still starting up..')
e.preventDefault()
return false
@ -941,6 +1056,22 @@ function millisecondsToStr(milliseconds) {
return s;
}
// https://gomakethings.com/finding-the-next-and-previous-sibling-elements-that-match-a-selector-with-vanilla-js/
function getNextSibling(elem, selector) {
// Get the next sibling element
var sibling = elem.nextElementSibling
// If there's no selector, return the first sibling
if (!selector) return sibling
// If the sibling matches our selector, use it
// If not, jump to the next sibling and continue the loop
while (sibling) {
if (sibling.matches(selector)) return sibling
sibling = sibling.nextElementSibling
}
}
function createCollapsibles(node) {
if (!node) {
node = document
@ -960,7 +1091,7 @@ function createCollapsibles(node) {
c.addEventListener('click', function() {
this.classList.toggle("active")
let content = this.nextElementSibling
let content = getNextSibling(this, '.collapsible-content')
if (content.style.display === "block") {
content.style.display = "none"
handle.innerHTML = '&#x2795;' // plus

View File

@ -3,6 +3,7 @@ import json
class Request:
session_id: str = "session"
prompt: str = ""
negative_prompt: str = ""
init_image: str = None # base64
mask: str = None # base64
num_outputs: int = 1
@ -30,6 +31,7 @@ class Request:
return {
"session_id": self.session_id,
"prompt": self.prompt,
"negative_prompt": self.negative_prompt,
"num_outputs": self.num_outputs,
"num_inference_steps": self.num_inference_steps,
"guidance_scale": self.guidance_scale,
@ -46,6 +48,7 @@ class Request:
return f'''
session_id: {self.session_id}
prompt: {self.prompt}
negative_prompt: {self.negative_prompt}
seed: {self.seed}
num_inference_steps: {self.num_inference_steps}
sampler: {self.sampler}

View File

@ -343,7 +343,7 @@ def do_mk_img(req: Request):
modelCS.to(device)
uc = None
if opt_scale != 1.0:
uc = modelCS.get_learned_conditioning(batch_size * [""])
uc = modelCS.get_learned_conditioning(batch_size * [req.negative_prompt])
if isinstance(prompts, tuple):
prompts = list(prompts)
@ -450,7 +450,7 @@ def do_mk_img(req: Request):
if return_orig_img:
save_image(img, img_out_path)
save_metadata(meta_out_path, prompts, opt_seed, opt_W, opt_H, opt_ddim_steps, opt_scale, opt_strength, opt_use_face_correction, opt_use_upscale, opt_sampler_name)
save_metadata(meta_out_path, prompts, opt_seed, opt_W, opt_H, opt_ddim_steps, opt_scale, opt_strength, opt_use_face_correction, opt_use_upscale, opt_sampler_name, req.negative_prompt)
if return_orig_img:
img_data = img_to_base64_str(img)
@ -511,8 +511,8 @@ def save_image(img, img_out_path):
except:
print('could not save the file', traceback.format_exc())
def save_metadata(meta_out_path, prompts, opt_seed, opt_W, opt_H, opt_ddim_steps, opt_scale, opt_prompt_strength, opt_correct_face, opt_upscale, sampler_name):
metadata = f"{prompts[0]}\nWidth: {opt_W}\nHeight: {opt_H}\nSeed: {opt_seed}\nSteps: {opt_ddim_steps}\nGuidance Scale: {opt_scale}\nPrompt Strength: {opt_prompt_strength}\nUse Face Correction: {opt_correct_face}\nUse Upscaling: {opt_upscale}\nSampler: {sampler_name}"
def save_metadata(meta_out_path, prompts, opt_seed, opt_W, opt_H, opt_ddim_steps, opt_scale, opt_prompt_strength, opt_correct_face, opt_upscale, sampler_name, negative_prompt):
metadata = f"{prompts[0]}\nWidth: {opt_W}\nHeight: {opt_H}\nSeed: {opt_seed}\nSteps: {opt_ddim_steps}\nGuidance Scale: {opt_scale}\nPrompt Strength: {opt_prompt_strength}\nUse Face Correction: {opt_correct_face}\nUse Upscaling: {opt_upscale}\nSampler: {sampler_name}\nNegative Prompt: {negative_prompt}"
try:
with open(meta_out_path, 'w') as f:

View File

@ -55,6 +55,7 @@ app.mount('/media', StaticFiles(directory=os.path.join(SD_UI_DIR, 'frontend/asse
class ImageRequest(BaseModel):
session_id: str = "session"
prompt: str = ""
negative_prompt: str = ""
init_image: str = None # base64
mask: str = None # base64
num_outputs: int = 1
@ -110,7 +111,10 @@ async def ping():
model_is_loading = True
from sd_internal import runtime
runtime.load_model_ckpt(ckpt_to_use="sd-v1-4")
custom_weight_path = os.path.join(SCRIPT_DIR, 'custom-model.ckpt')
ckpt_to_use = "sd-v1-4" if not os.path.exists(custom_weight_path) else "custom-model"
runtime.load_model_ckpt(ckpt_to_use=ckpt_to_use)
model_loaded = True
model_is_loading = False
@ -127,6 +131,7 @@ def image(req : ImageRequest):
r = Request()
r.session_id = req.session_id
r.prompt = req.prompt
r.negative_prompt = req.negative_prompt
r.init_image = req.init_image
r.mask = req.mask
r.num_outputs = req.num_outputs