From 4241fb9386c13afa561cbb427fff70a66ea6a511 Mon Sep 17 00:00:00 2001 From: rbertus2000 <91765399+rbertus2000@users.noreply.github.com> Date: Tue, 18 Oct 2022 22:38:37 +0200 Subject: [PATCH 01/17] fixed img_id for parallel renders --- ui/sd_internal/runtime.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/ui/sd_internal/runtime.py b/ui/sd_internal/runtime.py index efad434d..a7d28b81 100644 --- a/ui/sd_internal/runtime.py +++ b/ui/sd_internal/runtime.py @@ -197,7 +197,7 @@ def load_model_real_esrgan(real_esrgan_to_use): print('loaded ', real_esrgan_to_use, 'to', device, 'precision', precision) -def get_base_path(disk_path, session_id, prompt, img_id, ext, suffix=None): +def get_base_path(disk_path, session_id, prompt, ext, suffix=None): if disk_path is None: return None if session_id is None: return None if ext is None: raise Exception('Missing ext') @@ -206,6 +206,8 @@ def get_base_path(disk_path, session_id, prompt, img_id, ext, suffix=None): os.makedirs(session_out_path, exist_ok=True) prompt_flattened = filename_regex.sub('_', prompt)[:50] + img_id = base64.b64encode(int(time.time()).to_bytes(8, 'big')).decode() # Generate unique ID based on time. + img_id = img_id.translate({43:None, 47:None, 61:None})[-8:] # Remove + / = and keep last 8 chars. if suffix is not None: return os.path.join(session_out_path, f"{prompt_flattened}_{img_id}_{suffix}.{ext}") @@ -316,8 +318,7 @@ def do_mk_img(req: Request): opt_f = 8 opt_ddim_eta = 0.0 opt_init_img = req.init_image - img_id = base64.b64encode(int(time.time()).to_bytes(8, 'big')).decode() # Generate unique ID based on time. - img_id = img_id.translate({43:None, 47:None, 61:None})[-8:] # Remove + / = and keep last 8 chars. + print(req.to_string(), '\n device', device) @@ -479,9 +480,9 @@ def do_mk_img(req: Request): if req.save_to_disk_path is not None: if return_orig_img: - img_out_path = get_base_path(req.save_to_disk_path, req.session_id, prompts[0], img_id, req.output_format) + img_out_path = get_base_path(req.save_to_disk_path, req.session_id, prompts[0], req.output_format) save_image(img, img_out_path) - meta_out_path = get_base_path(req.save_to_disk_path, req.session_id, prompts[0], img_id, 'txt') + meta_out_path = get_base_path(req.save_to_disk_path, req.session_id, prompts[0], 'txt') save_metadata(meta_out_path, req, prompts[0], opt_seed) if return_orig_img: @@ -508,7 +509,7 @@ def do_mk_img(req: Request): response_image = ResponseImage(data=filtered_img_data, seed=req.seed) res.images.append(response_image) if req.save_to_disk_path is not None: - filtered_img_out_path = get_base_path(req.save_to_disk_path, req.session_id, prompts[0], img_id, req.output_format, "_".join(filters_applied)) + filtered_img_out_path = get_base_path(req.save_to_disk_path, req.session_id, prompts[0], req.output_format, "_".join(filters_applied)) save_image(filtered_image, filtered_img_out_path) response_image.path_abs = filtered_img_out_path del filtered_image From d2d9c2dd0f5dcd2919339350ae573b683920fc00 Mon Sep 17 00:00:00 2001 From: rbertus2000 <91765399+rbertus2000@users.noreply.github.com> Date: Wed, 19 Oct 2022 01:17:44 +0200 Subject: [PATCH 02/17] fixed corresponding txt file id --- ui/sd_internal/runtime.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/ui/sd_internal/runtime.py b/ui/sd_internal/runtime.py index a7d28b81..9deb31d2 100644 --- a/ui/sd_internal/runtime.py +++ b/ui/sd_internal/runtime.py @@ -197,7 +197,7 @@ def load_model_real_esrgan(real_esrgan_to_use): print('loaded ', real_esrgan_to_use, 'to', device, 'precision', precision) -def get_base_path(disk_path, session_id, prompt, ext, suffix=None): +def get_base_path(disk_path, session_id, prompt, img_id, ext, suffix=None): if disk_path is None: return None if session_id is None: return None if ext is None: raise Exception('Missing ext') @@ -206,8 +206,7 @@ def get_base_path(disk_path, session_id, prompt, ext, suffix=None): os.makedirs(session_out_path, exist_ok=True) prompt_flattened = filename_regex.sub('_', prompt)[:50] - img_id = base64.b64encode(int(time.time()).to_bytes(8, 'big')).decode() # Generate unique ID based on time. - img_id = img_id.translate({43:None, 47:None, 61:None})[-8:] # Remove + / = and keep last 8 chars. + if suffix is not None: return os.path.join(session_out_path, f"{prompt_flattened}_{img_id}_{suffix}.{ext}") @@ -463,6 +462,8 @@ def do_mk_img(req: Request): print("saving images") for i in range(batch_size): + img_id = base64.b64encode(int(time.time()+i).to_bytes(8, 'big')).decode() # Generate unique ID based on time. + img_id = img_id.translate({43:None, 47:None, 61:None})[-8:] # Remove + / = and keep last 8 chars. x_samples_ddim = modelFS.decode_first_stage(x_samples[i].unsqueeze(0)) x_sample = torch.clamp((x_samples_ddim + 1.0) / 2.0, min=0.0, max=1.0) @@ -480,9 +481,9 @@ def do_mk_img(req: Request): if req.save_to_disk_path is not None: if return_orig_img: - img_out_path = get_base_path(req.save_to_disk_path, req.session_id, prompts[0], req.output_format) + img_out_path = get_base_path(req.save_to_disk_path, req.session_id, prompts[0], img_id, req.output_format) save_image(img, img_out_path) - meta_out_path = get_base_path(req.save_to_disk_path, req.session_id, prompts[0], 'txt') + meta_out_path = get_base_path(req.save_to_disk_path, req.session_id, prompts[0], img_id, 'txt') save_metadata(meta_out_path, req, prompts[0], opt_seed) if return_orig_img: @@ -509,7 +510,7 @@ def do_mk_img(req: Request): response_image = ResponseImage(data=filtered_img_data, seed=req.seed) res.images.append(response_image) if req.save_to_disk_path is not None: - filtered_img_out_path = get_base_path(req.save_to_disk_path, req.session_id, prompts[0], req.output_format, "_".join(filters_applied)) + filtered_img_out_path = get_base_path(req.save_to_disk_path, req.session_id, prompts[0], img_id, req.output_format, "_".join(filters_applied)) save_image(filtered_image, filtered_img_out_path) response_image.path_abs = filtered_img_out_path del filtered_image From 105f07184716b3ad6599e2401a938c873141ea20 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 19 Oct 2022 13:50:05 +0530 Subject: [PATCH 03/17] Expand curly braces in prompts, for e.g. 'hello {foo,bar}' => 'hello foo' and 'hello bar' --- ui/index.html | 2 +- ui/media/js/main.js | 37 ++++++--- ui/media/js/utils.js | 173 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 201 insertions(+), 11 deletions(-) diff --git a/ui/index.html b/ui/index.html index 7a5abee7..b779616d 100644 --- a/ui/index.html +++ b/ui/index.html @@ -17,7 +17,7 @@
- + - + diff --git a/ui/media/js/main.js b/ui/media/js/main.js index 78969875..e0f77636 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -376,6 +376,10 @@ function showImages(reqBody, res, outputContainer, livePreview) { if (!req.use_upscale) { buttons.upscaleBtn = { text: 'Upscale', on_click: getStartNewTaskHandler('upscale') } } + + // include the plugins + Object.assign(buttons, PLUGINS['IMAGE_INFO_BUTTONS']) + const imgItemInfo = imageItemElem.querySelector('.imgItemInfo') const createButton = function(name, btnInfo) { const newButton = document.createElement('button') @@ -383,7 +387,8 @@ function showImages(reqBody, res, outputContainer, livePreview) { newButton.classList.add('tasksBtns') newButton.innerText = btnInfo.text newButton.addEventListener('click', function() { - btnInfo.on_click(req, imageItemElem) + let img = imageItemElem.querySelector('img') + btnInfo.on_click(req, img) }) imgItemInfo.appendChild(newButton) } @@ -392,9 +397,8 @@ function showImages(reqBody, res, outputContainer, livePreview) { }) } -function onUseAsInputClick(req, imageItemElem) { - const imageElem = imageItemElem.querySelector('img') - const imgData = imageElem.src +function onUseAsInputClick(req, img) { + const imgData = img.src initImageSelector.value = null initImagePreview.src = imgData @@ -406,13 +410,12 @@ function onUseAsInputClick(req, imageItemElem) { samplerSelectionContainer.style.display = 'none' } -function onDownloadImageClick(req, imageItemElem) { - const imageElem = imageItemElem.querySelector('img') - const imgData = imageElem.src - const imageSeed = imageElem.getAttribute('data-seed') - const imagePrompt = imageElem.getAttribute('data-prompt') - const imageInferenceSteps = imageElem.getAttribute('data-steps') - const imageGuidanceScale = imageElem.getAttribute('data-guidance') +function onDownloadImageClick(req, img) { + const imgData = img.src + const imageSeed = img.getAttribute('data-seed') + const imagePrompt = img.getAttribute('data-prompt') + const imageInferenceSteps = img.getAttribute('data-steps') + const imageGuidanceScale = img.getAttribute('data-guidance') const imgDownload = document.createElement('a') imgDownload.download = createFileName(imagePrompt, imageSeed, imageInferenceSteps, imageGuidanceScale, req['output_format']) @@ -421,12 +424,11 @@ function onDownloadImageClick(req, imageItemElem) { } function getStartNewTaskHandler(mode) { - return function(reqBody, imageItemElem) { + return function(reqBody, img) { if (!isServerAvailable()) { alert('The server is not available.') return } - const imageElem = imageItemElem.querySelector('img') const newTaskRequest = getCurrentUserRequest() switch (mode) { case 'img2img': @@ -438,7 +440,7 @@ function getStartNewTaskHandler(mode) { if (!newTaskRequest.reqBody.init_image || mode === 'img2img_X2') { newTaskRequest.reqBody.sampler = 'ddim' newTaskRequest.reqBody.prompt_strength = '0.5' - newTaskRequest.reqBody.init_image = imageElem.src + newTaskRequest.reqBody.init_image = img.src delete newTaskRequest.reqBody.mask } else { newTaskRequest.reqBody.seed = 1 + newTaskRequest.reqBody.seed @@ -471,10 +473,7 @@ function getStartNewTaskHandler(mode) { } } -function onMakeSimilarClick(req, imageItemElem) { - const similarImagesCount = 5 - const imageElem = imageItemElem.querySelector('img') - +function onMakeSimilarClick(req, img) { let newTaskRequest = getCurrentUserRequest() newTaskRequest.reqBody = Object.assign({}, req, { @@ -483,7 +482,7 @@ function onMakeSimilarClick(req, imageItemElem) { num_inference_steps: 50, guidance_scale: 7.5, prompt_strength: 0.7, - init_image: imageElem.src, + init_image: img.src, seed: Math.floor(Math.random() * 10000000) }) diff --git a/ui/media/js/plugins.js b/ui/media/js/plugins.js new file mode 100644 index 00000000..91d16c3c --- /dev/null +++ b/ui/media/js/plugins.js @@ -0,0 +1,23 @@ +const PLUGIN_API_VERSION = "1.0" + +const PLUGINS = { + /** + * Register new buttons to show on each output image. + * + * Example: + * PLUGINS['IMAGE_INFO_BUTTONS']['myCustomVariationButton'] = { + * text: 'Make a Similar Image', + * on_click: function(origRequest, image) { + * let newTaskRequest = getCurrentUserRequest() + * newTaskRequest.reqBody = Object.assign({}, origRequest, { + * init_image: image.src, + * prompt_strength: 0.7, + * seed: Math.floor(Math.random() * 10000000) + * }) + * newTaskRequest.seed = newTaskRequest.reqBody.seed + * createTask(newTaskRequest) + * } + * } + */ + IMAGE_INFO_BUTTONS: {} +} From af05d9419833e198760140d66cf1ee2a8bd50b7a Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 19 Oct 2022 20:10:45 +0530 Subject: [PATCH 09/17] Allow plugin buttons for image overlay to decide whether they should be displayed or not --- ui/media/js/main.js | 11 +++++++++-- ui/media/js/plugins.js | 5 +++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/ui/media/js/main.js b/ui/media/js/main.js index e0f77636..95b6ade1 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -381,18 +381,25 @@ function showImages(reqBody, res, outputContainer, livePreview) { Object.assign(buttons, PLUGINS['IMAGE_INFO_BUTTONS']) const imgItemInfo = imageItemElem.querySelector('.imgItemInfo') + const img = imageItemElem.querySelector('img') const createButton = function(name, btnInfo) { const newButton = document.createElement('button') newButton.classList.add(name) newButton.classList.add('tasksBtns') newButton.innerText = btnInfo.text newButton.addEventListener('click', function() { - let img = imageItemElem.querySelector('img') btnInfo.on_click(req, img) }) imgItemInfo.appendChild(newButton) } - Object.keys(buttons).forEach((name) => createButton(name, buttons[name])) + Object.keys(buttons).forEach((name) => { + const btn = buttons[name] + if (btn.filter && btn.filter(req, img) === false) { + return + } + + createButton(name, btn) + }) } }) } diff --git a/ui/media/js/plugins.js b/ui/media/js/plugins.js index 91d16c3c..51c30ecf 100644 --- a/ui/media/js/plugins.js +++ b/ui/media/js/plugins.js @@ -16,6 +16,11 @@ const PLUGINS = { * }) * newTaskRequest.seed = newTaskRequest.reqBody.seed * createTask(newTaskRequest) + * }, + * filter: function(origRequest, image) { + * // this is an optional function. return true/false to show/hide the button + * // if this function isn't set, the button will always be visible + * return true * } * } */ From 602686a5d27c4c6b71b4dd38d87a4f27d84aace5 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 19 Oct 2022 20:27:06 +0530 Subject: [PATCH 10/17] Move the current implementation of upscale/redo/double size into a custom plugin --- ui/media/js/main.js | 5 ----- ui/media/js/plugins.js | 51 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/ui/media/js/main.js b/ui/media/js/main.js index 95b6ade1..c266dd7b 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -369,13 +369,8 @@ function showImages(reqBody, res, outputContainer, livePreview) { const buttons = { 'imgUseBtn': { text: 'Use as Input', on_click: onUseAsInputClick }, 'imgSaveBtn': { text: 'Download', on_click: onDownloadImageClick }, - 'imgX2Btn': { text: 'Double Size', on_click: getStartNewTaskHandler('img2img_X2') }, - 'imgRedoBtn': { text: 'Redo', on_click: getStartNewTaskHandler('img2img') }, 'makeSimilarBtn': { text: 'Make Similar Images', on_click: onMakeSimilarClick }, } - if (!req.use_upscale) { - buttons.upscaleBtn = { text: 'Upscale', on_click: getStartNewTaskHandler('upscale') } - } // include the plugins Object.assign(buttons, PLUGINS['IMAGE_INFO_BUTTONS']) diff --git a/ui/media/js/plugins.js b/ui/media/js/plugins.js index 51c30ecf..92ca296c 100644 --- a/ui/media/js/plugins.js +++ b/ui/media/js/plugins.js @@ -26,3 +26,54 @@ const PLUGINS = { */ IMAGE_INFO_BUTTONS: {} } + + +PLUGINS['IMAGE_INFO_BUTTONS']['custom_imgX2Btn'] = { text: 'Double Size', on_click: getStartNewTaskHandler('img2img_X2') } +PLUGINS['IMAGE_INFO_BUTTONS']['custom_imgRedoBtn'] = { text: 'Redo', on_click: getStartNewTaskHandler('img2img') } +PLUGINS['IMAGE_INFO_BUTTONS']['custom_upscaleBtn'] = { text: 'Upscale', on_click: getStartNewTaskHandler('upscale'), filter: (req, img) => !req.use_upscale } + +function getStartNewTaskHandler(mode) { + return function(reqBody, img) { + const newTaskRequest = getCurrentUserRequest() + switch (mode) { + case 'img2img': + case 'img2img_X2': + newTaskRequest.reqBody = Object.assign({}, reqBody, { + num_outputs: 1, + use_cpu: useCPUField.checked, + }) + if (!newTaskRequest.reqBody.init_image || mode === 'img2img_X2') { + newTaskRequest.reqBody.sampler = 'ddim' + newTaskRequest.reqBody.prompt_strength = '0.5' + newTaskRequest.reqBody.init_image = img.src + delete newTaskRequest.reqBody.mask + } else { + newTaskRequest.reqBody.seed = 1 + newTaskRequest.reqBody.seed + } + if (mode === 'img2img_X2') { + newTaskRequest.reqBody.width = reqBody.width * 2 + newTaskRequest.reqBody.height = reqBody.height * 2 + newTaskRequest.reqBody.num_inference_steps = Math.min(100, reqBody.num_inference_steps * 2) + if (useUpscalingField.checked) { + newTaskRequest.reqBody.use_upscale = upscaleModelField.value + } else { + delete newTaskRequest.reqBody.use_upscale + } + } + break + case 'upscale': + newTaskRequest.reqBody = Object.assign({}, reqBody, { + num_outputs: 1, + //use_face_correction: 'GFPGANv1.3', + use_upscale: upscaleModelField.value, + }) + break + default: + throw new Error("Unknown upscale mode: " + mode) + } + newTaskRequest.seed = newTaskRequest.reqBody.seed + newTaskRequest.numOutputsTotal = 1 + newTaskRequest.batchCount = 1 + createTask(newTaskRequest) + } +} From bae0bec1cc9c64a603887479572715814900005c Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 19 Oct 2022 21:21:19 +0530 Subject: [PATCH 11/17] Change the image buttons plugins to a list instead of a dict --- ui/media/js/main.js | 20 +++++++++----------- ui/media/js/plugins.js | 12 ++++++------ 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/ui/media/js/main.js b/ui/media/js/main.js index c266dd7b..609e3cbe 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -366,20 +366,19 @@ function showImages(reqBody, res, outputContainer, livePreview) { const imageSeedLabel = imageItemElem.querySelector('.imgSeedLabel') imageSeedLabel.innerText = 'Seed: ' + req.seed - const buttons = { - 'imgUseBtn': { text: 'Use as Input', on_click: onUseAsInputClick }, - 'imgSaveBtn': { text: 'Download', on_click: onDownloadImageClick }, - 'makeSimilarBtn': { text: 'Make Similar Images', on_click: onMakeSimilarClick }, - } + let buttons = [ + { text: 'Use as Input', on_click: onUseAsInputClick }, + { text: 'Download', on_click: onDownloadImageClick }, + { text: 'Make Similar Images', on_click: onMakeSimilarClick }, + ] // include the plugins - Object.assign(buttons, PLUGINS['IMAGE_INFO_BUTTONS']) + buttons = buttons.concat(PLUGINS['IMAGE_INFO_BUTTONS']) const imgItemInfo = imageItemElem.querySelector('.imgItemInfo') const img = imageItemElem.querySelector('img') - const createButton = function(name, btnInfo) { + const createButton = function(btnInfo) { const newButton = document.createElement('button') - newButton.classList.add(name) newButton.classList.add('tasksBtns') newButton.innerText = btnInfo.text newButton.addEventListener('click', function() { @@ -387,13 +386,12 @@ function showImages(reqBody, res, outputContainer, livePreview) { }) imgItemInfo.appendChild(newButton) } - Object.keys(buttons).forEach((name) => { - const btn = buttons[name] + buttons.forEach(btn => { if (btn.filter && btn.filter(req, img) === false) { return } - createButton(name, btn) + createButton(btn) }) } }) diff --git a/ui/media/js/plugins.js b/ui/media/js/plugins.js index 92ca296c..bbbfe333 100644 --- a/ui/media/js/plugins.js +++ b/ui/media/js/plugins.js @@ -5,7 +5,7 @@ const PLUGINS = { * Register new buttons to show on each output image. * * Example: - * PLUGINS['IMAGE_INFO_BUTTONS']['myCustomVariationButton'] = { + * PLUGINS['IMAGE_INFO_BUTTONS'].push({ * text: 'Make a Similar Image', * on_click: function(origRequest, image) { * let newTaskRequest = getCurrentUserRequest() @@ -22,15 +22,15 @@ const PLUGINS = { * // if this function isn't set, the button will always be visible * return true * } - * } + * }) */ - IMAGE_INFO_BUTTONS: {} + IMAGE_INFO_BUTTONS: [] } -PLUGINS['IMAGE_INFO_BUTTONS']['custom_imgX2Btn'] = { text: 'Double Size', on_click: getStartNewTaskHandler('img2img_X2') } -PLUGINS['IMAGE_INFO_BUTTONS']['custom_imgRedoBtn'] = { text: 'Redo', on_click: getStartNewTaskHandler('img2img') } -PLUGINS['IMAGE_INFO_BUTTONS']['custom_upscaleBtn'] = { text: 'Upscale', on_click: getStartNewTaskHandler('upscale'), filter: (req, img) => !req.use_upscale } +PLUGINS['IMAGE_INFO_BUTTONS'].push({ text: 'Double Size', on_click: getStartNewTaskHandler('img2img_X2') }) +PLUGINS['IMAGE_INFO_BUTTONS'].push({ text: 'Redo', on_click: getStartNewTaskHandler('img2img') }) +PLUGINS['IMAGE_INFO_BUTTONS'].push({ text: 'Upscale', on_click: getStartNewTaskHandler('upscale'), filter: (req, img) => !req.use_upscale }) function getStartNewTaskHandler(mode) { return function(reqBody, img) { From e287df1320b14079af2171b6093dbc365257a135 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 19 Oct 2022 21:34:40 +0530 Subject: [PATCH 12/17] Allow loading UI plugins from a /plugins/ URL path, which loads files ending with .plugin.js inside the plugins/ui folder --- ui/index.html | 1 + ui/media/js/main.js | 52 +------------------------------------ ui/media/js/plugins.js | 58 ++++++++++-------------------------------- ui/server.py | 17 ++++++++++++- 4 files changed, 31 insertions(+), 97 deletions(-) diff --git a/ui/index.html b/ui/index.html index 07a1876c..0fcf28ca 100644 --- a/ui/index.html +++ b/ui/index.html @@ -266,6 +266,7 @@ async function init() { await getAppConfig() await getModels() await initSettings() + await loadUIPlugins() setInterval(healthCheck, HEALTH_PING_INTERVAL * 1000) healthCheck() diff --git a/ui/media/js/main.js b/ui/media/js/main.js index 609e3cbe..cb76fb35 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -369,7 +369,7 @@ function showImages(reqBody, res, outputContainer, livePreview) { let buttons = [ { text: 'Use as Input', on_click: onUseAsInputClick }, { text: 'Download', on_click: onDownloadImageClick }, - { text: 'Make Similar Images', on_click: onMakeSimilarClick }, + { text: 'Make Similar Images', on_click: onMakeSimilarClick } ] // include the plugins @@ -423,56 +423,6 @@ function onDownloadImageClick(req, img) { imgDownload.click() } -function getStartNewTaskHandler(mode) { - return function(reqBody, img) { - if (!isServerAvailable()) { - alert('The server is not available.') - return - } - const newTaskRequest = getCurrentUserRequest() - switch (mode) { - case 'img2img': - case 'img2img_X2': - newTaskRequest.reqBody = Object.assign({}, reqBody, { - num_outputs: 1, - use_cpu: useCPUField.checked, - }) - if (!newTaskRequest.reqBody.init_image || mode === 'img2img_X2') { - newTaskRequest.reqBody.sampler = 'ddim' - newTaskRequest.reqBody.prompt_strength = '0.5' - newTaskRequest.reqBody.init_image = img.src - delete newTaskRequest.reqBody.mask - } else { - newTaskRequest.reqBody.seed = 1 + newTaskRequest.reqBody.seed - } - if (mode === 'img2img_X2') { - newTaskRequest.reqBody.width = reqBody.width * 2 - newTaskRequest.reqBody.height = reqBody.height * 2 - newTaskRequest.reqBody.num_inference_steps = Math.min(100, reqBody.num_inference_steps * 2) - if (useUpscalingField.checked) { - newTaskRequest.reqBody.use_upscale = upscaleModelField.value - } else { - delete newTaskRequest.reqBody.use_upscale - } - } - break - case 'upscale': - newTaskRequest.reqBody = Object.assign({}, reqBody, { - num_outputs: 1, - //use_face_correction: 'GFPGANv1.3', - use_upscale: upscaleModelField.value, - }) - break - default: - throw new Error("Unknown upscale mode: " + mode) - } - newTaskRequest.seed = newTaskRequest.reqBody.seed - newTaskRequest.numOutputsTotal = 1 - newTaskRequest.batchCount = 1 - createTask(newTaskRequest) - } -} - function onMakeSimilarClick(req, img) { let newTaskRequest = getCurrentUserRequest() diff --git a/ui/media/js/plugins.js b/ui/media/js/plugins.js index bbbfe333..dce65e09 100644 --- a/ui/media/js/plugins.js +++ b/ui/media/js/plugins.js @@ -27,53 +27,21 @@ const PLUGINS = { IMAGE_INFO_BUTTONS: [] } +async function loadUIPlugins() { + try { + let res = await fetch('/get/ui_plugins') + if (res.status === 200) { + res = await res.json() + res.forEach(pluginPath => { + let script = document.createElement('script') + script.src = pluginPath -PLUGINS['IMAGE_INFO_BUTTONS'].push({ text: 'Double Size', on_click: getStartNewTaskHandler('img2img_X2') }) -PLUGINS['IMAGE_INFO_BUTTONS'].push({ text: 'Redo', on_click: getStartNewTaskHandler('img2img') }) -PLUGINS['IMAGE_INFO_BUTTONS'].push({ text: 'Upscale', on_click: getStartNewTaskHandler('upscale'), filter: (req, img) => !req.use_upscale }) + console.log('loading plugin', pluginPath) -function getStartNewTaskHandler(mode) { - return function(reqBody, img) { - const newTaskRequest = getCurrentUserRequest() - switch (mode) { - case 'img2img': - case 'img2img_X2': - newTaskRequest.reqBody = Object.assign({}, reqBody, { - num_outputs: 1, - use_cpu: useCPUField.checked, - }) - if (!newTaskRequest.reqBody.init_image || mode === 'img2img_X2') { - newTaskRequest.reqBody.sampler = 'ddim' - newTaskRequest.reqBody.prompt_strength = '0.5' - newTaskRequest.reqBody.init_image = img.src - delete newTaskRequest.reqBody.mask - } else { - newTaskRequest.reqBody.seed = 1 + newTaskRequest.reqBody.seed - } - if (mode === 'img2img_X2') { - newTaskRequest.reqBody.width = reqBody.width * 2 - newTaskRequest.reqBody.height = reqBody.height * 2 - newTaskRequest.reqBody.num_inference_steps = Math.min(100, reqBody.num_inference_steps * 2) - if (useUpscalingField.checked) { - newTaskRequest.reqBody.use_upscale = upscaleModelField.value - } else { - delete newTaskRequest.reqBody.use_upscale - } - } - break - case 'upscale': - newTaskRequest.reqBody = Object.assign({}, reqBody, { - num_outputs: 1, - //use_face_correction: 'GFPGANv1.3', - use_upscale: upscaleModelField.value, - }) - break - default: - throw new Error("Unknown upscale mode: " + mode) + document.head.appendChild(script) + }) } - newTaskRequest.seed = newTaskRequest.reqBody.seed - newTaskRequest.numOutputsTotal = 1 - newTaskRequest.batchCount = 1 - createTask(newTaskRequest) + } catch (e) { + console.log('error fetching plugin paths', e) } } diff --git a/ui/server.py b/ui/server.py index 48b364f1..61bc7f4d 100644 --- a/ui/server.py +++ b/ui/server.py @@ -12,6 +12,7 @@ sys.path.append(os.path.dirname(SD_UI_DIR)) CONFIG_DIR = os.path.abspath(os.path.join(SD_UI_DIR, '..', 'scripts')) MODELS_DIR = os.path.abspath(os.path.join(SD_DIR, '..', 'models')) +UI_PLUGINS_DIR = os.path.abspath(os.path.join(SD_DIR, '..', 'plugins', 'ui')) OUTPUT_DIRNAME = "Stable Diffusion UI" # in the user's home folder TASK_TTL = 15 * 60 # Discard last session's task timeout @@ -31,11 +32,15 @@ app = FastAPI() modifiers_cache = None outpath = os.path.join(os.path.expanduser("~"), OUTPUT_DIRNAME) +os.makedirs(UI_PLUGINS_DIR, exist_ok=True) + # don't show access log entries for URLs that start with the given prefix ACCESS_LOG_SUPPRESS_PATH_PREFIXES = ['/ping', '/image', '/modifier-thumbnails'] NOCACHE_HEADERS={"Cache-Control": "no-cache, no-store, must-revalidate", "Pragma": "no-cache", "Expires": "0"} -app.mount('/media', StaticFiles(directory=os.path.join(SD_UI_DIR, 'media/')), name="media") + +app.mount('/media', StaticFiles(directory=os.path.join(SD_UI_DIR, 'media')), name="media") +app.mount('/plugins', StaticFiles(directory=UI_PLUGINS_DIR), name="plugins") class SetAppConfigRequest(BaseModel): update_branch: str = "main" @@ -251,6 +256,15 @@ def getModels(): return models +def getUIPlugins(): + plugins = [] + + for file in os.listdir(UI_PLUGINS_DIR): + if file.endswith('.plugin.js'): + plugins.append(f'/plugins/{file}') + + return plugins + @app.get('/get/{key:path}') def read_web_data(key:str=None): if not key: # /get without parameters, stable-diffusion easter egg. @@ -264,6 +278,7 @@ def read_web_data(key:str=None): return JSONResponse(getModels(), headers=NOCACHE_HEADERS) elif key == 'modifiers': return FileResponse(os.path.join(SD_UI_DIR, 'modifiers.json'), headers=NOCACHE_HEADERS) elif key == 'output_dir': return JSONResponse({ 'output_dir': outpath }, headers=NOCACHE_HEADERS) + elif key == 'ui_plugins': return JSONResponse(getUIPlugins(), headers=NOCACHE_HEADERS) else: raise HTTPException(status_code=404, detail=f'Request for unknown {key}') # HTTP404 Not Found From 253d355bd2a7c8cfc2ff5ecfa8804721267dbce9 Mon Sep 17 00:00:00 2001 From: cmdr2 Date: Wed, 19 Oct 2022 21:58:51 +0530 Subject: [PATCH 13/17] New upscale button for images; Fix a bug where the string seed would get appended with numbers --- ui/index.html | 2 +- ui/media/js/main.js | 23 +++++++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/ui/index.html b/ui/index.html index 0fcf28ca..915ca350 100644 --- a/ui/index.html +++ b/ui/index.html @@ -108,7 +108,7 @@
  • Image Settings - +