Commit 73e1cd6f authored by evshiron's avatar evshiron

Merge branch 'master' into fix/encode-pnginfo

parents e21fcd72 89722fb5
...@@ -155,14 +155,15 @@ The documentation was moved from this README over to the project's [wiki](https: ...@@ -155,14 +155,15 @@ The documentation was moved from this README over to the project's [wiki](https:
- Swin2SR - https://github.com/mv-lab/swin2sr - Swin2SR - https://github.com/mv-lab/swin2sr
- LDSR - https://github.com/Hafiidz/latent-diffusion - LDSR - https://github.com/Hafiidz/latent-diffusion
- Ideas for optimizations - https://github.com/basujindal/stable-diffusion - Ideas for optimizations - https://github.com/basujindal/stable-diffusion
- Doggettx - Cross Attention layer optimization - https://github.com/Doggettx/stable-diffusion, original idea for prompt editing. - Cross Attention layer optimization - Doggettx - https://github.com/Doggettx/stable-diffusion, original idea for prompt editing.
- InvokeAI, lstein - Cross Attention layer optimization - https://github.com/invoke-ai/InvokeAI (originally http://github.com/lstein/stable-diffusion) - Cross Attention layer optimization - InvokeAI, lstein - https://github.com/invoke-ai/InvokeAI (originally http://github.com/lstein/stable-diffusion)
- Rinon Gal - Textual Inversion - https://github.com/rinongal/textual_inversion (we're not using his code, but we are using his ideas). - Textual Inversion - Rinon Gal - https://github.com/rinongal/textual_inversion (we're not using his code, but we are using his ideas).
- Idea for SD upscale - https://github.com/jquesnelle/txt2imghd - Idea for SD upscale - https://github.com/jquesnelle/txt2imghd
- Noise generation for outpainting mk2 - https://github.com/parlance-zz/g-diffuser-bot - Noise generation for outpainting mk2 - https://github.com/parlance-zz/g-diffuser-bot
- CLIP interrogator idea and borrowing some code - https://github.com/pharmapsychotic/clip-interrogator - CLIP interrogator idea and borrowing some code - https://github.com/pharmapsychotic/clip-interrogator
- Idea for Composable Diffusion - https://github.com/energy-based-model/Compositional-Visual-Generation-with-Composable-Diffusion-Models-PyTorch - Idea for Composable Diffusion - https://github.com/energy-based-model/Compositional-Visual-Generation-with-Composable-Diffusion-Models-PyTorch
- xformers - https://github.com/facebookresearch/xformers - xformers - https://github.com/facebookresearch/xformers
- DeepDanbooru - interrogator for anime diffusers https://github.com/KichangKim/DeepDanbooru - DeepDanbooru - interrogator for anime diffusers https://github.com/KichangKim/DeepDanbooru
- Security advice - RyotaK
- Initial Gradio script - posted on 4chan by an Anonymous user. Thank you Anonymous user. - Initial Gradio script - posted on 4chan by an Anonymous user. Thank you Anonymous user.
- (You) - (You)
addEventListener('keydown', (event) => { addEventListener('keydown', (event) => {
let target = event.originalTarget || event.composedPath()[0]; let target = event.originalTarget || event.composedPath()[0];
if (!target.hasAttribute("placeholder")) return; if (!target.matches("#toprow textarea.gr-text-input[placeholder]")) return;
if (!target.placeholder.toLowerCase().includes("prompt")) return;
if (! (event.metaKey || event.ctrlKey)) return; if (! (event.metaKey || event.ctrlKey)) return;
......
...@@ -238,12 +238,15 @@ def tests(argv): ...@@ -238,12 +238,15 @@ def tests(argv):
proc.kill() proc.kill()
def start_webui(): def start():
print(f"Launching Web UI with arguments: {' '.join(sys.argv[1:])}") print(f"Launching {'API server' if '--nowebui' in sys.argv else 'Web UI'} with arguments: {' '.join(sys.argv[1:])}")
import webui import webui
webui.webui() if '--nowebui' in sys.argv:
webui.api_only()
else:
webui.webui()
if __name__ == "__main__": if __name__ == "__main__":
prepare_enviroment() prepare_enviroment()
start_webui() start()
This diff is collapsed.
...@@ -104,6 +104,7 @@ ...@@ -104,6 +104,7 @@
"Seed travel": "Interpolazione semi", "Seed travel": "Interpolazione semi",
"Shift attention": "Sposta l'attenzione", "Shift attention": "Sposta l'attenzione",
"Text to Vector Graphics": "Da testo a grafica vettoriale", "Text to Vector Graphics": "Da testo a grafica vettoriale",
"Unprompted": "Unprompted",
"X/Y plot": "Grafico X/Y", "X/Y plot": "Grafico X/Y",
"X/Y/Z plot": "Grafico X/Y/Z", "X/Y/Z plot": "Grafico X/Y/Z",
"Dynamic Prompting v0.13.6": "Prompt dinamici v0.13.6", "Dynamic Prompting v0.13.6": "Prompt dinamici v0.13.6",
...@@ -259,6 +260,7 @@ ...@@ -259,6 +260,7 @@
"Save results as video": "Salva i risultati come video", "Save results as video": "Salva i risultati come video",
"Frames per second": "Fotogrammi al secondo", "Frames per second": "Fotogrammi al secondo",
"Iterate seed every line": "Iterare il seme per ogni riga", "Iterate seed every line": "Iterare il seme per ogni riga",
"Use same random seed for all lines": "Usa lo stesso seme casuale per tutte le righe",
"List of prompt inputs": "Elenco di prompt di input", "List of prompt inputs": "Elenco di prompt di input",
"Upload prompt inputs": "Carica un file contenente i prompt di input", "Upload prompt inputs": "Carica un file contenente i prompt di input",
"n": "Esegui n volte", "n": "Esegui n volte",
...@@ -294,6 +296,13 @@ ...@@ -294,6 +296,13 @@
"Transparent PNG": "PNG trasparente", "Transparent PNG": "PNG trasparente",
"Noise Tolerance": "Tolleranza al rumore", "Noise Tolerance": "Tolleranza al rumore",
"Quantize": "Quantizzare", "Quantize": "Quantizzare",
"Dry Run": "Esecuzione a vuoto (Debug)",
"NEW!": "NUOVO!",
"Premium Fantasy Card Template": "Premium Fantasy Card Template",
"is now available.": "è ora disponibile.",
"Generate a wide variety of creatures and characters in the style of a fantasy card game. Perfect for heroes, animals, monsters, and even crazy hybrids.": "Genera un'ampia varietà di creature e personaggi nello stile di un gioco di carte fantasy. Perfetto per eroi, animali, mostri e persino ibridi incredibili.",
"Learn More ➜": "Per saperne di più ➜",
"Purchases help fund the continued development of Unprompted. Thank you for your support!": "Gli acquisti aiutano a finanziare il continuo sviluppo di Unprompted. Grazie per il vostro sostegno!",
"X type": "Parametro asse X", "X type": "Parametro asse X",
"Nothing": "Niente", "Nothing": "Niente",
"Var. seed": "Seme della variazione", "Var. seed": "Seme della variazione",
...@@ -424,6 +433,7 @@ ...@@ -424,6 +433,7 @@
"Sigma adjustment for finding noise for image": "Regolazione Sigma per trovare il rumore per l'immagine", "Sigma adjustment for finding noise for image": "Regolazione Sigma per trovare il rumore per l'immagine",
"Tile size": "Dimensione piastrella", "Tile size": "Dimensione piastrella",
"Tile overlap": "Sovrapposizione piastrella", "Tile overlap": "Sovrapposizione piastrella",
"New seed for each tile": "Nuovo seme per ogni piastrella",
"alternate img2img imgage": "Immagine alternativa per img2img", "alternate img2img imgage": "Immagine alternativa per img2img",
"interpolation values": "Valori di interpolazione", "interpolation values": "Valori di interpolazione",
"Refinement loops": "Cicli di affinamento", "Refinement loops": "Cicli di affinamento",
...@@ -455,8 +465,9 @@ ...@@ -455,8 +465,9 @@
"Will upscale the image to twice the dimensions; use width and height sliders to set tile size": "Aumenterà l'immagine al doppio delle dimensioni; utilizzare i cursori di larghezza e altezza per impostare la dimensione della piastrella", "Will upscale the image to twice the dimensions; use width and height sliders to set tile size": "Aumenterà l'immagine al doppio delle dimensioni; utilizzare i cursori di larghezza e altezza per impostare la dimensione della piastrella",
"Upscaler": "Ampliamento immagine", "Upscaler": "Ampliamento immagine",
"Lanczos": "Lanczos", "Lanczos": "Lanczos",
"Nearest": "Nearest",
"LDSR": "LDSR", "LDSR": "LDSR",
"ESRGAN_4x": "ESRGAN_4x", "BSRGAN": "BSRGAN",
"ScuNET GAN": "ScuNET GAN", "ScuNET GAN": "ScuNET GAN",
"ScuNET PSNR": "ScuNET PSNR", "ScuNET PSNR": "ScuNET PSNR",
"SwinIR 4x": "SwinIR 4x", "SwinIR 4x": "SwinIR 4x",
...@@ -808,6 +819,7 @@ ...@@ -808,6 +819,7 @@
"image_path": "Percorso immagine", "image_path": "Percorso immagine",
"mp4_path": "Percorso MP4", "mp4_path": "Percorso MP4",
"Click here after the generation to show the video": "Clicca qui dopo la generazione per mostrare il video", "Click here after the generation to show the video": "Clicca qui dopo la generazione per mostrare il video",
"NOTE: If the 'Generate' button doesn't work, go in Settings and click 'Restart Gradio and Refresh...'.": "NOTA: se il pulsante 'Genera' non funziona, vai in Impostazioni e fai clic su 'Riavvia Gradio e Aggiorna...'.",
"Save Settings": "Salva le impostazioni", "Save Settings": "Salva le impostazioni",
"Load Settings": "Carica le impostazioni", "Load Settings": "Carica le impostazioni",
"Path relative to the webui folder." : "Percorso relativo alla cartella webui.", "Path relative to the webui folder." : "Percorso relativo alla cartella webui.",
...@@ -922,8 +934,8 @@ ...@@ -922,8 +934,8 @@
"Renew Page": "Aggiorna la pagina", "Renew Page": "Aggiorna la pagina",
"Number": "Numero", "Number": "Numero",
"set_index": "Imposta indice", "set_index": "Imposta indice",
"load_switch": "load_switch", "load_switch": "Carica",
"turn_page_switch": "turn_page_switch", "turn_page_switch": "Volta pagina",
"Checkbox": "Casella di controllo", "Checkbox": "Casella di controllo",
"Checkbox Group": "Seleziona immagini per", "Checkbox Group": "Seleziona immagini per",
"artists": "Artisti", "artists": "Artisti",
...@@ -956,6 +968,8 @@ ...@@ -956,6 +968,8 @@
"Save text information about generation parameters as chunks to png files": "Salva le informazioni di testo dei parametri di generazione come blocchi nel file png", "Save text information about generation parameters as chunks to png files": "Salva le informazioni di testo dei parametri di generazione come blocchi nel file png",
"Create a text file next to every image with generation parameters.": "Crea un file di testo assieme a ogni immagine con i parametri di generazione.", "Create a text file next to every image with generation parameters.": "Crea un file di testo assieme a ogni immagine con i parametri di generazione.",
"Save a copy of image before doing face restoration.": "Salva una copia dell'immagine prima di eseguire il restauro dei volti.", "Save a copy of image before doing face restoration.": "Salva una copia dell'immagine prima di eseguire il restauro dei volti.",
"Save a copy of image before applying highres fix.": "Salva una copia dell'immagine prima di applicare la correzione ad alta risoluzione.",
"Save a copy of image before applying color correction to img2img results": "Salva una copia dell'immagine prima di applicare la correzione del colore ai risultati di img2img",
"Quality for saved jpeg images": "Qualità delle immagini salvate in formato JPEG", "Quality for saved jpeg images": "Qualità delle immagini salvate in formato JPEG",
"If PNG image is larger than 4MB or any dimension is larger than 4000, downscale and save copy as JPG": "Se l'immagine PNG è più grande di 4 MB o qualsiasi dimensione è maggiore di 4000, ridimensiona e salva la copia come JPG", "If PNG image is larger than 4MB or any dimension is larger than 4000, downscale and save copy as JPG": "Se l'immagine PNG è più grande di 4 MB o qualsiasi dimensione è maggiore di 4000, ridimensiona e salva la copia come JPG",
"Use original name for output filename during batch process in extras tab": "Usa il nome originale per il nome del file di output durante l'elaborazione a lotti nella scheda 'Extra'", "Use original name for output filename during batch process in extras tab": "Usa il nome originale per il nome del file di output durante l'elaborazione a lotti nella scheda 'Extra'",
...@@ -997,12 +1011,14 @@ ...@@ -997,12 +1011,14 @@
"Filename join string": "Stringa per unire le parole estratte dal nome del file", "Filename join string": "Stringa per unire le parole estratte dal nome del file",
"Number of repeats for a single input image per epoch; used only for displaying epoch number": "Numero di ripetizioni per una singola immagine di input per epoca; utilizzato solo per visualizzare il numero di epoca", "Number of repeats for a single input image per epoch; used only for displaying epoch number": "Numero di ripetizioni per una singola immagine di input per epoca; utilizzato solo per visualizzare il numero di epoca",
"Save an csv containing the loss to log directory every N steps, 0 to disable": "Salva un file CSV contenente la perdita nella cartella di registrazione ogni N passaggi, 0 per disabilitare", "Save an csv containing the loss to log directory every N steps, 0 to disable": "Salva un file CSV contenente la perdita nella cartella di registrazione ogni N passaggi, 0 per disabilitare",
"Use cross attention optimizations while training": "Usa le ottimizzazioni di controllo dell'attenzione incrociato durante l'allenamento",
"Stable Diffusion": "Stable Diffusion", "Stable Diffusion": "Stable Diffusion",
"Checkpoints to cache in RAM": "Checkpoint da memorizzare nella RAM", "Checkpoints to cache in RAM": "Checkpoint da memorizzare nella RAM",
"SD VAE": "SD VAE",
"auto": "auto",
"Hypernetwork strength": "Forza della Iperrete", "Hypernetwork strength": "Forza della Iperrete",
"Inpainting conditioning mask strength": "Forza della maschera di condizionamento del Inpainting", "Inpainting conditioning mask strength": "Forza della maschera di condizionamento del Inpainting",
"Apply color correction to img2img results to match original colors.": "Applica la correzione del colore ai risultati di img2img in modo che corrispondano ai colori originali.", "Apply color correction to img2img results to match original colors.": "Applica la correzione del colore ai risultati di img2img in modo che corrispondano ai colori originali.",
"Save a copy of image before applying color correction to img2img results": "Salva una copia dell'immagine prima di applicare la correzione del colore ai risultati di img2img",
"With img2img, do exactly the amount of steps the slider specifies (normally you'd do less with less denoising).": "Con img2img, esegue esattamente la quantità di passi specificata dalla barra di scorrimento (normalmente se ne effettuano di meno con meno riduzione del rumore).", "With img2img, do exactly the amount of steps the slider specifies (normally you'd do less with less denoising).": "Con img2img, esegue esattamente la quantità di passi specificata dalla barra di scorrimento (normalmente se ne effettuano di meno con meno riduzione del rumore).",
"Enable quantization in K samplers for sharper and cleaner results. This may change existing seeds. Requires restart to apply.": "Abilita la quantizzazione nei campionatori K per risultati più nitidi e puliti. Questo può cambiare i semi esistenti. Richiede il riavvio per applicare la modifica.", "Enable quantization in K samplers for sharper and cleaner results. This may change existing seeds. Requires restart to apply.": "Abilita la quantizzazione nei campionatori K per risultati più nitidi e puliti. Questo può cambiare i semi esistenti. Richiede il riavvio per applicare la modifica.",
"Emphasis: use (text) to make model pay more attention to text and [text] to make it pay less attention": "Enfasi: utilizzare (testo) per fare in modo che il modello presti maggiore attenzione al testo e [testo] per fargli prestare meno attenzione", "Emphasis: use (text) to make model pay more attention to text and [text] to make it pay less attention": "Enfasi: utilizzare (testo) per fare in modo che il modello presti maggiore attenzione al testo e [testo] per fargli prestare meno attenzione",
...@@ -1194,8 +1210,8 @@ ...@@ -1194,8 +1210,8 @@
"Hue:0": "Hue:0", "Hue:0": "Hue:0",
"S:0": "S:0", "S:0": "S:0",
"L:0": "L:0", "L:0": "L:0",
"Load Canvas": "Carica Tela", "Load Canvas": "Carica Canvas",
"saveCanvas": "Salva Tela", "Save Canvas": "Salva Canvas",
"latest": "aggiornato", "latest": "aggiornato",
"behind": "da aggiornare", "behind": "da aggiornare",
"Description": "Descrizione", "Description": "Descrizione",
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
"Add model hash to generation information": "생성 정보에 모델 해시 추가", "Add model hash to generation information": "생성 정보에 모델 해시 추가",
"Add model name to generation information": "생성 정보에 모델 이름 추가", "Add model name to generation information": "생성 정보에 모델 이름 추가",
"Add number to filename when saving": "이미지를 저장할 때 파일명에 숫자 추가하기", "Add number to filename when saving": "이미지를 저장할 때 파일명에 숫자 추가하기",
"Adds a tab to the webui that allows the user to automatically extract keyframes from video, and manually extract 512x512 crops of those frames for use in model training.": "WebUI에 비디오로부터 자동으로 키프레임을 추출하고, 그 키프레임으로부터 모델 훈련에 사용될 512x512 이미지를 잘라낼 수 있는 탭을 추가합니다.",
"Aesthetic Gradients": "스타일 그라디언트", "Aesthetic Gradients": "스타일 그라디언트",
"Aesthetic Image Scorer": "스타일 이미지 스코어러", "Aesthetic Image Scorer": "스타일 이미지 스코어러",
"Aesthetic imgs embedding": "스타일 이미지 임베딩", "Aesthetic imgs embedding": "스타일 이미지 임베딩",
...@@ -260,6 +261,7 @@ ...@@ -260,6 +261,7 @@
"Keep -1 for seeds": "시드값 -1로 유지", "Keep -1 for seeds": "시드값 -1로 유지",
"keep whatever was there originally": "이미지 원본 유지", "keep whatever was there originally": "이미지 원본 유지",
"keyword": "프롬프트", "keyword": "프롬프트",
"Krita Plugin.": "Kirta 플러그인입니다.",
"Label": "라벨", "Label": "라벨",
"Lanczos": "Lanczos", "Lanczos": "Lanczos",
"Last prompt:": "마지막 프롬프트 : ", "Last prompt:": "마지막 프롬프트 : ",
...@@ -441,8 +443,8 @@ ...@@ -441,8 +443,8 @@
"See": "자세한 설명은", "See": "자세한 설명은",
"Seed": "시드", "Seed": "시드",
"Seed of a different picture to be mixed into the generation.": "결과물에 섞일 다른 그림의 시드", "Seed of a different picture to be mixed into the generation.": "결과물에 섞일 다른 그림의 시드",
"Select activation function of hypernetwork": "하이퍼네트워크 활성화 함수 선택", "Select activation function of hypernetwork. Recommended : Swish / Linear(none)": "하이퍼네트워크 활성화 함수 선택 - 추천 : Swish / Linear(None)",
"Select Layer weights initialization. relu-like - Kaiming, sigmoid-like - Xavier is recommended": "레이어 가중치 초기화 방식 선택 - relu류 : Kaiming 추천, sigmoid류 : Xavier 추천", "Select Layer weights initialization. Recommended: Kaiming for relu-like, Xavier for sigmoid-like, Normal otherwise": "레이어 가중치 초기화 방식 선택 - relu류 : Kaiming 추천, sigmoid류 : Xavier 추천, 그 외 : Normal",
"Select which Real-ESRGAN models to show in the web UI. (Requires restart)": "WebUI에 표시할 Real-ESRGAN 모델을 선택하십시오. (재시작 필요)", "Select which Real-ESRGAN models to show in the web UI. (Requires restart)": "WebUI에 표시할 Real-ESRGAN 모델을 선택하십시오. (재시작 필요)",
"Send seed when sending prompt or image to other interface": "다른 화면으로 프롬프트나 이미지를 보낼 때 시드도 함께 보내기", "Send seed when sending prompt or image to other interface": "다른 화면으로 프롬프트나 이미지를 보낼 때 시드도 함께 보내기",
"Send to extras": "부가기능으로 전송", "Send to extras": "부가기능으로 전송",
...@@ -458,7 +460,7 @@ ...@@ -458,7 +460,7 @@
"should be 2 or lower.": "이 2 이하여야 합니다.", "should be 2 or lower.": "이 2 이하여야 합니다.",
"Show generation progress in window title.": "창 타이틀에 생성 진행도 보여주기", "Show generation progress in window title.": "창 타이틀에 생성 진행도 보여주기",
"Show grid in results for web": "웹에서 결과창에 그리드 보여주기", "Show grid in results for web": "웹에서 결과창에 그리드 보여주기",
"Show image creation progress every N sampling steps. Set 0 to disable.": "N번째 샘플링 스텝마다 이미지 생성 과정 보이기 - 비활성화하려면 0으로 설정", "Show image creation progress every N sampling steps. Set to 0 to disable. Set to -1 to show after completion of batch.": "N번째 샘플링 스텝마다 이미지 생성 과정 보이기 - 비활성화하려면 0으로 설정, 배치 생성 완료시 보이려면 -1로 설정",
"Show images zoomed in by default in full page image viewer": "전체 페이지 이미지 뷰어에서 기본값으로 이미지 확대해서 보여주기", "Show images zoomed in by default in full page image viewer": "전체 페이지 이미지 뷰어에서 기본값으로 이미지 확대해서 보여주기",
"Show previews of all images generated in a batch as a grid": "배치에서 생성된 모든 이미지의 미리보기를 그리드 형식으로 보여주기", "Show previews of all images generated in a batch as a grid": "배치에서 생성된 모든 이미지의 미리보기를 그리드 형식으로 보여주기",
"Show progressbar": "프로그레스 바 보이기", "Show progressbar": "프로그레스 바 보이기",
...@@ -520,6 +522,7 @@ ...@@ -520,6 +522,7 @@
"Train Embedding": "임베딩 훈련", "Train Embedding": "임베딩 훈련",
"Train Hypernetwork": "하이퍼네트워크 훈련", "Train Hypernetwork": "하이퍼네트워크 훈련",
"Training": "훈련", "Training": "훈련",
"training-picker": "훈련용 선택기",
"txt2img": "텍스트→이미지", "txt2img": "텍스트→이미지",
"txt2img history": "텍스트→이미지 기록", "txt2img history": "텍스트→이미지 기록",
"uniform": "uniform", "uniform": "uniform",
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
"Checkpoint Merger": "Fusão de Checkpoint", "Checkpoint Merger": "Fusão de Checkpoint",
"Train": "Treinar", "Train": "Treinar",
"Settings": "Configurações", "Settings": "Configurações",
"Extensions": "Extensions", "Extensions": "Extensões",
"Prompt": "Prompt", "Prompt": "Prompt",
"Negative prompt": "Prompt negativo", "Negative prompt": "Prompt negativo",
"Run": "Executar", "Run": "Executar",
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,7 @@
"Highres. fix": "Ajuste de alta resolução", "Highres. fix": "Ajuste de alta resolução",
"Firstpass width": "Primeira Passagem da largura", "Firstpass width": "Primeira Passagem da largura",
"Firstpass height": "Primeira Passagem da altura", "Firstpass height": "Primeira Passagem da altura",
"Denoising strength": "Denoising strength", "Denoising strength": "Força do denoise",
"Batch count": "Quantidade por lote", "Batch count": "Quantidade por lote",
"Batch size": "Quantidade de lotes", "Batch size": "Quantidade de lotes",
"CFG Scale": "Escala CFG", "CFG Scale": "Escala CFG",
...@@ -480,6 +480,6 @@ ...@@ -480,6 +480,6 @@
"This string will be used to join split words into a single line if the option above is enabled.": "Esta string será usada para unir palavras divididas em uma única linha se a opção acima estiver habilitada.", "This string will be used to join split words into a single line if the option above is enabled.": "Esta string será usada para unir palavras divididas em uma única linha se a opção acima estiver habilitada.",
"Only applies to inpainting models. Determines how strongly to mask off the original image for inpainting and img2img. 1.0 means fully masked, which is the default behaviour. 0.0 means a fully unmasked conditioning. Lower values will help preserve the overall composition of the image, but will struggle with large changes.": "Aplicável somente para modelos de inpaint. Determina quanto deve mascarar da imagem original para inpaint e img2img. 1.0 significa totalmente mascarado, que é o comportamento padrão. 0.0 significa uma condição totalmente não mascarada. Valores baixos ajudam a preservar a composição geral da imagem, mas vai encontrar dificuldades com grandes mudanças.", "Only applies to inpainting models. Determines how strongly to mask off the original image for inpainting and img2img. 1.0 means fully masked, which is the default behaviour. 0.0 means a fully unmasked conditioning. Lower values will help preserve the overall composition of the image, but will struggle with large changes.": "Aplicável somente para modelos de inpaint. Determina quanto deve mascarar da imagem original para inpaint e img2img. 1.0 significa totalmente mascarado, que é o comportamento padrão. 0.0 significa uma condição totalmente não mascarada. Valores baixos ajudam a preservar a composição geral da imagem, mas vai encontrar dificuldades com grandes mudanças.",
"List of setting names, separated by commas, for settings that should go to the quick access bar at the top, rather than the usual setting tab. See modules/shared.py for setting names. Requires restarting to apply.": "Lista de nomes de configurações, separados por vírgulas, para configurações que devem ir para a barra de acesso rápido na parte superior, em vez da guia de configuração usual. Veja modules/shared.py para nomes de configuração. Necessita reinicialização para aplicar.", "List of setting names, separated by commas, for settings that should go to the quick access bar at the top, rather than the usual setting tab. See modules/shared.py for setting names. Requires restarting to apply.": "Lista de nomes de configurações, separados por vírgulas, para configurações que devem ir para a barra de acesso rápido na parte superior, em vez da guia de configuração usual. Veja modules/shared.py para nomes de configuração. Necessita reinicialização para aplicar.",
"If this values is non-zero, it will be added to seed and used to initialize RNG for noises when using samplers with Eta. You can use this to produce even more variation of images, or you can use this to match images of other software if you know what you are doing.": "Se este valor for diferente de zero, ele será adicionado à seed e usado para inicializar o RNG para ruídos ao usar amostragens com Tempo Estimado. Você pode usar isso para produzir ainda mais variações de imagens ou pode usar isso para combinar imagens de outro software se souber o que está fazendo." "If this values is non-zero, it will be added to seed and used to initialize RNG for noises when using samplers with Eta. You can use this to produce even more variation of images, or you can use this to match images of other software if you know what you are doing.": "Se este valor for diferente de zero, ele será adicionado à seed e usado para inicializar o RNG para ruídos ao usar amostragens com Tempo Estimado. Você pode usar isso para produzir ainda mais variações de imagens ou pode usar isso para combinar imagens de outro software se souber o que está fazendo.",
"Leave empty for auto": "Deixar desmarcado para automático" "Leave empty for auto": "Deixar desmarcado para automático"
} }
This diff is collapsed.
This diff is collapsed.
...@@ -2,15 +2,18 @@ import base64 ...@@ -2,15 +2,18 @@ import base64
import io import io
import time import time
import uvicorn import uvicorn
from gradio.processing_utils import decode_base64_to_file, decode_base64_to_image from threading import Lock
from fastapi import APIRouter, Depends, HTTPException from gradio.processing_utils import encode_pil_to_base64, decode_base64_to_file, decode_base64_to_image
from fastapi import APIRouter, Depends, FastAPI, HTTPException
import modules.shared as shared import modules.shared as shared
from modules.api.models import * from modules.api.models import *
from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img, process_images from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img, process_images
from modules.sd_samplers import all_samplers from modules.sd_samplers import all_samplers
from modules.extras import run_extras, run_pnginfo from modules.extras import run_extras, run_pnginfo
from PIL import PngImagePlugin from PIL import PngImagePlugin
from modules.sd_models import checkpoints_list
from modules.realesrgan_model import get_realesrgan_models
from typing import List
def upscaler_to_index(name: str): def upscaler_to_index(name: str):
try: try:
...@@ -50,7 +53,7 @@ def encode_pil_to_base64(image): ...@@ -50,7 +53,7 @@ def encode_pil_to_base64(image):
class Api: class Api:
def __init__(self, app, queue_lock): def __init__(self, app: FastAPI, queue_lock: Lock):
self.router = APIRouter() self.router = APIRouter()
self.app = app self.app = app
self.queue_lock = queue_lock self.queue_lock = queue_lock
...@@ -61,6 +64,18 @@ class Api: ...@@ -61,6 +64,18 @@ class Api:
self.app.add_api_route("/sdapi/v1/png-info", self.pnginfoapi, methods=["POST"], response_model=PNGInfoResponse) self.app.add_api_route("/sdapi/v1/png-info", self.pnginfoapi, methods=["POST"], response_model=PNGInfoResponse)
self.app.add_api_route("/sdapi/v1/progress", self.progressapi, methods=["GET"], response_model=ProgressResponse) self.app.add_api_route("/sdapi/v1/progress", self.progressapi, methods=["GET"], response_model=ProgressResponse)
self.app.add_api_route("/sdapi/v1/interrupt", self.interruptapi, methods=["POST"]) self.app.add_api_route("/sdapi/v1/interrupt", self.interruptapi, methods=["POST"])
self.app.add_api_route("/sdapi/v1/options", self.get_config, methods=["GET"], response_model=OptionsModel)
self.app.add_api_route("/sdapi/v1/options", self.set_config, methods=["POST"])
self.app.add_api_route("/sdapi/v1/cmd-flags", self.get_cmd_flags, methods=["GET"], response_model=FlagsModel)
self.app.add_api_route("/sdapi/v1/samplers", self.get_samplers, methods=["GET"], response_model=List[SamplerItem])
self.app.add_api_route("/sdapi/v1/upscalers", self.get_upscalers, methods=["GET"], response_model=List[UpscalerItem])
self.app.add_api_route("/sdapi/v1/sd-models", self.get_sd_models, methods=["GET"], response_model=List[SDModelItem])
self.app.add_api_route("/sdapi/v1/hypernetworks", self.get_hypernetworks, methods=["GET"], response_model=List[HypernetworkItem])
self.app.add_api_route("/sdapi/v1/face-restorers", self.get_face_restorers, methods=["GET"], response_model=List[FaceRestorerItem])
self.app.add_api_route("/sdapi/v1/realesrgan-models", self.get_realesrgan_models, methods=["GET"], response_model=List[RealesrganItem])
self.app.add_api_route("/sdapi/v1/prompt-styles", self.get_promp_styles, methods=["GET"], response_model=List[PromptStyleItem])
self.app.add_api_route("/sdapi/v1/artist-categories", self.get_artists_categories, methods=["GET"], response_model=List[str])
self.app.add_api_route("/sdapi/v1/artists", self.get_artists, methods=["GET"], response_model=List[ArtistItem])
def text2imgapi(self, txt2imgreq: StableDiffusionTxt2ImgProcessingAPI): def text2imgapi(self, txt2imgreq: StableDiffusionTxt2ImgProcessingAPI):
sampler_index = sampler_to_index(txt2imgreq.sampler_index) sampler_index = sampler_to_index(txt2imgreq.sampler_index)
...@@ -204,6 +219,66 @@ class Api: ...@@ -204,6 +219,66 @@ class Api:
return {} return {}
def get_config(self):
options = {}
for key in shared.opts.data.keys():
metadata = shared.opts.data_labels.get(key)
if(metadata is not None):
options.update({key: shared.opts.data.get(key, shared.opts.data_labels.get(key).default)})
else:
options.update({key: shared.opts.data.get(key, None)})
return options
def set_config(self, req: OptionsModel):
reqDict = vars(req)
for o in reqDict:
setattr(shared.opts, o, reqDict[o])
shared.opts.save(shared.config_filename)
return
def get_cmd_flags(self):
return vars(shared.cmd_opts)
def get_samplers(self):
return [{"name":sampler[0], "aliases":sampler[2], "options":sampler[3]} for sampler in all_samplers]
def get_upscalers(self):
upscalers = []
for upscaler in shared.sd_upscalers:
u = upscaler.scaler
upscalers.append({"name":u.name, "model_name":u.model_name, "model_path":u.model_path, "model_url":u.model_url})
return upscalers
def get_sd_models(self):
return [{"title":x.title, "model_name":x.model_name, "hash":x.hash, "filename": x.filename, "config": x.config} for x in checkpoints_list.values()]
def get_hypernetworks(self):
return [{"name": name, "path": shared.hypernetworks[name]} for name in shared.hypernetworks]
def get_face_restorers(self):
return [{"name":x.name(), "cmd_dir": getattr(x, "cmd_dir", None)} for x in shared.face_restorers]
def get_realesrgan_models(self):
return [{"name":x.name,"path":x.data_path, "scale":x.scale} for x in get_realesrgan_models(None)]
def get_promp_styles(self):
styleList = []
for k in shared.prompt_styles.styles:
style = shared.prompt_styles.styles[k]
styleList.append({"name":style[0], "prompt": style[1], "negative_prompr": style[2]})
return styleList
def get_artists_categories(self):
return shared.artist_db.cats
def get_artists(self):
return [{"name":x[0], "score":x[1], "category":x[2]} for x in shared.artist_db.artists]
def launch(self, server_name, port): def launch(self, server_name, port):
self.app.include_router(self.router) self.app.include_router(self.router)
uvicorn.run(self.app, host=server_name, port=port) uvicorn.run(self.app, host=server_name, port=port)
import inspect import inspect
from click import prompt
from pydantic import BaseModel, Field, create_model from pydantic import BaseModel, Field, create_model
from typing import Any, Optional from typing import Any, Optional, Union
from typing_extensions import Literal from typing_extensions import Literal
from inflection import underscore from inflection import underscore
from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img
from modules.shared import sd_upscalers from modules.shared import sd_upscalers, opts, parser
from typing import List
API_NOT_ALLOWED = [ API_NOT_ALLOWED = [
"self", "self",
...@@ -109,12 +109,12 @@ StableDiffusionImg2ImgProcessingAPI = PydanticModelGenerator( ...@@ -109,12 +109,12 @@ StableDiffusionImg2ImgProcessingAPI = PydanticModelGenerator(
).generate_model() ).generate_model()
class TextToImageResponse(BaseModel): class TextToImageResponse(BaseModel):
images: list[str] = Field(default=None, title="Image", description="The generated image in base64 format.") images: List[str] = Field(default=None, title="Image", description="The generated image in base64 format.")
parameters: dict parameters: dict
info: str info: str
class ImageToImageResponse(BaseModel): class ImageToImageResponse(BaseModel):
images: list[str] = Field(default=None, title="Image", description="The generated image in base64 format.") images: List[str] = Field(default=None, title="Image", description="The generated image in base64 format.")
parameters: dict parameters: dict
info: str info: str
...@@ -131,6 +131,7 @@ class ExtrasBaseRequest(BaseModel): ...@@ -131,6 +131,7 @@ class ExtrasBaseRequest(BaseModel):
upscaler_1: str = Field(default="None", title="Main upscaler", description=f"The name of the main upscaler to use, it has to be one of this list: {' , '.join([x.name for x in sd_upscalers])}") upscaler_1: str = Field(default="None", title="Main upscaler", description=f"The name of the main upscaler to use, it has to be one of this list: {' , '.join([x.name for x in sd_upscalers])}")
upscaler_2: str = Field(default="None", title="Secondary upscaler", description=f"The name of the secondary upscaler to use, it has to be one of this list: {' , '.join([x.name for x in sd_upscalers])}") upscaler_2: str = Field(default="None", title="Secondary upscaler", description=f"The name of the secondary upscaler to use, it has to be one of this list: {' , '.join([x.name for x in sd_upscalers])}")
extras_upscaler_2_visibility: float = Field(default=0, title="Secondary upscaler visibility", ge=0, le=1, allow_inf_nan=False, description="Sets the visibility of secondary upscaler, values should be between 0 and 1.") extras_upscaler_2_visibility: float = Field(default=0, title="Secondary upscaler visibility", ge=0, le=1, allow_inf_nan=False, description="Sets the visibility of secondary upscaler, values should be between 0 and 1.")
upscale_first: bool = Field(default=False, title="Upscale first", description="Should the upscaler run before restoring faces?")
class ExtraBaseResponse(BaseModel): class ExtraBaseResponse(BaseModel):
html_info: str = Field(title="HTML info", description="A series of HTML tags containing the process info.") html_info: str = Field(title="HTML info", description="A series of HTML tags containing the process info.")
...@@ -146,10 +147,10 @@ class FileData(BaseModel): ...@@ -146,10 +147,10 @@ class FileData(BaseModel):
name: str = Field(title="File name") name: str = Field(title="File name")
class ExtrasBatchImagesRequest(ExtrasBaseRequest): class ExtrasBatchImagesRequest(ExtrasBaseRequest):
imageList: list[FileData] = Field(title="Images", description="List of images to work on. Must be Base64 strings") imageList: List[FileData] = Field(title="Images", description="List of images to work on. Must be Base64 strings")
class ExtrasBatchImagesResponse(ExtraBaseResponse): class ExtrasBatchImagesResponse(ExtraBaseResponse):
images: list[str] = Field(title="Images", description="The generated images in base64 format.") images: List[str] = Field(title="Images", description="The generated images in base64 format.")
class PNGInfoRequest(BaseModel): class PNGInfoRequest(BaseModel):
image: str = Field(title="Image", description="The base64 encoded PNG image") image: str = Field(title="Image", description="The base64 encoded PNG image")
...@@ -165,3 +166,68 @@ class ProgressResponse(BaseModel): ...@@ -165,3 +166,68 @@ class ProgressResponse(BaseModel):
eta_relative: float = Field(title="ETA in secs") eta_relative: float = Field(title="ETA in secs")
state: dict = Field(title="State", description="The current state snapshot") state: dict = Field(title="State", description="The current state snapshot")
current_image: str = Field(default=None, title="Current image", description="The current image in base64 format. opts.show_progress_every_n_steps is required for this to work.") current_image: str = Field(default=None, title="Current image", description="The current image in base64 format. opts.show_progress_every_n_steps is required for this to work.")
fields = {}
for key, value in opts.data.items():
metadata = opts.data_labels.get(key)
optType = opts.typemap.get(type(value), type(value))
if (metadata is not None):
fields.update({key: (Optional[optType], Field(
default=metadata.default ,description=metadata.label))})
else:
fields.update({key: (Optional[optType], Field())})
OptionsModel = create_model("Options", **fields)
flags = {}
_options = vars(parser)['_option_string_actions']
for key in _options:
if(_options[key].dest != 'help'):
flag = _options[key]
_type = str
if(_options[key].default != None): _type = type(_options[key].default)
flags.update({flag.dest: (_type,Field(default=flag.default, description=flag.help))})
FlagsModel = create_model("Flags", **flags)
class SamplerItem(BaseModel):
name: str = Field(title="Name")
aliases: list[str] = Field(title="Aliases")
options: dict[str, str] = Field(title="Options")
class UpscalerItem(BaseModel):
name: str = Field(title="Name")
model_name: str | None = Field(title="Model Name")
model_path: str | None = Field(title="Path")
model_url: str | None = Field(title="URL")
class SDModelItem(BaseModel):
title: str = Field(title="Title")
model_name: str = Field(title="Model Name")
hash: str = Field(title="Hash")
filename: str = Field(title="Filename")
config: str = Field(title="Config file")
class HypernetworkItem(BaseModel):
name: str = Field(title="Name")
path: str | None = Field(title="Path")
class FaceRestorerItem(BaseModel):
name: str = Field(title="Name")
cmd_dir: str | None = Field(title="Path")
class RealesrganItem(BaseModel):
name: str = Field(title="Name")
path: str | None = Field(title="Path")
scale: int | None = Field(title="Scale")
class PromptStyleItem(BaseModel):
name: str = Field(title="Name")
prompt: str | None = Field(title="Prompt")
negative_prompt: str | None = Field(title="Negative Prompt")
class ArtistItem(BaseModel):
name: str = Field(title="Name")
score: float = Field(title="Score")
category: str = Field(title="Category")
\ No newline at end of file
...@@ -136,12 +136,13 @@ def run_extras(extras_mode, resize_mode, image, image_folder, input_dir, output_ ...@@ -136,12 +136,13 @@ def run_extras(extras_mode, resize_mode, image, image_folder, input_dir, output_
def run_upscalers_blend(params: List[UpscaleParams], image: Image.Image, info: str) -> Tuple[Image.Image, str]: def run_upscalers_blend(params: List[UpscaleParams], image: Image.Image, info: str) -> Tuple[Image.Image, str]:
blended_result: Image.Image = None blended_result: Image.Image = None
image_hash: str = hash(np.array(image.getdata()).tobytes())
for upscaler in params: for upscaler in params:
upscale_args = (upscaler.upscaler_idx, upscaling_resize, resize_mode, upscale_args = (upscaler.upscaler_idx, upscaling_resize, resize_mode,
upscaling_resize_w, upscaling_resize_h, upscaling_crop) upscaling_resize_w, upscaling_resize_h, upscaling_crop)
cache_key = LruCache.Key(image_hash=hash(np.array(image.getdata()).tobytes()), cache_key = LruCache.Key(image_hash=image_hash,
info_hash=hash(info), info_hash=hash(info),
args_hash=hash((upscale_args, upscale_first))) args_hash=hash(upscale_args))
cached_entry = cached_images.get(cache_key) cached_entry = cached_images.get(cache_key)
if cached_entry is None: if cached_entry is None:
res = upscale(image, *upscale_args) res = upscale(image, *upscale_args)
......
...@@ -35,7 +35,8 @@ class HypernetworkModule(torch.nn.Module): ...@@ -35,7 +35,8 @@ class HypernetworkModule(torch.nn.Module):
} }
activation_dict.update({cls_name.lower(): cls_obj for cls_name, cls_obj in inspect.getmembers(torch.nn.modules.activation) if inspect.isclass(cls_obj) and cls_obj.__module__ == 'torch.nn.modules.activation'}) activation_dict.update({cls_name.lower(): cls_obj for cls_name, cls_obj in inspect.getmembers(torch.nn.modules.activation) if inspect.isclass(cls_obj) and cls_obj.__module__ == 'torch.nn.modules.activation'})
def __init__(self, dim, state_dict=None, layer_structure=None, activation_func=None, weight_init='Normal', add_layer_norm=False, use_dropout=False): def __init__(self, dim, state_dict=None, layer_structure=None, activation_func=None, weight_init='Normal',
add_layer_norm=False, use_dropout=False, activate_output=False, last_layer_dropout=True):
super().__init__() super().__init__()
assert layer_structure is not None, "layer_structure must not be None" assert layer_structure is not None, "layer_structure must not be None"
...@@ -48,8 +49,8 @@ class HypernetworkModule(torch.nn.Module): ...@@ -48,8 +49,8 @@ class HypernetworkModule(torch.nn.Module):
# Add a fully-connected layer # Add a fully-connected layer
linears.append(torch.nn.Linear(int(dim * layer_structure[i]), int(dim * layer_structure[i+1]))) linears.append(torch.nn.Linear(int(dim * layer_structure[i]), int(dim * layer_structure[i+1])))
# Add an activation func # Add an activation func except last layer
if activation_func == "linear" or activation_func is None: if activation_func == "linear" or activation_func is None or (i >= len(layer_structure) - 2 and not activate_output):
pass pass
elif activation_func in self.activation_dict: elif activation_func in self.activation_dict:
linears.append(self.activation_dict[activation_func]()) linears.append(self.activation_dict[activation_func]())
...@@ -60,8 +61,8 @@ class HypernetworkModule(torch.nn.Module): ...@@ -60,8 +61,8 @@ class HypernetworkModule(torch.nn.Module):
if add_layer_norm: if add_layer_norm:
linears.append(torch.nn.LayerNorm(int(dim * layer_structure[i+1]))) linears.append(torch.nn.LayerNorm(int(dim * layer_structure[i+1])))
# Add dropout expect last layer # Add dropout except last layer
if use_dropout and i < len(layer_structure) - 3: if use_dropout and (i < len(layer_structure) - 3 or last_layer_dropout and i < len(layer_structure) - 2):
linears.append(torch.nn.Dropout(p=0.3)) linears.append(torch.nn.Dropout(p=0.3))
self.linear = torch.nn.Sequential(*linears) self.linear = torch.nn.Sequential(*linears)
...@@ -75,7 +76,7 @@ class HypernetworkModule(torch.nn.Module): ...@@ -75,7 +76,7 @@ class HypernetworkModule(torch.nn.Module):
w, b = layer.weight.data, layer.bias.data w, b = layer.weight.data, layer.bias.data
if weight_init == "Normal" or type(layer) == torch.nn.LayerNorm: if weight_init == "Normal" or type(layer) == torch.nn.LayerNorm:
normal_(w, mean=0.0, std=0.01) normal_(w, mean=0.0, std=0.01)
normal_(b, mean=0.0, std=0.005) normal_(b, mean=0.0, std=0)
elif weight_init == 'XavierUniform': elif weight_init == 'XavierUniform':
xavier_uniform_(w) xavier_uniform_(w)
zeros_(b) zeros_(b)
...@@ -127,7 +128,7 @@ class Hypernetwork: ...@@ -127,7 +128,7 @@ class Hypernetwork:
filename = None filename = None
name = None name = None
def __init__(self, name=None, enable_sizes=None, layer_structure=None, activation_func=None, weight_init=None, add_layer_norm=False, use_dropout=False): def __init__(self, name=None, enable_sizes=None, layer_structure=None, activation_func=None, weight_init=None, add_layer_norm=False, use_dropout=False, activate_output=False, **kwargs):
self.filename = None self.filename = None
self.name = name self.name = name
self.layers = {} self.layers = {}
...@@ -139,11 +140,15 @@ class Hypernetwork: ...@@ -139,11 +140,15 @@ class Hypernetwork:
self.weight_init = weight_init self.weight_init = weight_init
self.add_layer_norm = add_layer_norm self.add_layer_norm = add_layer_norm
self.use_dropout = use_dropout self.use_dropout = use_dropout
self.activate_output = activate_output
self.last_layer_dropout = kwargs['last_layer_dropout'] if 'last_layer_dropout' in kwargs else True
for size in enable_sizes or []: for size in enable_sizes or []:
self.layers[size] = ( self.layers[size] = (
HypernetworkModule(size, None, self.layer_structure, self.activation_func, self.weight_init, self.add_layer_norm, self.use_dropout), HypernetworkModule(size, None, self.layer_structure, self.activation_func, self.weight_init,
HypernetworkModule(size, None, self.layer_structure, self.activation_func, self.weight_init, self.add_layer_norm, self.use_dropout), self.add_layer_norm, self.use_dropout, self.activate_output, last_layer_dropout=self.last_layer_dropout),
HypernetworkModule(size, None, self.layer_structure, self.activation_func, self.weight_init,
self.add_layer_norm, self.use_dropout, self.activate_output, last_layer_dropout=self.last_layer_dropout),
) )
def weights(self): def weights(self):
...@@ -171,7 +176,9 @@ class Hypernetwork: ...@@ -171,7 +176,9 @@ class Hypernetwork:
state_dict['use_dropout'] = self.use_dropout state_dict['use_dropout'] = self.use_dropout
state_dict['sd_checkpoint'] = self.sd_checkpoint state_dict['sd_checkpoint'] = self.sd_checkpoint
state_dict['sd_checkpoint_name'] = self.sd_checkpoint_name state_dict['sd_checkpoint_name'] = self.sd_checkpoint_name
state_dict['activate_output'] = self.activate_output
state_dict['last_layer_dropout'] = self.last_layer_dropout
torch.save(state_dict, filename) torch.save(state_dict, filename)
def load(self, filename): def load(self, filename):
...@@ -191,12 +198,17 @@ class Hypernetwork: ...@@ -191,12 +198,17 @@ class Hypernetwork:
print(f"Layer norm is set to {self.add_layer_norm}") print(f"Layer norm is set to {self.add_layer_norm}")
self.use_dropout = state_dict.get('use_dropout', False) self.use_dropout = state_dict.get('use_dropout', False)
print(f"Dropout usage is set to {self.use_dropout}" ) print(f"Dropout usage is set to {self.use_dropout}" )
self.activate_output = state_dict.get('activate_output', True)
print(f"Activate last layer is set to {self.activate_output}")
self.last_layer_dropout = state_dict.get('last_layer_dropout', False)
for size, sd in state_dict.items(): for size, sd in state_dict.items():
if type(size) == int: if type(size) == int:
self.layers[size] = ( self.layers[size] = (
HypernetworkModule(size, sd[0], self.layer_structure, self.activation_func, self.weight_init, self.add_layer_norm, self.use_dropout), HypernetworkModule(size, sd[0], self.layer_structure, self.activation_func, self.weight_init,
HypernetworkModule(size, sd[1], self.layer_structure, self.activation_func, self.weight_init, self.add_layer_norm, self.use_dropout), self.add_layer_norm, self.use_dropout, self.activate_output, last_layer_dropout=self.last_layer_dropout),
HypernetworkModule(size, sd[1], self.layer_structure, self.activation_func, self.weight_init,
self.add_layer_norm, self.use_dropout, self.activate_output, last_layer_dropout=self.last_layer_dropout),
) )
self.name = state_dict.get('name', self.name) self.name = state_dict.get('name', self.name)
......
...@@ -49,7 +49,7 @@ def expand_crop_region(crop_region, processing_width, processing_height, image_w ...@@ -49,7 +49,7 @@ def expand_crop_region(crop_region, processing_width, processing_height, image_w
ratio_processing = processing_width / processing_height ratio_processing = processing_width / processing_height
if ratio_crop_region > ratio_processing: if ratio_crop_region > ratio_processing:
desired_height = (x2 - x1) * ratio_processing desired_height = (x2 - x1) / ratio_processing
desired_height_diff = int(desired_height - (y2-y1)) desired_height_diff = int(desired_height - (y2-y1))
y1 -= desired_height_diff//2 y1 -= desired_height_diff//2
y2 += desired_height_diff - desired_height_diff//2 y2 += desired_height_diff - desired_height_diff//2
......
...@@ -134,11 +134,7 @@ class StableDiffusionProcessing(): ...@@ -134,11 +134,7 @@ class StableDiffusionProcessing():
# Dummy zero conditioning if we're not using inpainting model. # Dummy zero conditioning if we're not using inpainting model.
# Still takes up a bit of memory, but no encoder call. # Still takes up a bit of memory, but no encoder call.
# Pretty sure we can just make this a 1x1 image since its not going to be used besides its batch size. # Pretty sure we can just make this a 1x1 image since its not going to be used besides its batch size.
return torch.zeros( return x.new_zeros(x.shape[0], 5, 1, 1)
x.shape[0], 5, 1, 1,
dtype=x.dtype,
device=x.device
)
height = height or self.height height = height or self.height
width = width or self.width width = width or self.width
...@@ -156,11 +152,7 @@ class StableDiffusionProcessing(): ...@@ -156,11 +152,7 @@ class StableDiffusionProcessing():
def img2img_image_conditioning(self, source_image, latent_image, image_mask = None): def img2img_image_conditioning(self, source_image, latent_image, image_mask = None):
if self.sampler.conditioning_key not in {'hybrid', 'concat'}: if self.sampler.conditioning_key not in {'hybrid', 'concat'}:
# Dummy zero conditioning if we're not using inpainting model. # Dummy zero conditioning if we're not using inpainting model.
return torch.zeros( return latent_image.new_zeros(latent_image.shape[0], 5, 1, 1)
latent_image.shape[0], 5, 1, 1,
dtype=latent_image.dtype,
device=latent_image.device
)
# Handle the different mask inputs # Handle the different mask inputs
if image_mask is not None: if image_mask is not None:
...@@ -174,11 +166,11 @@ class StableDiffusionProcessing(): ...@@ -174,11 +166,11 @@ class StableDiffusionProcessing():
# Inpainting model uses a discretized mask as input, so we round to either 1.0 or 0.0 # Inpainting model uses a discretized mask as input, so we round to either 1.0 or 0.0
conditioning_mask = torch.round(conditioning_mask) conditioning_mask = torch.round(conditioning_mask)
else: else:
conditioning_mask = torch.ones(1, 1, *source_image.shape[-2:]) conditioning_mask = source_image.new_ones(1, 1, *source_image.shape[-2:])
# Create another latent image, this time with a masked version of the original input. # Create another latent image, this time with a masked version of the original input.
# Smoothly interpolate between the masked and unmasked latent conditioning image using a parameter. # Smoothly interpolate between the masked and unmasked latent conditioning image using a parameter.
conditioning_mask = conditioning_mask.to(source_image.device) conditioning_mask = conditioning_mask.to(source_image.device).to(source_image.dtype)
conditioning_image = torch.lerp( conditioning_image = torch.lerp(
source_image, source_image,
source_image * (1.0 - conditioning_mask), source_image * (1.0 - conditioning_mask),
...@@ -426,13 +418,13 @@ def process_images(p: StableDiffusionProcessing) -> Processed: ...@@ -426,13 +418,13 @@ def process_images(p: StableDiffusionProcessing) -> Processed:
try: try:
for k, v in p.override_settings.items(): for k, v in p.override_settings.items():
opts.data[k] = v # we don't call onchange for simplicity which makes changing model, hypernet impossible setattr(opts, k, v) # we don't call onchange for simplicity which makes changing model, hypernet impossible
res = process_images_inner(p) res = process_images_inner(p)
finally: finally:
for k, v in stored_opts.items(): for k, v in stored_opts.items():
opts.data[k] = v setattr(opts, k, v)
return res return res
...@@ -509,6 +501,9 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: ...@@ -509,6 +501,9 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
if len(prompts) == 0: if len(prompts) == 0:
break break
if p.scripts is not None:
p.scripts.process_batch(p, batch_number=n, prompts=prompts, seeds=seeds, subseeds=subseeds)
with devices.autocast(): with devices.autocast():
uc = prompt_parser.get_learned_conditioning(shared.sd_model, len(prompts) * [p.negative_prompt], p.steps) uc = prompt_parser.get_learned_conditioning(shared.sd_model, len(prompts) * [p.negative_prompt], p.steps)
c = prompt_parser.get_multicond_learned_conditioning(shared.sd_model, prompts, p.steps) c = prompt_parser.get_multicond_learned_conditioning(shared.sd_model, prompts, p.steps)
...@@ -673,10 +668,17 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing): ...@@ -673,10 +668,17 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing):
images.save_image(image, self.outpath_samples, "", seeds[index], prompts[index], opts.samples_format, suffix="-before-highres-fix") images.save_image(image, self.outpath_samples, "", seeds[index], prompts[index], opts.samples_format, suffix="-before-highres-fix")
if opts.use_scale_latent_for_hires_fix: if opts.use_scale_latent_for_hires_fix:
samples = torch.nn.functional.interpolate(samples, size=(self.height // opt_f, self.width // opt_f), mode="bilinear")
for i in range(samples.shape[0]): for i in range(samples.shape[0]):
save_intermediate(samples, i) save_intermediate(samples, i)
samples = torch.nn.functional.interpolate(samples, size=(self.height // opt_f, self.width // opt_f), mode="bilinear")
# Avoid making the inpainting conditioning unless necessary as
# this does need some extra compute to decode / encode the image again.
if getattr(self, "inpainting_mask_weight", shared.opts.inpainting_mask_weight) < 1.0:
image_conditioning = self.img2img_image_conditioning(decode_first_stage(self.sd_model, samples), samples)
else:
image_conditioning = self.txt2img_image_conditioning(samples)
else: else:
decoded_samples = decode_first_stage(self.sd_model, samples) decoded_samples = decode_first_stage(self.sd_model, samples)
lowres_samples = torch.clamp((decoded_samples + 1.0) / 2.0, min=0.0, max=1.0) lowres_samples = torch.clamp((decoded_samples + 1.0) / 2.0, min=0.0, max=1.0)
...@@ -700,14 +702,14 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing): ...@@ -700,14 +702,14 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing):
samples = self.sd_model.get_first_stage_encoding(self.sd_model.encode_first_stage(decoded_samples)) samples = self.sd_model.get_first_stage_encoding(self.sd_model.encode_first_stage(decoded_samples))
image_conditioning = self.img2img_image_conditioning(decoded_samples, samples)
shared.state.nextjob() shared.state.nextjob()
self.sampler = sd_samplers.create_sampler_with_index(sd_samplers.samplers, self.sampler_index, self.sd_model) self.sampler = sd_samplers.create_sampler_with_index(sd_samplers.samplers, self.sampler_index, self.sd_model)
noise = create_random_tensors(samples.shape[1:], seeds=seeds, subseeds=subseeds, subseed_strength=subseed_strength, seed_resize_from_h=self.seed_resize_from_h, seed_resize_from_w=self.seed_resize_from_w, p=self) noise = create_random_tensors(samples.shape[1:], seeds=seeds, subseeds=subseeds, subseed_strength=subseed_strength, seed_resize_from_h=self.seed_resize_from_h, seed_resize_from_w=self.seed_resize_from_w, p=self)
image_conditioning = self.txt2img_image_conditioning(x)
# GC now before running the next img2img to prevent running out of memory # GC now before running the next img2img to prevent running out of memory
x = None x = None
devices.torch_gc() devices.torch_gc()
......
...@@ -46,25 +46,23 @@ class CFGDenoiserParams: ...@@ -46,25 +46,23 @@ class CFGDenoiserParams:
ScriptCallback = namedtuple("ScriptCallback", ["script", "callback"]) ScriptCallback = namedtuple("ScriptCallback", ["script", "callback"])
callbacks_app_started = [] callback_map = dict(
callbacks_model_loaded = [] callbacks_app_started=[],
callbacks_ui_tabs = [] callbacks_model_loaded=[],
callbacks_ui_settings = [] callbacks_ui_tabs=[],
callbacks_before_image_saved = [] callbacks_ui_settings=[],
callbacks_image_saved = [] callbacks_before_image_saved=[],
callbacks_cfg_denoiser = [] callbacks_image_saved=[],
callbacks_cfg_denoiser=[]
)
def clear_callbacks(): def clear_callbacks():
callbacks_model_loaded.clear() for callback_list in callback_map.values():
callbacks_ui_tabs.clear() callback_list.clear()
callbacks_ui_settings.clear()
callbacks_before_image_saved.clear()
callbacks_image_saved.clear()
callbacks_cfg_denoiser.clear()
def app_started_callback(demo: Optional[Blocks], app: FastAPI): def app_started_callback(demo: Optional[Blocks], app: FastAPI):
for c in callbacks_app_started: for c in callback_map['callbacks_app_started']:
try: try:
c.callback(demo, app) c.callback(demo, app)
except Exception: except Exception:
...@@ -72,7 +70,7 @@ def app_started_callback(demo: Optional[Blocks], app: FastAPI): ...@@ -72,7 +70,7 @@ def app_started_callback(demo: Optional[Blocks], app: FastAPI):
def model_loaded_callback(sd_model): def model_loaded_callback(sd_model):
for c in callbacks_model_loaded: for c in callback_map['callbacks_model_loaded']:
try: try:
c.callback(sd_model) c.callback(sd_model)
except Exception: except Exception:
...@@ -82,7 +80,7 @@ def model_loaded_callback(sd_model): ...@@ -82,7 +80,7 @@ def model_loaded_callback(sd_model):
def ui_tabs_callback(): def ui_tabs_callback():
res = [] res = []
for c in callbacks_ui_tabs: for c in callback_map['callbacks_ui_tabs']:
try: try:
res += c.callback() or [] res += c.callback() or []
except Exception: except Exception:
...@@ -92,7 +90,7 @@ def ui_tabs_callback(): ...@@ -92,7 +90,7 @@ def ui_tabs_callback():
def ui_settings_callback(): def ui_settings_callback():
for c in callbacks_ui_settings: for c in callback_map['callbacks_ui_settings']:
try: try:
c.callback() c.callback()
except Exception: except Exception:
...@@ -100,7 +98,7 @@ def ui_settings_callback(): ...@@ -100,7 +98,7 @@ def ui_settings_callback():
def before_image_saved_callback(params: ImageSaveParams): def before_image_saved_callback(params: ImageSaveParams):
for c in callbacks_before_image_saved: for c in callback_map['callbacks_before_image_saved']:
try: try:
c.callback(params) c.callback(params)
except Exception: except Exception:
...@@ -108,7 +106,7 @@ def before_image_saved_callback(params: ImageSaveParams): ...@@ -108,7 +106,7 @@ def before_image_saved_callback(params: ImageSaveParams):
def image_saved_callback(params: ImageSaveParams): def image_saved_callback(params: ImageSaveParams):
for c in callbacks_image_saved: for c in callback_map['callbacks_image_saved']:
try: try:
c.callback(params) c.callback(params)
except Exception: except Exception:
...@@ -116,7 +114,7 @@ def image_saved_callback(params: ImageSaveParams): ...@@ -116,7 +114,7 @@ def image_saved_callback(params: ImageSaveParams):
def cfg_denoiser_callback(params: CFGDenoiserParams): def cfg_denoiser_callback(params: CFGDenoiserParams):
for c in callbacks_cfg_denoiser: for c in callback_map['callbacks_cfg_denoiser']:
try: try:
c.callback(params) c.callback(params)
except Exception: except Exception:
...@@ -129,17 +127,33 @@ def add_callback(callbacks, fun): ...@@ -129,17 +127,33 @@ def add_callback(callbacks, fun):
callbacks.append(ScriptCallback(filename, fun)) callbacks.append(ScriptCallback(filename, fun))
def remove_current_script_callbacks():
stack = [x for x in inspect.stack() if x.filename != __file__]
filename = stack[0].filename if len(stack) > 0 else 'unknown file'
if filename == 'unknown file':
return
for callback_list in callback_map.values():
for callback_to_remove in [cb for cb in callback_list if cb.script == filename]:
callback_list.remove(callback_to_remove)
def remove_callbacks_for_function(callback_func):
for callback_list in callback_map.values():
for callback_to_remove in [cb for cb in callback_list if cb.callback == callback_func]:
callback_list.remove(callback_to_remove)
def on_app_started(callback): def on_app_started(callback):
"""register a function to be called when the webui started, the gradio `Block` component and """register a function to be called when the webui started, the gradio `Block` component and
fastapi `FastAPI` object are passed as the arguments""" fastapi `FastAPI` object are passed as the arguments"""
add_callback(callbacks_app_started, callback) add_callback(callback_map['callbacks_app_started'], callback)
def on_model_loaded(callback): def on_model_loaded(callback):
"""register a function to be called when the stable diffusion model is created; the model is """register a function to be called when the stable diffusion model is created; the model is
passed as an argument""" passed as an argument"""
add_callback(callbacks_model_loaded, callback) add_callback(callback_map['callbacks_model_loaded'], callback)
def on_ui_tabs(callback): def on_ui_tabs(callback):
...@@ -152,13 +166,13 @@ def on_ui_tabs(callback): ...@@ -152,13 +166,13 @@ def on_ui_tabs(callback):
title is tab text displayed to user in the UI title is tab text displayed to user in the UI
elem_id is HTML id for the tab elem_id is HTML id for the tab
""" """
add_callback(callbacks_ui_tabs, callback) add_callback(callback_map['callbacks_ui_tabs'], callback)
def on_ui_settings(callback): def on_ui_settings(callback):
"""register a function to be called before UI settings are populated; add your settings """register a function to be called before UI settings are populated; add your settings
by using shared.opts.add_option(shared.OptionInfo(...)) """ by using shared.opts.add_option(shared.OptionInfo(...)) """
add_callback(callbacks_ui_settings, callback) add_callback(callback_map['callbacks_ui_settings'], callback)
def on_before_image_saved(callback): def on_before_image_saved(callback):
...@@ -166,7 +180,7 @@ def on_before_image_saved(callback): ...@@ -166,7 +180,7 @@ def on_before_image_saved(callback):
The callback is called with one argument: The callback is called with one argument:
- params: ImageSaveParams - parameters the image is to be saved with. You can change fields in this object. - params: ImageSaveParams - parameters the image is to be saved with. You can change fields in this object.
""" """
add_callback(callbacks_before_image_saved, callback) add_callback(callback_map['callbacks_before_image_saved'], callback)
def on_image_saved(callback): def on_image_saved(callback):
...@@ -174,7 +188,7 @@ def on_image_saved(callback): ...@@ -174,7 +188,7 @@ def on_image_saved(callback):
The callback is called with one argument: The callback is called with one argument:
- params: ImageSaveParams - parameters the image was saved with. Changing fields in this object does nothing. - params: ImageSaveParams - parameters the image was saved with. Changing fields in this object does nothing.
""" """
add_callback(callbacks_image_saved, callback) add_callback(callback_map['callbacks_image_saved'], callback)
def on_cfg_denoiser(callback): def on_cfg_denoiser(callback):
...@@ -182,5 +196,4 @@ def on_cfg_denoiser(callback): ...@@ -182,5 +196,4 @@ def on_cfg_denoiser(callback):
The callback is called with one argument: The callback is called with one argument:
- params: CFGDenoiserParams - parameters to be passed to the inner model and sampling state details. - params: CFGDenoiserParams - parameters to be passed to the inner model and sampling state details.
""" """
add_callback(callbacks_cfg_denoiser, callback) add_callback(callback_map['callbacks_cfg_denoiser'], callback)
...@@ -18,6 +18,9 @@ class Script: ...@@ -18,6 +18,9 @@ class Script:
args_to = None args_to = None
alwayson = False alwayson = False
"""A gr.Group component that has all script's UI inside it"""
group = None
infotext_fields = None infotext_fields = None
"""if set in ui(), this is a list of pairs of gradio component + text; the text will be used when """if set in ui(), this is a list of pairs of gradio component + text; the text will be used when
parsing infotext to set the value for the component; see ui.py's txt2img_paste_fields for an example parsing infotext to set the value for the component; see ui.py's txt2img_paste_fields for an example
...@@ -70,6 +73,19 @@ class Script: ...@@ -70,6 +73,19 @@ class Script:
pass pass
def process_batch(self, p, *args, **kwargs):
"""
Same as process(), but called for every batch.
**kwargs will have those items:
- batch_number - index of current batch, from 0 to number of batches-1
- prompts - list of prompts for current batch; you can change contents of this list but changing the number of entries will likely break things
- seeds - list of seeds for current batch
- subseeds - list of subseeds for current batch
"""
pass
def postprocess(self, p, processed, *args): def postprocess(self, p, processed, *args):
""" """
This function is called after processing ends for AlwaysVisible scripts. This function is called after processing ends for AlwaysVisible scripts.
...@@ -218,8 +234,6 @@ class ScriptRunner: ...@@ -218,8 +234,6 @@ class ScriptRunner:
for control in controls: for control in controls:
control.custom_script_source = os.path.basename(script.filename) control.custom_script_source = os.path.basename(script.filename)
if not script.alwayson:
control.visible = False
if script.infotext_fields is not None: if script.infotext_fields is not None:
self.infotext_fields += script.infotext_fields self.infotext_fields += script.infotext_fields
...@@ -229,40 +243,41 @@ class ScriptRunner: ...@@ -229,40 +243,41 @@ class ScriptRunner:
script.args_to = len(inputs) script.args_to = len(inputs)
for script in self.alwayson_scripts: for script in self.alwayson_scripts:
with gr.Group(): with gr.Group() as group:
create_script_ui(script, inputs, inputs_alwayson) create_script_ui(script, inputs, inputs_alwayson)
script.group = group
dropdown = gr.Dropdown(label="Script", elem_id="script_list", choices=["None"] + self.titles, value="None", type="index") dropdown = gr.Dropdown(label="Script", elem_id="script_list", choices=["None"] + self.titles, value="None", type="index")
dropdown.save_to_config = True dropdown.save_to_config = True
inputs[0] = dropdown inputs[0] = dropdown
for script in self.selectable_scripts: for script in self.selectable_scripts:
create_script_ui(script, inputs, inputs_alwayson) with gr.Group(visible=False) as group:
create_script_ui(script, inputs, inputs_alwayson)
script.group = group
def select_script(script_index): def select_script(script_index):
if 0 < script_index <= len(self.selectable_scripts): selected_script = self.selectable_scripts[script_index - 1] if script_index>0 else None
script = self.selectable_scripts[script_index-1]
args_from = script.args_from
args_to = script.args_to
else:
args_from = 0
args_to = 0
return [ui.gr_show(True if i == 0 else args_from <= i < args_to or is_alwayson) for i, is_alwayson in enumerate(inputs_alwayson)] return [gr.update(visible=selected_script == s) for s in self.selectable_scripts]
def init_field(title): def init_field(title):
"""called when an initial value is set from ui-config.json to show script's UI components"""
if title == 'None': if title == 'None':
return return
script_index = self.titles.index(title) script_index = self.titles.index(title)
script = self.selectable_scripts[script_index] self.selectable_scripts[script_index].group.visible = True
for i in range(script.args_from, script.args_to):
inputs[i].visible = True
dropdown.init_field = init_field dropdown.init_field = init_field
dropdown.change( dropdown.change(
fn=select_script, fn=select_script,
inputs=[dropdown], inputs=[dropdown],
outputs=inputs outputs=[script.group for script in self.selectable_scripts]
) )
return inputs return inputs
...@@ -294,6 +309,15 @@ class ScriptRunner: ...@@ -294,6 +309,15 @@ class ScriptRunner:
print(f"Error running process: {script.filename}", file=sys.stderr) print(f"Error running process: {script.filename}", file=sys.stderr)
print(traceback.format_exc(), file=sys.stderr) print(traceback.format_exc(), file=sys.stderr)
def process_batch(self, p, **kwargs):
for script in self.alwayson_scripts:
try:
script_args = p.script_args[script.args_from:script.args_to]
script.process_batch(p, *script_args, **kwargs)
except Exception:
print(f"Error running process_batch: {script.filename}", file=sys.stderr)
print(traceback.format_exc(), file=sys.stderr)
def postprocess(self, p, processed): def postprocess(self, p, processed):
for script in self.alwayson_scripts: for script in self.alwayson_scripts:
try: try:
......
...@@ -163,11 +163,13 @@ def load_model_weights(model, checkpoint_info, vae_file="auto"): ...@@ -163,11 +163,13 @@ def load_model_weights(model, checkpoint_info, vae_file="auto"):
checkpoint_file = checkpoint_info.filename checkpoint_file = checkpoint_info.filename
sd_model_hash = checkpoint_info.hash sd_model_hash = checkpoint_info.hash
vae_file = sd_vae.resolve_vae(checkpoint_file, vae_file=vae_file) if shared.opts.sd_checkpoint_cache > 0 and hasattr(model, "sd_checkpoint_info"):
sd_vae.restore_base_vae(model)
checkpoints_loaded[model.sd_checkpoint_info] = model.state_dict().copy()
checkpoint_key = checkpoint_info vae_file = sd_vae.resolve_vae(checkpoint_file, vae_file=vae_file)
if checkpoint_key not in checkpoints_loaded: if checkpoint_info not in checkpoints_loaded:
print(f"Loading weights [{sd_model_hash}] from {checkpoint_file}") print(f"Loading weights [{sd_model_hash}] from {checkpoint_file}")
pl_sd = torch.load(checkpoint_file, map_location=shared.weight_load_location) pl_sd = torch.load(checkpoint_file, map_location=shared.weight_load_location)
...@@ -197,17 +199,15 @@ def load_model_weights(model, checkpoint_info, vae_file="auto"): ...@@ -197,17 +199,15 @@ def load_model_weights(model, checkpoint_info, vae_file="auto"):
model.first_stage_model.to(devices.dtype_vae) model.first_stage_model.to(devices.dtype_vae)
if shared.opts.sd_checkpoint_cache > 0:
# if PR #4035 were to get merged, restore base VAE first before caching
checkpoints_loaded[checkpoint_key] = model.state_dict().copy()
while len(checkpoints_loaded) > shared.opts.sd_checkpoint_cache:
checkpoints_loaded.popitem(last=False) # LRU
else: else:
vae_name = sd_vae.get_filename(vae_file) vae_name = sd_vae.get_filename(vae_file) if vae_file else None
print(f"Loading weights [{sd_model_hash}] with {vae_name} VAE from cache") vae_message = f" with {vae_name} VAE" if vae_name else ""
checkpoints_loaded.move_to_end(checkpoint_key) print(f"Loading weights [{sd_model_hash}]{vae_message} from cache")
model.load_state_dict(checkpoints_loaded[checkpoint_key]) model.load_state_dict(checkpoints_loaded[checkpoint_info])
if shared.opts.sd_checkpoint_cache > 0:
while len(checkpoints_loaded) > shared.opts.sd_checkpoint_cache:
checkpoints_loaded.popitem(last=False) # LRU
model.sd_model_hash = sd_model_hash model.sd_model_hash = sd_model_hash
model.sd_model_checkpoint = checkpoint_file model.sd_model_checkpoint = checkpoint_file
......
...@@ -44,6 +44,7 @@ parser.add_argument("--precision", type=str, help="evaluate at this precision", ...@@ -44,6 +44,7 @@ parser.add_argument("--precision", type=str, help="evaluate at this precision",
parser.add_argument("--share", action='store_true', help="use share=True for gradio and make the UI accessible through their site") parser.add_argument("--share", action='store_true', help="use share=True for gradio and make the UI accessible through their site")
parser.add_argument("--ngrok", type=str, help="ngrok authtoken, alternative to gradio --share", default=None) parser.add_argument("--ngrok", type=str, help="ngrok authtoken, alternative to gradio --share", default=None)
parser.add_argument("--ngrok-region", type=str, help="The region in which ngrok should start.", default="us") parser.add_argument("--ngrok-region", type=str, help="The region in which ngrok should start.", default="us")
parser.add_argument("--enable-insecure-extension-access", action='store_true', help="enable extensions tab regardless of other options")
parser.add_argument("--codeformer-models-path", type=str, help="Path to directory with codeformer model file(s).", default=os.path.join(models_path, 'Codeformer')) parser.add_argument("--codeformer-models-path", type=str, help="Path to directory with codeformer model file(s).", default=os.path.join(models_path, 'Codeformer'))
parser.add_argument("--gfpgan-models-path", type=str, help="Path to directory with GFPGAN model file(s).", default=os.path.join(models_path, 'GFPGAN')) parser.add_argument("--gfpgan-models-path", type=str, help="Path to directory with GFPGAN model file(s).", default=os.path.join(models_path, 'GFPGAN'))
parser.add_argument("--esrgan-models-path", type=str, help="Path to directory with ESRGAN model file(s).", default=os.path.join(models_path, 'ESRGAN')) parser.add_argument("--esrgan-models-path", type=str, help="Path to directory with ESRGAN model file(s).", default=os.path.join(models_path, 'ESRGAN'))
...@@ -99,7 +100,7 @@ restricted_opts = { ...@@ -99,7 +100,7 @@ restricted_opts = {
"outdir_save", "outdir_save",
} }
cmd_opts.disable_extension_access = cmd_opts.share or cmd_opts.listen cmd_opts.disable_extension_access = (cmd_opts.share or cmd_opts.listen) and not cmd_opts.enable_insecure_extension_access
devices.device, devices.device_interrogate, devices.device_gfpgan, devices.device_swinir, devices.device_esrgan, devices.device_scunet, devices.device_codeformer = \ devices.device, devices.device_interrogate, devices.device_gfpgan, devices.device_swinir, devices.device_esrgan, devices.device_scunet, devices.device_codeformer = \
(devices.cpu if any(y in cmd_opts.use_cpu for y in [x, 'all']) else devices.get_optimal_device() for x in ['sd', 'interrogate', 'gfpgan', 'swinir', 'esrgan', 'scunet', 'codeformer']) (devices.cpu if any(y in cmd_opts.use_cpu for y in [x, 'all']) else devices.get_optimal_device() for x in ['sd', 'interrogate', 'gfpgan', 'swinir', 'esrgan', 'scunet', 'codeformer'])
...@@ -146,6 +147,9 @@ class State: ...@@ -146,6 +147,9 @@ class State:
self.interrupted = True self.interrupted = True
def nextjob(self): def nextjob(self):
if opts.show_progress_every_n_steps == -1:
self.do_set_current_image()
self.job_no += 1 self.job_no += 1
self.sampling_step = 0 self.sampling_step = 0
self.current_image_sampling_step = 0 self.current_image_sampling_step = 0
...@@ -186,17 +190,21 @@ class State: ...@@ -186,17 +190,21 @@ class State:
"""sets self.current_image from self.current_latent if enough sampling steps have been made after the last call to this""" """sets self.current_image from self.current_latent if enough sampling steps have been made after the last call to this"""
def set_current_image(self): def set_current_image(self):
if self.sampling_step - self.current_image_sampling_step >= opts.show_progress_every_n_steps and opts.show_progress_every_n_steps > 0:
self.do_set_current_image()
def do_set_current_image(self):
if not parallel_processing_allowed: if not parallel_processing_allowed:
return return
if self.current_latent is None:
return
if opts.show_progress_grid:
self.current_image = sd_samplers.samples_to_image_grid(self.current_latent)
else:
self.current_image = sd_samplers.sample_to_image(self.current_latent)
if self.sampling_step - self.current_image_sampling_step >= opts.show_progress_every_n_steps and self.current_latent is not None: self.current_image_sampling_step = self.sampling_step
if opts.show_progress_grid:
self.current_image = sd_samplers.samples_to_image_grid(self.current_latent)
else:
self.current_image = sd_samplers.sample_to_image(self.current_latent)
self.current_image_sampling_step = self.sampling_step
state = State() state = State()
...@@ -351,7 +359,7 @@ options_templates.update(options_section(('interrogate', "Interrogate Options"), ...@@ -351,7 +359,7 @@ options_templates.update(options_section(('interrogate', "Interrogate Options"),
options_templates.update(options_section(('ui', "User interface"), { options_templates.update(options_section(('ui', "User interface"), {
"show_progressbar": OptionInfo(True, "Show progressbar"), "show_progressbar": OptionInfo(True, "Show progressbar"),
"show_progress_every_n_steps": OptionInfo(0, "Show image creation progress every N sampling steps. Set 0 to disable.", gr.Slider, {"minimum": 0, "maximum": 32, "step": 1}), "show_progress_every_n_steps": OptionInfo(0, "Show image creation progress every N sampling steps. Set to 0 to disable. Set to -1 to show after completion of batch.", gr.Slider, {"minimum": -1, "maximum": 32, "step": 1}),
"show_progress_grid": OptionInfo(True, "Show previews of all images generated in a batch as a grid"), "show_progress_grid": OptionInfo(True, "Show previews of all images generated in a batch as a grid"),
"return_grid": OptionInfo(True, "Show grid in results for web"), "return_grid": OptionInfo(True, "Show grid in results for web"),
"do_not_show_images": OptionInfo(False, "Do not show any images in results for web"), "do_not_show_images": OptionInfo(False, "Do not show any images in results for web"),
...@@ -396,6 +404,15 @@ class Options: ...@@ -396,6 +404,15 @@ class Options:
def __setattr__(self, key, value): def __setattr__(self, key, value):
if self.data is not None: if self.data is not None:
if key in self.data or key in self.data_labels: if key in self.data or key in self.data_labels:
assert not cmd_opts.freeze_settings, "changing settings is disabled"
comp_args = opts.data_labels[key].component_args
if isinstance(comp_args, dict) and comp_args.get('visible', True) is False:
raise RuntimeError(f"not possible to set {key} because it is restricted")
if cmd_opts.hide_ui_dir_config and key in restricted_opts:
raise RuntimeError(f"not possible to set {key} because it is restricted")
self.data[key] = value self.data[key] = value
return return
...@@ -412,6 +429,8 @@ class Options: ...@@ -412,6 +429,8 @@ class Options:
return super(Options, self).__getattribute__(item) return super(Options, self).__getattribute__(item)
def save(self, filename): def save(self, filename):
assert not cmd_opts.freeze_settings, "saving settings is disabled"
with open(filename, "w", encoding="utf8") as file: with open(filename, "w", encoding="utf8") as file:
json.dump(self.data, file, indent=4) json.dump(self.data, file, indent=4)
......
...@@ -276,7 +276,7 @@ def check_progress_call(id_part): ...@@ -276,7 +276,7 @@ def check_progress_call(id_part):
image = gr_show(False) image = gr_show(False)
preview_visibility = gr_show(False) preview_visibility = gr_show(False)
if opts.show_progress_every_n_steps > 0: if opts.show_progress_every_n_steps != 0:
shared.state.set_current_image() shared.state.set_current_image()
image = shared.state.current_image image = shared.state.current_image
...@@ -1052,6 +1052,8 @@ def create_ui(wrap_gradio_gpu_call): ...@@ -1052,6 +1052,8 @@ def create_ui(wrap_gradio_gpu_call):
extras_batch_output_dir = gr.Textbox(label="Output directory", **shared.hide_dirs, placeholder="Leave blank to save images to the default path.") extras_batch_output_dir = gr.Textbox(label="Output directory", **shared.hide_dirs, placeholder="Leave blank to save images to the default path.")
show_extras_results = gr.Checkbox(label='Show result images', value=True) show_extras_results = gr.Checkbox(label='Show result images', value=True)
submit = gr.Button('Generate', elem_id="extras_generate", variant='primary')
with gr.Tabs(elem_id="extras_resize_mode"): with gr.Tabs(elem_id="extras_resize_mode"):
with gr.TabItem('Scale by'): with gr.TabItem('Scale by'):
upscaling_resize = gr.Slider(minimum=1.0, maximum=8.0, step=0.05, label="Resize", value=4) upscaling_resize = gr.Slider(minimum=1.0, maximum=8.0, step=0.05, label="Resize", value=4)
...@@ -1079,8 +1081,6 @@ def create_ui(wrap_gradio_gpu_call): ...@@ -1079,8 +1081,6 @@ def create_ui(wrap_gradio_gpu_call):
with gr.Group(): with gr.Group():
upscale_before_face_fix = gr.Checkbox(label='Upscale Before Restoring Faces', value=False) upscale_before_face_fix = gr.Checkbox(label='Upscale Before Restoring Faces', value=False)
submit = gr.Button('Generate', elem_id="extras_generate", variant='primary')
result_images, html_info_x, html_info = create_output_panel("extras", opts.outdir_extras_samples) result_images, html_info_x, html_info = create_output_panel("extras", opts.outdir_extras_samples)
submit.click( submit.click(
...@@ -1182,8 +1182,8 @@ def create_ui(wrap_gradio_gpu_call): ...@@ -1182,8 +1182,8 @@ def create_ui(wrap_gradio_gpu_call):
new_hypernetwork_name = gr.Textbox(label="Name") new_hypernetwork_name = gr.Textbox(label="Name")
new_hypernetwork_sizes = gr.CheckboxGroup(label="Modules", value=["768", "320", "640", "1280"], choices=["768", "320", "640", "1280"]) new_hypernetwork_sizes = gr.CheckboxGroup(label="Modules", value=["768", "320", "640", "1280"], choices=["768", "320", "640", "1280"])
new_hypernetwork_layer_structure = gr.Textbox("1, 2, 1", label="Enter hypernetwork layer structure", placeholder="1st and last digit must be 1. ex:'1, 2, 1'") new_hypernetwork_layer_structure = gr.Textbox("1, 2, 1", label="Enter hypernetwork layer structure", placeholder="1st and last digit must be 1. ex:'1, 2, 1'")
new_hypernetwork_activation_func = gr.Dropdown(value="linear", label="Select activation function of hypernetwork", choices=modules.hypernetworks.ui.keys) new_hypernetwork_activation_func = gr.Dropdown(value="linear", label="Select activation function of hypernetwork. Recommended : Swish / Linear(none)", choices=modules.hypernetworks.ui.keys)
new_hypernetwork_initialization_option = gr.Dropdown(value = "Normal", label="Select Layer weights initialization. relu-like - Kaiming, sigmoid-like - Xavier is recommended", choices=["Normal", "KaimingUniform", "KaimingNormal", "XavierUniform", "XavierNormal"]) new_hypernetwork_initialization_option = gr.Dropdown(value = "Normal", label="Select Layer weights initialization. Recommended: Kaiming for relu-like, Xavier for sigmoid-like, Normal otherwise", choices=["Normal", "KaimingUniform", "KaimingNormal", "XavierUniform", "XavierNormal"])
new_hypernetwork_add_layer_norm = gr.Checkbox(label="Add layer normalization") new_hypernetwork_add_layer_norm = gr.Checkbox(label="Add layer normalization")
new_hypernetwork_use_dropout = gr.Checkbox(label="Use dropout") new_hypernetwork_use_dropout = gr.Checkbox(label="Use dropout")
overwrite_old_hypernetwork = gr.Checkbox(value=False, label="Overwrite Old Hypernetwork") overwrite_old_hypernetwork = gr.Checkbox(value=False, label="Overwrite Old Hypernetwork")
...@@ -1438,25 +1438,16 @@ def create_ui(wrap_gradio_gpu_call): ...@@ -1438,25 +1438,16 @@ def create_ui(wrap_gradio_gpu_call):
def run_settings(*args): def run_settings(*args):
changed = 0 changed = 0
assert not shared.cmd_opts.freeze_settings, "changing settings is disabled"
for key, value, comp in zip(opts.data_labels.keys(), args, components): for key, value, comp in zip(opts.data_labels.keys(), args, components):
if comp != dummy_component and not opts.same_type(value, opts.data_labels[key].default): assert comp == dummy_component or opts.same_type(value, opts.data_labels[key].default), f"Bad value for setting {key}: {value}; expecting {type(opts.data_labels[key].default).__name__}"
return f"Bad value for setting {key}: {value}; expecting {type(opts.data_labels[key].default).__name__}", opts.dumpjson()
for key, value, comp in zip(opts.data_labels.keys(), args, components): for key, value, comp in zip(opts.data_labels.keys(), args, components):
if comp == dummy_component: if comp == dummy_component:
continue continue
comp_args = opts.data_labels[key].component_args
if comp_args and isinstance(comp_args, dict) and comp_args.get('visible') is False:
continue
if cmd_opts.hide_ui_dir_config and key in restricted_opts:
continue
oldval = opts.data.get(key, None) oldval = opts.data.get(key, None)
opts.data[key] = value
setattr(opts, key, value)
if oldval != value: if oldval != value:
if opts.data_labels[key].onchange is not None: if opts.data_labels[key].onchange is not None:
...@@ -1466,20 +1457,18 @@ def create_ui(wrap_gradio_gpu_call): ...@@ -1466,20 +1457,18 @@ def create_ui(wrap_gradio_gpu_call):
opts.save(shared.config_filename) opts.save(shared.config_filename)
return f'{changed} settings changed.', opts.dumpjson() return opts.dumpjson(), f'{changed} settings changed.'
def run_settings_single(value, key): def run_settings_single(value, key):
assert not shared.cmd_opts.freeze_settings, "changing settings is disabled"
if not opts.same_type(value, opts.data_labels[key].default): if not opts.same_type(value, opts.data_labels[key].default):
return gr.update(visible=True), opts.dumpjson() return gr.update(visible=True), opts.dumpjson()
oldval = opts.data.get(key, None) oldval = opts.data.get(key, None)
if cmd_opts.hide_ui_dir_config and key in restricted_opts: try:
setattr(opts, key, value)
except Exception:
return gr.update(value=oldval), opts.dumpjson() return gr.update(value=oldval), opts.dumpjson()
opts.data[key] = value
if oldval != value: if oldval != value:
if opts.data_labels[key].onchange is not None: if opts.data_labels[key].onchange is not None:
opts.data_labels[key].onchange() opts.data_labels[key].onchange()
...@@ -1632,9 +1621,9 @@ def create_ui(wrap_gradio_gpu_call): ...@@ -1632,9 +1621,9 @@ def create_ui(wrap_gradio_gpu_call):
text_settings = gr.Textbox(elem_id="settings_json", value=lambda: opts.dumpjson(), visible=False) text_settings = gr.Textbox(elem_id="settings_json", value=lambda: opts.dumpjson(), visible=False)
settings_submit.click( settings_submit.click(
fn=run_settings, fn=wrap_gradio_call(run_settings, extra_outputs=[gr.update()]),
inputs=components, inputs=components,
outputs=[result, text_settings], outputs=[text_settings, result],
) )
for i, k, item in quicksettings_list: for i, k, item in quicksettings_list:
......
...@@ -86,7 +86,7 @@ def extension_table(): ...@@ -86,7 +86,7 @@ def extension_table():
code += f""" code += f"""
<tr> <tr>
<td><label><input class="gr-check-radio gr-checkbox" name="enable_{html.escape(ext.name)}" type="checkbox" {'checked="checked"' if ext.enabled else ''}>{html.escape(ext.name)}</label></td> <td><label><input class="gr-check-radio gr-checkbox" name="enable_{html.escape(ext.name)}" type="checkbox" {'checked="checked"' if ext.enabled else ''}>{html.escape(ext.name)}</label></td>
<td><a href="{html.escape(ext.remote or '')}">{html.escape(ext.remote or '')}</a></td> <td><a href="{html.escape(ext.remote or '')}" target="_blank">{html.escape(ext.remote or '')}</a></td>
<td{' class="extension_status"' if ext.remote is not None else ''}>{ext_status}</td> <td{' class="extension_status"' if ext.remote is not None else ''}>{ext_status}</td>
</tr> </tr>
""" """
......
...@@ -14,7 +14,7 @@ class Script(scripts.Script): ...@@ -14,7 +14,7 @@ class Script(scripts.Script):
return cmd_opts.allow_code return cmd_opts.allow_code
def ui(self, is_img2img): def ui(self, is_img2img):
code = gr.Textbox(label="Python code", visible=False, lines=1) code = gr.Textbox(label="Python code", lines=1)
return [code] return [code]
......
...@@ -132,7 +132,7 @@ class Script(scripts.Script): ...@@ -132,7 +132,7 @@ class Script(scripts.Script):
info = gr.HTML("<p style=\"margin-bottom:0.75em\">Recommended settings: Sampling Steps: 80-100, Sampler: Euler a, Denoising strength: 0.8</p>") info = gr.HTML("<p style=\"margin-bottom:0.75em\">Recommended settings: Sampling Steps: 80-100, Sampler: Euler a, Denoising strength: 0.8</p>")
pixels = gr.Slider(label="Pixels to expand", minimum=8, maximum=256, step=8, value=128) pixels = gr.Slider(label="Pixels to expand", minimum=8, maximum=256, step=8, value=128)
mask_blur = gr.Slider(label='Mask blur', minimum=0, maximum=64, step=1, value=8, visible=False) mask_blur = gr.Slider(label='Mask blur', minimum=0, maximum=64, step=1, value=8)
direction = gr.CheckboxGroup(label="Outpainting direction", choices=['left', 'right', 'up', 'down'], value=['left', 'right', 'up', 'down']) direction = gr.CheckboxGroup(label="Outpainting direction", choices=['left', 'right', 'up', 'down'], value=['left', 'right', 'up', 'down'])
noise_q = gr.Slider(label="Fall-off exponent (lower=higher detail)", minimum=0.0, maximum=4.0, step=0.01, value=1.0) noise_q = gr.Slider(label="Fall-off exponent (lower=higher detail)", minimum=0.0, maximum=4.0, step=0.01, value=1.0)
color_variation = gr.Slider(label="Color variation", minimum=0.0, maximum=1.0, step=0.01, value=0.05) color_variation = gr.Slider(label="Color variation", minimum=0.0, maximum=1.0, step=0.01, value=0.05)
......
...@@ -22,8 +22,8 @@ class Script(scripts.Script): ...@@ -22,8 +22,8 @@ class Script(scripts.Script):
return None return None
pixels = gr.Slider(label="Pixels to expand", minimum=8, maximum=256, step=8, value=128) pixels = gr.Slider(label="Pixels to expand", minimum=8, maximum=256, step=8, value=128)
mask_blur = gr.Slider(label='Mask blur', minimum=0, maximum=64, step=1, value=4, visible=False) mask_blur = gr.Slider(label='Mask blur', minimum=0, maximum=64, step=1, value=4)
inpainting_fill = gr.Radio(label='Masked content', choices=['fill', 'original', 'latent noise', 'latent nothing'], value='fill', type="index", visible=False) inpainting_fill = gr.Radio(label='Masked content', choices=['fill', 'original', 'latent noise', 'latent nothing'], value='fill', type="index")
direction = gr.CheckboxGroup(label="Outpainting direction", choices=['left', 'right', 'up', 'down'], value=['left', 'right', 'up', 'down']) direction = gr.CheckboxGroup(label="Outpainting direction", choices=['left', 'right', 'up', 'down'], value=['left', 'right', 'up', 'down'])
return [pixels, mask_blur, inpainting_fill, direction] return [pixels, mask_blur, inpainting_fill, direction]
......
...@@ -83,13 +83,14 @@ def cmdargs(line): ...@@ -83,13 +83,14 @@ def cmdargs(line):
def load_prompt_file(file): def load_prompt_file(file):
if (file is None): if file is None:
lines = [] lines = []
else: else:
lines = [x.strip() for x in file.decode('utf8', errors='ignore').split("\n")] lines = [x.strip() for x in file.decode('utf8', errors='ignore').split("\n")]
return None, "\n".join(lines), gr.update(lines=7) return None, "\n".join(lines), gr.update(lines=7)
class Script(scripts.Script): class Script(scripts.Script):
def title(self): def title(self):
return "Prompts from file or textbox" return "Prompts from file or textbox"
...@@ -107,9 +108,9 @@ class Script(scripts.Script): ...@@ -107,9 +108,9 @@ class Script(scripts.Script):
# We don't shrink back to 1, because that causes the control to ignore [enter], and it may # We don't shrink back to 1, because that causes the control to ignore [enter], and it may
# be unclear to the user that shift-enter is needed. # be unclear to the user that shift-enter is needed.
prompt_txt.change(lambda tb: gr.update(lines=7) if ("\n" in tb) else gr.update(lines=2), inputs=[prompt_txt], outputs=[prompt_txt]) prompt_txt.change(lambda tb: gr.update(lines=7) if ("\n" in tb) else gr.update(lines=2), inputs=[prompt_txt], outputs=[prompt_txt])
return [checkbox_iterate, checkbox_iterate_batch, file, prompt_txt] return [checkbox_iterate, checkbox_iterate_batch, prompt_txt]
def run(self, p, checkbox_iterate, checkbox_iterate_batch, file, prompt_txt: str): def run(self, p, checkbox_iterate, checkbox_iterate_batch, prompt_txt: str):
lines = [x.strip() for x in prompt_txt.splitlines()] lines = [x.strip() for x in prompt_txt.splitlines()]
lines = [x for x in lines if len(x) > 0] lines = [x for x in lines if len(x) > 0]
...@@ -157,5 +158,4 @@ class Script(scripts.Script): ...@@ -157,5 +158,4 @@ class Script(scripts.Script):
if checkbox_iterate: if checkbox_iterate:
p.seed = p.seed + (p.batch_size * p.n_iter) p.seed = p.seed + (p.batch_size * p.n_iter)
return Processed(p, images, p.seed, "")
return Processed(p, images, p.seed, "")
\ No newline at end of file
...@@ -18,8 +18,8 @@ class Script(scripts.Script): ...@@ -18,8 +18,8 @@ class Script(scripts.Script):
def ui(self, is_img2img): def ui(self, is_img2img):
info = gr.HTML("<p style=\"margin-bottom:0.75em\">Will upscale the image to twice the dimensions; use width and height sliders to set tile size</p>") info = gr.HTML("<p style=\"margin-bottom:0.75em\">Will upscale the image to twice the dimensions; use width and height sliders to set tile size</p>")
overlap = gr.Slider(minimum=0, maximum=256, step=16, label='Tile overlap', value=64, visible=False) overlap = gr.Slider(minimum=0, maximum=256, step=16, label='Tile overlap', value=64)
upscaler_index = gr.Radio(label='Upscaler', choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name, type="index", visible=False) upscaler_index = gr.Radio(label='Upscaler', choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name, type="index")
return [info, overlap, upscaler_index] return [info, overlap, upscaler_index]
......
...@@ -263,12 +263,12 @@ class Script(scripts.Script): ...@@ -263,12 +263,12 @@ class Script(scripts.Script):
current_axis_options = [x for x in axis_options if type(x) == AxisOption or type(x) == AxisOptionImg2Img and is_img2img] current_axis_options = [x for x in axis_options if type(x) == AxisOption or type(x) == AxisOptionImg2Img and is_img2img]
with gr.Row(): with gr.Row():
x_type = gr.Dropdown(label="X type", choices=[x.label for x in current_axis_options], value=current_axis_options[1].label, visible=False, type="index", elem_id="x_type") x_type = gr.Dropdown(label="X type", choices=[x.label for x in current_axis_options], value=current_axis_options[1].label, type="index", elem_id="x_type")
x_values = gr.Textbox(label="X values", visible=False, lines=1) x_values = gr.Textbox(label="X values", lines=1)
with gr.Row(): with gr.Row():
y_type = gr.Dropdown(label="Y type", choices=[x.label for x in current_axis_options], value=current_axis_options[0].label, visible=False, type="index", elem_id="y_type") y_type = gr.Dropdown(label="Y type", choices=[x.label for x in current_axis_options], value=current_axis_options[0].label, type="index", elem_id="y_type")
y_values = gr.Textbox(label="Y values", visible=False, lines=1) y_values = gr.Textbox(label="Y values", lines=1)
draw_legend = gr.Checkbox(label='Draw legend', value=True) draw_legend = gr.Checkbox(label='Draw legend', value=True)
include_lone_images = gr.Checkbox(label='Include Separate Images', value=False) include_lone_images = gr.Checkbox(label='Include Separate Images', value=False)
......
import unittest
import requests
class UtilsTests(unittest.TestCase):
def setUp(self):
self.url_options = "http://localhost:7860/sdapi/v1/options"
self.url_cmd_flags = "http://localhost:7860/sdapi/v1/cmd-flags"
self.url_samplers = "http://localhost:7860/sdapi/v1/samplers"
self.url_upscalers = "http://localhost:7860/sdapi/v1/upscalers"
self.url_sd_models = "http://localhost:7860/sdapi/v1/sd-models"
self.url_hypernetworks = "http://localhost:7860/sdapi/v1/hypernetworks"
self.url_face_restorers = "http://localhost:7860/sdapi/v1/face-restorers"
self.url_realesrgan_models = "http://localhost:7860/sdapi/v1/realesrgan-models"
self.url_prompt_styles = "http://localhost:7860/sdapi/v1/prompt-styles"
self.url_artist_categories = "http://localhost:7860/sdapi/v1/artist-categories"
self.url_artists = "http://localhost:7860/sdapi/v1/artists"
def test_options_get(self):
self.assertEqual(requests.get(self.url_options).status_code, 200)
def test_options_write(self):
response = requests.get(self.url_options)
self.assertEqual(response.status_code, 200)
pre_value = response.json()["send_seed"]
self.assertEqual(requests.post(self.url_options, json={"send_seed":not pre_value}).status_code, 200)
response = requests.get(self.url_options)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json()["send_seed"], not pre_value)
requests.post(self.url_options, json={"send_seed": pre_value})
def test_cmd_flags(self):
self.assertEqual(requests.get(self.url_cmd_flags).status_code, 200)
def test_samplers(self):
self.assertEqual(requests.get(self.url_samplers).status_code, 200)
def test_upscalers(self):
self.assertEqual(requests.get(self.url_upscalers).status_code, 200)
def test_sd_models(self):
self.assertEqual(requests.get(self.url_sd_models).status_code, 200)
def test_hypernetworks(self):
self.assertEqual(requests.get(self.url_hypernetworks).status_code, 200)
def test_face_restorers(self):
self.assertEqual(requests.get(self.url_face_restorers).status_code, 200)
def test_realesrgan_models(self):
self.assertEqual(requests.get(self.url_realesrgan_models).status_code, 200)
def test_prompt_styles(self):
self.assertEqual(requests.get(self.url_prompt_styles).status_code, 200)
def test_artist_categories(self):
self.assertEqual(requests.get(self.url_artist_categories).status_code, 200)
def test_artists(self):
self.assertEqual(requests.get(self.url_artists).status_code, 200)
\ No newline at end of file
...@@ -141,6 +141,12 @@ def webui(): ...@@ -141,6 +141,12 @@ def webui():
# after initial launch, disable --autolaunch for subsequent restarts # after initial launch, disable --autolaunch for subsequent restarts
cmd_opts.autolaunch = False cmd_opts.autolaunch = False
# gradio uses a very open CORS policy via app.user_middleware, which makes it possible for
# an attacker to trick the user into opening a malicious HTML page, which makes a request to the
# running web ui and do whatever the attcker wants, including installing an extension and
# runnnig its code. We disable this here. Suggested by RyotaK.
app.user_middleware = [x for x in app.user_middleware if x.cls.__name__ != 'CORSMiddleware']
app.add_middleware(GZipMiddleware, minimum_size=1000) app.add_middleware(GZipMiddleware, minimum_size=1000)
if launch_api: if launch_api:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment