| import torch |
| import base64 |
| import io |
| import math |
| import gc |
| from PIL import Image, ImageOps |
| from diffusers import StableDiffusionXLPipeline, StableDiffusionXLInpaintPipeline, DPMSolverMultistepScheduler |
| from transformers import CLIPVisionModelWithProjection, CLIPImageProcessor |
| import os |
|
|
| class EndpointHandler(): |
| def __init__(self, path=""): |
| print("Loading ARX Elite Engine...") |
| model_path = os.path.join(path, "biglust.safetensors") |
| |
| |
| print("Loading Base Model...") |
| base_pipe = StableDiffusionXLPipeline.from_single_file( |
| model_path, |
| torch_dtype=torch.float16, |
| use_safetensors=True, |
| safety_checker=None, |
| low_cpu_mem_usage=True |
| ) |
| |
| |
| print("Converting Base Model to Inpainting Architecture...") |
| components = base_pipe.components |
| self.pipe = StableDiffusionXLInpaintPipeline(**components) |
| |
| |
| del base_pipe |
| gc.collect() |
| torch.cuda.empty_cache() |
| |
| |
| print("Loading CLIP Vision Encoder...") |
| self.pipe.image_encoder = CLIPVisionModelWithProjection.from_pretrained( |
| "h94/IP-Adapter", |
| subfolder="models/image_encoder", |
| torch_dtype=torch.float16, |
| low_cpu_mem_usage=True |
| ).to("cuda") |
| self.pipe.feature_extractor = CLIPImageProcessor() |
| |
| |
| print("Loading IP-Adapter weights...") |
| self.pipe.load_ip_adapter( |
| "h94/IP-Adapter", |
| subfolder="sdxl_models", |
| weight_name="ip-adapter-plus_sdxl_vit-h.safetensors" |
| ) |
|
|
| self.pipe.scheduler = DPMSolverMultistepScheduler.from_config( |
| self.pipe.scheduler.config, |
| use_karras_sigmas=True, |
| algorithm_type="sde-dpmsolver++" |
| ) |
| |
| self.pipe.to("cuda") |
| print("ARX Inpainting + IP-Adapter Ready.") |
|
|
| def decode_base64_image(self, image_string): |
| if "," in image_string: |
| image_string = image_string.split(",")[1] |
| image_bytes = base64.b64decode(image_string) |
| return Image.open(io.BytesIO(image_bytes)).convert("RGB") |
|
|
| def encode_image_base64(self, image): |
| buffered = io.BytesIO() |
| image.save(buffered, format="PNG") |
| return base64.b64encode(buffered.getvalue()).decode('utf-8') |
|
|
| def __call__(self, data): |
| inputs = data.pop("inputs", data) |
| |
| prompt = inputs.get("prompt", "masterpiece, best quality") |
| negative_prompt = inputs.get("negative_prompt", "blurry, lowres, bad anatomy, worst quality, ugly") |
| |
| strength = float(inputs.get("strength", 0.85)) |
| guidance_scale = float(inputs.get("guidance_scale", 7.5)) |
| num_inference_steps = int(inputs.get("steps", 30)) |
| |
| ip_scale = float(inputs.get("ip_scale", 0.6)) |
| |
| init_image_b64 = inputs.get("init_image") |
| mask_image_b64 = inputs.get("mask_image") |
| reference_image_b64 = inputs.get("reference_image") |
|
|
| if not init_image_b64 or not mask_image_b64: |
| return {"error": "Missing init_image or mask_image."} |
|
|
| raw_init = self.decode_base64_image(init_image_b64) |
| raw_mask = self.decode_base64_image(mask_image_b64).convert("L") |
| |
| orig_w, orig_h = raw_init.size |
|
|
| |
| max_size = 1024 |
| if orig_w > orig_h: |
| new_w = max_size |
| new_h = int(max_size * (orig_h / orig_w)) |
| else: |
| new_h = max_size |
| new_w = int(max_size * (orig_w / orig_h)) |
| |
| scaled_init = raw_init.resize((new_w, new_h), Image.LANCZOS) |
| scaled_mask = raw_mask.resize((new_w, new_h), Image.LANCZOS) |
|
|
| |
| pad_w = math.ceil(new_w / 64) * 64 |
| pad_h = math.ceil(new_h / 64) * 64 |
| |
| init_padded = Image.new("RGB", (pad_w, pad_h), (0, 0, 0)) |
| init_padded.paste(scaled_init, (0, 0)) |
| |
| mask_padded = Image.new("L", (pad_w, pad_h), 0) |
| mask_padded.paste(scaled_mask, (0, 0)) |
|
|
| |
| kwargs = { |
| "prompt": prompt, |
| "negative_prompt": negative_prompt, |
| "image": init_padded, |
| "mask_image": mask_padded, |
| "width": pad_w, |
| "height": pad_h, |
| "strength": strength, |
| "guidance_scale": guidance_scale, |
| "num_inference_steps": num_inference_steps |
| } |
|
|
| |
| if reference_image_b64: |
| raw_ref = self.decode_base64_image(reference_image_b64) |
| ip_image = ImageOps.fit(raw_ref, (224, 224), method=Image.LANCZOS) |
| kwargs["ip_adapter_image"] = ip_image |
| self.pipe.set_ip_adapter_scale(ip_scale) |
| else: |
| blank_image = Image.new("RGB", (224, 224), (0, 0, 0)) |
| kwargs["ip_adapter_image"] = blank_image |
| self.pipe.set_ip_adapter_scale(0.0) |
|
|
| |
| result_padded = self.pipe(**kwargs).images[0] |
| |
| |
| cropped_result = result_padded.crop((0, 0, new_w, new_h)) |
| |
| |
| final_result = cropped_result.resize((orig_w, orig_h), Image.LANCZOS) |
|
|
| return {"image": self.encode_image_base64(final_result)} |