見出し画像

sdxlサーバ、モデルリロード版

モデルをクライエントから指定できるようにしたバージョン。

api_sdxl_v1_1_server_t.py   サーバ本体

import torch
import cv2
from PIL import Image
#from  n_lcm_lora_sdxl_i2i_t2i_class_v1 import DiffusersPipelineManager
from  n_lcm_lora_sdxl_i2i_t2i_class_v1_1_t import DiffusersPipelineManager
import pickle

# ===================================     FastAPI  ==============================
from fastapi import FastAPI, File, UploadFile, Form,  Query
from fastapi.responses import HTMLResponse,StreamingResponse,JSONResponse,Response
from pydantic import BaseModel
from io import BytesIO
import io
import json
import base64
import datetime
import string
import time
import gc

app = FastAPI()

#------ DiffusersPipelineManagerの定義と初期化
Df=DiffusersPipelineManager()

#------ 独自LoRAの定義
lola_models_list=[
            ["./models/animagine-xl-2.0/megu_sports_sdxl_v02.safetensors","style",0.8],
            ["./models/animagine-xl-2.0/anime-detailer-xl.safetensors","detaile",1.0],
            ["./models/animagine-xl-2.0/style-enhancer-xl.safetensors","enhancer",0.5]]
 #----- pipelineのロード時の引数定義
model="./models/animagine-xl-2.0/animagine-xl-2.0.safetensors"
#model="cagliostrolab/animagine-xl-4.0"
#model="./models/animagine-xl-2.0/animagine-xl-3.1.safetensors"
#model="cagliostrolab/animagine-xl-3.1"
#model="stabilityai/stable-diffusion-xl-base-1.0"
lcm_lora_en=True
lcm_lora_path="./models/SSD-1B-anime/lcm-ssd1b-anime.safetensors"
vae_en=True
vae_path="madebyollin/sdxl-vae-fp16-fix"
lora_en=False
lola_models=lola_models_list
ddism_en=False
set_unet=True
torch_compile=False


def load_model(model, torch_compile=None, lcm_lora_en=None):
    #----- pipelineのロード "t2i"・"i2i_pipe"・"inpaint_pipe"はデフォルトでロードでOK、他はVRAMの容量次第
    print("pipelineのロード開始")
    pipeline_name_list=[]
    pipe_t2i=Df.pipeline_init("t2i",
                              pipeline=None,
                              model=model,
                              vae_en=vae_en,
                              vae_path=vae_path,
                              lcm_lora_en=lcm_lora_en,
                              lcm_lora_path=lcm_lora_path,
                              lora_en=lora_en,
                              lola_models=lola_models,
                              ddism_en=ddism_en,
                              set_unet=set_unet,
                              torch_compile=torch_compile
                              )
    pipeline_name_list.append("t2i")
    pipe_i2i=Df.pipeline_init("i2i_pipe",pipeline=pipe_t2i)#pipelineの使い回し
    pipeline_name_list.append("pipe_i2i")
    pipe_inpaint=Df.pipeline_init("inpaint_pipe",pipeline=pipe_t2i)#pipelineの使い回し
    pipeline_name_list.append("pipe_inpaint")
    return pipe_t2i,pipe_i2i,pipe_inpaint,pipeline_name_list

pipe_t2i,pipe_i2i,pipe_inpaint,pipeline_name_list=load_model(model)

def load_contrilnet(control_type):
    if control_type=="canny":
        control_pipe=Df.pipeline_init("canny",model=model) #Canny ContorolNET用pipeの新規定義
        pipeline_name_list.append("canny")
    elif control_type=="openpose":
        control_pipe=Df.pipeline_init("openpose",model=model) #openpose ContorolNET用pipeの新規定義
        pipeline_name_list.append("openpose")
    elif control_type=="openpose_lola":
        control_pipe=Df.pipeline_init("openpose_lola",model=model,  lola_models=lola_models) #lola_models指定
        pipeline_name_list.append("openpose_lola")
    elif control_type=="depth":
        control_pipe = Df.pipeline_init("depth",model=model)
        pipeline_name_list.append("depth")
    elif control_type=="t2i_adapter":
        control_pipe=Df.pipeline_init("t2i_adapter",model=model_t2i, lora_en=False)#t2i_adapter用
        pipeline_name_list.append("t2i_adapter")
    elif control_type=="t2i_ip":
        control_pipe=Df.pipeline_init("t2i_ip",model=model, lcm_lora_en=True,  mul_ip=True, ip_scal1=0.9,ip_scal2=0.5)
        pipeline_name_list.append("t2i_ip")
    elif control_type=="i2i_ip":
        control_pipe=Df.pipeline_init("i2i_ip",model=model, lcm_lora_en=True,  mul_ip=True, ip_scal1=0.9,ip_scal2=0.5)
        pipeline_name_list.append("i2i_ip")
    elif control_type=="t2i_sip":
        control_pipe=Df.pipeline_init("t2i_ip",model=model, lcm_lora_en=True,  single_ip=True,ip_scal1=0.6)
        pipeline_name_list.append("t2i_ip")
    elif control_type=="i2i_sip":
        control_pipe=Df.pipeline_init("i2i_ip",model=model, lcm_lora_en=True,  single_ip=True,ip_scal1=0.6)
        pipeline_name_list.append("i2i_ip")
    return control_pipe

