From 1b4d04737ac513cbd55958bb60a4f85166f3484b Mon Sep 17 00:00:00 2001 From: Bruno Seoane Date: Sat, 22 Oct 2022 20:13:16 -0300 Subject: [PATCH 01/71] Remove unused imports --- modules/api/api.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/modules/api/api.py b/modules/api/api.py index 5b0c934e..a5136b4b 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -1,11 +1,9 @@ from modules.api.processing import StableDiffusionProcessingAPI from modules.processing import StableDiffusionProcessingTxt2Img, process_images from modules.sd_samplers import all_samplers -from modules.extras import run_pnginfo import modules.shared as shared import uvicorn -from fastapi import Body, APIRouter, HTTPException -from fastapi.responses import JSONResponse +from fastapi import APIRouter, HTTPException from pydantic import BaseModel, Field, Json import json import io @@ -18,7 +16,6 @@ class TextToImageResponse(BaseModel): parameters: Json info: Json - class Api: def __init__(self, app, queue_lock): self.router = APIRouter() From b02926df1393df311db734af149fb9faf4389cbe Mon Sep 17 00:00:00 2001 From: Bruno Seoane Date: Sat, 22 Oct 2022 20:24:04 -0300 Subject: [PATCH 02/71] Moved moodels to their own file and extracted base64 conversion to its own function --- modules/api/api.py | 17 ++++++----------- modules/api/models.py | 8 ++++++++ 2 files changed, 14 insertions(+), 11 deletions(-) create mode 100644 modules/api/models.py diff --git a/modules/api/api.py b/modules/api/api.py index a5136b4b..c17d7580 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -4,17 +4,17 @@ from modules.sd_samplers import all_samplers import modules.shared as shared import uvicorn from fastapi import APIRouter, HTTPException -from pydantic import BaseModel, Field, Json import json import io import base64 +from modules.api.models import * sampler_to_index = lambda name: next(filter(lambda row: name.lower() == row[1].name.lower(), enumerate(all_samplers)), None) -class TextToImageResponse(BaseModel): - images: list[str] = Field(default=None, title="Image", description="The generated image in base64 format.") - parameters: Json - info: Json +def img_to_base64(img): + buffer = io.BytesIO() + img.save(buffer, format="png") + return base64.b64encode(buffer.getvalue()) class Api: def __init__(self, app, queue_lock): @@ -41,15 +41,10 @@ class Api: with self.queue_lock: processed = process_images(p) - b64images = [] - for i in processed.images: - buffer = io.BytesIO() - i.save(buffer, format="png") - b64images.append(base64.b64encode(buffer.getvalue())) + b64images = list(map(img_to_base64, processed.images)) return TextToImageResponse(images=b64images, parameters=json.dumps(vars(txt2imgreq)), info=json.dumps(processed.info)) - def img2imgapi(self): raise NotImplementedError diff --git a/modules/api/models.py b/modules/api/models.py new file mode 100644 index 00000000..a7d247d8 --- /dev/null +++ b/modules/api/models.py @@ -0,0 +1,8 @@ +from pydantic import BaseModel, Field, Json + +class TextToImageResponse(BaseModel): + images: list[str] = Field(default=None, title="Image", description="The generated image in base64 format.") + parameters: Json + info: Json + + \ No newline at end of file From 28e26c2bef217ae82eb9e980cceb3f67ef22e109 Mon Sep 17 00:00:00 2001 From: Bruno Seoane Date: Sat, 22 Oct 2022 23:13:32 -0300 Subject: [PATCH 03/71] Add "extra" single image operation - Separate extra modes into 3 endpoints so the user ddoesn't ahve to handle so many unused parameters. - Add response model for codumentation --- modules/api/api.py | 43 ++++++++++++++++++++++++++++++++++++++----- modules/api/models.py | 26 +++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 6 deletions(-) diff --git a/modules/api/api.py b/modules/api/api.py index c17d7580..3b804373 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -8,20 +8,42 @@ import json import io import base64 from modules.api.models import * +from PIL import Image +from modules.extras import run_extras + +def upscaler_to_index(name: str): + try: + return [x.name.lower() for x in shared.sd_upscalers].index(name.lower()) + except: + raise HTTPException(status_code=400, detail="Upscaler not found") sampler_to_index = lambda name: next(filter(lambda row: name.lower() == row[1].name.lower(), enumerate(all_samplers)), None) -def img_to_base64(img): +def img_to_base64(img: str): buffer = io.BytesIO() img.save(buffer, format="png") return base64.b64encode(buffer.getvalue()) +def base64_to_bytes(base64Img: str): + if "," in base64Img: + base64Img = base64Img.split(",")[1] + return io.BytesIO(base64.b64decode(base64Img)) + +def base64_to_images(base64Imgs: list[str]): + imgs = [] + for img in base64Imgs: + img = Image.open(base64_to_bytes(img)) + imgs.append(img) + return imgs + + class Api: def __init__(self, app, queue_lock): self.router = APIRouter() self.app = app self.queue_lock = queue_lock - self.app.add_api_route("/sdapi/v1/txt2img", self.text2imgapi, methods=["POST"]) + self.app.add_api_route("/sdapi/v1/txt2img", self.text2imgapi, methods=["POST"], response_model=TextToImageResponse) + self.app.add_api_route("/sdapi/v1/extra-single-image", self.extras_single_image_api, methods=["POST"], response_model=ExtrasSingleImageResponse) def text2imgapi(self, txt2imgreq: StableDiffusionProcessingAPI ): sampler_index = sampler_to_index(txt2imgreq.sampler_index) @@ -45,12 +67,23 @@ class Api: return TextToImageResponse(images=b64images, parameters=json.dumps(vars(txt2imgreq)), info=json.dumps(processed.info)) - def img2imgapi(self): raise NotImplementedError - def extrasapi(self): - raise NotImplementedError + def extras_single_image_api(self, req: ExtrasSingleImageRequest): + upscaler1Index = upscaler_to_index(req.upscaler_1) + upscaler2Index = upscaler_to_index(req.upscaler_2) + + reqDict = vars(req) + reqDict.pop('upscaler_1') + reqDict.pop('upscaler_2') + + reqDict['image'] = base64_to_images([reqDict['image']])[0] + + with self.queue_lock: + result = run_extras(**reqDict, extras_upscaler_1=upscaler1Index, extras_upscaler_2=upscaler2Index, extras_mode=0, image_folder="", input_dir="", output_dir="") + + return ExtrasSingleImageResponse(image="data:image/png;base64,"+img_to_base64(result[0]), html_info_x=result[1], html_info=result[2]) def pnginfoapi(self): raise NotImplementedError diff --git a/modules/api/models.py b/modules/api/models.py index a7d247d8..dcf1ab54 100644 --- a/modules/api/models.py +++ b/modules/api/models.py @@ -1,8 +1,32 @@ from pydantic import BaseModel, Field, Json +from typing_extensions import Literal +from modules.shared import sd_upscalers class TextToImageResponse(BaseModel): images: list[str] = Field(default=None, title="Image", description="The generated image in base64 format.") parameters: Json info: Json - \ No newline at end of file +class ExtrasBaseRequest(BaseModel): + resize_mode: Literal[0, 1] = Field(default=0, title="Resize Mode", description="Sets the resize mode: 0 to upscale by upscaling_resize amount, 1 to upscale up to upscaling_resize_h x upscaling_resize_w.") + show_extras_results: bool = Field(default=True, title="Show results", description="Should the backend return the generated image?") + gfpgan_visibility: float = Field(default=0, title="GFPGAN Visibility", ge=0, le=1, allow_inf_nan=False, description="Sets the visibility of GFPGAN, values should be between 0 and 1.") + codeformer_visibility: float = Field(default=0, title="CodeFormer Visibility", ge=0, le=1, allow_inf_nan=False, description="Sets the visibility of CodeFormer, values should be between 0 and 1.") + codeformer_weight: float = Field(default=0, title="CodeFormer Weight", ge=0, le=1, allow_inf_nan=False, description="Sets the weight of CodeFormer, values should be between 0 and 1.") + upscaling_resize: float = Field(default=2, title="Upscaling Factor", ge=1, le=4, description="By how much to upscale the image, only used when resize_mode=0.") + upscaling_resize_w: int = Field(default=512, title="Target Width", ge=1, description="Target width for the upscaler to hit. Only used when resize_mode=1.") + upscaling_resize_h: int = Field(default=512, title="Target Height", ge=1, description="Target height for the upscaler to hit. Only used when resize_mode=1.") + upscaling_crop: bool = Field(default=True, title="Crop to fit", description="Should the upscaler crop the image to fit in the choosen size?") + 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])}") + 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.") + +class ExtraBaseResponse(BaseModel): + html_info_x: str + html_info: str + +class ExtrasSingleImageRequest(ExtrasBaseRequest): + image: str = Field(default="", title="Image", description="Image to work on, must be a Base64 string containing the image's data.") + +class ExtrasSingleImageResponse(ExtraBaseResponse): + image: str = Field(default=None, title="Image", description="The generated image in base64 format.") \ No newline at end of file From 0523704dade0508bf3ae0c8cb799b1ae332d449b Mon Sep 17 00:00:00 2001 From: Bruno Seoane Date: Sun, 23 Oct 2022 12:27:50 -0300 Subject: [PATCH 04/71] Update run_extras to use the temp filename In batch mode run_extras tries to preserve the original file name of the images. The problem is that this makes no sense since the user only gets a list of images in the UI, trying to manually save them shows that this images have random temp names. Also, trying to keep "orig_name" in the API is a hassle that adds complexity to the consuming UI since the client has to use (or emulate) an input (type=file) element in a form. Using the normal file name not only doesn't change the output and functionality in the original UI but also helps keep the API simple. --- modules/extras.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/extras.py b/modules/extras.py index 22c5a1c1..29ac312e 100644 --- a/modules/extras.py +++ b/modules/extras.py @@ -33,7 +33,7 @@ def run_extras(extras_mode, resize_mode, image, image_folder, input_dir, output_ for img in image_folder: image = Image.open(img) imageArr.append(image) - imageNameArr.append(os.path.splitext(img.orig_name)[0]) + imageNameArr.append(os.path.splitext(img.name)[0]) elif extras_mode == 2: assert not shared.cmd_opts.hide_ui_dir_config, '--hide-ui-dir-config option must be disabled' From 4ff852ffb50859f2eae75375cab94dd790a46886 Mon Sep 17 00:00:00 2001 From: Bruno Seoane Date: Sun, 23 Oct 2022 13:07:59 -0300 Subject: [PATCH 05/71] Add batch processing "extras" endpoint --- modules/api/api.py | 25 +++++++++++++++++++++++-- modules/api/models.py | 15 ++++++++++++++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/modules/api/api.py b/modules/api/api.py index 3b804373..528134a8 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -10,6 +10,7 @@ import base64 from modules.api.models import * from PIL import Image from modules.extras import run_extras +from gradio import processing_utils def upscaler_to_index(name: str): try: @@ -44,6 +45,7 @@ class Api: self.queue_lock = queue_lock self.app.add_api_route("/sdapi/v1/txt2img", self.text2imgapi, methods=["POST"], response_model=TextToImageResponse) self.app.add_api_route("/sdapi/v1/extra-single-image", self.extras_single_image_api, methods=["POST"], response_model=ExtrasSingleImageResponse) + self.app.add_api_route("/sdapi/v1/extra-batch-image", self.extras_batch_images_api, methods=["POST"], response_model=ExtrasBatchImagesResponse) def text2imgapi(self, txt2imgreq: StableDiffusionProcessingAPI ): sampler_index = sampler_to_index(txt2imgreq.sampler_index) @@ -78,12 +80,31 @@ class Api: reqDict.pop('upscaler_1') reqDict.pop('upscaler_2') - reqDict['image'] = base64_to_images([reqDict['image']])[0] + reqDict['image'] = processing_utils.decode_base64_to_file(reqDict['image']) with self.queue_lock: result = run_extras(**reqDict, extras_upscaler_1=upscaler1Index, extras_upscaler_2=upscaler2Index, extras_mode=0, image_folder="", input_dir="", output_dir="") - return ExtrasSingleImageResponse(image="data:image/png;base64,"+img_to_base64(result[0]), html_info_x=result[1], html_info=result[2]) + return ExtrasSingleImageResponse(image=processing_utils.encode_pil_to_base64(result[0]), html_info_x=result[1], html_info=result[2]) + + def extras_batch_images_api(self, req: ExtrasBatchImagesRequest): + upscaler1Index = upscaler_to_index(req.upscaler_1) + upscaler2Index = upscaler_to_index(req.upscaler_2) + + reqDict = vars(req) + reqDict.pop('upscaler_1') + reqDict.pop('upscaler_2') + + reqDict['image_folder'] = list(map(processing_utils.decode_base64_to_file, reqDict['imageList'])) + reqDict.pop('imageList') + + with self.queue_lock: + result = run_extras(**reqDict, extras_upscaler_1=upscaler1Index, extras_upscaler_2=upscaler2Index, extras_mode=1, image="", input_dir="", output_dir="") + + return ExtrasBatchImagesResponse(images=list(map(processing_utils.encode_pil_to_base64, result[0])), html_info_x=result[1], html_info=result[2]) + + def extras_folder_processing_api(self): + raise NotImplementedError def pnginfoapi(self): raise NotImplementedError diff --git a/modules/api/models.py b/modules/api/models.py index dcf1ab54..bbd0ef53 100644 --- a/modules/api/models.py +++ b/modules/api/models.py @@ -29,4 +29,17 @@ class ExtrasSingleImageRequest(ExtrasBaseRequest): image: str = Field(default="", title="Image", description="Image to work on, must be a Base64 string containing the image's data.") class ExtrasSingleImageResponse(ExtraBaseResponse): - image: str = Field(default=None, title="Image", description="The generated image in base64 format.") \ No newline at end of file + image: str = Field(default=None, title="Image", description="The generated image in base64 format.") + +class SerializableImage(BaseModel): + path: str = Field(title="Path", description="The image's path ()") + +class ImageItem(BaseModel): + data: str = Field(title="image data") + name: str = Field(title="filename") + +class ExtrasBatchImagesRequest(ExtrasBaseRequest): + imageList: list[str] = Field(title="Images", description="List of images to work on. Must be Base64 strings") + +class ExtrasBatchImagesResponse(ExtraBaseResponse): + images: list[str] = Field(title="Images", description="The generated images in base64 format.") \ No newline at end of file From e0ca4dfbc10e0af8dfc4185e5e758f33fd2f0d81 Mon Sep 17 00:00:00 2001 From: Bruno Seoane Date: Sun, 23 Oct 2022 15:13:37 -0300 Subject: [PATCH 06/71] Update endpoints to use gradio's own utils functions --- modules/api/api.py | 71 +++++++++++++++++++++---------------------- modules/api/models.py | 4 +-- 2 files changed, 36 insertions(+), 39 deletions(-) diff --git a/modules/api/api.py b/modules/api/api.py index 3f490ce2..3acb1f36 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -20,27 +20,27 @@ def upscaler_to_index(name: str): sampler_to_index = lambda name: next(filter(lambda row: name.lower() == row[1].name.lower(), enumerate(all_samplers)), None) -def img_to_base64(img: str): - buffer = io.BytesIO() - img.save(buffer, format="png") - return base64.b64encode(buffer.getvalue()) +# def img_to_base64(img: str): +# buffer = io.BytesIO() +# img.save(buffer, format="png") +# return base64.b64encode(buffer.getvalue()) -def base64_to_bytes(base64Img: str): - if "," in base64Img: - base64Img = base64Img.split(",")[1] - return io.BytesIO(base64.b64decode(base64Img)) +# def base64_to_bytes(base64Img: str): +# if "," in base64Img: +# base64Img = base64Img.split(",")[1] +# return io.BytesIO(base64.b64decode(base64Img)) -def base64_to_images(base64Imgs: list[str]): - imgs = [] - for img in base64Imgs: - img = Image.open(base64_to_bytes(img)) - imgs.append(img) - return imgs +# def base64_to_images(base64Imgs: list[str]): +# imgs = [] +# for img in base64Imgs: +# img = Image.open(base64_to_bytes(img)) +# imgs.append(img) +# return imgs class ImageToImageResponse(BaseModel): images: list[str] = Field(default=None, title="Image", description="The generated image in base64 format.") - parameters: Json - info: Json + parameters: dict + info: str class Api: @@ -49,17 +49,17 @@ class Api: self.app = app self.queue_lock = queue_lock self.app.add_api_route("/sdapi/v1/txt2img", self.text2imgapi, methods=["POST"], response_model=TextToImageResponse) - self.app.add_api_route("/sdapi/v1/img2img", self.img2imgapi, methods=["POST"]) + self.app.add_api_route("/sdapi/v1/img2img", self.img2imgapi, methods=["POST"], response_model=ImageToImageResponse) self.app.add_api_route("/sdapi/v1/extra-single-image", self.extras_single_image_api, methods=["POST"], response_model=ExtrasSingleImageResponse) self.app.add_api_route("/sdapi/v1/extra-batch-image", self.extras_batch_images_api, methods=["POST"], response_model=ExtrasBatchImagesResponse) - def __base64_to_image(self, base64_string): - # if has a comma, deal with prefix - if "," in base64_string: - base64_string = base64_string.split(",")[1] - imgdata = base64.b64decode(base64_string) - # convert base64 to PIL image - return Image.open(io.BytesIO(imgdata)) + # def __base64_to_image(self, base64_string): + # # if has a comma, deal with prefix + # if "," in base64_string: + # base64_string = base64_string.split(",")[1] + # imgdata = base64.b64decode(base64_string) + # # convert base64 to PIL image + # return Image.open(io.BytesIO(imgdata)) def text2imgapi(self, txt2imgreq: StableDiffusionTxt2ImgProcessingAPI): sampler_index = sampler_to_index(txt2imgreq.sampler_index) @@ -79,11 +79,9 @@ class Api: with self.queue_lock: processed = process_images(p) - b64images = list(map(img_to_base64, processed.images)) - - return TextToImageResponse(images=b64images, parameters=json.dumps(vars(txt2imgreq)), info=json.dumps(processed.info)) - + b64images = list(map(processing_utils.encode_pil_to_base64, processed.images)) + return TextToImageResponse(images=b64images, parameters=json.dumps(vars(txt2imgreq)), info=processed.info) def img2imgapi(self, img2imgreq: StableDiffusionImg2ImgProcessingAPI): sampler_index = sampler_to_index(img2imgreq.sampler_index) @@ -98,7 +96,7 @@ class Api: mask = img2imgreq.mask if mask: - mask = self.__base64_to_image(mask) + mask = processing_utils.decode_base64_to_image(mask) populate = img2imgreq.copy(update={ # Override __init__ params @@ -113,7 +111,7 @@ class Api: imgs = [] for img in init_images: - img = self.__base64_to_image(img) + img = processing_utils.decode_base64_to_image(img) imgs = [img] * p.batch_size p.init_images = imgs @@ -121,13 +119,12 @@ class Api: with self.queue_lock: processed = process_images(p) - b64images = [] - for i in processed.images: - buffer = io.BytesIO() - i.save(buffer, format="png") - b64images.append(base64.b64encode(buffer.getvalue())) - - return ImageToImageResponse(images=b64images, parameters=json.dumps(vars(img2imgreq)), info=json.dumps(processed.info)) + b64images = list(map(processing_utils.encode_pil_to_base64, processed.images)) + # for i in processed.images: + # buffer = io.BytesIO() + # i.save(buffer, format="png") + # b64images.append(base64.b64encode(buffer.getvalue())) + return ImageToImageResponse(images=b64images, parameters=vars(img2imgreq), info=processed.info) def extras_single_image_api(self, req: ExtrasSingleImageRequest): upscaler1Index = upscaler_to_index(req.upscaler_1) diff --git a/modules/api/models.py b/modules/api/models.py index bbd0ef53..209f8af5 100644 --- a/modules/api/models.py +++ b/modules/api/models.py @@ -4,8 +4,8 @@ from modules.shared import sd_upscalers class TextToImageResponse(BaseModel): images: list[str] = Field(default=None, title="Image", description="The generated image in base64 format.") - parameters: Json - info: Json + parameters: str + info: str class ExtrasBaseRequest(BaseModel): resize_mode: Literal[0, 1] = Field(default=0, title="Resize Mode", description="Sets the resize mode: 0 to upscale by upscaling_resize amount, 1 to upscale up to upscaling_resize_h x upscaling_resize_w.") From 866b36d705a338d299aba385788729d60f7d48c8 Mon Sep 17 00:00:00 2001 From: Bruno Seoane Date: Sun, 23 Oct 2022 15:35:49 -0300 Subject: [PATCH 07/71] Move processing's models into models.py It didn't make sense to have two differente files for the same and "models" is a more descriptive name. --- modules/api/api.py | 59 ++++---------------- modules/api/models.py | 112 +++++++++++++++++++++++++++++++++++++- modules/api/processing.py | 106 ------------------------------------ 3 files changed, 120 insertions(+), 157 deletions(-) delete mode 100644 modules/api/processing.py diff --git a/modules/api/api.py b/modules/api/api.py index 3acb1f36..20e85e82 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -1,16 +1,11 @@ -from modules.api.processing import StableDiffusionTxt2ImgProcessingAPI, StableDiffusionImg2ImgProcessingAPI +import uvicorn +from gradio import processing_utils +from fastapi import APIRouter, HTTPException +import modules.shared as shared +from modules.api.models import * from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img, process_images from modules.sd_samplers import all_samplers -import modules.shared as shared -import uvicorn -from fastapi import APIRouter, HTTPException -import json -import io -import base64 -from modules.api.models import * -from PIL import Image from modules.extras import run_extras -from gradio import processing_utils def upscaler_to_index(name: str): try: @@ -20,29 +15,6 @@ def upscaler_to_index(name: str): sampler_to_index = lambda name: next(filter(lambda row: name.lower() == row[1].name.lower(), enumerate(all_samplers)), None) -# def img_to_base64(img: str): -# buffer = io.BytesIO() -# img.save(buffer, format="png") -# return base64.b64encode(buffer.getvalue()) - -# def base64_to_bytes(base64Img: str): -# if "," in base64Img: -# base64Img = base64Img.split(",")[1] -# return io.BytesIO(base64.b64decode(base64Img)) - -# def base64_to_images(base64Imgs: list[str]): -# imgs = [] -# for img in base64Imgs: -# img = Image.open(base64_to_bytes(img)) -# imgs.append(img) -# return imgs - -class ImageToImageResponse(BaseModel): - images: list[str] = Field(default=None, title="Image", description="The generated image in base64 format.") - parameters: dict - info: str - - class Api: def __init__(self, app, queue_lock): self.router = APIRouter() @@ -51,15 +23,7 @@ class Api: self.app.add_api_route("/sdapi/v1/txt2img", self.text2imgapi, methods=["POST"], response_model=TextToImageResponse) self.app.add_api_route("/sdapi/v1/img2img", self.img2imgapi, methods=["POST"], response_model=ImageToImageResponse) self.app.add_api_route("/sdapi/v1/extra-single-image", self.extras_single_image_api, methods=["POST"], response_model=ExtrasSingleImageResponse) - self.app.add_api_route("/sdapi/v1/extra-batch-image", self.extras_batch_images_api, methods=["POST"], response_model=ExtrasBatchImagesResponse) - - # def __base64_to_image(self, base64_string): - # # if has a comma, deal with prefix - # if "," in base64_string: - # base64_string = base64_string.split(",")[1] - # imgdata = base64.b64decode(base64_string) - # # convert base64 to PIL image - # return Image.open(io.BytesIO(imgdata)) + self.app.add_api_route("/sdapi/v1/extra-batch-images", self.extras_batch_images_api, methods=["POST"], response_model=ExtrasBatchImagesResponse) def text2imgapi(self, txt2imgreq: StableDiffusionTxt2ImgProcessingAPI): sampler_index = sampler_to_index(txt2imgreq.sampler_index) @@ -81,7 +45,7 @@ class Api: b64images = list(map(processing_utils.encode_pil_to_base64, processed.images)) - return TextToImageResponse(images=b64images, parameters=json.dumps(vars(txt2imgreq)), info=processed.info) + return TextToImageResponse(images=b64images, parameters=vars(txt2imgreq), info=processed.info) def img2imgapi(self, img2imgreq: StableDiffusionImg2ImgProcessingAPI): sampler_index = sampler_to_index(img2imgreq.sampler_index) @@ -120,10 +84,7 @@ class Api: processed = process_images(p) b64images = list(map(processing_utils.encode_pil_to_base64, processed.images)) - # for i in processed.images: - # buffer = io.BytesIO() - # i.save(buffer, format="png") - # b64images.append(base64.b64encode(buffer.getvalue())) + return ImageToImageResponse(images=b64images, parameters=vars(img2imgreq), info=processed.info) def extras_single_image_api(self, req: ExtrasSingleImageRequest): @@ -134,12 +95,12 @@ class Api: reqDict.pop('upscaler_1') reqDict.pop('upscaler_2') - reqDict['image'] = processing_utils.decode_base64_to_file(reqDict['image']) + reqDict['image'] = processing_utils.decode_base64_to_image(reqDict['image']) with self.queue_lock: result = run_extras(**reqDict, extras_upscaler_1=upscaler1Index, extras_upscaler_2=upscaler2Index, extras_mode=0, image_folder="", input_dir="", output_dir="") - return ExtrasSingleImageResponse(image=processing_utils.encode_pil_to_base64(result[0]), html_info_x=result[1], html_info=result[2]) + return ExtrasSingleImageResponse(image=processing_utils.encode_pil_to_base64(result[0][0]), html_info_x=result[1], html_info=result[2]) def extras_batch_images_api(self, req: ExtrasBatchImagesRequest): upscaler1Index = upscaler_to_index(req.upscaler_1) diff --git a/modules/api/models.py b/modules/api/models.py index 209f8af5..362e6277 100644 --- a/modules/api/models.py +++ b/modules/api/models.py @@ -1,10 +1,118 @@ -from pydantic import BaseModel, Field, Json +import inspect +from pydantic import BaseModel, Field, Json, create_model +from typing import Any, Optional from typing_extensions import Literal +from inflection import underscore +from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img from modules.shared import sd_upscalers +API_NOT_ALLOWED = [ + "self", + "kwargs", + "sd_model", + "outpath_samples", + "outpath_grids", + "sampler_index", + "do_not_save_samples", + "do_not_save_grid", + "extra_generation_params", + "overlay_images", + "do_not_reload_embeddings", + "seed_enable_extras", + "prompt_for_display", + "sampler_noise_scheduler_override", + "ddim_discretize" +] + +class ModelDef(BaseModel): + """Assistance Class for Pydantic Dynamic Model Generation""" + + field: str + field_alias: str + field_type: Any + field_value: Any + + +class PydanticModelGenerator: + """ + Takes in created classes and stubs them out in a way FastAPI/Pydantic is happy about: + source_data is a snapshot of the default values produced by the class + params are the names of the actual keys required by __init__ + """ + + def __init__( + self, + model_name: str = None, + class_instance = None, + additional_fields = None, + ): + def field_type_generator(k, v): + # field_type = str if not overrides.get(k) else overrides[k]["type"] + # print(k, v.annotation, v.default) + field_type = v.annotation + + return Optional[field_type] + + def merge_class_params(class_): + all_classes = list(filter(lambda x: x is not object, inspect.getmro(class_))) + parameters = {} + for classes in all_classes: + parameters = {**parameters, **inspect.signature(classes.__init__).parameters} + return parameters + + + self._model_name = model_name + self._class_data = merge_class_params(class_instance) + self._model_def = [ + ModelDef( + field=underscore(k), + field_alias=k, + field_type=field_type_generator(k, v), + field_value=v.default + ) + for (k,v) in self._class_data.items() if k not in API_NOT_ALLOWED + ] + + for fields in additional_fields: + self._model_def.append(ModelDef( + field=underscore(fields["key"]), + field_alias=fields["key"], + field_type=fields["type"], + field_value=fields["default"])) + + def generate_model(self): + """ + Creates a pydantic BaseModel + from the json and overrides provided at initialization + """ + fields = { + d.field: (d.field_type, Field(default=d.field_value, alias=d.field_alias)) for d in self._model_def + } + DynamicModel = create_model(self._model_name, **fields) + DynamicModel.__config__.allow_population_by_field_name = True + DynamicModel.__config__.allow_mutation = True + return DynamicModel + +StableDiffusionTxt2ImgProcessingAPI = PydanticModelGenerator( + "StableDiffusionProcessingTxt2Img", + StableDiffusionProcessingTxt2Img, + [{"key": "sampler_index", "type": str, "default": "Euler"}] +).generate_model() + +StableDiffusionImg2ImgProcessingAPI = PydanticModelGenerator( + "StableDiffusionProcessingImg2Img", + StableDiffusionProcessingImg2Img, + [{"key": "sampler_index", "type": str, "default": "Euler"}, {"key": "init_images", "type": list, "default": None}, {"key": "denoising_strength", "type": float, "default": 0.75}, {"key": "mask", "type": str, "default": None}] +).generate_model() + class TextToImageResponse(BaseModel): images: list[str] = Field(default=None, title="Image", description="The generated image in base64 format.") - parameters: str + parameters: dict + info: str + +class ImageToImageResponse(BaseModel): + images: list[str] = Field(default=None, title="Image", description="The generated image in base64 format.") + parameters: dict info: str class ExtrasBaseRequest(BaseModel): diff --git a/modules/api/processing.py b/modules/api/processing.py deleted file mode 100644 index f551fa35..00000000 --- a/modules/api/processing.py +++ /dev/null @@ -1,106 +0,0 @@ -from array import array -from inflection import underscore -from typing import Any, Dict, Optional -from pydantic import BaseModel, Field, create_model -from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img -import inspect - - -API_NOT_ALLOWED = [ - "self", - "kwargs", - "sd_model", - "outpath_samples", - "outpath_grids", - "sampler_index", - "do_not_save_samples", - "do_not_save_grid", - "extra_generation_params", - "overlay_images", - "do_not_reload_embeddings", - "seed_enable_extras", - "prompt_for_display", - "sampler_noise_scheduler_override", - "ddim_discretize" -] - -class ModelDef(BaseModel): - """Assistance Class for Pydantic Dynamic Model Generation""" - - field: str - field_alias: str - field_type: Any - field_value: Any - - -class PydanticModelGenerator: - """ - Takes in created classes and stubs them out in a way FastAPI/Pydantic is happy about: - source_data is a snapshot of the default values produced by the class - params are the names of the actual keys required by __init__ - """ - - def __init__( - self, - model_name: str = None, - class_instance = None, - additional_fields = None, - ): - def field_type_generator(k, v): - # field_type = str if not overrides.get(k) else overrides[k]["type"] - # print(k, v.annotation, v.default) - field_type = v.annotation - - return Optional[field_type] - - def merge_class_params(class_): - all_classes = list(filter(lambda x: x is not object, inspect.getmro(class_))) - parameters = {} - for classes in all_classes: - parameters = {**parameters, **inspect.signature(classes.__init__).parameters} - return parameters - - - self._model_name = model_name - self._class_data = merge_class_params(class_instance) - self._model_def = [ - ModelDef( - field=underscore(k), - field_alias=k, - field_type=field_type_generator(k, v), - field_value=v.default - ) - for (k,v) in self._class_data.items() if k not in API_NOT_ALLOWED - ] - - for fields in additional_fields: - self._model_def.append(ModelDef( - field=underscore(fields["key"]), - field_alias=fields["key"], - field_type=fields["type"], - field_value=fields["default"])) - - def generate_model(self): - """ - Creates a pydantic BaseModel - from the json and overrides provided at initialization - """ - fields = { - d.field: (d.field_type, Field(default=d.field_value, alias=d.field_alias)) for d in self._model_def - } - DynamicModel = create_model(self._model_name, **fields) - DynamicModel.__config__.allow_population_by_field_name = True - DynamicModel.__config__.allow_mutation = True - return DynamicModel - -StableDiffusionTxt2ImgProcessingAPI = PydanticModelGenerator( - "StableDiffusionProcessingTxt2Img", - StableDiffusionProcessingTxt2Img, - [{"key": "sampler_index", "type": str, "default": "Euler"}] -).generate_model() - -StableDiffusionImg2ImgProcessingAPI = PydanticModelGenerator( - "StableDiffusionProcessingImg2Img", - StableDiffusionProcessingImg2Img, - [{"key": "sampler_index", "type": str, "default": "Euler"}, {"key": "init_images", "type": list, "default": None}, {"key": "denoising_strength", "type": float, "default": 0.75}, {"key": "mask", "type": str, "default": None}] -).generate_model() \ No newline at end of file From 1e625624ba6ab3dfc167f0a5226780bb9b50fb58 Mon Sep 17 00:00:00 2001 From: Bruno Seoane Date: Sun, 23 Oct 2022 16:01:16 -0300 Subject: [PATCH 08/71] Add folder processing endpoint Also minor refactor --- modules/api/api.py | 56 +++++++++++++++++++++++-------------------- modules/api/models.py | 6 ++++- 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/modules/api/api.py b/modules/api/api.py index 20e85e82..7b4fbe29 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -1,5 +1,5 @@ import uvicorn -from gradio import processing_utils +from gradio.processing_utils import encode_pil_to_base64, decode_base64_to_file, decode_base64_to_image from fastapi import APIRouter, HTTPException import modules.shared as shared from modules.api.models import * @@ -11,10 +11,18 @@ def upscaler_to_index(name: str): try: return [x.name.lower() for x in shared.sd_upscalers].index(name.lower()) except: - raise HTTPException(status_code=400, detail="Upscaler not found") + raise HTTPException(status_code=400, detail=f"Invalid upscaler, needs to be on of these: {' , '.join([x.name for x in sd_upscalers])}") sampler_to_index = lambda name: next(filter(lambda row: name.lower() == row[1].name.lower(), enumerate(all_samplers)), None) +def setUpscalers(req: dict): + reqDict = vars(req) + reqDict['extras_upscaler_1'] = upscaler_to_index(req.upscaler_1) + reqDict['extras_upscaler_2'] = upscaler_to_index(req.upscaler_2) + reqDict.pop('upscaler_1') + reqDict.pop('upscaler_2') + return reqDict + class Api: def __init__(self, app, queue_lock): self.router = APIRouter() @@ -24,6 +32,7 @@ class Api: self.app.add_api_route("/sdapi/v1/img2img", self.img2imgapi, methods=["POST"], response_model=ImageToImageResponse) self.app.add_api_route("/sdapi/v1/extra-single-image", self.extras_single_image_api, methods=["POST"], response_model=ExtrasSingleImageResponse) self.app.add_api_route("/sdapi/v1/extra-batch-images", self.extras_batch_images_api, methods=["POST"], response_model=ExtrasBatchImagesResponse) + self.app.add_api_route("/sdapi/v1/extra-folder-images", self.extras_folder_processing_api, methods=["POST"], response_model=ExtrasBatchImagesResponse) def text2imgapi(self, txt2imgreq: StableDiffusionTxt2ImgProcessingAPI): sampler_index = sampler_to_index(txt2imgreq.sampler_index) @@ -43,7 +52,7 @@ class Api: with self.queue_lock: processed = process_images(p) - b64images = list(map(processing_utils.encode_pil_to_base64, processed.images)) + b64images = list(map(encode_pil_to_base64, processed.images)) return TextToImageResponse(images=b64images, parameters=vars(txt2imgreq), info=processed.info) @@ -60,7 +69,7 @@ class Api: mask = img2imgreq.mask if mask: - mask = processing_utils.decode_base64_to_image(mask) + mask = decode_base64_to_image(mask) populate = img2imgreq.copy(update={ # Override __init__ params @@ -75,7 +84,7 @@ class Api: imgs = [] for img in init_images: - img = processing_utils.decode_base64_to_image(img) + img = decode_base64_to_image(img) imgs = [img] * p.batch_size p.init_images = imgs @@ -83,43 +92,38 @@ class Api: with self.queue_lock: processed = process_images(p) - b64images = list(map(processing_utils.encode_pil_to_base64, processed.images)) + b64images = list(map(encode_pil_to_base64, processed.images)) return ImageToImageResponse(images=b64images, parameters=vars(img2imgreq), info=processed.info) def extras_single_image_api(self, req: ExtrasSingleImageRequest): - upscaler1Index = upscaler_to_index(req.upscaler_1) - upscaler2Index = upscaler_to_index(req.upscaler_2) + reqDict = setUpscalers(req) - reqDict = vars(req) - reqDict.pop('upscaler_1') - reqDict.pop('upscaler_2') - - reqDict['image'] = processing_utils.decode_base64_to_image(reqDict['image']) + reqDict['image'] = decode_base64_to_image(reqDict['image']) with self.queue_lock: - result = run_extras(**reqDict, extras_upscaler_1=upscaler1Index, extras_upscaler_2=upscaler2Index, extras_mode=0, image_folder="", input_dir="", output_dir="") + result = run_extras(extras_mode=0, image_folder="", input_dir="", output_dir="", **reqDict) - return ExtrasSingleImageResponse(image=processing_utils.encode_pil_to_base64(result[0][0]), html_info_x=result[1], html_info=result[2]) + return ExtrasSingleImageResponse(image=encode_pil_to_base64(result[0][0]), html_info_x=result[1], html_info=result[2]) def extras_batch_images_api(self, req: ExtrasBatchImagesRequest): - upscaler1Index = upscaler_to_index(req.upscaler_1) - upscaler2Index = upscaler_to_index(req.upscaler_2) + reqDict = setUpscalers(req) - reqDict = vars(req) - reqDict.pop('upscaler_1') - reqDict.pop('upscaler_2') - - reqDict['image_folder'] = list(map(processing_utils.decode_base64_to_file, reqDict['imageList'])) + reqDict['image_folder'] = list(map(decode_base64_to_file, reqDict['imageList'])) reqDict.pop('imageList') with self.queue_lock: - result = run_extras(**reqDict, extras_upscaler_1=upscaler1Index, extras_upscaler_2=upscaler2Index, extras_mode=1, image="", input_dir="", output_dir="") + result = run_extras(extras_mode=1, image="", input_dir="", output_dir="", **reqDict) - return ExtrasBatchImagesResponse(images=list(map(processing_utils.encode_pil_to_base64, result[0])), html_info_x=result[1], html_info=result[2]) + return ExtrasBatchImagesResponse(images=list(map(encode_pil_to_base64, result[0])), html_info_x=result[1], html_info=result[2]) - def extras_folder_processing_api(self): - raise NotImplementedError + def extras_folder_processing_api(self, req:ExtrasFoldersRequest): + reqDict = setUpscalers(req) + + with self.queue_lock: + result = run_extras(extras_mode=2, image=None, image_folder=None, **reqDict) + + return ExtrasBatchImagesResponse(images=list(map(encode_pil_to_base64, result[0])), html_info_x=result[1], html_info=result[2]) def pnginfoapi(self): raise NotImplementedError diff --git a/modules/api/models.py b/modules/api/models.py index 362e6277..6f096807 100644 --- a/modules/api/models.py +++ b/modules/api/models.py @@ -150,4 +150,8 @@ class ExtrasBatchImagesRequest(ExtrasBaseRequest): imageList: list[str] = Field(title="Images", description="List of images to work on. Must be Base64 strings") class ExtrasBatchImagesResponse(ExtraBaseResponse): - images: list[str] = Field(title="Images", description="The generated images in base64 format.") \ No newline at end of file + images: list[str] = Field(title="Images", description="The generated images in base64 format.") + +class ExtrasFoldersRequest(ExtrasBaseRequest): + input_dir: str = Field(title="Input directory", description="Directory path from where to take the images") + output_dir: str = Field(title="Output directory", description="Directory path to put the processsed images into") From 90f02c75220d187e075203a4e3b450bfba392c4d Mon Sep 17 00:00:00 2001 From: Bruno Seoane Date: Sun, 23 Oct 2022 16:03:30 -0300 Subject: [PATCH 09/71] Remove unused field and class --- modules/api/api.py | 6 +++--- modules/api/models.py | 6 +----- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/modules/api/api.py b/modules/api/api.py index 7b4fbe29..799e3701 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -104,7 +104,7 @@ class Api: with self.queue_lock: result = run_extras(extras_mode=0, image_folder="", input_dir="", output_dir="", **reqDict) - return ExtrasSingleImageResponse(image=encode_pil_to_base64(result[0][0]), html_info_x=result[1], html_info=result[2]) + return ExtrasSingleImageResponse(image=encode_pil_to_base64(result[0][0]), html_info=result[1]) def extras_batch_images_api(self, req: ExtrasBatchImagesRequest): reqDict = setUpscalers(req) @@ -115,7 +115,7 @@ class Api: with self.queue_lock: result = run_extras(extras_mode=1, image="", input_dir="", output_dir="", **reqDict) - return ExtrasBatchImagesResponse(images=list(map(encode_pil_to_base64, result[0])), html_info_x=result[1], html_info=result[2]) + return ExtrasBatchImagesResponse(images=list(map(encode_pil_to_base64, result[0])), html_info=result[1]) def extras_folder_processing_api(self, req:ExtrasFoldersRequest): reqDict = setUpscalers(req) @@ -123,7 +123,7 @@ class Api: with self.queue_lock: result = run_extras(extras_mode=2, image=None, image_folder=None, **reqDict) - return ExtrasBatchImagesResponse(images=list(map(encode_pil_to_base64, result[0])), html_info_x=result[1], html_info=result[2]) + return ExtrasBatchImagesResponse(images=list(map(encode_pil_to_base64, result[0])), html_info=result[1]) def pnginfoapi(self): raise NotImplementedError diff --git a/modules/api/models.py b/modules/api/models.py index 6f096807..e461d397 100644 --- a/modules/api/models.py +++ b/modules/api/models.py @@ -130,8 +130,7 @@ class ExtrasBaseRequest(BaseModel): 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.") class ExtraBaseResponse(BaseModel): - html_info_x: str - html_info: str + html_info: str = Field(title="HTML info", description="A series of HTML tags containing the process info.") class ExtrasSingleImageRequest(ExtrasBaseRequest): image: str = Field(default="", title="Image", description="Image to work on, must be a Base64 string containing the image's data.") @@ -139,9 +138,6 @@ class ExtrasSingleImageRequest(ExtrasBaseRequest): class ExtrasSingleImageResponse(ExtraBaseResponse): image: str = Field(default=None, title="Image", description="The generated image in base64 format.") -class SerializableImage(BaseModel): - path: str = Field(title="Path", description="The image's path ()") - class ImageItem(BaseModel): data: str = Field(title="image data") name: str = Field(title="filename") From 994aaadf0861366b9e6f219e1a3c25a233fbb63c Mon Sep 17 00:00:00 2001 From: yfszzx Date: Mon, 24 Oct 2022 16:44:36 +0800 Subject: [PATCH 10/71] a strange bug --- extensions/stable-diffusion-webui-inspiration | 2 +- modules/ui.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/extensions/stable-diffusion-webui-inspiration b/extensions/stable-diffusion-webui-inspiration index a0b96664..c50c03ac 160000 --- a/extensions/stable-diffusion-webui-inspiration +++ b/extensions/stable-diffusion-webui-inspiration @@ -1 +1 @@ -Subproject commit a0b96664d2524b87916ae463fbb65411b13a569b +Subproject commit c50c03ac8fd2f6317d17d0c8c7c1ce26e6fe5cd7 diff --git a/modules/ui.py b/modules/ui.py index a73b9ff0..8c6dc026 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -55,6 +55,7 @@ mimetypes.init() mimetypes.add_type('application/javascript', '.js') txt2img_paste_fields = [] img2img_paste_fields = [] +init_img_components = {} if not cmd_opts.share and not cmd_opts.listen: @@ -1174,6 +1175,9 @@ def create_ui(wrap_gradio_gpu_call): outputs=[init_img_with_mask], ) + global init_img_components + init_img_components = {"img2img":init_img, "inpaint":init_img_with_mask, "extras":extras_image} + with gr.Blocks(analytics_enabled=False) as pnginfo_interface: with gr.Row().style(equal_height=False): with gr.Column(variant='panel'): From 595dca85af9e26b5d76cd64659a5bdd9da4f2b89 Mon Sep 17 00:00:00 2001 From: Bruno Seoane Date: Mon, 24 Oct 2022 08:32:18 -0300 Subject: [PATCH 11/71] Reverse run_extras change Update serialization on the batch images endpoint --- modules/api/api.py | 7 ++++++- modules/api/models.py | 8 ++++---- modules/extras.py | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/modules/api/api.py b/modules/api/api.py index 799e3701..67b783de 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -109,7 +109,12 @@ class Api: def extras_batch_images_api(self, req: ExtrasBatchImagesRequest): reqDict = setUpscalers(req) - reqDict['image_folder'] = list(map(decode_base64_to_file, reqDict['imageList'])) + def prepareFiles(file): + file = decode_base64_to_file(file.data, file_path=file.name) + file.orig_name = file.name + return file + + reqDict['image_folder'] = list(map(prepareFiles, reqDict['imageList'])) reqDict.pop('imageList') with self.queue_lock: diff --git a/modules/api/models.py b/modules/api/models.py index e461d397..fca2f991 100644 --- a/modules/api/models.py +++ b/modules/api/models.py @@ -138,12 +138,12 @@ class ExtrasSingleImageRequest(ExtrasBaseRequest): class ExtrasSingleImageResponse(ExtraBaseResponse): image: str = Field(default=None, title="Image", description="The generated image in base64 format.") -class ImageItem(BaseModel): - data: str = Field(title="image data") - name: str = Field(title="filename") +class FileData(BaseModel): + data: str = Field(title="File data", description="Base64 representation of the file") + name: str = Field(title="File name") class ExtrasBatchImagesRequest(ExtrasBaseRequest): - imageList: list[str] = 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): images: list[str] = Field(title="Images", description="The generated images in base64 format.") diff --git a/modules/extras.py b/modules/extras.py index 29ac312e..22c5a1c1 100644 --- a/modules/extras.py +++ b/modules/extras.py @@ -33,7 +33,7 @@ def run_extras(extras_mode, resize_mode, image, image_folder, input_dir, output_ for img in image_folder: image = Image.open(img) imageArr.append(image) - imageNameArr.append(os.path.splitext(img.name)[0]) + imageNameArr.append(os.path.splitext(img.orig_name)[0]) elif extras_mode == 2: assert not shared.cmd_opts.hide_ui_dir_config, '--hide-ui-dir-config option must be disabled' From ff305acd51cc71c5eea8aee0f537a26a6d1ba2a1 Mon Sep 17 00:00:00 2001 From: yfszzx Date: Tue, 25 Oct 2022 15:33:43 +0800 Subject: [PATCH 12/71] some rights for extensions --- extensions/stable-diffusion-webui-inspiration | 2 +- modules/shared.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/stable-diffusion-webui-inspiration b/extensions/stable-diffusion-webui-inspiration index c50c03ac..c4b9f5c2 160000 --- a/extensions/stable-diffusion-webui-inspiration +++ b/extensions/stable-diffusion-webui-inspiration @@ -1 +1 @@ -Subproject commit c50c03ac8fd2f6317d17d0c8c7c1ce26e6fe5cd7 +Subproject commit c4b9f5c233c8619ee140b763e706edc26cae0f1d diff --git a/modules/shared.py b/modules/shared.py index 76cbb1bd..7b1fadf2 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -82,6 +82,7 @@ parser.add_argument("--api", action='store_true', help="use api=True to launch t parser.add_argument("--nowebui", action='store_true', help="use api=True to launch the api instead of the webui") parser.add_argument("--ui-debug-mode", action='store_true', help="Don't load model to quickly launch UI") parser.add_argument("--device-id", type=str, help="Select the default CUDA device to use (export CUDA_VISIBLE_DEVICES=0,1,etc might be needed before)", default=None) +parser.add_argument("--administrator", type=str, help="Administrator rights", default=None) cmd_opts = parser.parse_args() restricted_opts = [ From 9ba439b53313ef78984dd8e39f25b34501188ee2 Mon Sep 17 00:00:00 2001 From: yfszzx Date: Tue, 25 Oct 2022 18:48:07 +0800 Subject: [PATCH 13/71] need some rights for extensions --- modules/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/shared.py b/modules/shared.py index 7b1fadf2..b5975707 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -82,7 +82,7 @@ parser.add_argument("--api", action='store_true', help="use api=True to launch t parser.add_argument("--nowebui", action='store_true', help="use api=True to launch the api instead of the webui") parser.add_argument("--ui-debug-mode", action='store_true', help="Don't load model to quickly launch UI") parser.add_argument("--device-id", type=str, help="Select the default CUDA device to use (export CUDA_VISIBLE_DEVICES=0,1,etc might be needed before)", default=None) -parser.add_argument("--administrator", type=str, help="Administrator rights", default=None) +parser.add_argument("--administrator", action='store_true', help="Administrator rights", default=False) cmd_opts = parser.parse_args() restricted_opts = [ From f9549d1cbb3f1d7d1f0fb70375a06e31f9c5dd9d Mon Sep 17 00:00:00 2001 From: random_thoughtss Date: Tue, 25 Oct 2022 11:14:12 -0700 Subject: [PATCH 14/71] Added option to use unmasked conditioning image. --- modules/processing.py | 6 +++++- modules/shared.py | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/processing.py b/modules/processing.py index c61bbfbd..96f56b0d 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -768,7 +768,11 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): # Create another latent image, this time with a masked version of the original input. conditioning_mask = conditioning_mask.to(image.device) - conditioning_image = image * (1.0 - conditioning_mask) + + conditioning_image = image + if shared.opts.inpainting_mask_image: + conditioning_image = conditioning_image * (1.0 - conditioning_mask) + conditioning_image = self.sd_model.get_first_stage_encoding(self.sd_model.encode_first_stage(conditioning_image)) # Create the concatenated conditioning tensor to be fed to `c_concat` diff --git a/modules/shared.py b/modules/shared.py index 308fccce..1d0ff1a1 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -320,6 +320,7 @@ options_templates.update(options_section(('sampler-params', "Sampler parameters" 's_tmin': OptionInfo(0.0, "sigma tmin", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), 's_noise': OptionInfo(1.0, "sigma noise", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), 'eta_noise_seed_delta': OptionInfo(0, "Eta noise seed delta", gr.Number, {"precision": 0}), + "inpainting_mask_image": OptionInfo(True, "Mask original image for conditioning used by inpainting model."), })) From 605d27687f433c0fefb9025aace7dc94f0ebd454 Mon Sep 17 00:00:00 2001 From: random_thoughtss Date: Tue, 25 Oct 2022 12:20:54 -0700 Subject: [PATCH 15/71] Added conditioning image masking to xy_grid. Use `True` and `False` to select values. --- modules/processing.py | 2 +- scripts/xy_grid.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/processing.py b/modules/processing.py index 96f56b0d..23ee5e02 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -770,7 +770,7 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): conditioning_mask = conditioning_mask.to(image.device) conditioning_image = image - if shared.opts.inpainting_mask_image: + if getattr(self, "inpainting_mask_image", shared.opts.inpainting_mask_image): conditioning_image = conditioning_image * (1.0 - conditioning_mask) conditioning_image = self.sd_model.get_first_stage_encoding(self.sd_model.encode_first_stage(conditioning_image)) diff --git a/scripts/xy_grid.py b/scripts/xy_grid.py index eff0c942..0843adcc 100644 --- a/scripts/xy_grid.py +++ b/scripts/xy_grid.py @@ -153,6 +153,8 @@ def str_permutations(x): """dummy function for specifying it in AxisOption's type when you want to get a list of permutations""" return x +def str_to_bool(x): + return "true" in x.lower().strip() AxisOption = namedtuple("AxisOption", ["label", "type", "apply", "format_value", "confirm"]) AxisOptionImg2Img = namedtuple("AxisOptionImg2Img", ["label", "type", "apply", "format_value", "confirm"]) @@ -178,6 +180,7 @@ axis_options = [ AxisOption("Eta", float, apply_field("eta"), format_value_add_label, None), AxisOption("Clip skip", int, apply_clip_skip, format_value_add_label, None), AxisOption("Denoising", float, apply_field("denoising_strength"), format_value_add_label, None), + AxisOption("Mask Conditioning Image", str_to_bool, apply_field("inpainting_mask_image"), format_value_add_label, None), ] From 8b4f32779f28010fc8077e8fcfb85a3205b36bc2 Mon Sep 17 00:00:00 2001 From: random_thoughtss Date: Tue, 25 Oct 2022 13:15:08 -0700 Subject: [PATCH 16/71] Switch to a continous blend for cond. image. --- modules/processing.py | 9 ++++++--- modules/shared.py | 2 +- scripts/xy_grid.py | 5 +---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/processing.py b/modules/processing.py index 23ee5e02..02292bdc 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -769,9 +769,12 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): # Create another latent image, this time with a masked version of the original input. conditioning_mask = conditioning_mask.to(image.device) - conditioning_image = image - if getattr(self, "inpainting_mask_image", shared.opts.inpainting_mask_image): - conditioning_image = conditioning_image * (1.0 - conditioning_mask) + # Smoothly interpolate between the masked and unmasked latent conditioning image. + conditioning_image = torch.lerp( + image, + image * (1.0 - conditioning_mask), + getattr(self, "inpainting_mask_weight", shared.opts.inpainting_mask_weight) + ) conditioning_image = self.sd_model.get_first_stage_encoding(self.sd_model.encode_first_stage(conditioning_image)) diff --git a/modules/shared.py b/modules/shared.py index 1d0ff1a1..e0ffb824 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -320,7 +320,7 @@ options_templates.update(options_section(('sampler-params', "Sampler parameters" 's_tmin': OptionInfo(0.0, "sigma tmin", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), 's_noise': OptionInfo(1.0, "sigma noise", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), 'eta_noise_seed_delta': OptionInfo(0, "Eta noise seed delta", gr.Number, {"precision": 0}), - "inpainting_mask_image": OptionInfo(True, "Mask original image for conditioning used by inpainting model."), + "inpainting_mask_weight": OptionInfo(1.0, "Blend betweeen an unmasked and masked conditioning image for inpainting models.", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}), })) diff --git a/scripts/xy_grid.py b/scripts/xy_grid.py index 0843adcc..f5255786 100644 --- a/scripts/xy_grid.py +++ b/scripts/xy_grid.py @@ -153,9 +153,6 @@ def str_permutations(x): """dummy function for specifying it in AxisOption's type when you want to get a list of permutations""" return x -def str_to_bool(x): - return "true" in x.lower().strip() - AxisOption = namedtuple("AxisOption", ["label", "type", "apply", "format_value", "confirm"]) AxisOptionImg2Img = namedtuple("AxisOptionImg2Img", ["label", "type", "apply", "format_value", "confirm"]) @@ -180,7 +177,7 @@ axis_options = [ AxisOption("Eta", float, apply_field("eta"), format_value_add_label, None), AxisOption("Clip skip", int, apply_clip_skip, format_value_add_label, None), AxisOption("Denoising", float, apply_field("denoising_strength"), format_value_add_label, None), - AxisOption("Mask Conditioning Image", str_to_bool, apply_field("inpainting_mask_image"), format_value_add_label, None), + AxisOption("Cond. Image Mask Weight", float, apply_field("inpainting_mask_weight"), format_value_add_label, None), ] From 53b48d93fb9acd66ad631129afd837049449a76b Mon Sep 17 00:00:00 2001 From: xmodar Date: Wed, 26 Oct 2022 02:12:53 +0300 Subject: [PATCH 17/71] Add complete retranslation for the Arabic localization Used the following resources: - https://www.almaany.com/ - https://translate.google.com/ - https://techtionary.thinktech.sa/ - https://sdaia.gov.sa/files/Dictionary.pdf The translations are ordered by the way they appear in the UI. This version abandons literal translation and adds explainations where possible. --- localizations/ar_AR.json | 808 +++++++++++++++++++-------------------- 1 file changed, 387 insertions(+), 421 deletions(-) diff --git a/localizations/ar_AR.json b/localizations/ar_AR.json index 1195a8e9..21c24e62 100644 --- a/localizations/ar_AR.json +++ b/localizations/ar_AR.json @@ -1,456 +1,422 @@ { "rtl": true, - "⤡": "⤡", - "⊞": "⊞", - "×": "×", - "❮": "❮", - "❯": "❯", - "Loading...": "جار التحميل...", - "view": "معاينة", - "api": "api", - "•": "•", - "built with gradio": "مبني باستخدام Gradio", - "Stable Diffusion checkpoint": "نماذج الانتشار المستقر", - "txt2img": "نص لصورة", - "img2img": "صورة لصورة", - "Extras": "الإضافات", - "PNG Info": "معلومات PNG", - "Checkpoint Merger": "دمج النماذج", - "Train": "التدريب", - "Create aesthetic embedding": "Create aesthetic embedding", - "Image Browser": "مستعرض الصور", - "Settings": "الإعدادات", - "Prompt": "الموجه", - "Negative prompt": "الموجه السلبي", - "Run": "تشغيل", - "Skip": "تخطي", - "Interrupt": "إيقاف", - "Generate": "إنشاء", - "Style 1": "نمط 1", - "Style 2": "نمط 2", - "Label": "الوسم", - "File": "ملف", - "Drop File Here": "اسحب الملف هنا", + "Loading...": "لحظة...", + "view": "اعرض ", + "api": "واجهة البرمجة", + "built with gradio": "مبني باستخدام gradio", + "Stable Diffusion checkpoint": "ملف أوزان Stable Diffusion", + "txt2img": "نص إلى صورة", + "Prompt": "الطلب", + "Prompt (press Ctrl+Enter or Alt+Enter to generate)": "الطلب (لبدء الإنتاج Ctrl+Enter أو Alt+Enter اضغط)", + "Negative prompt": "عكس الطلب", + "Negative prompt (press Ctrl+Enter or Alt+Enter to generate)": "عكس الطلب (لبدء الإنتاج Ctrl+Enter أو Alt+Enter اضغط)", + "Add a random artist to the prompt.": "أضف فنان عشوائي للطلب", + "Read generation parameters from prompt or last generation if prompt is empty into user interface.": "اقرأ عوامل الإنتاج من الطلب أو من الإنتاج السابق إذا كان الطلب فارغا", + "Save style": "احتفظ بالطلب وعكسه كإضافة", + "Apply selected styles to current prompt": "ألحق الإضافات المحددة إلى الطلب وعكسه", + "Generate": "أنتج", + "Skip": "تخطى", + "Stop processing current image and continue processing.": "تجاهل إنتاج هذة الحزمة وانتقل إلى الحزمة التالية", + "Interrupt": "توقف", + "Stop processing images and return any results accumulated so far.": "توقف عن الإنتاج واعرض ما تم إلى الآن", + "Style 1": "الإضافة 1", + "Style to apply; styles have components for both positive and negative prompts and apply to both": "الإضافات (styles) عبارة عن كلمات تتكرر كثيرا يتم إلحاقها بالطلب وعكسه عند الرغبة", + "Style 2": "الإضافة 2", + "Do not do anything special": "لا يغير شيئا", + "Sampling Steps": "عدد الخطوات", + "Sampling method": "أسلوب الخطو", + "Which algorithm to use to produce the image": "Sampler: اسم نظام تحديد طريقة تغيير المسافات بين الخطوات", + "Euler Ancestral - very creative, each can get a completely different picture depending on step count, setting steps to higher than 30-40 does not help": "Euler Ancestral: طريقة مبدعة يمكن أن تنتج صور مختلفة على حسب عدد الخطوات، لا تتغير بعد 30-40 خطوة", + "Denoising Diffusion Implicit Models - best at inpainting": "Denoising Diffusion Implicit Models: الأفضل في التحبير", + "Width": "العرض", + "Height": "الإرتفاع", + "Restore faces": "تحسين الوجوه", + "Tiling": "ترصيف", + "Produce an image that can be tiled.": "أنتج صور يمكن ترصيفها بجانب بعضها كالبلاط", + "Highres. fix": "أقل دقة", + "Use a two step process to partially create an image at smaller resolution, upscale, and then improve details in it without changing composition": "أنتج صورة بدقة منخفضة ثم قم برفع الدقة فيما بعد لتسريع الإنتاج عندما تكون الدقة المطلوبة كبيرة", + "Firstpass width": "العرض الأولي", + "Firstpass height": "الإرتفاع الأولي", + "Denoising strength": "قسط المشوار", + "Determines how little respect the algorithm should have for image's content. At 0, nothing will change, and at 1 you'll get an unrelated image. With values below 1.0, processing will take less steps than the Sampling Steps slider specifies.": "Denoising strength: حدد مقدار جزء المشوار المراد قطعه (1: كامل المشوار ينتج صورة مختلفة تماما، 0: ثابت لا يغير الصورة)", + "Batch count": "عدد الحزم", + "How many batches of images to create": "يتم إنتاج الصور على دفعات، كل دفعة فيها حزمة من الصور", + "Batch size": "حجم الحزمة", + "How many image to create in a single batch": "Batch size: إنتاج حزمة صور أسرع من إنتاجهم فرادى، حدد عدد الصور في كل حزمة", + "CFG Scale": "التركيز", + "Classifier Free Guidance Scale - how strongly the image should conform to prompt - lower values produce more creative results": "CFG scale: يحدد مقدار التركيز على تلبية الطلب وتجنب عكسه، كلما زاد قل الإبداع", + "Seed": "البذرة", + "A value that determines the output of random number generator - if you create an image with same parameters and seed as another image, you'll get the same result": "Seed: رقم طبيعي عشوائي يسمح بإعادة إنتاج نفس الصورة إذا توافقت قيم العوامل الأخرى", + "Set seed to -1, which will cause a new random number to be used every time": "استخدم بذرة جديدة في كل مرة (نرمز لهذا الخيار بجعل قيمة البذرة 1-)", + "Reuse seed from last generation, mostly useful if it was randomed": "أعد استخدام البذرة من الإنتاج السابق", + "Extra": "مزج", + "Variation seed": "بذرة الممزوج", + "Seed of a different picture to be mixed into the generation.": "Variation seed: بذرة صورة أخرى ليتم مزجها مع الصورة الحالية", + "Variation strength": "أثر الممزوج", + "How strong of a variation to produce. At 0, there will be no effect. At 1, you will get the complete picture with variation seed (except for ancestral samplers, where you will just get something).": "Variation seed strength: مقدار أثر الصورة المدمجة على النتيجة النهائية (0: لا أثر، 1: أثر كامل ما عدا عند استخدام أسلوب خطو سلفي Ancestral)", + "Resize seed from width": "عرض الممزوج", + "Resize seed from height": "إرتفاع الممزوج", + "Make an attempt to produce a picture similar to what would have been produced with same seed at specified resolution": "Seed resize from: حدد دقة صورة الممزوج (0: نفس دقة الإنتاج)", + "Open for Clip Aesthetic!": "تضمين تجميلي", + "Aesthetic weight": "أثر التضمين", + "Aesthetic steps": "عدد الخطوات", + "Aesthetic learning rate": "معدل التعلم", + "Slerp interpolation": "امزج بطريقة كروية", + "Aesthetic imgs embedding": "التضمين", + "None": "بدون", + "Aesthetic text for imgs": "الطلب (اختياري)", + "This text is used to rotate the feature space of the imgs embs": "لإعادة توجيه التضمين التجميلي", + "Slerp angle": "أثر الطلب", + "Is negative text": "الطلب عكسي", + "Script": "أداة", + "Prompt matrix": "مصفوفة طلبات", + "Put variable parts at start of prompt": "الجزء المتغير في بداية الطلب", + "Prompts from file or textbox": "قائمة طلبات", + "Show Textbox": "أظهر مربع النص", + "File with inputs": "ملف نصي للطلبات", + "Drop File Here": "اسقط ملف هنا", "-": "-", "or": "أو", - "Click to Upload": "انقر للتحميل", - "Image": "صورة", - "Check progress": "تحقق من التقدم", - "Check progress (first)": "تحقق من التقدم (الأول)", - "Sampling Steps": "خطوات أخذ العينة", - "Sampling method": "نظام أخذ العينات", - "Euler a": "Euler a", - "Euler": "Euler", - "LMS": "LMS", - "Heun": "Heun", - "DPM2": "DPM2", - "DPM2 a": "DPM2 a", - "DPM fast": "DPM fast", - "DPM adaptive": "DPM adaptive", - "LMS Karras": "LMS Karras", - "DPM2 Karras": "DPM2 Karras", - "DPM2 a Karras": "DPM2 a Karras", - "DDIM": "DDIM", - "PLMS": "PLMS", - "Width": "العرض", - "Height": "الارتفاع", - "Restore faces": "ترميم الوجوه", - "Tiling": "تبليط", - "Highres. fix": "إصلاح الصور عالية الدقة", - "Firstpass width": "عرض المرور الأول", - "Firstpass height": "ارتفاع المرور الأول", - "Denoising strength": "قوة تقليل الضوضاء", - "Batch count": "عدد الدُفعات", - "Batch size": "حجم الدفعة", - "CFG Scale": "مقياس التقارب من الموجه (CFG)", - "Seed": "البذرة", - "Extra": "إضافي", - "Variation seed": "تباين البذرة", - "Variation strength": "قوة التباين", - "Resize seed from width": "تغيير حجم البذرة من العرض", - "Resize seed from height": "تغيير حجم البذرة من الارتفاع", - "Open for Clip Aesthetic!": "Open for Clip Aesthetic!", - "▼": "▼", - "Aesthetic weight": "Aesthetic weight", - "Aesthetic steps": "Aesthetic steps", - "Aesthetic learning rate": "Aesthetic learning rate", - "Slerp interpolation": "Slerp interpolation", - "Aesthetic imgs embedding": "Aesthetic imgs embedding", - "None": "لايوجد", - "Aesthetic text for imgs": "Aesthetic text for imgs", - "Slerp angle": "Slerp angle", - "Is negative text": "Is negative text", - "Script": "سكريبت", - "Prompt matrix": "مصفوفة الموجهات", - "Prompts from file or textbox": "موجهات من ملف أو مربع النص", - "X/Y plot": "الرسم البياني X/Y", - "Put variable parts at start of prompt": "ضع الأجزاء المتغيرة في بداية الموجه", - "Show Textbox": "إظهار مربع النص", - "File with inputs": "ملف يحتوي المدخلات", - "Prompts": "الموجهات", - "X type": "نوع X", - "Nothing": "لا شئ", - "Var. seed": "تغير البذرة", - "Var. strength": "قوة التغيير", - "Steps": "الخطوات", - "Prompt S/R": "موجه S / R", - "Prompt order": "ترتيب الموجهات", - "Sampler": "نظام أخذ العينات", - "Checkpoint name": "اسم النموذج", + "Click to Upload": "انقر للرفع", + "Prompts": "قائمة الطلبات", + "X/Y plot": "مصفوفة عوامل", + "X type": "العامل الأول", + "Nothing": "لا شيء", + "Var. seed": "بذرة الممزوج", + "Var. strength": "أثر الممزوج", + "Steps": "عدد الخطوات", + "Prompt S/R": "كلمات بديلة", + "Prompt order": "ترتيب الكلمات", + "Sampler": "أسلوب الخطو", + "Checkpoint name": "ملف الأوزان", "Hypernetwork": "الشبكة الفائقة", "Hypernet str.": "قوة الشبكة الفائقة", - "Sigma Churn": "دفع سيجما", - "Sigma min": "أصغر سيجما", - "Sigma max": "أكبر سيجما", - "Sigma noise": "ضجة سيجما", - "Eta": "الوقت المتوقع", - "Clip skip": "تخطي Clip", - "Denoising": "تقليل الضوضاء", - "X values": "قيم X", - "Y type": "نوع Y", - "Y values": "قيم Y", - "Draw legend": "ارسم مفتاح التوضيح", - "Include Separate Images": "قم بتضمين الصور منفصلة", - "Keep -1 for seeds": "احتفظ بـقيمة -1 للبذور", - "Drop Image Here": "إسقاط الصورة هنا", - "Save": "حفظ", - "Send to img2img": "أرسل إلى صورة لصورة", - "Send to inpaint": "أرسل إلى إعادة الرسم الجزئي", - "Send to extras": "أرسل إلى الإضافات", - "Make Zip when Save?": "إنشاء ملف مضغوط عند الحفظ؟", - "Textbox": "مربع النص", - "Interrogate\nCLIP": "استجواب\n CLIP", - "Inpaint": "إعادة الرسم الجزئي", - "Batch img2img": "دفعات صورة لصورة", - "Image for img2img": "صورة (صورة لصورة)", - "Image for inpainting with mask": "صورة (إعادة الرسم الجزئي)", - "Mask": "القناع", - "Mask blur": "ضبابية القناع", - "Mask mode": "أسلوب القناع", - "Draw mask": "رسم القناع", - "Upload mask": "تحميل القناع", - "Masking mode": "أسلوب التقنيع", - "Inpaint masked": "إعادة الرسم الجزئي (المنطقة المقنعة)", - "Inpaint not masked": "إعادة الرسم الجزئي (المنطقة الغير مقنعة)", - "Masked content": "المحتوى المقنع", - "fill": "الملأ", - "original": "الأصلي", - "latent noise": "الضوضاء الكامنة", - "latent nothing": "لا شيء كامن", - "Inpaint at full resolution": "إعادة الرسم الجزئي بدقة كاملة", - "Inpaint at full resolution padding, pixels": "إعادة الرسم الجزئي بدقة كاملة, الحشو, بيكسل", - "Process images in a directory on the same machine where the server is running.": "معالجة الصور في المجلد على نفس الجهاز حيث يتم تشغيل الخادم.", - "Use an empty output directory to save pictures normally instead of writing to the output directory.": "استخدم مجلد إخراج فارغ لحفظ الصور بشكل طبيعي بدلاً من الكتابة إلى مجلد المخرجات.", - "Input directory": "مجلد المدخلات", - "Output directory": "مجلد المخرجات", - "Resize mode": "وضعية تغيير الحجم", - "Just resize": "تغييير الحجم فقط", - "Crop and resize": "اقتصاص وتغيير الحجم", - "Resize and fill": "تغيير الحجم والتعبئة", - "img2img alternative test": "صورة لصورة البديلة", - "Loopback": "الحلقة الراجعة", - "Outpainting mk2": "الرسم الخارجي نسخة 2", - "Poor man's outpainting": "الرسم الخارجي للفقراء", - "SD upscale": "ترقية الانتشار المستقر", - "should be 2 or lower.": "should be 2 or lower.", - "Override `Sampling method` to Euler?(this method is built for it)": "Override `Sampling method` to Euler?(this method is built for it)", - "Override `prompt` to the same value as `original prompt`?(and `negative prompt`)": "Override `prompt` to the same value as `original prompt`?(and `negative prompt`)", - "Original prompt": "Original prompt", - "Original negative prompt": "Original negative prompt", - "Override `Sampling Steps` to the same value as `Decode steps`?": "Override `Sampling Steps` to the same value as `Decode steps`?", - "Decode steps": "Decode steps", - "Override `Denoising strength` to 1?": "Override `Denoising strength` to 1?", - "Decode CFG scale": "Decode CFG scale", - "Randomness": "Randomness", - "Sigma adjustment for finding noise for image": "Sigma adjustment for finding noise for image", - "Loops": "Loops", - "Denoising strength change factor": "معامل قوة تقليل الضوضاء", - "Recommended settings: Sampling Steps: 80-100, Sampler: Euler a, Denoising strength: 0.8": "الإعدادات الموصى بها: خطوات أخذ العينات: 80-100 ، طريقة أخذ العينات: Euler a ، تقليل الضوضاء: 0.8", - "Pixels to expand": "عدد البكسل للتوسيع", - "Outpainting direction": "إتجاه الرسم الخارجي", + "Sigma Churn": "العشوائية (Schurn)", + "Sigma min": "أدنى تشويش (Stmin)", + "Sigma max": "أقصى تشويش (Stmax)", + "Sigma noise": "التشويش (Snoise)", + "Eta": "العامل Eta η", + "Clip skip": "تخطي آخر طبقات CLIP", + "Denoising": "قسط المشوار", + "X values": "قيم العامل الأول", + "Separate values for X axis using commas.": "افصل القيم بفواصل (,) من اليسار إلى اليمين", + "Y type": "العامل الثاني", + "Y values": "قيم العامل الثاني", + "Separate values for Y axis using commas.": "افصل القيم بفواصل (,) من الأعلى إلى الأسفل", + "Draw legend": "أضف الهامش", + "Include Separate Images": "أضف الصور منفصلة", + "Keep -1 for seeds": "استخدم بذور عشوائية", + "Save": "احفظ", + "Write image to a directory (default - log/images) and generation parameters into csv file.": "احفظ الصور مع ملف العوامل بصيغة CSV", + "Send to img2img": "أرسل لصورة إلى صورة", + "Send to inpaint": "أرسل للتحبير", + "Send to extras": "أرسل للمعالجة", + "Open images output directory": "افتح مجلد الصور المخرجة", + "Make Zip when Save?": "ضع النتائج في ملف مضغوط عند الحفظ", + "img2img": "صورة إلى صورة", + "Interrogate\nCLIP": "استجواب\nCLIP", + "Drop Image Here": "اسقط صورة هنا", + "Just resize": "تغيير الحجم فقط", + "Resize image to target resolution. Unless height and width match, you will get incorrect aspect ratio.": "غير حجم الصورة بدون مراعات اتزان الأبعاد", + "Crop and resize": "تغيير الحجم وقص الأطراف", + "Resize the image so that entirety of target resolution is filled with the image. Crop parts that stick out.": "غير حجم الصورة واقتص الأطراف الزائدة", + "Resize and fill": "تغيير الحجم وتبطين الأطراف", + "Resize the image so that entirety of image is inside target resolution. Fill empty space with image's colors.": "غير حجم الصورة واملأ الأطراف الزائدة بألوان من الصورة", + "img2img alternative test": "استجواب الصورة (تجريبي)", + "should be 2 or lower.": "يفترض أن يكون 2 أو أقل", + "Override `Sampling method` to Euler?(this method is built for it)": "استخدم أسلوب خطو Euler (مستحسن)", + "Override `prompt` to the same value as `original prompt`?(and `negative prompt`)": "استبدل الطلب وعكسه في الأعلى بالطلب الأصلي وعكسه التاليين", + "Original prompt": "الطلب الأصلي", + "Original negative prompt": "عكس الطلب الأصلي", + "Override `Sampling Steps` to the same value as `Decode steps`?": "استدبل عدد الخطوات بعدد الخطوات الأصلية", + "Decode steps": "عدد الخطوات الأصلية", + "Override `Denoising strength` to 1?": "اجعل قسط المشوار 1", + "Decode CFG scale": "التركيز", + "Randomness": "العشوائية", + "Sigma adjustment for finding noise for image": "لا تسمح بتثبيت قيمة التباين", + "Loopback": "اجترار وتكرار", + "Loops": "عدد المرات", + "How many times to repeat processing an image and using it as input for the next iteration": "كم مرة يتم أخذ مخرجات الإنتاج كمدخلات وإعادة الإنتاج مرة أخرى", + "Denoising strength change factor": "معدل تغيير قسط المشوار", + "In loopback mode, on each loop the denoising strength is multiplied by this value. <1 means decreasing variety so your sequence will converge on a fixed picture. >1 means increasing variety so your sequence will become more and more chaotic.": "يتم ضرب قسط المشوار بهذا الرقم في كل مرة، إذا استخدمت رقم أصغر من 1 يمكن الرسو على نتيجة، وإذا استخدمت رقم أكبر من 1 تصبح النتيجة عشوائية", + "Outpainting mk2": "توسيع الصورة (mk2)", + "Recommended settings: Sampling Steps: 80-100, Sampler: Euler a, Denoising strength: 0.8": "يفضل استخدام: 80-100 خطوة، أسلوب Euler a، قسط المشوار 0.8", + "Pixels to expand": "عدد البيكسلات", + "Mask blur": "تنعيم القناع", + "How much to blur the mask before processing, in pixels.": "مقدرا تنعيم القناع قبل استخدامه (يقاس بالبيكسل)", + "Outpainting direction": "اتجاه توسيع الصورة", "left": "يسار", "right": "يمين", "up": "فوق", "down": "تحت", - "Fall-off exponent (lower=higher detail)": "أس التناقص (الأدنى = تفاصيل أعلى)", - "Color variation": "اختلاف اللون", - "Will upscale the image to twice the dimensions; use width and height sliders to set tile size": "سيقوم بترقية الصورة إلى ضعف الأبعاد ؛ استخدم شريط تمرير العرض والارتفاع لضبط حجم التبليط", - "Tile overlap": "تداخل التبليط", - "Upscaler": "المرقي", - "Lanczos": "Lanczos", + "Fall-off exponent (lower=higher detail)": "قوة السقوط (كلما قلت زادت التفاصيل)", + "Color variation": "تنوع الألوان", + "Poor man's outpainting": "توسيع الصورة (بدائي)", + "Masked content": "محتويات القناع", + "What to put inside the masked area before processing it with Stable Diffusion.": "ما يوضع مكان الفراغ في الصورة الذي نريد إنتاج محتوياته", + "fill": "ألوان", + "fill it with colors of the image": "املأ باستخدام ألوان مأخوذة من باقي الصورة", + "original": "بدون تغيير", + "keep whatever was there originally": "أبق محتويات ما تحت القناع كما هي", + "latent noise": "تشويش كامن", + "fill it with latent space noise": "املأه باستخدام تشويش من الفضاء الكامن", + "latent nothing": "تصفير كامن", + "fill it with latent space zeroes": "استبدل مكان القناع في الفضاء الكامن بأصفار", + "SD upscale": "مضاعفة الدقة", + "Will upscale the image to twice the dimensions; use width and height sliders to set tile size": "سيتم تكبير حجم الصورة إلى الضعف، استخدم الطول والإرتفاع في الأعلى لتحديد حجم نافذة المكبر", + "Tile overlap": "تداخل النافذة", + "For SD upscale, how much overlap in pixels should there be between tiles. Tiles overlap so that when they are merged back into one picture, there is no clearly visible seam.": "المكبر ينظر إلى أجزاء الصورة من خلال نافذة لتكبير المحتوى ثم ينتقل إلى الجزء المجاور، يفضل أن يكون هناك تداخل بين كل رقعة لكي لا يكون هناك اختلاف واضح بينهم", + "Upscaler": "طريقة التكبير", + "Inpaint": "تحبير", + "Draw mask": "ارسم القناع", + "Upload mask": "ارفع القناع", + "Inpaint masked": "تحبير ما بداخل القناع", + "Inpaint not masked": "تحبير ما حول القناع", + "Inpaint at full resolution": "تحبير بالدقة الكاملة", + "Upscale masked region to target resolution, do inpainting, downscale back and paste into original image": "كبر ما يراد تحبيره ثم صغر النتيجة وألصقها في مكانها", + "Inpaint at full resolution padding, pixels": "عدد بيكسلات التبطين", + "Batch img2img": "صور إلى صور", + "Process images in a directory on the same machine where the server is running.": "حدد مسار مجلد صور موجود في جهاز الخادم", + "Use an empty output directory to save pictures normally instead of writing to the output directory.": "يمكنك أيضا تحديد مجلد حفظ النتائج (غير الإفتراضي)", + "Input directory": "مجلد المدخلات", + "Output directory": "مجلد المخرجات", + "Extras": "معالجة", "Single Image": "صورة واحدة", - "Batch Process": "معالجة الدفعات", - "Batch from Directory": "دفعة من المجلد", - "Source": "مصدر", - "Show result images": "إظهار نتائج الصور ", - "Scale by": "رفع الحجم بمقدار", - "Scale to": "رفع الحجم إلى", + "Source": "المصدر", + "Scale by": "مضاعفة الدقة", "Resize": "تغيير الحجم", - "Crop to fit": "اقتصاص للتوافق", - "Upscaler 2 visibility": "إظهار المرقي 2", - "GFPGAN visibility": "إظهار GFPGAN", - "CodeFormer visibility": "إظهار CodeFormer", - "CodeFormer weight (0 = maximum effect, 1 = minimum effect)": "وزن CodeFormer (0 = أقصى تأثير ، 1 = تأثير أدنى)", + "Upscaler 1": "المكبر الأول", + "Upscaler 2": "المكبر الثاني", + "Upscaler 2 visibility": "أثر المكبر الثاني", + "GFPGAN visibility": "أثر GFPGAN (محسن وجوه)", + "CodeFormer visibility": "أثر CodeFormer (محسن وجوه)", + "CodeFormer weight (0 = maximum effect, 1 = minimum effect)": "وزن CodeFormer (يزيد التفاصيل على حساب الجودة)", + "Scale to": "دقة محددة", + "Crop to fit": "قص الأطراف الزائدة إذا لم تتناسب الأبعاد", + "Batch Process": "حزمة صور", + "Batch from Directory": "حزمة من مجلد", + "A directory on the same machine where the server is running.": "مسار مجلد صور موجود في جهاز الخادم", + "Leave blank to save images to the default path.": "اتركه فارغا لاستخدام المسار الإفتراضي", + "Show result images": "اعرض الصور الناتجة", "Open output directory": "افتح مجلد المخرجات", - "Send to txt2img": "أرسل إلى كتابة لصورة", - "A merger of the two checkpoints will be generated in your": "سيتم إنشاء نموذجمدمج من النموذجين في", - "checkpoint": "النموذج", - "directory.": "المجلد.", - "Primary model (A)": "النموذج الأساسي (أ)", - "Secondary model (B)": "النموذج الثانوي (ب)", - "Tertiary model (C)": "النموذج الثالث (ج)", - "Custom Name (Optional)": "اسم مخصص (اختياري)", - "Multiplier (M) - set to 0 to get model A": "المضاعف (M) - اضبط على 0 للحصول على النموذج أ", - "Interpolation Method": "طريقة الاستنباط", - "Weighted sum": "المجموع الموزون", - "Add difference": "أضافة الاختلاف", - "Save as float16": "حفظ float16", - "See": "شاهد", - "wiki": "ويكي", - "for detailed explanation.": "للحصول على شرح مفصل.", - "Create embedding": "إنشاء التضمين", - "Create hypernetwork": "إنشاء شبكة فائقة", - "Preprocess images": "تجهيز الصور", + "PNG Info": "عوامل الصورة", + "Send to txt2img": "أرسل لنص إلى صورة", + "Checkpoint Merger": "مزج الأوزان", + "A merger of the two checkpoints will be generated in your": "سيتم مزج الأوزان التالية وحفظ الأوزان المدجمة مع ", + "checkpoint": "الأوزان", + "directory.": " مجلد.", + "Primary model (A)": "الأوزان الأولى (A)", + "Secondary model (B)": "الأوزان الثانية (B)", + "Tertiary model (C)": "الأوزان الثالثة (C)", + "Custom Name (Optional)": "الاسم الجديد (اختياري)", + "Multiplier (M) - set to 0 to get model A": "العامل M: مسافة الإبتعاد عن الأوزان الأولى A", + "Interpolation Method": "طريقة المزج", + "Weighted sum": "خطية", + "Result = A * (1 - M) + B * M": "النتيجة = A * (1 - M) + B * M", + "Add difference": "جمع الفرق", + "Result = A + (B - C) * M": "النتيجة = A + (B - C) * M", + "Save as float16": "احفظ بدقة float16", + "Run": "تشغيل", + "Train": "تدريب", + "See": "اقرأ", + "wiki": "الـwiki ", + "for detailed explanation.": "لمعرفة المزيد", + "Create embedding": "إنشاء تضمين", "Name": "الاسم", - "Initialization text": "نص التهيئة", - "Number of vectors per token": "عدد المتجهات لكل رمز", - "Overwrite Old Embedding": "الكتابة فوق التضمين القديم", - "Modules": "الوحدات", - "Enter hypernetwork layer structure": "أدخل بنية طبقة الشبكة الفائقة", - "Select activation function of hypernetwork": "حدد وظيفة تنشيط الشبكة الفائقة", - "linear": "خطي (Linear)", - "relu": "الوحدة الخطية المعدلة (Relu)", - "leakyrelu": "الوحدة الخطية المعدلة المسربة (Leakyrelu)", - "elu": "الوحدة الأسية الخطية (Elu)", - "swish": "Swish", - "Add layer normalization": "أضف طبقة التسوية", - "Use dropout": "استخدم الهبوط", - "Overwrite Old Hypernetwork": "الكتابة فوق الشبكة الفائقة القديمة", - "Source directory": "مجلد المصدر", - "Destination directory": "مجلد النتائج", - "Existing Caption txt Action": "الإجراء النصي للتعليق المتوفر", + "Initialization text": "النص المبدأي", + "Number of vectors per token": "عدد المتجهات لكل وحدة لغوية", + "Overwrite Old Embedding": "استبدل التضمين القديم", + "Create hypernetwork": "إنشاء شبكة فائقة", + "Modules": "الأجزاء", + "Enter hypernetwork layer structure": "ترتيب مضاعفات عرض الطبقات", + "1st and last digit must be 1. ex:'1, 2, 1'": "المضاعفين الأول والأخير يجب أن يكونا 1، مثال: 1, 2, 1", + "Select activation function of hypernetwork": "دالة التنشيط", + "linear": "خطية (بدون)", + "relu": "ريلو (ReLU)", + "leakyrelu": "ريلو المتسربة (LeakyReLU)", + "elu": "إيلو (ELU)", + "swish": "سويش (Swish)", + "Add layer normalization": "أضف تسوية الطبقات (LayerNorm)", + "Use dropout": "استخدم الإسقاط (Dropout)", + "Overwrite Old Hypernetwork": "استبدل الشبكة الفائقة القديمة", + "Preprocess images": "معالجة مسبقة للصور", + "Source directory": "مجلد المخرجات", + "Destination directory": "مجلد المخرجات", + "Existing Caption txt Action": "اذا كانت الصورة لديها توصيف (طلب)", "ignore": "تجاهل", - "copy": "نسخ", - "prepend": "أضف قبل", + "copy": "انسخ", + "prepend": "أسبق", "append": "ألحق", - "Create flipped copies": "قم بإنشاء نسخ مقلوبة", - "Split oversized images": "تقسيم الصور كبيرة الحجم", - "Use BLIP for caption": "استخدم BLIP للتعليق", - "Use deepbooru for caption": "استخدم deepbooru للتعليق", - "Split image threshold": "حد تقسيم الصورة", - "Split image overlap ratio": "نسبة تداخل الصورة المنقسمة", - "Preprocess": "تجهيز الصور", - "Train an embedding or Hypernetwork; you must specify a directory with a set of 1:1 ratio images": "تدريب التضمين أو الشبكة الفائقة ؛ يجب تحديد مجلد بمجموعة من الصور بنسبة أبعاد 1: 1", - "[wiki]": "[ويكي]", + "Create flipped copies": "انشئ نسخ معكوسة للصور", + "Split oversized images": "قسّم الصور الكبيرة", + "Split image threshold": "حد تقسيم الصور الكبيرة", + "Split image overlap ratio": "نسبة تداخل اقسام الصور الكبيرة", + "Use BLIP for caption": "استخدم BLIP لتوصيف الصور", + "Use deepbooru for caption": "استخدم deepbooru لتوصيف الصور", + "Preprocess": "معالجة مسبقة", + "Train an embedding or Hypernetwork; you must specify a directory with a set of 1:1 ratio images": "درب التضمين أو الشبكة الفائقة: يجب تحديد مجلد يحتوي صور مربعة فقط ", + "[wiki]": "[wiki]", "Embedding": "التضمين", "Embedding Learning rate": "معدل تعلم التضمين", "Hypernetwork Learning rate": "معدل تعلم الشبكة الفائقة", "Dataset directory": "مجلد مجموعة البيانات", + "Path to directory with input images": "مسار مجلد الصور المدخلة", "Log directory": "مجلد السجل", - "Prompt template file": "ملف قالب الموجهات", - "Max steps": "الخطوات القصوى", - "Save an image to log directory every N steps, 0 to disable": "حفظ صورة في مجلد السجل كل N خطوات ، 0 للتعطيل", - "Save a copy of embedding to log directory every N steps, 0 to disable": "حفظ نسخة من التضمين في مجلد السجل كل N خطوات ، 0 للتعطيل", - "Save images with embedding in PNG chunks": "حفظ التضمين مع الصور في أجزاء PNG", - "Read parameters (prompt, etc...) from txt2img tab when making previews": "قراءة المتغيرات (الموجه ، إلخ ...) من علامة تبويب نص لصورة عند إجراء المعاينات", - "Train Hypernetwork": "تدريب الشبكة الفائقة", - "Train Embedding": "تدريب التضمين", - "Create an aesthetic embedding out of any number of images": "Create an aesthetic embedding out of any number of images", - "Create images embedding": "Create images embedding", - "extras": "إضافات", - "favorites": "المفضلة", - "custom fold": "custom fold", - "Load": "تحميل", + "Path to directory where to write outputs": "مسار مجلد الصور المخرجة", + "Prompt template file": "ملف صيغ الطلبات", + "Max steps": "أقصى عدد خطوات التدريب", + "Save an image to log directory every N steps, 0 to disable": "احفظ صورة في السجل بعد كل كم خطوة تدريب (لا تحفظ إذا كان 0)", + "Save a copy of embedding to log directory every N steps, 0 to disable": "احفظ التضمين في السجل بعد كل كم خطوة تدريب (لا تحفظ إذا كان 0)", + "Save images with embedding in PNG chunks": "احفظ التضمين بداخل ملف الصورة كعامل يمكن استخراجه من عوامل الصورة (صيغة PNG)", + "Read parameters (prompt, etc...) from txt2img tab when making previews": "استخدم قيم العوامل الموجودة في تبويب نص إلى صورة لعرض نتائجهم أثناء التدريب", + "Train Hypernetwork": "درّب الشبكة الفائقة", + "Train Embedding": "درّب التضمين", + "Create aesthetic embedding": "تضمين تجميلي", + "Create an aesthetic embedding out of any number of images": "انشئ تضمين تجميلي يعبر عن مجموعة من الصور", + "Create images embedding": "انشئ التضمين التجميلي", + "Image Browser": "معرض الصور", + "Load": "حمّل", "Images directory": "مجلد الصور", - "Prev batch": "الدفعة السابقة", - "Next batch": "الدفعة التالية", "First Page": "الصفحة الأولى", "Prev Page": "الصفحة السابقة", - "Page Index": "فهرس الصفحات", + "Page Index": "رقم الصفحة", "Next Page": "الصفحة التالية", - "End Page": "صفحة النهاية", - "number of images to delete consecutively next": "عدد الصور المطلوب حذفها على التوالي بعد ذلك", - "Delete": "حذف", - "Generate Info": "معلومات الإنشاء", + "End Page": "الصفحة الأخيرة", + "number of images to delete consecutively next": "عدد الصور المتتالية للحذف", + "Delete": "احذف", + "Generate Info": "معلومات عامة", "File Name": "اسم الملف", - "Collect": "جمع", - "Refresh page": "إعادة تحميل الصفحة", - "Date to": "التاريخ إلى", - "Number": "الرقم", - "set_index": "وضع الفهرس", - "Checkbox": "صندوق اختيار", - "Apply settings": "تطبيق الإعدادات", - "Saving images/grids": "حفظ الصور / الإطار الشبكي", - "Always save all generated images": "احفظ دائمًا جميع الصور التي تم إنشائها", - "File format for images": "تنسيق ملفات الصور", - "Images filename pattern": "نمط اسم ملفات الصور", - "Add number to filename when saving": "Add number to filename when saving", - "Always save all generated image grids": "احفظ دائمًا جميع الإطارات الشبكية للصور التي تم إنشاؤها", - "File format for grids": "تنسيق ملفات الإطارات الشبكية", - "Add extended info (seed, prompt) to filename when saving grid": "أضف معلومات إضافية (البذرة ، الموجه) إلى اسم الملف عند حفظ الإطار الشبكي", - "Do not save grids consisting of one picture": "لا تحفظ الإطارات الشبكية التي تتكون من صورة واحدة", - "Prevent empty spots in grid (when set to autodetect)": "منع المناطق الفارغة في الإطار الشبكي (عند الضبط على الاكتشاف التلقائي)", - "Grid row count; use -1 for autodetect and 0 for it to be same as batch size": "عدد صفوف الإطار الشبكي استخدم -1 للاكتشاف التلقائي و 0 ليكون نفس حجم الدُفعة", - "Save text information about generation parameters as chunks to png files": "احفظ معلومات نصية حول متغيرات الإنشاء كمقاطع في ملفات png", - "Create a text file next to every image with generation parameters.": "قم بإنشاء ملف نصي بجوار كل صورة باستخدام متغيرات الإنشاء.", - "Save a copy of image before doing face restoration.": "احفظ نسخة من الصورة قبل القيام بترميم الوجوه.", - "Quality for saved jpeg images": "جودة الصور المحفوظة بتنسيق jpeg", - "If PNG image is larger than 4MB or any dimension is larger than 4000, downscale and save copy as JPG": "إذا كانت صورة PNG أكبر من 4 ميجابايت أو كان أي بُعد أكبر من 4000 ، قم بتقليل حجم الصورة وحفظها بتنسيق JPG", - "Use original name for output filename during batch process in extras tab": "استخدم الاسم الأصلي لاسم ملف الإخراج أثناء عملية الدُفعات في علامة تبويب الإضافات", - "When using 'Save' button, only save a single selected image": "عند استخدام زر 'حفظ' ، احفظ فقط صورة واحدة محددة", - "Do not add watermark to images": "لا تقم بإضافة العلامة المائية للصور", - "Paths for saving": "مسارات الحفظ", - "Output directory for images; if empty, defaults to three directories below": "مجلد المخرجات للصور ؛ إذا كان فارغا ، يتم تعيينه افتراضيًا إلى المجلدات الثلاثة أدناه", - "Output directory for txt2img images": "مجلد المخرجات لصور نص لصورة", - "Output directory for img2img images": "مجلد المخرجات لصور صورة لصورة", - "Output directory for images from extras tab": "مجلد المخرجات لصور علامة تبويب الإضافات", - "Output directory for grids; if empty, defaults to two directories below": "مجلد المخرجات للإطارات الشبكية ؛ إذا كان فارغا ، يتم تعيينه افتراضيًا إلى المجلدين أدناه", - "Output directory for txt2img grids": "مجلد المخرجات للإطارات الشبكية نص لصورة", - "Output directory for img2img grids": "مجلد المخرجات للإطارات الشبكية صورة لصورة", - "Directory for saving images using the Save button": "مجلد لحفظ الصور باستخدام زر حفظ", - "Saving to a directory": "يتم الحفظ إلى المجلد..", - "Save images to a subdirectory": "حفظ الصور في مجلد فرعي", - "Save grids to a subdirectory": "حفظ الإطارات الشبكية في مجلد فرعي", + "Collect": "اجمع", + "extras": "معالجة", + "favorites": "المفضلة", + "custom fold": "مجلد آخر", + "Input images directory": "مجلد الصور المدخلة", + "Settings": "إعدادات", + "Apply settings": "طبق الإعدادت", + "Saving images/grids": "حفظ الصور وجداولها", + "Always save all generated images": "احفظ كل الصور المنتجة", + "File format for images": "صيغة ملفات الصور", + "Images filename pattern": "نمط تسمية الصور", + "Use following tags to define how filenames for images are chosen: [steps], [cfg], [prompt], [prompt_no_styles], [prompt_spaces], [width], [height], [styles], [sampler], [seed], [model_hash], [prompt_words], [date], [datetime], [datetime], [datetime