diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b221c9d2..58e173bff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,21 +15,24 @@ Plus tons of fixes... download using built-in **Huggingface** downloader: `segmind/SSD-1B` - new model type: [LCM: Latent Consistency Models](https://github.com/openai/consistency_models) near-instant generate in a as little as 3 steps! + combined with OpenVINO, generate on CPU takes less than 10 seconds: download using built-in **Huggingface** downloader: `SimianLuo/LCM_Dreamshaper_v7` - support for **Custom pipelines**, thanks @disty0 download using built-in **Huggingface** downloader think of them as plugins for diffusers not unlike original extensions that modify behavior of `ldm` backend list of community pipelines: - and make sure to check our reference custom pipeline: `Disty0/zero123plus-pipeline` - which generates 4 output images with different camera positions: front, side, top, back! + - new custom pipeline: `Disty0/zero123plus-pipeline` + generate 4 output images with different camera positions: front, side, top, back! + for more details, see - extend support for [Free-U](https://github.com/ChenyangSi/FreeU) improve generations quality at no cost (other than finding params that work for you) - **General** - add **Lora OFT** support, thanks @antis0007 and @ai-casanova - **Upscalers** - - **compile compile** option, thanks @disty0 + - **compile** option, thanks @disty0 - **chaiNNer** add high quality models from [Helaman](https://openmodeldb.info/users/helaman) - - redesigned **progress bar** with full details on current operation + - redesigned **Progress bar** with full details on current operation + - **Extra networks** sort by name, size, date, etc. - new option: *settings -> images -> keep incomplete* can be used to skip vae decode on aborted/skipped/interrupted image generations - remove external clone of items in `/repositories` diff --git a/extensions-builtin/Lora/ui_extra_networks_lora.py b/extensions-builtin/Lora/ui_extra_networks_lora.py index 8730feca3..d8049ce84 100644 --- a/extensions-builtin/Lora/ui_extra_networks_lora.py +++ b/extensions-builtin/Lora/ui_extra_networks_lora.py @@ -53,6 +53,8 @@ class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage): "local_preview": f"{path}.{shared.opts.samples_format}", "metadata": json.dumps(l.metadata, indent=4) if l.metadata else None, "tags": tags, + "mtime": os.path.getmtime(l.filename), + "size": os.path.getsize(l.filename), } return item except Exception as e: diff --git a/extensions-builtin/sd-webui-controlnet b/extensions-builtin/sd-webui-controlnet index 4ac716a6e..f2aafcf2b 160000 --- a/extensions-builtin/sd-webui-controlnet +++ b/extensions-builtin/sd-webui-controlnet @@ -1 +1 @@ -Subproject commit 4ac716a6e81553f0ac935cac2c1fcd9cece838c3 +Subproject commit f2aafcf2beb99a03cbdf7db73852228ccd6bd1d6 diff --git a/html/locale_en.json b/html/locale_en.json index 5a046245f..0e3b770f4 100644 --- a/html/locale_en.json +++ b/html/locale_en.json @@ -16,6 +16,7 @@ {"id":"","label":"⇰","localized":"","hint":"Apply selected styles to current prompt"}, {"id":"","label":"⇩","localized":"","hint":"Save parameters from last generated image as style template"}, {"id":"","label":"🕮","localized":"","hint":"Save parameters from last generated image as style template"}, + {"id":"","label":"⇕","localized":"","hint":"Sort by: Name asc/desc, Size largest/smallest, Time newest/oldest"}, {"id":"","label":"⟲","localized":"","hint":"Refresh"}, {"id":"","label":"✕","localized":"","hint":"Close"}, {"id":"","label":"⊜","localized":"","hint":"Fill"}, diff --git a/javascript/black-teal.css b/javascript/black-teal.css index 095c9c4d8..b7ba073b4 100644 --- a/javascript/black-teal.css +++ b/javascript/black-teal.css @@ -88,7 +88,8 @@ svg.feather.feather-image, .feather .feather-image { display: none } .tab-nav { zoom: 120%; margin-top: 10px; margin-bottom: 10px; border-bottom: 2px solid var(--highlight-color) !important; padding-bottom: 2px; } .label-wrap { margin: 8px 0px 4px 0px; } .gradio-button.tool { border: none; background: none; box-shadow: none; filter: hue-rotate(340deg) saturate(0.5); } -#tab_extensions table td, #tab_extensions table th, #tab_config table td, #tab_config table th { border: none; padding: 0.5em; } +#tab_extensions table td, #tab_extensions table th, #tab_config table td, #tab_config table th { border: none; } +#tab_extensions table tr:hover, #tab_config table tr:hover { background-color: var(--neutral-500) !important; } #tab_extensions table, #tab_config table { width: 96vw } #tab_extensions table thead, #tab_config table thead { background-color: var(--neutral-700); } #tab_extensions table, #tab_config table { background-color: #222222; } diff --git a/javascript/extraNetworks.js b/javascript/extraNetworks.js index 45a3bc64e..28feebaf8 100644 --- a/javascript/extraNetworks.js +++ b/javascript/extraNetworks.js @@ -58,7 +58,8 @@ function readCardTags(el, tags) { e.preventDefault(); e.stopPropagation(); const textarea = activePromptTextarea[getENActiveTab()]; - if (textarea.value.indexOf(tag) !== -1) textarea.value = textarea.value.replace(tag, ''); + if (textarea.value.indexOf(` ${tag}`) !== -1) textarea.value = textarea.value.replace(` ${tag}`, ''); + else if (textarea.value.indexOf(`${tag} `) !== -1) textarea.value = textarea.value.replace(` ${tag} `, ''); else textarea.value += ` ${tag}`; updateInput(textarea); }; @@ -144,6 +145,34 @@ function tryToRemoveExtraNetworkFromPrompt(textarea, text) { return false; } +let sortVal = 0; + +function sortExtraNetworks() { + const sortDesc = ['Name [A-Z]', 'Name [Z-A]', 'Date [Newest]', 'Date [Oldest]', 'Size [Largest]', 'Size [Smallest]']; + const tabname = getENActiveTab(); + const pagename = getENActivePage(); + if (!tabname || !pagename) return 'sort error: unknown page'; + const pg = gradioApp().getElementById(`${tabname.toLowerCase()}_${pagename.toLowerCase()}_cards`); + const cards = Array.from(pg.querySelectorAll('.card') || []); + if (cards.length === 0) return 'sort: no cards'; + cards.sort((a, b) => { + switch (sortVal) { + case 0: return a.dataset.name ? a.dataset.name.localeCompare(b.dataset.name) : 0; + case 1: return b.dataset.name ? b.dataset.name.localeCompare(a.dataset.name) : 0; + case 2: return a.dataset.mtime && !isNaN(a.dataset.mtime) ? parseFloat(b.dataset.mtime) - parseFloat(a.dataset.mtime) : 0; + case 3: return b.dataset.mtime && !isNaN(b.dataset.mtime) ? parseFloat(a.dataset.mtime) - parseFloat(b.dataset.mtime) : 0; + case 4: return a.dataset.size && !isNaN(a.dataset.size) ? parseFloat(b.dataset.size) - parseFloat(a.dataset.size) : 0; + case 5: return b.dataset.size && !isNaN(b.dataset.size) ? parseFloat(a.dataset.size) - parseFloat(b.dataset.size) : 0; + } + return 0; + }); + for (const card of cards) pg.appendChild(card); + const desc = sortDesc[sortVal]; + sortVal = (sortVal + 1) % 6; + log('sortExtraNetworks', tabname, pagename, desc); + return `sort page ${pagename} cards ${cards.length} by ${desc}`; +} + function refreshExtraNetworks(tabname) { log('refreshExtraNetworks', tabname, gradioApp().querySelector(`#${tabname}_extra_networks textarea`)?.value); gradioApp().querySelector(`#${tabname}_extra_networks textarea`)?.dispatchEvent(new Event('input')); @@ -195,6 +224,7 @@ function setupExtraNetworksForTab(tabname) { const btnScan = gradioApp().getElementById(`${tabname}_extra_scan`); const btnSave = gradioApp().getElementById(`${tabname}_extra_save`); const btnClose = gradioApp().getElementById(`${tabname}_extra_close`); + const btnSort = gradioApp().getElementById(`${tabname}_extra_sort`); const btnModel = gradioApp().getElementById(`${tabname}_extra_model`); const btnApply = gradioApp().getElementById(`${tabname}_extra_apply`); const buttons = document.createElement('span'); @@ -204,6 +234,7 @@ function setupExtraNetworksForTab(tabname) { if (btnApply) buttons.appendChild(btnApply); if (btnScan) buttons.appendChild(btnScan); if (btnSave) buttons.appendChild(btnSave); + if (btnSort) buttons.appendChild(btnSort); if (btnClose) buttons.appendChild(btnClose); btnModel.onclick = () => btnModel.classList.toggle('toolbutton-selected'); tabs.appendChild(buttons); diff --git a/javascript/sdnext.css b/javascript/sdnext.css index 1a2df08b4..e1a8b2c48 100644 --- a/javascript/sdnext.css +++ b/javascript/sdnext.css @@ -163,6 +163,7 @@ table.settings-value-table td { padding: 0.4em; border: 1px solid #ccc; max-widt /* extensions */ #tab_extensions table, #tab_config table{ border-collapse: collapse; } #tab_extensions table td, #tab_extensions table th, #tab_config table td, #tab_config table th { border: 1px solid #ccc; padding: 0.25em 0.5em; } +#tab_extensions table tr:hover, #tab_config table tr:hover { background-color: var(--neutral-500) !important; } #tab_extensions table input[type="checkbox"] { margin-right: 0.5em; appearance: checkbox; } #tab_extensions button{ max-width: 16em; } #tab_extensions input[disabled="disabled"]{ opacity: 0.5; } @@ -195,9 +196,10 @@ table.settings-value-table td { padding: 0.4em; border: 1px solid #ccc; max-widt .extra-network-cards .card:hover .overlay { background: rgba(0, 0, 0, 0.40); } .extra-network-cards .card:hover .preview { box-shadow: none; filter: grayscale(100%); } .extra-network-cards .card:hover .overlay { background: rgba(0, 0, 0, 0.40); } -.extra-network-cards .card .overlay .tags { margin: 4px; display: none; overflow-wrap: break-word; } -.extra-network-cards .card .overlay .tag { padding: 2px; margin: 2px; background: var(--neutral-700); cursor: pointer; display: inline-block; } +.extra-network-cards .card .overlay .tags { display: none; overflow-wrap: break-word; } +.extra-network-cards .card .overlay .tag { padding: 3px; background: rgba(70, 70, 70, 0.60); font-size: var(--text-lg); cursor: pointer; display: inline-block; margin-bottom: 4px; } .extra-network-cards .card .actions > span { padding: 4px; } +.extra-network-cards .card .actions > span:hover { color: var(--highlight-color); } .extra-network-cards .card:hover .actions { display: block; } .extra-network-cards .card:hover .overlay .tags { display: block; } .extra-network-cards .card .actions { font-size: 3em; display: none; text-align-last: right; cursor: pointer; font-variant: unicase; position: absolute; z-index: 100; right: 0; height: 0.7em; width: 100%; background: rgba(0, 0, 0, 0.40); } diff --git a/modules/processing.py b/modules/processing.py index 70db97412..bf098664a 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -72,6 +72,30 @@ def apply_overlay(image, paste_loc, index, overlays): return image +def create_binary_mask(image): + if image.mode == 'RGBA' and image.getextrema()[-1] != (255, 255): + image = image.split()[-1].convert("L").point(lambda x: 255 if x > 128 else 0) + else: + image = image.convert('L') + return image + + +def images_tensor_to_samples(image, approximation=None, model=None): + if model is None: + model = shared.sd_model + model.first_stage_model.to(devices.dtype_vae) + image = image.to(shared.device, dtype=devices.dtype_vae) + image = image * 2 - 1 + if len(image) > 1: + x_latent = torch.stack([ + model.get_first_stage_encoding(model.encode_first_stage(torch.unsqueeze(img, 0)))[0] + for img in image + ]) + else: + x_latent = model.get_first_stage_encoding(model.encode_first_stage(image)) + return x_latent + + def txt2img_image_conditioning(sd_model, x, width, height): if sd_model.model.conditioning_key in {'hybrid', 'concat'}: # Inpainting models # The "masked-image" in this case will just be all zeros since the entire image is masked. diff --git a/modules/styles.py b/modules/styles.py index ea7c2dc7b..3c1a27865 100644 --- a/modules/styles.py +++ b/modules/styles.py @@ -9,7 +9,7 @@ from modules import paths class Style(): - def __init__(self, name: str, desc: str = "", prompt: str = "", negative_prompt: str = "", extra: str = "", filename: str = "", preview: str = ""): + def __init__(self, name: str, desc: str = "", prompt: str = "", negative_prompt: str = "", extra: str = "", filename: str = "", preview: str = "", mtime: float = 0): self.name = name self.description = desc self.prompt = prompt @@ -17,6 +17,7 @@ class Style(): self.extra = extra self.filename = filename self.preview = preview + self.mtime = mtime def merge_prompts(style_prompt: str, prompt: str) -> str: if "{prompt}" in style_prompt: @@ -105,7 +106,8 @@ class StyleDatabase: negative_prompt=style.get("negative", ""), extra=style.get("extra", ""), preview=style.get("preview", None), - filename=fn + filename=fn, + mtime=os.path.getmtime(fn), ) except Exception as e: log.error(f'Failed to load style: file={fn} error={e}') diff --git a/modules/ui_extra_networks.py b/modules/ui_extra_networks.py index 0fff0776f..0cd39302c 100644 --- a/modules/ui_extra_networks.py +++ b/modules/ui_extra_networks.py @@ -118,14 +118,14 @@ class ExtraNetworksPage: self.list_time = 0 # class additional is to keep old extensions happy self.card = ''' -
+
{title}
- 🛈 + 🛈
    @@ -282,6 +282,8 @@ class ExtraNetworksPage: "search_term": item.get("search_term", ""), "description": item.get("description") or "", "card_click": item.get("onclick", '"' + html.escape(f'return cardClicked({item.get("prompt", None)}, {"true" if self.allow_negative_prompt else "false"})') + '"'), + "mtime": item.get("mtime", 0), + "size": item.get("size", 0), } alias = item.get("alias", None) if alias is not None: @@ -392,6 +394,7 @@ class ExtraNetworksUi: self.button_refresh: gr.Button = None self.button_scan: gr.Button = None self.button_save: gr.Button = None + self.button_sort: gr.Button = None self.button_apply: gr.Button = None self.button_close: gr.Button = None self.button_model: gr.Checkbox = None @@ -485,7 +488,8 @@ def create_ui(container, button_parent, tabname, skip_indexing = False): ui.button_refresh = ToolButton(symbols.refresh, elem_id=tabname+"_extra_refresh") ui.button_scan = ToolButton(symbols.scan, elem_id=tabname+"_extra_scan", visible=True) ui.button_save = ToolButton(symbols.book, elem_id=tabname+"_extra_save", visible=False) - ui.button_close = ToolButton(symbols.close, elem_id=tabname+"_extra_close") + ui.button_sort = ToolButton(symbols.sort, elem_id=tabname+"_extra_sort", visible=True) + ui.button_close = ToolButton(symbols.close, elem_id=tabname+"_extra_close", visible=True) ui.button_model = ToolButton(symbols.refine, elem_id=tabname+"_extra_model", visible=True) ui.search = gr.Textbox('', show_label=False, elem_id=tabname+"_extra_search", placeholder="Search...", elem_classes="textbox", lines=2, container=False) ui.description = gr.Textbox('', show_label=False, elem_id=tabname+"_description", elem_classes="textbox", lines=2, interactive=False, container=False) @@ -700,9 +704,14 @@ def create_ui(container, button_parent, tabname, skip_indexing = False): res = show_details(text=None, img=None, desc=None, info=None, meta=None, params=params) return res + def ui_sort_cards(msg): + shared.log.debug(f'Extra networks: {msg}') + return msg + dummy_state = gr.State(value=False) # pylint: disable=abstract-class-instantiated button_parent.click(fn=toggle_visibility, inputs=[ui.visible], outputs=[ui.visible, container, button_parent]) ui.button_close.click(fn=toggle_visibility, inputs=[ui.visible], outputs=[ui.visible, container]) + ui.button_sort.click(fn=ui_sort_cards, _js='sortExtraNetworks', inputs=[ui.search], outputs=[ui.description]) ui.button_refresh.click(fn=ui_refresh_click, _js='getENActivePage', inputs=[ui.search], outputs=ui.pages) ui.button_scan.click(fn=ui_scan_click, _js='getENActivePage', inputs=[ui.search], outputs=ui.pages) ui.button_save.click(fn=ui_save_click, inputs=[], outputs=ui.details_components + [ui.details]) diff --git a/modules/ui_extra_networks_checkpoints.py b/modules/ui_extra_networks_checkpoints.py index 8ddf3eb1a..b2ffbd80f 100644 --- a/modules/ui_extra_networks_checkpoints.py +++ b/modules/ui_extra_networks_checkpoints.py @@ -30,6 +30,8 @@ class ExtraNetworksPageCheckpoints(ui_extra_networks.ExtraNetworksPage): "info": self.find_info(fn), "metadata": checkpoint.metadata, "onclick": '"' + html.escape(f"""return selectCheckpoint({json.dumps(name)})""") + '"', + "mtime": os.path.getmtime(checkpoint.filename), + "size": os.path.getsize(checkpoint.filename), } yield record except Exception as e: diff --git a/modules/ui_extra_networks_hypernets.py b/modules/ui_extra_networks_hypernets.py index b692974e4..cf6a02ae7 100644 --- a/modules/ui_extra_networks_hypernets.py +++ b/modules/ui_extra_networks_hypernets.py @@ -25,6 +25,8 @@ class ExtraNetworksPageHypernetworks(ui_extra_networks.ExtraNetworksPage): "search_term": self.search_terms_from_path(name), "prompt": json.dumps(f""), "local_preview": f"{fn}.{shared.opts.samples_format}", + "mtime": os.path.getmtime(path), + "size": os.path.getsize(path), } except Exception as e: shared.log.debug(f"Extra networks error: type=hypernetwork file={path} {e}") diff --git a/modules/ui_extra_networks_styles.py b/modules/ui_extra_networks_styles.py index 96a584e8d..49a34affb 100644 --- a/modules/ui_extra_networks_styles.py +++ b/modules/ui_extra_networks_styles.py @@ -85,6 +85,8 @@ class ExtraNetworksPageStyles(ui_extra_networks.ExtraNetworksPage): "extra": getattr(style, 'extra', ''), "local_preview": f"{fn}.{shared.opts.samples_format}", "onclick": '"' + html.escape(f"""return selectStyle({json.dumps(name)})""") + '"', + "mtime": getattr(style, 'mtime', 0), + "size": os.path.getsize(style.filename), } except Exception as e: shared.log.debug(f"Extra networks error: type=style file={k} {e}") diff --git a/modules/ui_extra_networks_textual_inversion.py b/modules/ui_extra_networks_textual_inversion.py index efd28b5d1..5c750b813 100644 --- a/modules/ui_extra_networks_textual_inversion.py +++ b/modules/ui_extra_networks_textual_inversion.py @@ -56,6 +56,8 @@ class ExtraNetworksPageTextualInversion(ui_extra_networks.ExtraNetworksPage): "prompt": json.dumps(os.path.splitext(embedding.name)[0]), "local_preview": f"{path}.{shared.opts.samples_format}", "tags": tags, + "mtime": os.path.getmtime(embedding.filename), + "size": os.path.getsize(embedding.filename), } except Exception as e: shared.log.debug(f"Extra networks error: type=embedding file={embedding.filename} {e}") diff --git a/modules/ui_extra_networks_vae.py b/modules/ui_extra_networks_vae.py index cadc901be..09e7565e9 100644 --- a/modules/ui_extra_networks_vae.py +++ b/modules/ui_extra_networks_vae.py @@ -28,6 +28,8 @@ class ExtraNetworksPageVAEs(ui_extra_networks.ExtraNetworksPage): "info": self.find_info(fn), "metadata": {}, "onclick": '"' + html.escape(f"""return selectVAE({json.dumps(name)})""") + '"', + "mtime": os.path.getmtime(filename), + "size": os.path.getsize(filename), } yield record except Exception as e: diff --git a/modules/ui_symbols.py b/modules/ui_symbols.py index 010a70050..2da7945b0 100644 --- a/modules/ui_symbols.py +++ b/modules/ui_symbols.py @@ -11,6 +11,7 @@ networks = '🌐' paste = '⇦' refine = '⌾' switch = '⇅' +sort = '⇕' detect = '📐' folder = '📂' random = '🎲️' diff --git a/wiki b/wiki index 87b508db6..2e3f37151 160000 --- a/wiki +++ b/wiki @@ -1 +1 @@ -Subproject commit 87b508db6fdaee55b0dd9ae9bec9a37c1c8f9198 +Subproject commit 2e3f3715172ef9b1540d3849fe652a3a51ea8f98