# 利用できるpipeline名 → i2i/canny/pipe_i2i/pipe_inpaint/canny/openpose//depth/t2i_adapter
#--------pipeline名からpipeを選択して返す
def pipline_sel(pipeline):
    if pipeline=="t2i":
        return pipe_t2i
    elif  pipeline=="pipe_i2i":
        return pipe_i2i
    elif  pipeline=="pipe_inpaint":
        return pipe_inpaint
    elif  pipeline=="canny":
         return pipe_canny
    elif  pipeline=="openpose":
         return pipe_openpose
    elif  pipeline=="depth":
         return pipe_depth 
    elif  pipeline=="t2i_adapter":
         return pipe_t2i_adapter
    elif  pipeline=="t2i_sip":
         return pipe_t2i_sip
    elif  pipeline=="t2i_mip":
         return pipe_t2i_mip
    elif  pipeline=="i2i_sip":
         return pipe_i2i_sip
    elif  pipeline=="i2i_mip":
         return pipe_i2i_mip
    elif  pipeline=="inpaint_sip":
         return pipe_inpaint_sip
    else:
        return pipe_t2i

#-------------------- FastAPI エンドポイント--------------------------

@app.post("/get_pipeline/")
def get_pipeline():
    return pipeline_name_list

@app.post("/reload_model/")
def reload_model(model_id: str = Form(...), del_pipe: bool = Form(False),torch_compile: bool = Form(False),lcm_lora_en: bool = Form(False),):
    global pipe_t2i, pipe_i2i, pipe_inpaint, pipeline_name_list
    try:
        if del_pipe:
            print("Deleting previously loaded pipelines and freeing GPU VRAM.")

            # 既存のパイプラインを CPU に移動し、メモリを解放
            for attr in ["pipe_t2i", "pipe_i2i", "pipe_inpaint"]:
                pipeline = globals().get(attr)
                if pipeline is not None:
                    try:
                        print(f"Releasing {attr} components before moving to CPU...")
                        # 各コンポーネントを明示的に削除
                        if hasattr(pipeline, "unet"):
                            pipeline.unet = None
                        if hasattr(pipeline, "vae"):
                            pipeline.vae = None
                        if hasattr(pipeline, "text_encoder"):
                            pipeline.text_encoder = None
                        if hasattr(pipeline, "tokenizer"):
                            pipeline.tokenizer = None

                        # CPU に移動
                        pipeline.to("cpu")  
                        print(f"{attr} moved to CPU.")
                    except Exception as e:
                        print(f"Failed to move {attr} to CPU:", e)

                    # 明示的に削除
                    del pipeline
                    globals()[attr] = None

            # 明示的に削除
            try:
                del pipe_t2i, pipe_i2i, pipe_inpaint
            except:
                print("some pipe was not loaded")
            # メモリ解放
            torch.cuda.empty_cache()  # GPU キャッシュを解放
            gc.collect()  # Python のガベージコレクション実行

            pipeline_name_list = []

        # モデルを再ロード
        print("Reloading pipeline for", model_id)
        pipe_t2i, pipe_i2i, pipe_inpaint, pipeline_name_list = load_model(model_id, torch_compile=torch_compile, lcm_lora_en=lcm_lora_en)
    except:
        print("Failed to reload pipeline.")
        return {"status": "failed"}
    return pipeline_name_list
   
@app.post("/get_canny/")
def get_canny(image: UploadFile = File(...),  low_threshold : int = Form(100) , high_threshold: int = Form(200)):
    image_data = image.file.read()
    image_data = Image.open(BytesIO( image_data))  # バイナリデータをPIL形式に変換
    image_data = image_data.convert("RGB")
    canny_image = Df.get_canny(original_image=image_data , low_threshold = low_threshold , high_threshold =high_threshold)
    canny_data = pickle.dumps(canny_image, 5)  # tx_dataはpklデータ
    return Response(content=canny_data , media_type="application/octet-stream")

@app.post("/get_openpose/")
def get_openpose(image: UploadFile = File(...)):
    image_data = image.file.read()
    image_data = Image.open(BytesIO( image_data))  # バイナリデータをPIL形式に変換
    image_data = image_data.convert("RGB")
    openpose_image = Df.get_openpose(image_data )    
    openpose_data = pickle.dumps(openpose_image, 5)  # tx_dataはpklデータ
    return Response(content= openpose_data , media_type="application/octet-stream")

@app.post("/get_depth_map/")
def get_depth_map(image: UploadFile = File(...)):
    image_data = image.file.read()
    image_data = Image.open(BytesIO( image_data))  # バイナリデータをPIL形式に変換
    image_data = image_data.convert("RGB")
    depth_image = Df.get_depth_map( image_data)
    depth_data = pickle.dumps(depth_image , 5)  # tx_dataはpklデータ
    return Response(content= depth_data, media_type="application/octet-stream")

