This commit is contained in:
anapnoe
2023-04-30 00:17:36 +03:00
10 changed files with 166 additions and 131 deletions
+58
View File
@@ -0,0 +1,58 @@
## Upcoming version:
### Features:
* switch to torch 2.0.0 (except for AMD GPUs)
* visual improvements to custom code scripts
* add filename patterns: [clip_skip], [hasprompt<>], [batch_number], [generation_number]
* add support for saving init images in img2img, and record their hashes in infotext for reproducability
* automatically select current word when adjusting weight with ctrl+up/down
* add dropdowns for X/Y/Z plot
* setting: Stable Diffusion/Random number generator source: makes it possible to make images generated from a given manual seed consistent across different GPUs
* support Gradio's theme API
* use TCMalloc on Linux by default; possible fix for memory leaks
* (optimization) option to remove negative conditioning at low sigma values #9177
* embed model merge metadata in .safetensors file
* extension settings backup/restore feature #9169
* add "resize by" and "resize to" tabs to img2img
* add option "keep original size" to textual inversion images preprocess
* image viewer scrolling via analog stick
* button to restore the progress from session lost / tab reload
### Minor:
* gradio bumped to 3.28.1
* in extra tab, change extras "scale to" to sliders
* add labels to tool buttons to make it possible to hide them
* add tiled inference support for ScuNET
* add branch support for extension installation
* change linux installation script to insall into current directory rather than /home/username
* sort textual inversion embeddings by name (case insensitive)
* allow styles.csv to be symlinked or mounted in docker
* remove the "do not add watermark to images" option
* make selected tab configurable with UI config
* extra networks UI in now fixed height and scrollable
* add disable_tls_verify arg for use with self-signed certs
### Extensions:
* Add reload callback
* add is_hr_pass field for processing
### Bug Fixes:
* fix broken batch image processing on 'Extras/Batch Process' tab
* add "None" option to extra networks dropdowns
* fix FileExistsError for CLIP Interrogator
* fix /sdapi/v1/txt2img endpoint not working on Linux #9319
* fix disappearing live previews and progressbar during slow tasks
* fix fullscreen image view not working properly in some cases
* prevent alwayson_scripts args param resizing script_arg list when they are inserted in it
* fix prompt schedule for second order samplers
* fix image mask/composite for weird resolutions #9628
* use correct images for previews when using AND (see #9491)
* one broken image in img2img batch won't stop all processing
* fix image orientation bug in train/preprocess
* fix Ngrok recreating tunnels every reload
* fix --realesrgan-models-path and --ldsr-models-path not working
* fix --skip-install not working
* outpainting Mk2 & Poorman should use the SAMPLE file format to save images, not GRID file format
* do not fail all Loras if some have failed to load when making a picture
## Before versions:
* everything
+2
View File
@@ -22,10 +22,12 @@ titles = {
"\u{1f4cb}": "Apply selected styles to current prompt",
"\u{1f4d2}": "Paste available values into the field",
"\u{1f3b4}": "Show/hide extra networks",
"\u{1f5e8}": "Interogate Clip",
"\u{1f5ea}": "Interogate Deepbooru",
"\u{1F300}": "Restore progress",
"Inpaint a part of image": "Draw a mask over an image, and the script will regenerate the masked area with content according to prompt",
"SD upscale": "Upscale image normally, split result into tiles, improve each tile using img2img, merge whole image back",
+2 -2
View File
@@ -66,7 +66,7 @@ function randomId(){
// starts sending progress requests to "/internal/progress" uri, creating progressbar above progressbarContainer element and
// preview inside gallery element. Cleans up all created stuff when the task is over and calls atEnd.
// calls onProgress every time there is a progress update
function requestProgress(id_task, progressbarContainer, gallery, atEnd, onProgress){
function requestProgress(id_task, progressbarContainer, gallery, atEnd, onProgress, inactivityTimeout=40){
var dateStart = new Date()
var wasEverActive = false
var parentProgressbar = progressbarContainer.parentNode
@@ -155,7 +155,7 @@ function requestProgress(id_task, progressbarContainer, gallery, atEnd, onProgre
return
}
if(elapsedFromStart > 40 && !res.queued && !res.active){
if(elapsedFromStart > inactivityTimeout && !res.queued && !res.active){
removeProgressBar()
return
}
+47 -26
View File
@@ -159,13 +159,24 @@ function showSubmitButtons(tabname, show){
gradioApp().getElementById(tabname+'_skip').style.display = show ? "none" : "block"
}
function showRestoreProgressButton(tabname, show){
button = gradioApp().getElementById(tabname + "_restore_progress")
if(! button) return
button.style.display = show ? "flex" : "none"
}
function submit(){
rememberGallerySelection('txt2img_gallery')
showSubmitButtons('txt2img', false)
var id = randomId()
localStorage.setItem("txt2img_task_id", id);
requestProgress(id, gradioApp().getElementById('txt2img_gallery_container'), gradioApp().getElementById('txt2img_gallery'), function(){
showSubmitButtons('txt2img', true)
localStorage.removeItem("txt2img_task_id")
showRestoreProgressButton('txt2img', false)
})
var res = create_submit_args(arguments)
@@ -180,8 +191,12 @@ function submit_img2img(){
showSubmitButtons('img2img', false)
var id = randomId()
localStorage.setItem("img2img_task_id", id);
requestProgress(id, gradioApp().getElementById('img2img_gallery_container'), gradioApp().getElementById('img2img_gallery'), function(){
showSubmitButtons('img2img', true)
localStorage.removeItem("img2img_task_id")
showRestoreProgressButton('img2img', false)
})
var res = create_submit_args(arguments)
@@ -192,6 +207,36 @@ function submit_img2img(){
return res
}
function restoreProgressTxt2img(x){
id = localStorage.getItem("txt2img_task_id")
if(id) {
requestProgress(id, gradioApp().getElementById('txt2img_gallery_container'), gradioApp().getElementById('txt2img_gallery'), function(){
showSubmitButtons('txt2img', true)
}, null, 0)
}
return [id]
}
function restoreProgressImg2img(x){
id = localStorage.getItem("img2img_task_id")
if(id) {
requestProgress(id, gradioApp().getElementById('img2img_gallery_container'), gradioApp().getElementById('img2img_gallery'), function(){
showSubmitButtons('img2img', true)
}, null, 0)
}
return [id]
}
onUiLoaded(function () {
showRestoreProgressButton('txt2img', localStorage.getItem("txt2img_task_id"))
showRestoreProgressButton('img2img', localStorage.getItem("img2img_task_id"))
});
function modelmerger(){
var id = randomId()
requestProgress(id, gradioApp().getElementById('modelmerger_results_panel'), null, function(){})
@@ -1546,32 +1591,6 @@ function selectCheckpoint(name){
gradioApp().getElementById('change_checkpoint').click()
}
function restoreProgress (task_tag) {
if (task_tag) {
let successHandler = ({ current_task }) => {
if (current_task) {
showSubmitButtons(task_tag, false)
requestProgress(current_task, gradioApp().getElementById(`${task_tag}_gallery_container`), gradioApp().getElementById(`${task_tag}_gallery`), function(){
showSubmitButtons(task_tag, true)
})
}
}
let errorHandler = e => window.alert(`invalid internal api respsonse. message: ${e}`)
fetch("./internal/current_task")
.then(res => res.json())
.then(successHandler)
.catch(errorHandler)
}
var res = create_submit_args(arguments)
res[0] = 0
return res
}
function currentImg2imgSourceResolution(_, _, scaleBy){
var img = gradioApp().querySelector('#mode_img2img > div[style="display: block;"] img')
return img ? [img.naturalWidth, img.naturalHeight, scaleBy] : [0, 0, scaleBy]
@@ -1603,3 +1622,5 @@ window.onload = function() {
setTimeout(function(){document.body.style.display = "block";},1000)
}
+1 -1
View File
@@ -222,7 +222,7 @@ def run_extensions_installers(settings_file):
def prepare_environment():
torch_command = os.environ.get('TORCH_COMMAND', "pip install torch==2.0.0 torchvision==0.15.1 --index-url https://download.pytorch.org/whl/cu118")
torch_command = os.environ.get('TORCH_COMMAND', "pip install torch==2.0.0 torchvision==0.15.1 --extra-index-url https://download.pytorch.org/whl/cu118")
requirements_file = os.environ.get('REQS_FILE', "requirements_versions.txt")
xformers_package = os.environ.get('XFORMERS_PACKAGE', 'xformers==0.0.17')
+6 -13
View File
@@ -4,16 +4,10 @@ import threading
import traceback
import time
import gradio as gr
from modules import shared, progress
queue_lock = threading.Lock()
queue_lock_condition = threading.Condition(lock=queue_lock)
def wrap_session_call(func):
def f(request: gr.Request, *args, **kwargs):
return func(request, *args, **kwargs)
return f
def wrap_queued_call(func):
def f(*args, **kwargs):
@@ -26,31 +20,30 @@ def wrap_queued_call(func):
def wrap_gradio_gpu_call(func, extra_outputs=None):
def f(request: gr.Request, *args, **kwargs):
user = request.username
def f(*args, **kwargs):
# if the first argument is a string that says "task(...)", it is treated as a job id
if len(args) > 0 and type(args[0]) == str and args[0][0:5] == "task(" and args[0][-1] == ")":
id_task = args[0]
progress.add_task_to_queue(user, id_task)
progress.add_task_to_queue(id_task)
else:
id_task = None
with queue_lock:
shared.state.begin()
progress.start_task(user, id_task)
progress.start_task(id_task)
try:
res = func(*args, **kwargs)
progress.record_results(id_task, res)
finally:
progress.finish_task(user, id_task)
progress.set_last_task_result(user, id_task, res)
progress.finish_task(id_task)
shared.state.end()
return res
return wrap_session_call(wrap_gradio_call(f, extra_outputs=extra_outputs, add_stats=True))
return wrap_gradio_call(f, extra_outputs=extra_outputs, add_stats=True)
def wrap_gradio_call(func, extra_outputs=None, add_stats=False):
+24 -66
View File
@@ -4,84 +4,46 @@ import time
import gradio as gr
from pydantic import BaseModel, Field
from typing import Optional
from fastapi import Depends, Security
from fastapi.security import APIKeyCookie
from modules import call_queue
from modules.shared import opts
import modules.shared as shared
current_task_user = None
current_task = None
pending_tasks = {}
finished_tasks = []
recorded_results = []
recorded_results_limit = 2
def start_task(user, id_task):
def start_task(id_task):
global current_task
global current_task_user
current_task_user = user
current_task = id_task
pending_tasks.pop((user, id_task), None)
pending_tasks.pop(id_task, None)
def finish_task(user, id_task):
def finish_task(id_task):
global current_task
global current_task_user
if current_task == id_task:
current_task = None
if current_task_user == user:
current_task_user = None
finished_tasks.append((user, id_task))
finished_tasks.append(id_task)
if len(finished_tasks) > 16:
finished_tasks.pop(0)
def add_task_to_queue(user, id_job):
pending_tasks[(user, id_job)] = time.time()
last_task_id = None
last_task_result = None
last_task_user = None
def set_last_task_result(user, id_job, result):
global last_task_id
global last_task_result
global last_task_user
last_task_id = id_job
last_task_result = result
last_task_user = user
def record_results(id_task, res):
recorded_results.append((id_task, res))
if len(recorded_results) > recorded_results_limit:
recorded_results.pop(0)
def restore_progress_call(request: gr.Request):
if current_task is None:
def add_task_to_queue(id_job):
pending_tasks[id_job] = time.time()
# image, generation_info, html_info, html_log
return tuple(list([None, None, None, None]))
else:
user = request.username
if current_task_user == user:
t_task = current_task
with call_queue.queue_lock_condition:
call_queue.queue_lock_condition.wait_for(lambda: t_task == last_task_id)
return last_task_result
return tuple(list([None, None, None, None]))
class CurrentTaskResponse(BaseModel):
current_task: str = Field(default=None, title="Task ID", description="id of the current progress task")
class ProgressRequest(BaseModel):
id_task: str = Field(default=None, title="Task ID", description="id of the task to get progress for")
@@ -102,21 +64,6 @@ class ProgressResponse(BaseModel):
def setup_progress_api(app):
return app.add_api_route("/internal/progress", progressapi, methods=["POST"], response_model=ProgressResponse)
def setup_current_task_api(app):
def get_current_user(token: Optional[str] = Security(APIKeyCookie(name="access-token", auto_error=False))):
return None if token is None else app.tokens.get(token)
def current_task_api(current_user: str = Depends(get_current_user)):
if app.auth is None or current_task_user == current_user:
current_user_task = current_task
else:
current_user_task = None
return CurrentTaskResponse(current_task=current_user_task)
return app.add_api_route("/internal/current_task", current_task_api, methods=["GET"], response_model=CurrentTaskResponse)
def progressapi(req: ProgressRequest):
active = req.id_task == current_task
@@ -156,4 +103,15 @@ def progressapi(req: ProgressRequest):
else:
live_preview = None
return ProgressResponse(active=active, queued=queued, completed=completed, progress=progress, eta=eta, live_preview=live_preview, id_live_preview=id_live_preview, textinfo=shared.state.textinfo)
return ProgressResponse(active=active, queued=queued, completed=completed, progress=progress, eta=eta, live_preview=live_preview, id_live_preview=id_live_preview, textinfo=shared.state.textinfo)
def restore_progress(id_task):
while id_task == current_task or id_task in pending_tasks:
time.sleep(0.1)
res = next(iter([x[1] for x in recorded_results if id_task == x[0]]), None)
if res is not None:
return res
return gr.update(), gr.update(), gr.update(), f"Couldn't restore progress for {id_task}: results either have been discarded or never were obtained"
+25 -22
View File
@@ -19,7 +19,7 @@ import numpy as np
from PIL import Image, PngImagePlugin
from modules.call_queue import wrap_gradio_gpu_call, wrap_queued_call, wrap_gradio_call
from modules import sd_hijack, sd_models, localization, script_callbacks, ui_extensions, deepbooru, sd_vae, extra_networks, postprocessing, ui_components, ui_common, ui_postprocessing
from modules import sd_hijack, sd_models, localization, script_callbacks, ui_extensions, deepbooru, sd_vae, extra_networks, postprocessing, ui_components, ui_common, ui_postprocessing, progress
from modules.ui_components import FormRow, FormColumn, FormGroup, ToolButton, FormHTML
from modules.paths import script_path, data_path
@@ -41,7 +41,6 @@ from modules.textual_inversion import textual_inversion
import modules.hypernetworks.ui
from modules.generation_parameters_copypaste import image_from_url_text
import modules.extras
from modules.progress import restore_progress_call
warnings.filterwarnings("default" if opts.show_warnings else "ignore", category=UserWarning)
@@ -86,6 +85,7 @@ extra_networks_symbol = '\U0001F3B4' # 🎴
switch_values_symbol = '\u2B80' # ⮀
restore_progress_symbol = '\U0001F300' # 🌀
interogate_bubble_symbol = '\U0001F5E8' # 🗨
interogate_2bubble_symbol = '\U0001F5EA' # 🗪
def plaintext_to_html(text):
@@ -356,7 +356,8 @@ def create_toprow(is_img2img):
if is_img2img:
button_interrogate = ToolButton(value=interogate_bubble_symbol, elem_id="interrogate")
button_deepbooru = ToolButton(value=interogate_2bubble_symbol, elem_id="deepbooru")
restore_progress_button = ToolButton(value=restore_progress_symbol, elem_id=f"{id_part}_restore_progress")
restore_progress_button = ToolButton(value=restore_progress_symbol, elem_id=f"{id_part}_restore_progress", visible=False)
token_button = gr.Button(visible=False, elem_id=f"{id_part}_token_button")
negative_token_button = gr.Button(visible=False, elem_id=f"{id_part}_negative_token_button")
@@ -637,15 +638,16 @@ def create_ui():
res_switch_btn.click(lambda w, h: (h, w), inputs=[width, height], outputs=[width, height], show_progress=False)
restore_progress_button.click(
fn=restore_progress_call,
_js="() => restoreProgress('txt2img')",
inputs=[],
outputs=[
txt2img_gallery,
generation_info,
html_info,
html_log,
]
fn=progress.restore_progress,
_js="restoreProgressTxt2img",
inputs=[dummy_component],
outputs=[
txt2img_gallery,
generation_info,
html_info,
html_log,
],
show_progress=False,
)
txt_prompt_img.change(
@@ -1052,15 +1054,16 @@ def create_ui():
res_switch_btn.click(lambda w, h: (h, w), inputs=[width, height], outputs=[width, height], show_progress=False)
restore_progress_button.click(
fn=restore_progress_call,
_js="() => restoreProgress('img2img')",
inputs=[],
outputs=[
img2img_gallery,
generation_info,
html_info,
html_log,
]
fn=progress.restore_progress,
_js="restoreProgressImg2img",
inputs=[dummy_component],
outputs=[
img2img_gallery,
generation_info,
html_info,
html_log,
],
show_progress=False,
)
img2img_interrogate.click(
@@ -1700,7 +1703,7 @@ def create_ui():
gr.HTML(shared.html("licenses.html"), elem_id="licenses")
gr.Button(value="Show all pages", elem_id="settings_show_all_pages")
def unload_sd_weights():
modules.sd_models.unload_model_weights()
+1
View File
@@ -3867,6 +3867,7 @@ div[data-testid="image"] canvas {
[id^="spotlight"].menu .spl-prev {
transform: translateX(0);
}
[id^="spotlight"].menu .spl-next {
transform: translateX(0) scaleX(-1);
}
-1
View File
@@ -339,7 +339,6 @@ def webui():
setup_middleware(app)
modules.progress.setup_progress_api(app)
modules.progress.setup_current_task_api(app)
if launch_api:
create_api(app)