dikdimon's picture
Upload exhm using SD-Hub extension
194b4ef verified
from typing import Any
from fastapi import FastAPI, Body
from pydantic import BaseModel
import modules.script_callbacks as script_callbacks
from modules import shared
from modules.api.api import encode_pil_to_base64, decode_base64_to_image
from modules.call_queue import queue_lock
from replacer.generate import generate
from replacer.generation_args import GenerationArgs, HiresFixArgs
from replacer.tools import generateSeed
from replacer.ui.tools_ui import IS_WEBUI_1_9, prepareExpectedUIBehavior
from replacer.extensions import replacer_extensions
def replacer_api(_, app: FastAPI):
from scripts.sam import sam_model_list
from scripts.dino import dino_model_list
try:
from lama_cleaner_masked_content.inpaint import lamaInpaint
lama_cleaner_available = True
except Exception as e:
lama_cleaner_available = False
class ReplaceRequest(BaseModel):
input_image: str = "base64 image"
detection_prompt: str = ""
avoidance_prompt: str = ""
positive_prompt: str = ""
negative_prompt: str = ""
width: int = 512
height: int = 512
sam_model_name: str = sam_model_list[0] if sam_model_list else ""
dino_model_name: str = dino_model_list[0]
seed: int = -1
sampler: str = "DPM++ 2M SDE" if IS_WEBUI_1_9 else "DPM++ 2M SDE Karras"
scheduler: str = "Automatic"
steps: int = 20
box_threshold: float = 0.3
mask_expand: int = 35
mask_blur: int = 4
mask_num: str = "Random"
max_resolution_on_detection = 1280
cfg_scale: float = 5.5
denoise: float = 1.0
inpaint_padding = 40
inpainting_mask_invert: bool = False
upscaler_for_img2img : str = ""
fix_steps : bool = False
inpainting_fill : int = 0
sd_model_checkpoint : str = ""
clip_skip: int = 1
rotation_fix: str = '-' # choices: '-', '⟲', '⟳', '🗘'
extra_include: list = ["mask", "box", "cut", "preview", "script"]
variation_seed: int = -1
variation_strength: float = 0.0
integer_only_masked: bool = False
forbid_too_small_crop_region: bool = True
correct_aspect_ratio: bool = True
use_hires_fix: bool = False
hf_upscaler: str = "ESRGAN_4x"
hf_steps: int = 4
hf_sampler: str = "Use same sampler"
hf_scheduler: str = "Use same scheduler"
hf_denoise: float = 0.35
hf_cfg_scale: float = 1.0
hf_positive_prompt_suffix: str = "<lora:lcm-lora-sdv1-5:1>"
hf_size_limit: int = 1800
hf_above_limit_upscaler: str = "Lanczos"
hf_unload_detection_models: bool = True
hf_disable_cn: bool = True
hf_extra_mask_expand: int = 5
hf_positive_prompt: str = ""
hf_negative_prompt: str = ""
hf_sd_model_checkpoint: str = "Use same checkpoint"
hf_extra_inpaint_padding: int = 250
hf_extra_mask_blur: int = 2
hf_randomize_seed: bool = True
hf_soft_inpaint: str = "Same"
hf_supersampling: float = 1.6
scripts : dict = {} # ControlNet and Soft Inpainting. See apiExample.py for example
@app.post("/replacer/replace")
async def api_replacer_replace(data: ReplaceRequest = Body(...)) -> Any:
image = decode_base64_to_image(data.input_image).convert("RGBA")
cn_args, soft_inpaint_args = replacer_extensions.prepareScriptsArgs_api(data.scripts)
hires_fix_args = HiresFixArgs(
upscaler = data.hf_upscaler,
steps = data.hf_steps,
sampler = data.hf_sampler,
scheduler=data.hf_scheduler,
denoise = data.hf_denoise,
cfg_scale = data.hf_cfg_scale,
positive_prompt_suffix = data.hf_positive_prompt_suffix,
size_limit = data.hf_size_limit,
above_limit_upscaler = data.hf_above_limit_upscaler,
unload_detection_models = data.hf_unload_detection_models,
disable_cn = data.hf_disable_cn,
extra_mask_expand = data.hf_extra_mask_expand,
positive_prompt = data.hf_positive_prompt,
negative_prompt = data.hf_negative_prompt,
sd_model_checkpoint = data.hf_sd_model_checkpoint,
extra_inpaint_padding = data.hf_extra_inpaint_padding,
extra_mask_blur = data.hf_extra_mask_blur,
randomize_seed = data.hf_randomize_seed,
soft_inpaint = data.hf_soft_inpaint,
supersampling = data.hf_supersampling,
)
gArgs = GenerationArgs(
positivePrompt=data.positive_prompt,
negativePrompt=data.negative_prompt,
detectionPrompt=data.detection_prompt,
avoidancePrompt=data.avoidance_prompt,
upscalerForImg2Img=data.upscaler_for_img2img,
seed=data.seed,
samModel=data.sam_model_name,
grdinoModel=data.dino_model_name,
boxThreshold=data.box_threshold,
maskExpand=data.mask_expand,
maxResolutionOnDetection=data.max_resolution_on_detection,
steps=data.steps,
sampler_name=data.sampler,
scheduler=data.scheduler,
mask_blur=data.mask_blur,
inpainting_fill=data.inpainting_fill,
batch_count=1,
batch_size=1,
cfg_scale=data.cfg_scale,
denoising_strength=data.denoise,
height=data.height,
width=data.width,
inpaint_full_res_padding=data.inpaint_padding,
img2img_fix_steps=data.fix_steps,
inpainting_mask_invert=data.inpainting_mask_invert,
images=[image],
override_sd_model=True,
sd_model_checkpoint=data.sd_model_checkpoint,
mask_num=data.mask_num,
avoidance_mask=None,
only_custom_mask=False,
custom_mask=None,
use_inpaint_diff=False,
clip_skip=data.clip_skip,
pass_into_hires_fix_automatically=data.use_hires_fix,
save_before_hires_fix=False,
do_not_use_mask=False,
rotation_fix=data.rotation_fix,
variation_seed=data.variation_seed,
variation_strength=data.variation_strength,
integer_only_masked=data.integer_only_masked,
forbid_too_small_crop_region=data.forbid_too_small_crop_region,
correct_aspect_ratio=data.correct_aspect_ratio,
hires_fix_args=hires_fix_args,
cn_args=cn_args,
soft_inpaint_args=soft_inpaint_args,
)
prepareExpectedUIBehavior(gArgs)
with queue_lock:
shared.state.begin('api /replacer/replace')
try:
processed, allExtraImages = generate(gArgs, "", False, data.extra_include)
finally:
shared.state.end()
return {
"image": encode_pil_to_base64(processed.images[0]).decode(),
"extra_images": [encode_pil_to_base64(x).decode() for x in allExtraImages],
"info": processed.info,
"json": processed.js(),
}
@app.post("/replacer/available_options")
async def api_replacer_available_options() -> Any:
return {
"sam_model_name": sam_model_list,
"dino_model_name": dino_model_list,
"upscalers": [""] + [x.name for x in shared.sd_upscalers],
"lama_cleaner_available": lama_cleaner_available, # inpainting_fill=4, https://github.com/light-and-ray/sd-webui-lama-cleaner-masked-content
"available_scripts": replacer_extensions.getAvailableScripts_api(),
}
script_callbacks.on_app_started(replacer_api)