@app.post("/generate/")
def generate(
    pipeline: str = Form(...),
    image: UploadFile = File(None),
    mask_image: UploadFile =  File(None),
    embe:bool =  Form(False),
    prompt_embe:list =  Form(None),
    prompt_embe0:str =  Form(None),
    prompt_embe1:str =  Form(None),
    prompt:str = Form(None),
    prompt_2:str = Form(None),
    negative_prompt:str = Form(None),
    negative_prompt_2:str = Form(None),
    num_inference_steps:int =Form(4),
    strength:float = Form(0.1),
    guidance_scale:float = Form(1.0),
    seed:int= Form(0),
    auto_seed:bool = Form(True),
    height :int= Form(None),
    width:int = Form(None),
    num_images_per_prompt:int = Form(1),
    guess_mode:bool = Form(None),
    freeu:bool = Form(False),
    freeu_list:list=Form(None),
    output_type:str = Form("pil"),
    crops_coords_top_left:list = Form([0,0]),
    controlnet_conditioning_scale:float = Form(1.0),
    adapter_conditioning_scale:float=Form(None),
    adapter_conditioning_factor:int=Form(None),
    ip_adapter:bool=Form(False),
    style_images:UploadFile =File(None),   
    ip_image:UploadFile =File(None),   
    ):
    #−−−−−pipeの設定
    try:
        pipe = pipline_sel(pipeline)
        
        #−−−−−conditioning, pooledがGPUにTensor形式でロードされるのでprompt_embeddingをサーバ側で実行
        if embe:
            prompt_embe=[prompt_embe0,prompt_embe1]
            conditioning, pooled = Df.prompt_embedding(pipe , prompt_embe)
        else:    #embeがFalseならconditioning、pooled はNoneを設定
            conditioning = None
            pooled = None
        #−−−−−Seed値を取得 auto_seedがTrueならtorch.initial_seed( )

        if seed == None:    
            if auto_seed: #seedが無くてauto_seedが有効(True)の時
                seedx= torch.initial_seed( )
                generator = torch.manual_seed(seedx )
            else:                   #seedが無くてauto_seedが無効(False)の時
                generator = None
        else: #seedが指定された場合
            generator = torch.manual_seed(seed )
        """
        if auto_seed:
            seedx= torch.initial_seed( )
            generator = torch.manual_seed(seedx )
        else:
            if seed == None:    #auto_seedがFalseでseedがない場合
                generator = None
            else:                          #auto_seedがFalseでseedが指定された場合
                generator = torch.manual_seed(seed )
        """
        # −−−−− 受け取ったイメージをPIL(RGB)へ変換
        if image:                    #inpaint input イメージ
            image_data =image.file.read()
            image = Image.open(BytesIO(image_data))  # バイナリデータをPIL形式に変換
            image = image.convert("RGB")
        else:
            image = None
        if mask_image:       #inpaint マスク イメージ
                mask_data =mask_image.file.read()
                mask_image = Image.open(BytesIO( mask_data))  # バイナリデータをPIL形式に変換
                mask_image = mask_image.convert("RGB")
        else:
            mask_image = None
        # −−−−− IP-Adapter用の画像の再現      
        if ip_adapter:
            if ip_image:
                ip_data= ip_image.file.read()
                ip_image = Image.open(BytesIO(ip_data))  # バイナリデータをPIL形式に変換
                ip_image = ip_image.convert("RGB")
                #face_image .show()
        # −−−−− "t2i_mip"と"i2i_mip"の時、受け取ったStyle IPイメージをstyle_images_listへ復元
                if pipeline=="t2i_mip" or  pipeline=="i2i_mip":
                    style_images_rb=style_images.file.read()
                    style_images_list = pickle.loads(style_images_rb)
                else:
                    style_images_list=None
        else:
            ip_image =None
            style_images_list=None
            
        #−−−−−freeuの設定
        if freeu:
            #freeu_list=eval(freeu_list[0])
            freeu_list=[float(item) for item in freeu_list]
            print("++++++++++++++++++++++++++++++++++++++  freeu_list", freeu_list)
        crops_coords_top_left=(crops_coords_top_left[0],crops_coords_top_left[1])
        #−−−−−こから画像の生成
        print("prompt=",prompt)
        print("prompt_2=",prompt_2)
        # t2i_adapter専用
        if pipeline=="t2i_adapter":
            start_time = time.time()
            image_list = Df.generate_t2a_adapter(
                pipe,
                image=image,
                prompt=prompt,
                prompt_2=prompt_2,
                negative_prompt = negative_prompt,
                negative_prompt_2 = negative_prompt_2,
                generator=generator,
                height = height,
                width = width,
                num_images_per_prompt = num_images_per_prompt,
                freeu=freeu,
                freeu_list=freeu_list,
                output_type=output_type,
                num_inference_steps= num_inference_steps,
                adapter_conditioning_scale=adapter_conditioning_scale,   
                adapter_conditioning_factor= adapter_conditioning_factor,
                guidance_scale=guidance_scale,
            )
            print("Generation time = ",(time.time() - start_time)*1000,"mS")
        # t2i/i2i/inpaint/canny/openpose/depth
        else:
            start_time = time.time()
            image_list = Df.generate(
                pipe,
                image=image,
                prompt=prompt ,
                prompt_2=prompt_2,
                mask_image=mask_image,
                embe=embe,
                conditioning=conditioning ,#prompt_embeds=conditioning,
                pooled=pooled ,#pooled_prompt_embeds=pooled,
                negative_prompt = negative_prompt,
                negative_prompt_2 = negative_prompt_2,
                num_inference_steps= num_inference_steps,
                strength=strength,
                guidance_scale=guidance_scale,
                generator=generator,
                height = height,
                width = width,
                num_images_per_prompt = num_images_per_prompt,
                guess_mode=guess_mode,
                freeu=freeu,
                freeu_list=freeu_list,
                output_type=output_type,
                crops_coords_top_left=crops_coords_top_left,
                controlnet_conditioning_scale=controlnet_conditioning_scale,
                ip_adapter=ip_adapter,
                ip_image = ip_image ,
                style_images=style_images_list,
            )
            print("Generation time = ",(time.time() - start_time)*1000,"mS")
        #−−−−−生成画像を返信
        images_data = pickle.dumps(image_list, 5)  # tx_dataはpklデータ
    except:
        print("Failed to generate image.")
        return {"status": "failed"}
    return Response(content= images_data, media_type="application/octet-stream")
 
if __name__ == "__main__":
    import uvicorn
    #uvicorn.run(app, host="0.0.0.0", port=64656)
    uvicorn.run(app, host="192.168.5.73", port=64656)


n_lcm_lora_sdxl_i2i_t2i_class_v1_1_t.py   サーバで使うクラス

from diffusers import StableDiffusionXLImg2ImgPipeline  ,  StableDiffusionXLPipeline  ,  AutoPipelineForText2Image, AutoPipelineForImage2Image ,StableDiffusionDepth2ImgPipeline, StableDiffusionXLInpaintPipeline
from diffusers import DiffusionPipeline, LCMScheduler, UNet2DConditionModel , AutoencoderKL ,  AutoPipelineForInpainting,StableDiffusionXLAdapterPipeline
from diffusers import StableDiffusionXLControlNetPipeline, ControlNetModel, AutoencoderKL, UniPCMultistepScheduler, T2IAdapter, LCMScheduler,DDIMScheduler
from diffusers.utils import load_image,make_image_grid
from compel import Compel,ReturnedEmbeddingsType
from controlnet_aux import OpenposeDetector
#Depthで使用
from transformers import DPTFeatureExtractor, DPTForDepthEstimation,CLIPVisionModelWithProjection
import torch
from datetime import datetime
import time
import numpy as np
import cv2
from PIL import Image

class DiffusersPipelineManager:
     def __init__(self):
        #----- デフォルト用定義
        #VAE
        self.org_vae = "madebyollin/sdxl-vae-fp16-fix"
        #model
        self.org_model="./models/animagine-xl-2.0/animagine-xl-2.0.safetensors"
        #self.org_model="./models/sd_xl_1.0/sd_xl_base_1.0.safetensors",
        #self.org_model="./models/blue_pencil-XL-LCM/bluePencilXLLCM_v200.safetensors"
        
        #LoRA
        self.org_lora_model_list=[
            ["./models/animagine-xl-2.0/megu_sports_sdxl_v02.safetensors","style",0.8],
            ["./models/animagine-xl-2.0/anime-detailer-xl.safetensors","detaile",1.0],
            ["./models/animagine-xl-2.0/style-enhancer-xl.safetensors","enhancer",0.5]]

        #embe TEST プロンプトの準備
        self.prompt_embe=["masterpiece, best quality, 1girl, solo, flower, long hair, outdoors, letterboxed, school uniform, day, sky, looking up, short sleeves, parted lips, shirt, cloud,black hair, sunlight, white shirt, serafuku, upper body, from side, pink flower, blurry, brown hair, blue sky, depth of field",
          "at shcool park"]

        #self.lcm_lora="latent-consistency/lcm-lora-sdxl"
        self.lcm_lora="./models/SSD-1B-anime/lcm-ssd1b-anime.safetensors"

     def  pipeline_init(self,
                mode:str="t2i",              #指定できるpipe名 t2i/i2i/canny/i2i_pipe/inpaint_pipe/canny/openpose/canny_openpose/depth/t2i_adapter_canny
                pipeline=None,               #pipeloneの再利用の時に参照する生成済みpipeline名指定 デフォルト:無し
                model:str=None,              #ロードするSDXLモデル名 デフォルト:無し
                vae_en:bool=True,            #VAE有効無効設定  デフォルト:有効
                vae_path:str=None,           #VAE有効時のVAEへのパス デフォルト:無し
                lcm_lora_en:bool=True,       #LCM-LoRA有効無効設定 無効にするとLCM-LoRAとLCMスケジュ-ラがロードされない デフォルト 有効
                lcm_lora_path=None,          #LCM-LoRA有効のLCM-LoRAへのパス デフォルト:無し
                lora_en:bool=False,          #Style-LoRAの有効無効設定 デフォルト 有効
                lola_models:list=None,       #Style-LoRAの有効時のStyle-LoRAへのパスリスト 複数のStyle-LoRAをロード出来る デフォルト:無し
                ddism_en:bool=False,         # ddism¥スケジューラ有効化
                single_ip:bool=False,        # IP-Adapter 有効化
                mul_ip:bool=False,           # Multi IP-Adapter 有効化
                ip_scal1:float=None,         #  IP-AdapterのScale/Multi IP-Adapter のScale1
                ip_scal2:float=None,         #  Multi IP-Adapter のScale2
                set_unet:bool=False,         #UNETの有効無効設定 デフォルト 有効
                torch_compile:bool=False,
                ):
          
        # デフォルト引数が None の場合、クラス属性を使用
        if model is None:
            model = self.org_model
        if vae_path is None:
            vae_path = self.org_vae
        if lcm_lora_path is None:
            lcm_lora_path = self.lcm_lora
        if lola_models is None:
            lola_models = self.org_lora_model_list         
        #UNETの準備
        if lcm_lora_en:
            unet = UNet2DConditionModel.from_pretrained(
                "latent-consistency/lcm-sdxl",
                torch_dtype=torch.float16,
                variant="fp16",)
        #VAEの準備
        if vae_en:
            vae = AutoencoderKL.from_pretrained(vae_path, torch_dtype=torch.float16, use_safetensors=True)
        else:
            print("VAE disabled")
            vae = None

       
        #pipelineの準備
        if mode=="t2i":
            if lcm_lora_en:
                 print("+++++t2i lcm_lora_en=True")
                 if model.split("/")[1]=="models":  #V1.2 ./models/で始まる場合はHuggingFaceではなくfrom_single_fileでロード
                    try:
                         pipe = StableDiffusionXLPipeline.from_single_file(model,safety_checker=None,feature_extractor=None,unet=unet,torch_dtype=torch.float16,vae=vae, variant="fp16",).to("cuda") 
                         print("Use singlefile & bf16")
                    except:
                         print("Use singlefile & fp32")
                         pipe = StableDiffusionXLPipeline.from_single_file(model,safety_checker=None,feature_extractor=None,unet=unet,torch_dtype=torch.float16,vae=vae,).to("cuda")
                 else:
                    try:                              #1.2 ./models/で始まらない場合はHuggingFaceからダウンロードする
                         pipe = StableDiffusionXLPipeline.from_pretrained(model,safety_checker=None,feature_extractor=None,unet=unet,torch_dtype=torch.float16,vae=vae,variant="fp16",).to("cuda")
                         print("Use hf & bf16")
                    except:
                         print("Use hf & fp32")
                         pipe = StableDiffusionXLPipeline.from_pretrained(model,safety_checker=None,feature_extractor=None,unet=unet,torch_dtype=torch.float16,vae=vae,).to("cuda")
            else:
                 print("+++++t2i lcm_lora_en=False")
                 if model.split("/")[1]=="models":  #V1.2 ./models/で始まる場合はHuggingFaceではなくfrom_single_fileでロード
                    try:
                         pipe = StableDiffusionXLPipeline.from_single_file(model,safety_checker=None,feature_extractor=None,torch_dtype=torch.float16,vae=vae,variant="fp16", ).to("cuda")
                         print("Use hf & bf16")
                    except:
                         print("Use hf & fp32")
                         pipe = StableDiffusionXLPipeline.from_single_file(model,safety_checker=None,feature_extractor=None,torch_dtype=torch.float16,vae=vae,).to("cuda")
                 else:                              #1.2 ./models/で始まらない場合はHuggingFaceからダウンロードする
                    try:
                         pipe = StableDiffusionXLPipeline.from_pretrained(model,safety_checker=None,feature_extractor=None,torch_dtype=torch.float16,vae=vae,variant="fp16",).to("cuda")
                         print("Use hf & bf16")
                    except:
                         print("Use hf & fp32")
                         pipe = StableDiffusionXLPipeline.from_pretrained(model,safety_checker=None,feature_extractor=None,torch_dtype=torch.float16,vae=vae,).to("cuda")

        #"i2i_pipe","inpaint_pipe"は"t2i"の設定を引き継ぐので再設定はできない。設定を変える場合は"i2i"を指定すること。。
        elif mode=="i2i_pipe":
            print("+++++i2i pipe  loading")
            pipe = AutoPipelineForImage2Image.from_pipe(pipeline).to("cuda")
            return pipe
        elif mode=="inpaint_pipe":
            print("+++++inpaint_pipe loading")
            pipe = AutoPipelineForInpainting.from_pipe(pipeline).to("cuda")
            return pipe

        #ControlNet名を指定してpipeを設定するとき。 対象は="canny" , "openpose" , "depth"の3種類
        elif mode=="canny" or mode=="openpose"  or mode=="depth":
             if mode=="canny" :
                 controlnet = ControlNetModel.from_pretrained("diffusers/controlnet-canny-sdxl-1.0", torch_dtype=torch.float16, use_safetensors=True )
             elif mode=="openpose":
                 controlnet = ControlNetModel.from_pretrained("thibaud/controlnet-openpose-sdxl-1.0", torch_dtype=torch.float16)
             elif mode=="depth":
                  controlnet = ControlNetModel.from_pretrained("diffusers/controlnet-depth-sdxl-1.0", variant="fp16", use_safetensors=True, torch_dtype=torch.float16,).to("cuda")
             else:
                  print("ContorolNET error")
             #pipe = StableDiffusionXLControlNetPipeline.from_pretrained(  #HuggingFaceからダウンロードする場合
             pipe = StableDiffusionXLControlNetPipeline.from_single_file(
                   model,
                   controlnet=controlnet,
                   vae=vae,
                   torch_dtype=torch.float16,
                   use_safetensors=True )
             pipe.enable_model_cpu_offload()
        #canny_openpose 指定 マルチControlNeのpipeを設定するとき。
        elif mode=="canny_openpose":
            print("+++++canny_openpose loading")
            controlnets = [
                 ControlNetModel.from_pretrained("diffusers/controlnet-canny-sdxl-1.0", torch_dtype=torch.float16, use_safetensors=True ),
                 ControlNetModel.from_pretrained( "thibaud/controlnet-openpose-sdxl-1.0", torch_dtype=torch.float16 ),
                  ]
            #pipe = StableDiffusionXLControlNetPipeline.from_pretrained(   #HuggingFaceからダウンロードする場合
            pipe = StableDiffusionXLControlNetPipeline.from_single_file(
                   model,
                   controlnet=controlnets,
                   vae=vae,
                   torch_dtype=torch.float16,
                   use_safetensors=True)
            pipe.scheduler = UniPCMultistepScheduler.from_config(pipe.scheduler.config)
            pipe.enable_model_cpu_offload()
        #T2I-Adapterのpipeを作成する時
        elif mode=="t2i_adapter_canny":
             print("+++++t2i_adapter_canny  loading")
             adapter = T2IAdapter.from_pretrained("TencentARC/t2i-adapter-canny-sdxl-1.0", torch_dtype=torch.float16, varient="fp16").to("cuda")
             unet = UNet2DConditionModel.from_pretrained("latent-consistency/lcm-sdxl", torch_dtype=torch.float16, variant="fp16",)
             pipe = StableDiffusionXLAdapterPipeline.from_pretrained(      #HuggingFaceからダウンロードする場合
                    model,
                    unet=unet,
                    adapter=adapter,
                     vae=vae,
                     torch_dtype=torch.float16,
                     variant="fp16", 
                      ).to("cuda")

        # IP-Adapterのpipeを設定
        elif mul_ip:
                  print("-----mul_ip",mode)
                  image_encoder = CLIPVisionModelWithProjection.from_pretrained(
                        "h94/IP-Adapter",
                        subfolder="models/image_encoder",
                        torch_dtype=torch.float16,
                         )
                  if  mode=="t2i_ip":
                       pipe = StableDiffusionXLPipeline.from_single_file(model, torch_dtype=torch.float16, image_encoder=image_encoder).to("cuda")
                  if mode=="i2i_ip":
                      pipe = StableDiffusionXLImg2ImgPipeline.from_single_file(model, torch_dtype=torch.float16, image_encoder=image_encoder).to("cuda")
                  """
                  #当面 IP-Adapter inpaintは使えません
                  if mode=="inpaint_ip":
                       pipe = StableDiffusionXLInpaintPipeline.from_single_file(model, torch_dtype=torch.float16, image_encoder=image_encoder).to("cuda")
                  """   
                  print("+++++ multi Ip-Adapter loading")
                  pipe.load_ip_adapter("h94/IP-Adapter", subfolder="sdxl_models", weight_name=["ip-adapter-plus_sdxl_vit-h.safetensors", "ip-adapter-plus-face_sdxl_vit-h.safetensors"])
                  pipe.set_ip_adapter_scale([ip_scal1, ip_scal2])
        elif single_ip:
                  print("-----single_ip",mode)
                  '''
                  if  mode=="t2i_ip":
                       print("+++++t2i_ip  loading")
                       pipe = StableDiffusionXLPipeline.from_single_file(model, torch_dtype=torch.float16 ).to("cuda")
                  if mode=="i2i_ip":
                      print("+++++ i2i_ip  loading")
                      pipe = StableDiffusionXLImg2ImgPipeline.from_single_file(model, torch_dtype=torch.float16).to("cuda")

                  #IP-Adapter inpaintはVRAMが16G必要
                  if mode=="inpaint_ip":
                       print("+++++ inpaint_ip")
                       pipe = StableDiffusionXLInpaintPipeline.from_single_file(model, torch_dtype=torch.float16).to("cuda")
                  '''
                  print("+++++ single-Ip-Adapter loading")
                  pipe.load_ip_adapter("h94/IP-Adapter", subfolder="sdxl_models", weight_name="ip-adapter_sdxl.bin")
                  #pipe.load_ip_adapter("h94/IP-Adapter", subfolder="sdxl_models", weight_name="ip-adapter-plus-face_sdxl_vit-h.safetensors")#Need  DDIMScheduler and EulerDiscreteScheduler
                  pipe.set_ip_adapter_scale(ip_scal1)
        else:
            print("error=100")   #pipe名指定のエラー
            return "100"


        # Set scheduler LCMスケージューラーのセット
        if lcm_lora_en:   # Reload the scheduler with custom config
            pipe.scheduler = LCMScheduler.from_config(pipe.scheduler.config)# Reload the scheduler with custom config
        if ddism_en:   # Reload the scheduler with custom config
            pipe.scheduler = DDIMScheduler.from_config(pipe.scheduler.config)
            
        #LCM-LoRAのロード
        if lcm_lora_en:
            #pipe.load_lora_weights("latent-consistency/lcm-lora-sdxl", adapter_name="lcm") #HuggingFaceからダウンロードする場合
            pipe.load_lora_weights(lcm_lora_path, adapter_name="lcm")
            adapters_list=["lcm"]
        else:
            adapters_list=[]

        #Style-LoRAのロード     
        if lora_en :
            n_of_lora=len( lola_models)
            if lcm_lora_en:
                adapter_weights_list=[1.0]
            else:
                adapter_weights_list=[]
            #loRAのモデルとアダプタ名及びウエイトリスト作成
            for n in range(n_of_lora):
                lora_name=lola_models[n][0]
                adapter_name=lola_models[n][1]
                adapters_list.append(adapter_name)
                weight=lola_models[n][2]
                adapter_weights_list.append(weight)
                pipe.load_lora_weights(lora_name, adapter_name=adapter_name)
            # Combine LoRAs
            pipe.set_adapters(adapters_list, adapter_weights=adapter_weights_list)
            print("adapters_list",adapters_list,"adapter_weights",adapter_weights_list)
        # Set the unet to channels last, which accelerates inference a little bit
        if set_unet:
            pipe.unet = pipe.unet.to(memory_format=torch.channels_last)
            if torch_compile:
               print("torch.compile")
               pipe.unet = torch.compile(pipe.unet, mode="reduce-overhead", fullgraph=True)

        #Disable the progress bar
        #pipe.set_progress_bar_config(disable=True)

        return pipe

     #-----prompt_embeddings プロンプトをロードするクラス
     #prompt = "a red cat++ playing with a ball----"   prompt  Enhace / Reduce
     def prompt_embedding(self, pipe, prompt):
        #compel_proc = Compel(tokenizer=pipe.tokenizer, text_encoder=pipe.text_encoder)
        compel = Compel(
              tokenizer=[pipe.tokenizer, pipe.tokenizer_2] ,
              text_encoder=[pipe.text_encoder, pipe.text_encoder_2],
              returned_embeddings_type=ReturnedEmbeddingsType.PENULTIMATE_HIDDEN_STATES_NON_NORMALIZED,
              requires_pooled=[False, True]
                )
        conditioning, pooled = compel(prompt)
        return conditioning, pooled

    #-----load_image for i2i 指定イメージをロードするクラス
     def load_image_img(self,image_path):
        image =load_image(image_path)
        #image = pipe.image_processor.preprocess(
        #        Image.open(image_path) .convert("RGB").resize((512,) * 2, Image.Resampling.LANCZOS))
        #image = Image.open(image_path)#同じ
        return image
     
     #cannyイメージ作成クラス
     def get_canny(self , original_image , low_threshold = 100 , high_threshold = 200):
          image = np.array(original_image)
          image = cv2.Canny(image, low_threshold, high_threshold)
          image = image[:, :, None]
          image = np.concatenate([image, image, image], axis=2)
          canny_image = Image.fromarray(image)
          return canny_image
     
     #openposeイメージ作成クラス
     def get_openpose(self , image ):
          openpose = OpenposeDetector.from_pretrained("lllyasviel/ControlNet")
          openpose_image = openpose(image)
          return openpose_image
               
     #Depthイメージ作成クラス
     def get_depth_map(self, image):
          feature_extractor = DPTFeatureExtractor.from_pretrained("Intel/dpt-hybrid-midas")
          depth_estimator = DPTForDepthEstimation.from_pretrained("Intel/dpt-hybrid-midas").to("cuda")
          image = feature_extractor(images=image, return_tensors="pt").pixel_values.to("cuda")
          with torch.no_grad(), torch.autocast("cuda"):
               depth_map = depth_estimator(image).predicted_depth
          depth_map = torch.nn.functional.interpolate(depth_map.unsqueeze(1), size=(1024, 1024), mode="bicubic", align_corners=False, )
          depth_min = torch.amin(depth_map, dim=[1, 2, 3], keepdim=True)
          depth_max = torch.amax(depth_map, dim=[1, 2, 3], keepdim=True)
          depth_map = (depth_map - depth_min) / (depth_max - depth_min)
          image = torch.cat([depth_map] * 3, dim=1)
          image = image.permute(0, 2, 3, 1).cpu().numpy()[0]
          image = Image.fromarray((image * 255.0).clip(0, 255).astype(np.uint8))
          return image

    #画像の生成 
    #generate t2i/i2i/inpaint/cannyで使用
    #generate_controlnet  openpose/depth/Multi-ContorolNETで使用
    #generate_t2a_adapter  T2I- Adapterで使用
     
     """3種類の生成モード共通のパラメータ
        pipe
        利用するpipeを指定。必ず指定しなければならない。
        image=None,
   入力イメージを指定する。パスではなくイメージオブジェクト(PILが標準、CV2も使えるはず(ためしていない)
        mask_image=None,
   インペイントなどのマスクイメージを指定。パスではなくイメージオブジェクト(PILが標準、CV2も使えるはず(ためしていない)
        embe:bool=True,
         プロンプトエンべデッディング有効・無効。有効時はCONPLEによるエンべディングを行う。プロンプトのウエイト指定も可能
             ウエイト指定の例  prompt = "a red cat++ playing with a ball----"   +はprompt  Enhace /-は Reduce
        prompt:str=None,
             プロンプト 文章も使えます。ただし英語のみ
        conditioning=None,
        pooled=None,
             conditioningとpooledはプロンプトエンべデッディングでCONPLEから得られる変数を記述
        negative_prompt=None,
             ネガティブプロンプト
        num_inference_steps:int=4,
             生成時のSTEP、大きいほうがきれいになる。lmcは4が標準だが適時上げる
        strength:float=0.1,
             プロンプトの効きを調整する0〜1.0
        guidance_scale:float=1.0,
             ガイダンススケール 1.0で無効、大きいと生成時間が伸びる。上げれば画像がきれいになるわけででない
        generator=None,
             SEEDを指定する。例 generator = torch.manual_seed(0 )でマニュアルでSEED指定
        height:int = None,
             画像の高さを指定する
        width:int = None,
             画像の幅を指定する
        num_images_per_prompt:int = 1,
             プロンプトあたりの生成画像数を指定して複数生成させることが出来る
        guess_mode:bool=None,
             ガウシアン効果の有効無効指定
        freeu:bool=False,
             freeu効果の有効無効指定。有効にすると生成画像が鮮やかな色になる。
        output_type:str="pil",
             画像出力形式を指定する。ndarreyが指定可能("nd")
        crops_coords_top_left=(0, 0),
         生成する主画像の位置を指定する
        controlnet_conditioning_scale=1.0,
             コントロールネットの効き具合を指定する
        ip_adapter:bool=False,
             IP-Adaperを使う
        style_images:list=None,
             Multi-IP-Adaper用のStyle画像リスト
        face_image=None,
         IP-Adaper用の入力画像 / Multi-IP-Adaper用のface画像リスト
     T2I-Adaper特有のパラメータ
        adapter_conditioning_scale:float=None,   
        adapter_conditioning_factor:int=None,
         アダプタの効き具合の設定をする
     """
    
    #-----image generation  t2i/i2i /inpaint and Multi-IP-Adapter  t2i
     def generate(self,
        pipe,
        image=None,
        mask_image=None,
        negative_prompt_embeds=  None,
        embe:bool=True,
        prompt:str=None,
        prompt_2:str=None,
        conditioning=None,
        pooled=None,
        negative_prompt=None,
        negative_prompt_2=None,
        num_inference_steps:int=4,
        strength:float=0.1,
        guidance_scale:float=1.0,
        generator=None,
        height:int = None,
        width:int = None,
        num_images_per_prompt:int = 1,
        guess_mode:bool=None,
        freeu:bool=False,
        freeu_list:list=[0.9, 0.2,1.2,1.4],
        output_type:str="pil",
        crops_coords_top_left=(0, 0),
        controlnet_conditioning_scale=1.0,
        ip_adapter:bool=False,
        style_images:list=None,
        ip_image =None,          
        ):
        print("++++++++++++++++ @@@@@@@@@@@@ +++++++++generate")
        if freeu:        # Freeu enable
            #pipe.enable_freeu(s1=0.9, s2=0.2, b1=1.2, b2=1.4)
            pipe.enable_freeu(s1=freeu_list[0],s2=freeu_list[1],b1=freeu_list[2],b2=freeu_list[3])
        if ip_adapter:
                if style_images==None:
                       print("Single_IP-Adapter")
                       ip_adapter_image=ip_image 
                else:
                       print("Multi_IP-Adapter")
                       ip_adapter_image=[style_images, ip_image ]
                       
                image = pipe(
                    image=image,
                    prompt=prompt,
                    mask_image=mask_image,
                    prompt_embeds=None,
                    pooled_prompt_embeds=None,
                    negative_prompt=negative_prompt, 
                    num_inference_steps=num_inference_steps,
                    generator=generator,
                    height=height,
                    width= width,
                    guidance_scale=guidance_scale,
                    #strength=strength,     #  ip_adapterではエラーになります。ip_adapter==False:のときとの違いはここだけ
                    num_images_per_prompt=num_images_per_prompt,
                    guess_mode=guess_mode,
                    crops_coords_top_left=crops_coords_top_left,
                    controlnet_conditioning_scale=controlnet_conditioning_scale,
                    ip_adapter_image= ip_adapter_image,
                    style_images=style_images,
                    ).images
        elif ip_adapter==False:
                print("++++++++++++++@@@@@@@@@@@@@@ embe=", embe)
                image = pipe(
                    image=image,
                    prompt=prompt,     # prompt is passed to OAI CLIP-ViT/L-14
                    prompt_2=prompt_2, # prompt_2 is passed to OpenCLIP-ViT/bigG-14
                    mask_image=mask_image,
                    prompt_embeds=conditioning,
                    pooled_prompt_embeds=pooled,
                    negative_prompt=negative_prompt,
                    negative_prompt_2=negative_prompt_2,
                    num_inference_steps=num_inference_steps,
                    strength=strength,
                    generator=generator,
                    height =height,
                    width = width,
                    num_images_per_prompt=num_images_per_prompt,
                    guess_mode=guess_mode,
                    output_type=output_type,
                    guidance_scale=guidance_scale,
                    crops_coords_top_left=crops_coords_top_left,
                    controlnet_conditioning_scale=controlnet_conditioning_scale,
                    ).images
        return  image
     #-----image generation  controlnet
     def generate_controlnet(self,
        pipe,
        image=None,
        prompt:str=None,
        negative_prompt=None,
        num_inference_steps:int=4,
        guidance_scale:float=1.0,
        generator=None,
        height:int = None,
        width:int = None,
        num_images_per_prompt:int = 1,
        guess_mode:bool=None,
        freeu:bool=False,
        output_type:str="pil",
        crops_coords_top_left=(0, 0),
        controlnet_conditioning_scale=1.0,
             ):
        if freeu:        # Freeu enable
            pipe.enable_freeu(s1=0.9, s2=0.2, b1=1.2, b2=1.4)
        image = pipe( 
                    prompt=prompt,
                    image=image,
                    negative_prompt=negative_prompt,
                    num_inference_steps=num_inference_steps,
                    generator=generator,
                    height =height,
                    width = width,
                    num_images_per_prompt=num_images_per_prompt,
                    guess_mode=guess_mode,
                    guidance_scale=guidance_scale,
                    crops_coords_top_left=crops_coords_top_left,
                    controlnet_conditioning_scale=controlnet_conditioning_scale,
                    ).images
        return  image
     #-----image generation  t2a_adapter
     def generate_t2a_adapter(self,
        pipe,
        image=None,
        prompt:str=None,
        negative_prompt=None,
        num_inference_steps:int=4,
        guidance_scale:float=1.0,
        generator=None,
        height:int = None,
        width:int = None,
        num_images_per_prompt:int = 1,
        freeu:bool=False,
        output_type:str="pil",
        crops_coords_top_left=(0, 0),
        adapter_conditioning_scale:float=None,   
        adapter_conditioning_factor:int=None,
             ):
        if freeu:        # Freeu enable
            pipe.enable_freeu(s1=0.9, s2=0.2, b1=1.2, b2=1.4)
        image = pipe( 
                    prompt=prompt,
                    image=image,
                    negative_prompt=negative_prompt,
                    num_inference_steps=num_inference_steps,
                    generator=generator,
                    height =height,
                    width = width,
                    num_images_per_prompt=num_images_per_prompt,
                    guidance_scale=guidance_scale,
                    crops_coords_top_left=crops_coords_top_left,
                    adapter_conditioning_scale=adapter_conditioning_scale,
                    adapter_conditioning_factor=adapter_conditioning_factor,
                    ).images
        return  image