3063 lines
142 KiB
Python
3063 lines
142 KiB
Python
# !/usr/bin/env python
|
|
# -*- coding: UTF-8 -*-
|
|
|
|
import logging
|
|
import os
|
|
import re
|
|
import random
|
|
import torch
|
|
import gc
|
|
from omegaconf import OmegaConf
|
|
from PIL import Image
|
|
import numpy as np
|
|
import cv2
|
|
from diffusers import ( DDIMScheduler,
|
|
KDPM2AncestralDiscreteScheduler, LMSDiscreteScheduler,
|
|
DPMSolverMultistepScheduler, DPMSolverSinglestepScheduler,
|
|
EulerDiscreteScheduler, HeunDiscreteScheduler,
|
|
KDPM2DiscreteScheduler,
|
|
EulerAncestralDiscreteScheduler, UniPCMultistepScheduler,
|
|
DDPMScheduler, LCMScheduler)
|
|
|
|
from .msdiffusion.models.projection import Resampler
|
|
from .msdiffusion.models.modelWrapper import MSAdapter as MSAdapterWarpper
|
|
from .utils.style_template import styles
|
|
from .utils.load_models_utils import get_lora_dict
|
|
from .PuLID.pulid.utils import resize_numpy_image_long
|
|
from transformers import AutoModel, AutoTokenizer
|
|
from comfy.utils import common_upscale,ProgressBar
|
|
import folder_paths
|
|
from comfy.clip_vision import load as clip_load
|
|
from .utils.gradio_utils import process_original_text,character_to_dict
|
|
import json
|
|
|
|
cur_path = os.path.dirname(os.path.abspath(__file__))
|
|
photomaker_dir=os.path.join(folder_paths.models_dir, "photomaker")
|
|
base_pt = os.path.join(photomaker_dir,"pt")
|
|
device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"
|
|
|
|
lora_get = get_lora_dict()
|
|
lora_lightning_list = lora_get["lightning_xl_lora"]
|
|
|
|
|
|
SAMPLER_NAMES = ["euler", "euler_cfg_pp", "euler_ancestral", "euler_ancestral_cfg_pp", "heun", "heunpp2","dpm_2", "dpm_2_ancestral",
|
|
"lms", "dpm_fast", "dpm_adaptive", "dpmpp_2s_ancestral", "dpmpp_2s_ancestral_cfg_pp", "dpmpp_sde", "dpmpp_sde_gpu",
|
|
"dpmpp_2m", "dpmpp_2m_sde", "dpmpp_2m_sde_gpu", "dpmpp_3m_sde", "dpmpp_3m_sde_gpu", "ddpm", "lcm",
|
|
"ipndm", "ipndm_v", "deis","ddim", "uni_pc", "uni_pc_bh2"]
|
|
|
|
SCHEDULER_NAMES = ["normal", "karras", "exponential", "sgm_uniform", "simple", "ddim_uniform", "beta"]
|
|
|
|
|
|
def gc_cleanup():
|
|
gc.collect()
|
|
torch.cuda.empty_cache()
|
|
|
|
|
|
def get_scheduler(name,scheduler_):
|
|
scheduler = False
|
|
if name == "euler" or name =="euler_cfg_pp":
|
|
scheduler = EulerDiscreteScheduler()
|
|
elif name == "euler_ancestral" or name =="euler_ancestral_cfg_pp":
|
|
scheduler = EulerAncestralDiscreteScheduler()
|
|
elif name == "ddim":
|
|
scheduler = DDIMScheduler()
|
|
elif name == "ddpm":
|
|
scheduler = DDPMScheduler()
|
|
elif name == "dpmpp_2m":
|
|
scheduler = DPMSolverMultistepScheduler()
|
|
elif name == "dpmpp_2m" and scheduler_=="karras":
|
|
scheduler = DPMSolverMultistepScheduler(use_karras_sigmas=True)
|
|
elif name == "dpmpp_2m_sde":
|
|
scheduler = DPMSolverMultistepScheduler(algorithm_type="sde-dpmsolver++")
|
|
elif name == "dpmpp_2m" and scheduler_=="karras":
|
|
scheduler = DPMSolverMultistepScheduler(use_karras_sigmas=True, algorithm_type="sde-dpmsolver++")
|
|
elif name == "dpmpp_sde" or name == "dpmpp_sde_gpu":
|
|
scheduler = DPMSolverSinglestepScheduler()
|
|
elif (name == "dpmpp_sde" or name == "dpmpp_sde_gpu") and scheduler_=="karras":
|
|
scheduler = DPMSolverSinglestepScheduler(use_karras_sigmas=True)
|
|
elif name == "dpm_2":
|
|
scheduler = KDPM2DiscreteScheduler()
|
|
elif name == "dpm_2" and scheduler_=="karras":
|
|
scheduler = KDPM2DiscreteScheduler(use_karras_sigmas=True)
|
|
elif name == "dpm_2_ancestral":
|
|
scheduler = KDPM2AncestralDiscreteScheduler()
|
|
elif name == "dpm_2_ancestral" and scheduler_=="karras":
|
|
scheduler = KDPM2AncestralDiscreteScheduler(use_karras_sigmas=True)
|
|
elif name == "heun":
|
|
scheduler = HeunDiscreteScheduler()
|
|
elif name == "lcm":
|
|
scheduler = LCMScheduler()
|
|
elif name == "lms":
|
|
scheduler = LMSDiscreteScheduler()
|
|
elif name == "lms" and scheduler_=="karras":
|
|
scheduler = LMSDiscreteScheduler(use_karras_sigmas=True)
|
|
elif name == "uni_pc":
|
|
scheduler = UniPCMultistepScheduler()
|
|
else:
|
|
scheduler = EulerDiscreteScheduler()
|
|
return scheduler
|
|
|
|
|
|
|
|
def get_extra_function(extra_function,extra_param,photomake_ckpt_path,image,infer_mode):
|
|
auraface=False
|
|
use_photov2=False
|
|
cached=False
|
|
inject=False
|
|
onnx_provider="gpu"
|
|
img2img_mode = True if isinstance(image, torch.Tensor) else False
|
|
trigger_words_dual="best"
|
|
dual_lora_scale=1.0
|
|
dreamo_mode="ip"
|
|
|
|
if extra_function:
|
|
extra_function = extra_function.strip().lower()
|
|
if "auraface" in extra_function:
|
|
auraface=True
|
|
|
|
|
|
if extra_param:
|
|
extra_param = extra_param.strip().lower()
|
|
if "cache" in extra_param:
|
|
cached=True
|
|
if "inject" in extra_param:
|
|
inject=True
|
|
if "cpu" in extra_param:
|
|
onnx_provider="cpu"
|
|
if "id" in extra_param:
|
|
dreamo_mode="id"
|
|
elif "style" in extra_param:
|
|
dreamo_mode="style"
|
|
if "[" in extra_param:
|
|
trigger_words_param=extra_param.split("[")[1].split("]")[0]
|
|
trigger_words_dual=trigger_words_param.split(",")[0]
|
|
dual_lora_scale=float(trigger_words_param.split(",")[1])
|
|
|
|
|
|
if isinstance(photomake_ckpt_path, str) and img2img_mode:
|
|
use_photov2 = True if "v2" in photomake_ckpt_path else False
|
|
|
|
return auraface,use_photov2,img2img_mode,cached,inject,onnx_provider,dreamo_mode,trigger_words_dual,dual_lora_scale
|
|
|
|
def extract_content_from_brackets(text):
|
|
# 正则表达式匹配多对方括号内的内容
|
|
return re.findall(r'\[(.*?)\]', text)
|
|
|
|
def phi2narry(img):
|
|
img = torch.from_numpy(np.array(img).astype(np.float32) / 255.0).unsqueeze(0)
|
|
return img
|
|
|
|
def tensor_to_image(tensor):
|
|
image_np = tensor.squeeze().mul(255).clamp(0, 255).byte().numpy()
|
|
image = Image.fromarray(image_np, mode='RGB')
|
|
return image
|
|
|
|
def tensortopil_list(tensor_in):
|
|
d1, _, _, _ = tensor_in.size()
|
|
if d1 == 1:
|
|
img_list = [tensor_to_image(tensor_in)]
|
|
else:
|
|
tensor_list = torch.chunk(tensor_in, chunks=d1)
|
|
img_list=[tensor_to_image(i) for i in tensor_list]
|
|
return img_list
|
|
|
|
def tensortopil_list_upscale(tensor_in,width,height):
|
|
d1, _, _, _ = tensor_in.size()
|
|
if d1 == 1:
|
|
img_list = [nomarl_upscale(tensor_in,width,height)]
|
|
else:
|
|
tensor_list = torch.chunk(tensor_in, chunks=d1)
|
|
img_list=[nomarl_upscale(i,width,height) for i in tensor_list]
|
|
return img_list
|
|
|
|
def tensortolist(tensor_in,width,height):
|
|
d1, _, _, _ = tensor_in.size()
|
|
if d1 == 1:
|
|
tensor_list = [nomarl_tensor_upscale(tensor_in,width,height)]
|
|
else:
|
|
tensor_list_ = torch.chunk(tensor_in, chunks=d1)
|
|
tensor_list=[nomarl_tensor_upscale(i,width,height) for i in tensor_list_]
|
|
return tensor_list
|
|
|
|
|
|
|
|
def nomarl_tensor_upscale(tensor, width, height):
|
|
samples = tensor.movedim(-1, 1)
|
|
samples = common_upscale(samples, width, height, "nearest-exact", "center")
|
|
samples = samples.movedim(1, -1)
|
|
return samples
|
|
def nomarl_upscale(img, width, height):
|
|
samples = img.movedim(-1, 1)
|
|
img = common_upscale(samples, width, height, "nearest-exact", "center")
|
|
samples = img.movedim(1, -1)
|
|
img = tensor_to_image(samples)
|
|
return img
|
|
def nomarl_upscale_tensor(img, width, height):
|
|
samples = img.movedim(-1, 1)
|
|
img = common_upscale(samples, width, height, "nearest-exact", "center")
|
|
samples = img.movedim(1, -1)
|
|
return samples
|
|
|
|
def center_crop(img):
|
|
width, height = img.size
|
|
square = min(width, height)
|
|
left = (width - square) / 2
|
|
top = (height - square) / 2
|
|
right = (width + square) / 2
|
|
bottom = (height + square) / 2
|
|
return img.crop((left, top, right, bottom))
|
|
|
|
def center_crop_s(img, new_width, new_height):
|
|
width, height = img.size
|
|
left = (width - new_width) / 2
|
|
top = (height - new_height) / 2
|
|
right = (width + new_width) / 2
|
|
bottom = (height + new_height) / 2
|
|
return img.crop((left, top, right, bottom))
|
|
|
|
|
|
def contains_brackets(s):
|
|
return '[' in s or ']' in s
|
|
|
|
def has_parentheses(s):
|
|
return bool(re.search(r'\(.*?\)', s))
|
|
def extract_content_from_brackets_(text):
|
|
# 正则表达式匹配多对圆括号内的内容
|
|
return re.findall(r'\((.*?)\)', text)
|
|
|
|
def narry_list(list_in):
|
|
for i in range(len(list_in)):
|
|
value = list_in[i]
|
|
modified_value = phi2narry(value)
|
|
list_in[i] = modified_value
|
|
return list_in
|
|
def remove_punctuation_from_strings(lst):
|
|
pattern = r"[\W]+$" # 匹配字符串末尾的所有非单词字符
|
|
return [re.sub(pattern, '', s) for s in lst]
|
|
|
|
def phi_list(list_in):
|
|
for i in range(len(list_in)):
|
|
value = list_in[i]
|
|
list_in[i] = value
|
|
return list_in
|
|
|
|
def narry_list_pil(list_in):
|
|
for i in range(len(list_in)):
|
|
value = list_in[i]
|
|
modified_value = tensor_to_image(value)
|
|
list_in[i] = modified_value
|
|
return list_in
|
|
|
|
|
|
def setup_seed(seed):
|
|
torch.manual_seed(seed)
|
|
if device == "cuda":
|
|
torch.cuda.manual_seed_all(seed)
|
|
np.random.seed(seed)
|
|
random.seed(seed)
|
|
torch.backends.cudnn.deterministic = True
|
|
|
|
def apply_style_positive(style_name: str, positive: str):
|
|
p, n = styles.get(style_name, styles[style_name])
|
|
#print(p, "test0", n)
|
|
return p.replace("{prompt}", positive),n
|
|
|
|
def apply_style(style_name: str, positives: list, negative: str = ""):
|
|
p, n = styles.get(style_name, styles[style_name])
|
|
#print(p,"test1",n)
|
|
return [
|
|
p.replace("{prompt}", positive) for positive in positives
|
|
], n + " " + negative
|
|
|
|
|
|
|
|
|
|
|
|
def face_bbox_to_square(bbox):
|
|
## l, t, r, b to square l, t, r, b
|
|
l,t,r,b = bbox
|
|
cent_x = (l + r) / 2
|
|
cent_y = (t + b) / 2
|
|
w, h = r - l, b - t
|
|
r = max(w, h) / 2
|
|
|
|
l0 = cent_x - r
|
|
r0 = cent_x + r
|
|
t0 = cent_y - r
|
|
b0 = cent_y + r
|
|
|
|
return [l0, t0, r0, b0]
|
|
|
|
|
|
|
|
def insight_face_loader(infer_mode,use_photov2,auraface,onnx_provider="cpu",mask_repo="briaai/RMBG-1.4"):
|
|
insightface_root_path= folder_paths.base_path
|
|
if infer_mode=="story" and use_photov2:
|
|
from .utils.insightface_package import FaceAnalysis2
|
|
if auraface:
|
|
from huggingface_hub import snapshot_download
|
|
snapshot_download(
|
|
"fal/AuraFace-v1",
|
|
local_dir="models/auraface",
|
|
)
|
|
app_face = FaceAnalysis2(name="auraface",
|
|
providers=["CUDAExecutionProvider", "CPUExecutionProvider"], root=insightface_root_path,
|
|
allowed_modules=['detection', 'recognition'])
|
|
else:
|
|
app_face = FaceAnalysis2(providers=['CUDAExecutionProvider'],
|
|
allowed_modules=['detection', 'recognition'])
|
|
app_face.prepare(ctx_id=0, det_size=(640, 640))
|
|
pipeline_mask = None
|
|
app_face_ = None
|
|
elif infer_mode=="kolor_face" :
|
|
from .kolors.models.sample_ipadapter_faceid_plus import FaceInfoGenerator
|
|
from huggingface_hub import snapshot_download
|
|
snapshot_download(
|
|
'DIAMONIK7777/antelopev2',
|
|
local_dir='models/antelopev2',
|
|
)
|
|
app_face = FaceInfoGenerator(root_dir=insightface_root_path)
|
|
pipeline_mask = None
|
|
app_face_ = None
|
|
elif infer_mode=="story_maker":
|
|
from insightface.app import FaceAnalysis
|
|
from transformers import pipeline
|
|
pipeline_mask = pipeline("image-segmentation", model=mask_repo,
|
|
trust_remote_code=True)
|
|
|
|
app_face = FaceAnalysis(name='buffalo_l', root=insightface_root_path,
|
|
providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])
|
|
app_face.prepare(ctx_id=0, det_size=(640, 640))
|
|
app_face_ = None
|
|
|
|
elif infer_mode=="story_and_maker":
|
|
from insightface.app import FaceAnalysis
|
|
from transformers import pipeline
|
|
pipeline_mask = pipeline("image-segmentation", model=mask_repo,
|
|
trust_remote_code=True)
|
|
if use_photov2:
|
|
from .utils.insightface_package import FaceAnalysis2
|
|
if auraface:
|
|
from huggingface_hub import snapshot_download
|
|
snapshot_download(
|
|
"fal/AuraFace-v1",
|
|
local_dir="models/auraface",
|
|
)
|
|
app_face = FaceAnalysis2(name="auraface",
|
|
providers=["CUDAExecutionProvider", "CPUExecutionProvider"],
|
|
root=insightface_root_path,
|
|
allowed_modules=['detection', 'recognition'])
|
|
else:
|
|
app_face = FaceAnalysis2(providers=['CUDAExecutionProvider'],
|
|
allowed_modules=['detection', 'recognition'])
|
|
app_face.prepare(ctx_id=0, det_size=(640, 640))
|
|
app_face_ = FaceAnalysis(name='buffalo_l', root=insightface_root_path,
|
|
providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])
|
|
app_face_.prepare(ctx_id=0, det_size=(640, 640))
|
|
else:
|
|
app_face = FaceAnalysis(name='buffalo_l', root=insightface_root_path,
|
|
providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])
|
|
app_face.prepare(ctx_id=0, det_size=(640, 640))
|
|
app_face_ = None
|
|
elif infer_mode=="flux_pulid":
|
|
import insightface
|
|
from facexlib.parsing import init_parsing_model
|
|
from facexlib.utils.face_restoration_helper import FaceRestoreHelper
|
|
from insightface.app import FaceAnalysis
|
|
from huggingface_hub import snapshot_download
|
|
# antelopev2
|
|
snapshot_download('DIAMONIK7777/antelopev2', local_dir='models/antelopev2')
|
|
|
|
providers = ['CPUExecutionProvider'] if onnx_provider == 'cpu' else ['CUDAExecutionProvider', 'CPUExecutionProvider']
|
|
app_face = FaceAnalysis(name='antelopev2', root=insightface_root_path, providers=providers)
|
|
app_face.prepare(ctx_id=0, det_size=(640, 640))
|
|
# face_helper
|
|
face_helper = FaceRestoreHelper(
|
|
upscale_factor=1,
|
|
face_size=512,
|
|
crop_ratio=(1, 1),
|
|
det_model='retinaface_resnet50',
|
|
save_ext='png',
|
|
device=device,)
|
|
face_helper.face_parse = None
|
|
face_helper.face_parse = init_parsing_model(model_name='bisenet', device=device)
|
|
app_face_ = face_helper
|
|
handler_ante = insightface.model_zoo.get_model('models/antelopev2/glintr100.onnx',providers=providers)
|
|
handler_ante.prepare(ctx_id=0)
|
|
pipeline_mask = handler_ante
|
|
elif infer_mode=="infiniteyou":
|
|
from facexlib.recognition import init_recognition_model
|
|
from insightface.app import FaceAnalysis
|
|
# Load face encoder
|
|
|
|
app_face = FaceAnalysis(name='antelopev2',
|
|
root=insightface_root_path, providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])
|
|
app_face.prepare(ctx_id=0, det_size=(640, 640))
|
|
app_face_ = init_recognition_model('arcface', device='cuda')
|
|
pipeline_mask = None
|
|
else:
|
|
app_face = None
|
|
pipeline_mask = None
|
|
app_face_ = None
|
|
return app_face,pipeline_mask,app_face_
|
|
|
|
|
|
def get_float(str_in):
|
|
list_str=str_in.split(",")
|
|
float_box=[float(x) for x in list_str]
|
|
return float_box
|
|
|
|
|
|
def adjust_indices(original_indices, deleted_indices):
|
|
"""根据删除的索引列表调整原索引"""
|
|
# 处理删除列表为空的特殊情况
|
|
if not deleted_indices:
|
|
return original_indices.copy() # 直接返回原索引,无需调整
|
|
|
|
deleted_sorted = sorted(deleted_indices)
|
|
adjusted = []
|
|
for idx in original_indices:
|
|
# 计算偏移量:有多少个删除索引 < 当前索引
|
|
offset = sum(1 for d in deleted_sorted if d < idx)
|
|
new_idx = idx - offset
|
|
# 如果原索引未被删除,则保留
|
|
if idx not in deleted_sorted:
|
|
adjusted.append(new_idx)
|
|
else:
|
|
adjusted.append(-1) # 标记为无效
|
|
return adjusted
|
|
|
|
|
|
def get_insight_dict(app_face,app_face_,pipeline_mask,infer_mode,use_photov2,image_list,
|
|
character_list_,condition_image,width, height,model=None,image_proj_model=None):
|
|
input_id_emb_s_dict = {}
|
|
input_id_img_s_dict = {}
|
|
input_id_emb_un_dict = {}
|
|
|
|
for ind, img in enumerate(image_list): # 最大只有2个ID
|
|
if infer_mode == "story" and use_photov2:
|
|
from .utils.insightface_package import analyze_faces
|
|
img_ = np.array(img)
|
|
img_ = cv2.cvtColor(img_, cv2.COLOR_RGB2BGR)
|
|
faces = analyze_faces(app_face, img_, )
|
|
id_embed_list = torch.from_numpy((faces[0]['embedding']))
|
|
crop_image = img_
|
|
uncond_id_embeddings = None
|
|
elif infer_mode == "kolor_face":
|
|
device = (
|
|
"cuda"
|
|
if torch.cuda.is_available()
|
|
else "mps" if torch.backends.mps.is_available() else "cpu"
|
|
)
|
|
face_info = app_face.get_faceinfo_one_img(img)
|
|
face_bbox_square = face_bbox_to_square(face_info["bbox"])
|
|
crop_image = img.crop(face_bbox_square)
|
|
crop_image = crop_image.resize((336, 336))
|
|
face_embeds = torch.from_numpy(np.array([face_info["embedding"]]))
|
|
id_embed_list = face_embeds.to(device, dtype=torch.float16)
|
|
uncond_id_embeddings = None
|
|
elif infer_mode=="story_maker":
|
|
crop_image = pipeline_mask(img, return_mask=True).convert('RGB') # outputs a pillow mask
|
|
# timestamp = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
|
|
# crop_image.copy().save(os.path.join(folder_paths.get_output_directory(),f"{timestamp}_mask.png"))
|
|
face_info = app_face.get(cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR))
|
|
id_embed_list = sorted(face_info,key=lambda x: (x['bbox'][2] - x['bbox'][0]) * (x['bbox'][3] - x['bbox'][1]))[-1] # only use the maximum face
|
|
|
|
uncond_id_embeddings = img
|
|
elif infer_mode=="story_and_maker":
|
|
if use_photov2:
|
|
from .utils.insightface_package import analyze_faces
|
|
img = np.array(img)
|
|
img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
|
|
faces = analyze_faces(app_face, img, )
|
|
id_embed_list = torch.from_numpy((faces[0]['embedding']))
|
|
crop_image = pipeline_mask(img, return_mask=True).convert(
|
|
'RGB') # outputs a pillow mask
|
|
face_info = app_face_.get(cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR))
|
|
uncond_id_embeddings = sorted(face_info,key=lambda x: (x['bbox'][2] - x['bbox'][0]) * (x['bbox'][3] - x['bbox'][1]))[-1] # only use the maximum face
|
|
#photomake_mode = "v2"
|
|
# make+v2模式下,emb存v2的向量,corp 和 unemb 存make的向量
|
|
else: # V1不需要调用emb
|
|
crop_image = pipeline_mask(img, return_mask=True).convert(
|
|
'RGB') # outputs a pillow mask
|
|
face_info = app_face.get(cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR))
|
|
id_embed_list = sorted(face_info,key=lambda x: (x['bbox'][2] - x['bbox'][0]) * (x['bbox'][3] - x['bbox'][1]))[-1] # only use the maximum face
|
|
uncond_id_embeddings = img
|
|
|
|
elif infer_mode=="flux_pulid":
|
|
id_image = resize_numpy_image_long(img, 1024)
|
|
use_true_cfg = abs(1.0 - 1.0) > 1e-2
|
|
|
|
id_embed_list, uncond_id_embeddings=model.pulid_model.get_id_embedding_( id_image,app_face_,app_face,pipeline_mask,cal_uncond=use_true_cfg)
|
|
|
|
|
|
#id_embed_list, uncond_id_embeddings = pipe.pulid_model.get_id_embedding(id_image,cal_uncond=use_true_cfg)
|
|
crop_image = img
|
|
|
|
elif infer_mode=="infiniteyou":
|
|
|
|
def _detect_face(app_face, id_image_cv2):
|
|
face_info = app_face.get(id_image_cv2)
|
|
if face_info:
|
|
return face_info
|
|
else:
|
|
print("No face detected in the input ID image")
|
|
return []
|
|
from .pipelines.pipeline_infu_flux import extract_arcface_bgr_embedding,resize_and_pad_image,draw_kps
|
|
# Extract ID embeddings
|
|
print('Preparing ID embeddings')
|
|
id_image_cv2 = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
|
|
face_info = _detect_face(app_face,id_image_cv2)
|
|
if len(face_info) == 0:
|
|
raise ValueError('No face detected in the input ID image')
|
|
|
|
face_info = sorted(face_info, key=lambda x:(x['bbox'][2]-x['bbox'][0])*(x['bbox'][3]-x['bbox'][1]))[-1] # only use the maximum face
|
|
landmark = face_info['kps']
|
|
id_embed = extract_arcface_bgr_embedding(id_image_cv2, landmark, app_face_)
|
|
id_embed = id_embed.clone().unsqueeze(0).float().cuda()
|
|
id_embed = id_embed.reshape([1, -1, 512])
|
|
id_embed = id_embed.to(device='cuda', dtype=torch.bfloat16)
|
|
with torch.no_grad():
|
|
id_embed = image_proj_model(id_embed)
|
|
bs_embed, seq_len, _ = id_embed.shape
|
|
id_embed = id_embed.repeat(1, 1, 1)
|
|
id_embed = id_embed.view(bs_embed * 1, seq_len, -1)
|
|
id_embed = id_embed.to(device='cuda', dtype=torch.bfloat16)
|
|
|
|
# Load control image
|
|
print('Preparing the control image')
|
|
if isinstance(condition_image, torch.Tensor):
|
|
e1, _, _, _ = condition_image.size()
|
|
if e1 == 1:
|
|
cn_image_load = [nomarl_upscale(condition_image, width, height)]
|
|
else:
|
|
img_list = list(torch.chunk(condition_image, chunks=e1))
|
|
cn_image_load = [nomarl_upscale(img, width, height) for img in img_list]
|
|
# control_image = control_image.convert("RGB")
|
|
# control_image = resize_and_pad_image(control_image, (width, height))
|
|
control_image=[] #如果是单人多张控制图,可能导致失序,所以统一改成列表,并在采样的时候依次调用
|
|
for img in cn_image_load:
|
|
face_info = _detect_face(app_face,cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR))
|
|
if len(face_info) == 0:
|
|
print('No face detected in the control image,use empty image,无法识别到面部,加载全黑图片替代.') #卡通人物很难识别,所以避免反复加载, 直接用黑图
|
|
face_cn=Image.fromarray(np.zeros([height, width, 3]).astype(np.uint8))
|
|
else:
|
|
face_info = sorted(face_info, key=lambda x:(x['bbox'][2]-x['bbox'][0])*(x['bbox'][3]-x['bbox'][1]))[-1] # only use the maximum face
|
|
face_cn = draw_kps(img, face_info['kps'])
|
|
control_image.append(face_cn)
|
|
else:
|
|
out_img = np.zeros([height, width, 3])
|
|
control_image = Image.fromarray(out_img.astype(np.uint8))
|
|
id_embed_list=id_embed
|
|
crop_image = control_image # inf use crop to control img
|
|
uncond_id_embeddings = None
|
|
else:
|
|
id_embed_list = None
|
|
uncond_id_embeddings = None
|
|
crop_image = None
|
|
input_id_img_s_dict[character_list_[ind]] = [crop_image]
|
|
input_id_emb_s_dict[character_list_[ind]] = [id_embed_list]
|
|
input_id_emb_un_dict[character_list_[ind]] = [uncond_id_embeddings]
|
|
|
|
|
|
app_face = None
|
|
app_face_= None
|
|
pipeline_mask =None
|
|
gc_cleanup()
|
|
|
|
if isinstance(condition_image, torch.Tensor) and infer_mode=="story_maker":
|
|
e1, _, _, _ = condition_image.size()
|
|
if e1 == 1:
|
|
cn_image_load = [nomarl_upscale(condition_image, width, height)]
|
|
else:
|
|
img_list = list(torch.chunk(condition_image, chunks=e1))
|
|
cn_image_load = [nomarl_upscale(img, width, height) for img in img_list]
|
|
input_id_cloth_dict = {}
|
|
if len(cn_image_load)>2:
|
|
cn_image_load_role=cn_image_load[0:2]
|
|
else:
|
|
cn_image_load_role=cn_image_load
|
|
for ind, img in enumerate(cn_image_load_role):
|
|
input_id_cloth_dict[character_list_[ind]] = [img]
|
|
if len(cn_image_load)>2: #处理多张control img
|
|
my_list=cn_image_load[2:]
|
|
for ind,img in enumerate(my_list):
|
|
input_id_cloth_dict[f"dual{ind}"] = [img]
|
|
else:
|
|
input_id_cloth_dict = {}
|
|
return input_id_emb_s_dict,input_id_img_s_dict,input_id_emb_un_dict,input_id_cloth_dict
|
|
|
|
|
|
def load_model_tag(repo,device,select_method):
|
|
if "flor" in select_method.lower():#"thwri/CogFlorence-2-Large-Freeze"
|
|
#pip install flash_attn
|
|
from transformers import AutoModelForCausalLM, AutoProcessor, AutoConfig
|
|
model = AutoModelForCausalLM.from_pretrained(repo, trust_remote_code=True).to(
|
|
device)
|
|
processor = AutoProcessor.from_pretrained(repo, trust_remote_code=True)
|
|
else:
|
|
model = AutoModel.from_pretrained(repo, trust_remote_code=True)
|
|
processor = AutoTokenizer.from_pretrained(repo, trust_remote_code=True)#tokenizer
|
|
model.eval()
|
|
return model,processor
|
|
|
|
class StoryLiteTag:
|
|
def __init__(self, device,temperature,select_method,repo="pzc163/MiniCPMv2_6-prompt-generator",):
|
|
self.device = device
|
|
self.repo = repo
|
|
self.select_method=select_method
|
|
self.model, self.processor=load_model_tag(self.repo, self.device,self.select_method)
|
|
self.temperature=temperature
|
|
def run_tag(self,image):
|
|
if "flor" in self.select_method.lower():
|
|
inputs = self.processor(text="<MORE_DETAILED_CAPTION>" , images=image, return_tensors="pt").to(device)
|
|
generated_ids = self.model.generate(
|
|
input_ids=inputs["input_ids"],
|
|
pixel_values=inputs["pixel_values"],
|
|
max_new_tokens=1024,
|
|
num_beams=3,
|
|
do_sample=True
|
|
)
|
|
generated_text = self.processor.batch_decode(generated_ids, skip_special_tokens=False)[0]
|
|
parsed_answer = self.processor.post_process_generation(generated_text, task="<MORE_DETAILED_CAPTION>" ,
|
|
image_size=(image.width, image.height))
|
|
res=parsed_answer["<MORE_DETAILED_CAPTION>"]
|
|
else:
|
|
question = 'Provide a detailed description of the details and content contained in the image, and generate a short prompt that can be used for image generation tasks in Stable Diffusion,remind you only need respons prompt itself and no other information.'
|
|
msgs = [{'role': 'user', 'content': [image, question]}]
|
|
res = self.model.chat(
|
|
image=None,
|
|
msgs=msgs,
|
|
tokenizer=self.processor,# tokenizer
|
|
temperature=self.temperature
|
|
)
|
|
res=res.split(":",1)[1].strip('"')
|
|
s=res.strip()
|
|
res=re.sub(r'^\n+|\n+$', '', s)
|
|
res.strip("'")
|
|
logging.info(f"{res}")
|
|
return res
|
|
|
|
|
|
def images_generator(img_list: list, ):
|
|
# get img size
|
|
sizes = {}
|
|
for image_ in img_list:
|
|
if isinstance(image_, Image.Image):
|
|
count = sizes.get(image_.size, 0)
|
|
sizes[image_.size] = count + 1
|
|
elif isinstance(image_, np.ndarray):
|
|
count = sizes.get(image_.shape[:2][::-1], 0)
|
|
sizes[image_.shape[:2][::-1]] = count + 1
|
|
else:
|
|
raise "unsupport image list,must be pil or cv2!!!"
|
|
size = max(sizes.items(), key=lambda x: x[1])[0]
|
|
yield size[0], size[1]
|
|
|
|
# any to tensor
|
|
def load_image(img_in):
|
|
if isinstance(img_in, Image.Image):
|
|
img_in = img_in.convert("RGB")
|
|
i = np.array(img_in, dtype=np.float32)
|
|
i = torch.from_numpy(i).div_(255)
|
|
if i.shape[0] != size[1] or i.shape[1] != size[0]:
|
|
i = torch.from_numpy(i).movedim(-1, 0).unsqueeze(0)
|
|
i = common_upscale(i, size[0], size[1], "lanczos", "center")
|
|
i = i.squeeze(0).movedim(0, -1).numpy()
|
|
return i
|
|
elif isinstance(img_in, np.ndarray):
|
|
i = cv2.cvtColor(img_in, cv2.COLOR_BGR2RGB).astype(np.float32)
|
|
i = torch.from_numpy(i).div_(255)
|
|
print(i.shape)
|
|
return i
|
|
else:
|
|
raise "unsupport image list,must be pil,cv2 or tensor!!!"
|
|
|
|
total_images = len(img_list)
|
|
processed_images = 0
|
|
pbar = ProgressBar(total_images)
|
|
images = map(load_image, img_list)
|
|
try:
|
|
prev_image = next(images)
|
|
while True:
|
|
next_image = next(images)
|
|
yield prev_image
|
|
processed_images += 1
|
|
pbar.update_absolute(processed_images, total_images)
|
|
prev_image = next_image
|
|
except StopIteration:
|
|
pass
|
|
if prev_image is not None:
|
|
yield prev_image
|
|
|
|
|
|
def load_images_list(img_list: list, ):
|
|
gen = images_generator(img_list)
|
|
(width, height) = next(gen)
|
|
images = torch.from_numpy(np.fromiter(gen, np.dtype((np.float32, (height, width, 3)))))
|
|
if len(images) == 0:
|
|
raise FileNotFoundError(f"No images could be loaded .")
|
|
return images
|
|
|
|
|
|
def fitter_cf_model_type(model):
|
|
VALUE_=model.model.model_type.value
|
|
if VALUE_ == 8:
|
|
cf_model_type = "FLUX"
|
|
elif VALUE_ == 4:
|
|
cf_model_type = "CASCADE"
|
|
elif VALUE_ == 6:
|
|
cf_model_type = "FLOW"
|
|
elif VALUE_ == 5:
|
|
cf_model_type = "EDM"
|
|
elif VALUE_ == 1:
|
|
cf_model_type = "EPS"
|
|
elif VALUE_ == 2:
|
|
cf_model_type = "V_PREDICTION"
|
|
elif VALUE_ == 3:
|
|
cf_model_type = "V_PREDICTION_EDM"
|
|
elif VALUE_ == 7:
|
|
cf_model_type = "PREDICTION_CONTINUOUS"
|
|
else:
|
|
raise "unsupport checkpoints"
|
|
return cf_model_type
|
|
|
|
def pre_text2infer(role_text,scene_text,lora_trigger_words,use_lora,tag_list):
|
|
'''
|
|
|
|
Args:
|
|
role_index_dict: {'[Taylor]': [0, 1], '[Lecun]': [2, 3]}
|
|
invert_role_index_dict{0: ['[Taylor]'], 1: ['[Taylor]'], 2: ['[Lecun]'], 3: ['[Lecun]']}
|
|
ref_role_index_dict {'[Taylor]': [0, 1], '[Lecun]': [2, 3]}
|
|
ref_role_totals[0, 1, 2, 3]
|
|
role_list ['[Taylor]', '[Lecun]']
|
|
role_dict {'[Taylor]': ' a woman img, wearing a white T-shirt, blue loose hair.', '[Lecun]': ' a man img,wearing a suit,black hair.'}
|
|
nc_txt_list [' a panda']
|
|
nc_indexs[4]
|
|
positions_index_dual[]
|
|
positions_index_char_1 0 2
|
|
positions_index_char_2 []
|
|
prompts_dual[] #['[A] play whith [B] in the garden']
|
|
index_char_1_list [0, 1]
|
|
index_char_2_list[2, 3]
|
|
|
|
Returns:
|
|
character_index_dict:{'[Taylor]': [0, 3], '[sam]': [1, 2]},if 1 role {'[Taylor]': [0, 1, 2]}
|
|
|
|
'''
|
|
add_trigger_words = " " + lora_trigger_words + " style "
|
|
# pre role_text
|
|
role_dict, role_list = character_to_dict(role_text, use_lora, add_trigger_words)
|
|
|
|
id_len=len(role_dict)
|
|
#pre scene_text
|
|
scene_text_origin = [i.strip() for i in scene_text.splitlines()]
|
|
scene_text_origin = [i for i in scene_text_origin if '[' in i] # 删除空行
|
|
prompt_sence = [i for i in scene_text_origin if not len(extract_content_from_brackets(i)) >= 2] # 剔除双角色的场景词
|
|
|
|
positions_index_dual = [index for index, prompt in enumerate(scene_text_origin) if
|
|
len(extract_content_from_brackets(prompt)) >= 2] #获取双角色出现的位置
|
|
prompts_dual = [prompt for prompt in scene_text_origin if len(extract_content_from_brackets(prompt)) >= 2] # 改成单句中双方括号方法,利于MS组句,[A]... [B]...[C]
|
|
|
|
#print(id_len,role_list)
|
|
if id_len == 2:
|
|
index_char_1_list=[index for index, prompt in enumerate(scene_text_origin) if role_list[0] in prompt]
|
|
positions_index_char_1 = index_char_1_list[0] # 获取角色出现的索引列表,并获取首次出现的位置
|
|
index_char_2_list=[index for index, prompt in enumerate(scene_text_origin) if role_list[1] in prompt]
|
|
positions_index_char_2 = index_char_2_list[0] # 获取角色出现的索引列表,并获取首次出现的位置
|
|
|
|
# print(positions_index_char_1, positions_index_char_2) #0,2
|
|
else:
|
|
index_char_1_list=[index for index, prompt in enumerate(scene_text_origin) if role_list[0] in prompt]
|
|
index_char_2_list=[]
|
|
positions_index_char_1=[]
|
|
positions_index_char_2=[]
|
|
|
|
if index_char_1_list and index_char_2_list: #dual 情况下,该列表有误,须排除重复元素
|
|
common_ = set(index_char_1_list) & set(index_char_2_list)
|
|
if common_:
|
|
index_char_1_list=[x for x in index_char_1_list if x not in common_]
|
|
index_char_2_list = [x for x in index_char_2_list if x not in common_]
|
|
|
|
|
|
clipped_prompts = prompt_sence[:] # copy
|
|
|
|
nc_indexs = []
|
|
for ind, prompt in enumerate(clipped_prompts): #获取NC的index
|
|
if "[NC]" in prompt:
|
|
nc_indexs.append(ind)
|
|
if ind < id_len:
|
|
raise f"The first [role] row need be a id prompts, cannot use [NC]!"
|
|
prompts = [
|
|
i if "[NC]" not in i else i.replace("[NC]", "") for i in clipped_prompts]
|
|
#去除#
|
|
prompts = [prompt.rpartition("#")[0] if "#" in prompt else prompt for prompt in prompts]
|
|
|
|
# character_dict:{'[Taylor]': ' a woman img, wearing a white T-shirt, blue loose hair.'},character_list:['[Taylor]'] 1role
|
|
#character_dict:{'[Taylor]': ' a woman img, wearing a white T-shirt, blue loose hair.', '[Lecun]': ' a man img,wearing a suit,black hair.'},character_list:['[Taylor]', '[Lecun]'] 2 role
|
|
#获取字典,实际用词等
|
|
role_index_dict, invert_role_index_dict, replace_prompts, ref_role_index_dict, ref_role_totals= process_original_text(role_dict, prompts)
|
|
|
|
if tag_list: #tag方法
|
|
if len(tag_list) < len(replace_prompts):
|
|
raise "The number of input condition images is less than the number of scene prompts!"
|
|
replace_prompts = [prompt + " " + tag_list[i] for i, prompt in enumerate(replace_prompts)]
|
|
|
|
if nc_indexs:
|
|
for x in nc_indexs: # 获取NC列表
|
|
nc_txt_list = [item for i, item in enumerate(replace_prompts) if i == x]
|
|
|
|
for x in nc_indexs: # 去除NC列表
|
|
replace_prompts = [item for i, item in enumerate(replace_prompts) if i != x]
|
|
else:
|
|
nc_txt_list = []
|
|
|
|
|
|
replace_prompts=[i for i in replace_prompts]
|
|
|
|
return replace_prompts,role_index_dict,invert_role_index_dict,ref_role_index_dict,ref_role_totals,role_list,role_dict,nc_txt_list,nc_indexs,positions_index_char_1,positions_index_char_2,positions_index_dual,prompts_dual,index_char_1_list,index_char_2_list
|
|
|
|
|
|
def convert_cf2diffuser(model):
|
|
from diffusers.pipelines.stable_diffusion.convert_from_ckpt import convert_ldm_unet_checkpoint
|
|
from diffusers import UNet2DConditionModel
|
|
config_file = os.path.join(cur_path,"local_repo/unet/config.json")
|
|
cf_state_dict = model.diffusion_model.state_dict()
|
|
unet_state_dict = model.model_config.process_unet_state_dict_for_saving(cf_state_dict)
|
|
unet_config = UNet2DConditionModel.load_config(config_file)
|
|
Unet = UNet2DConditionModel.from_config(unet_config).to(device, torch.float16)
|
|
cf_state_dict = convert_ldm_unet_checkpoint(unet_state_dict, Unet.config)
|
|
Unet.load_state_dict(cf_state_dict, strict=False)
|
|
del cf_state_dict,unet_state_dict
|
|
gc.collect()
|
|
torch.cuda.empty_cache()
|
|
return Unet
|
|
|
|
|
|
def cf_clip(txt_list, clip, infer_mode,role_list,input_split=True):
|
|
|
|
if infer_mode == "classic":
|
|
input_split= False
|
|
if len(role_list)==1:
|
|
input_split= False
|
|
if input_split:
|
|
role_emb_dict={}
|
|
for role,role_text_list in zip(role_list,txt_list):
|
|
pos_cond_list=[]
|
|
for j in role_text_list:
|
|
tokens_p = clip.tokenize(j)
|
|
output_p = clip.encode_from_tokens(tokens_p, return_dict=True) # {"pooled_output":tensor}
|
|
cond_p = output_p.pop("cond")
|
|
if cond_p.shape[1] / 77 > 1 and infer_mode != "classic":
|
|
# logging.warning("prompt'tokens length is abvoe 77,split it")
|
|
cond_p = torch.chunk(cond_p, cond_p.shape[1] // 77, dim=1)[0]
|
|
positive = [cond_p, output_p]
|
|
pos_cond_list.append(positive)
|
|
role_emb_dict[role]=pos_cond_list
|
|
return role_emb_dict
|
|
|
|
pos_cond_list = []
|
|
for i in txt_list[0]:
|
|
tokens_p = clip.tokenize(i)
|
|
output_p = clip.encode_from_tokens(tokens_p, return_dict=True) # {"pooled_output":tensor}
|
|
cond_p = output_p.pop("cond")
|
|
if cond_p.shape[1] / 77 > 1 and infer_mode != "classic":
|
|
# logging.warning("prompt'tokens length is abvoe 77,split it")
|
|
cond_p = torch.chunk(cond_p, cond_p.shape[1] // 77, dim=1)[0]
|
|
if infer_mode == "classic":
|
|
positive = [[cond_p, output_p]]
|
|
else:
|
|
positive = [cond_p, output_p]
|
|
# logging.info(f"sampler text is {i}")
|
|
pos_cond_list.append(positive)
|
|
return pos_cond_list
|
|
|
|
def get_eot_idx_cf(tokenizer, prompt):
|
|
words = prompt.split()
|
|
start = 1
|
|
for w in words:
|
|
start += len(tokenizer.encode(w)) - 2
|
|
return start
|
|
|
|
def get_phrase_idx_cf(tokenizer, phrase, prompt, get_last_word=False, num=0):
|
|
def is_equal_words(pr_words, ph_words):
|
|
if len(pr_words) != len(ph_words):
|
|
return False
|
|
for pr_word, ph_word in zip(pr_words, ph_words):
|
|
if "-"+ph_word not in pr_word and ph_word != re.sub(r'[.!?,:]$', '', pr_word):
|
|
return False
|
|
return True
|
|
|
|
phrase_words = phrase.split()
|
|
if len(phrase_words) == 0:
|
|
return [0, 0], None
|
|
if get_last_word:
|
|
phrase_words = phrase_words[-1:]
|
|
# prompt_words = re.findall(r'\b[\w\'-]+\b', prompt)
|
|
prompt_words = prompt.split()
|
|
start = 1
|
|
end = 0
|
|
res_words = phrase_words
|
|
for i in range(len(prompt_words)):
|
|
if is_equal_words(prompt_words[i:i+len(phrase_words)], phrase_words):
|
|
if num != 0:
|
|
# skip this one
|
|
num -= 1
|
|
continue
|
|
end = start
|
|
res_words = prompt_words[i:i+len(phrase_words)]
|
|
res_words = [re.sub(r'[.!?,:]$', '', w) for w in res_words]
|
|
prompt_words[i+len(phrase_words)-1] = res_words[-1] # remove the last punctuation
|
|
for j in range(i, i+len(phrase_words)):
|
|
end += len(tokenizer.encode(prompt_words[j])) - 2
|
|
break
|
|
else:
|
|
start += len(tokenizer.encode(prompt_words[i])) - 2
|
|
|
|
if end == 0:
|
|
return [0, 0], None
|
|
|
|
return [start, end], res_words
|
|
|
|
def get_phrases_idx_cf(tokenizer, phrases, prompt):
|
|
res = []
|
|
phrase_cnt = {}
|
|
for phrase in phrases:
|
|
if phrase in phrase_cnt:
|
|
cur_cnt = phrase_cnt[phrase]
|
|
phrase_cnt[phrase] += 1
|
|
else:
|
|
cur_cnt = 0
|
|
phrase_cnt[phrase] = 1
|
|
res.append(get_phrase_idx_cf(tokenizer, phrase, prompt, num=cur_cnt)[0])
|
|
return res
|
|
|
|
def replicate_data_by_indices(data_list, index_list1, index_list2):
|
|
# 确定新列表的长度
|
|
max_index = max(max(index_list1), max(index_list2)) if (index_list1 and index_list2) else 0
|
|
new_list = [None] * (max_index + 1)
|
|
|
|
# 根据索引填充数据
|
|
for idx in index_list1:
|
|
new_list[idx] = data_list[0]
|
|
for idx in index_list2:
|
|
new_list[idx] = data_list[1]
|
|
|
|
return new_list
|
|
|
|
|
|
|
|
def get_ms_phrase_emb(boxes, device, weight_dtype, drop_grounding_tokens, bsz, phrase_idxes,
|
|
num_samples, eot_idxes,phrases,clip,tokenizer):
|
|
cross_attention_kwargs = None
|
|
grounding_kwargs = None
|
|
if boxes is not None:
|
|
boxes = torch.tensor(boxes).to(device, weight_dtype) #torch.Size([1, 2, 4])
|
|
if phrases is not None:
|
|
drop_grounding_tokens = drop_grounding_tokens if drop_grounding_tokens is not None else [0] * bsz
|
|
batch_boxes = boxes.view(bsz * boxes.shape[1], -1).to(device)
|
|
phrase_input_ids=[]
|
|
for phrase in phrases:
|
|
phrase_input_id = tokenizer(phrase, max_length=tokenizer.model_max_length,
|
|
padding="max_length", truncation=True,
|
|
return_tensors="pt").input_ids
|
|
int_list = phrase_input_id.tolist()[0]
|
|
clean_input_ids_1=[(i,1.0) for i in int_list]
|
|
clean_input_ids_2=[(i,1.0) for i in int_list if i==int_list[0] or i==49407 ]
|
|
clean_input_ids_2=clean_input_ids_2[:77] if len(clean_input_ids_2) >=77 else clean_input_ids_2 + [(0,1.0)]*(77 - len(clean_input_ids_2))
|
|
phrase_input_ids.append([clean_input_ids_1,clean_input_ids_2])
|
|
|
|
phrase_embeds_list = []
|
|
for i in phrase_input_ids:
|
|
_, pooled_prompt_embed_= clip.cond_stage_model.clip_l.encode_token_weights(i)
|
|
pooled_prompt_embed_= pooled_prompt_embed_.to(device,dtype=weight_dtype)
|
|
phrase_embeds_list.append(pooled_prompt_embed_)
|
|
#phrase_input_id = clip.tokenize(phrase, return_word_ids=False)["l"]
|
|
#output_=clip.cond_stage_model.clip_l.encode_token_weights(phrase_input_id)[1]
|
|
|
|
phrase_embeds=torch.cat(phrase_embeds_list,dim=0)
|
|
#print(phrase_embeds.shape,phrase_embeds.is_cuda,batch_boxes.shape,batch_boxes.is_cuda)#torch.Size([2, 768]) torch.Size([2 4])
|
|
grounding_kwargs = {"boxes": batch_boxes, "phrase_embeds": phrase_embeds,"drop_grounding_tokens": drop_grounding_tokens}
|
|
else:
|
|
grounding_kwargs = None
|
|
boxes = torch.repeat_interleave(boxes, repeats=num_samples, dim=0)
|
|
uncond_boxes = torch.zeros_like(boxes)
|
|
boxes = torch.cat([uncond_boxes, boxes], dim=0)
|
|
cross_attention_kwargs = {"boxes": boxes}
|
|
|
|
if phrase_idxes is not None:
|
|
# phrase_idxes = torch.tensor(phrase_idxes).to(device, torch.int)
|
|
# eot_idxes = torch.tensor(eot_idxes).to(device, torch.int)
|
|
phrase_idxes = torch.tensor(phrase_idxes).to(device, weight_dtype)
|
|
eot_idxes = torch.tensor(eot_idxes).to(device, weight_dtype)
|
|
|
|
phrase_idxes = torch.repeat_interleave(phrase_idxes, repeats=num_samples, dim=0)
|
|
eot_idxes = torch.repeat_interleave(eot_idxes, repeats=num_samples, dim=0)
|
|
uncond_phrase_idxes = torch.zeros_like(phrase_idxes)
|
|
uncond_eot_idxes = torch.zeros_like(eot_idxes)
|
|
phrase_idxes = torch.cat([uncond_phrase_idxes, phrase_idxes], dim=0)
|
|
eot_idxes = torch.cat([uncond_eot_idxes, eot_idxes], dim=0)
|
|
if cross_attention_kwargs is None:
|
|
cross_attention_kwargs = {"phrase_idxes": phrase_idxes, "eot_idxes": eot_idxes}
|
|
else:
|
|
cross_attention_kwargs["phrase_idxes"] = phrase_idxes
|
|
cross_attention_kwargs["eot_idxes"] = eot_idxes
|
|
|
|
return cross_attention_kwargs,grounding_kwargs
|
|
def Infer_MSdiffusion(model, photomake_or_ipadapter_path,image_embeds, prompt_embeds_, negative_prompt_embeds_,grounding_kwargs,cross_attention_kwargs, bsz,mask_threshold,height,width,steps,seed,cfg,pooled_prompt_embeds,negative_pooled_prompt_embeds,):
|
|
image_proj_model = Resampler(
|
|
dim=1280,
|
|
depth=4,
|
|
dim_head=64,
|
|
heads=20,
|
|
num_queries=16,
|
|
embedding_dim=1664,
|
|
output_dim=model.unet.config.cross_attention_dim,
|
|
ff_mult=4,
|
|
latent_init_mode="grounding",
|
|
phrase_embeddings_dim=768,
|
|
).to(device, dtype=torch.float16)
|
|
PIPE = MSAdapterWarpper(model, image_proj_model, ckpt_path=photomake_or_ipadapter_path, device=device, num_tokens=16)
|
|
samples=PIPE.generate(model, image_embeds, prompt_embeds_, negative_prompt_embeds_,grounding_kwargs,cross_attention_kwargs, bsz,height,width,steps,seed,cfg,pooled_prompt_embeds,negative_pooled_prompt_embeds,scale=1.0,
|
|
num_samples=bsz, weight_dtype=torch.float16, subject_scales=None, mask_threshold=mask_threshold, start_step=5)
|
|
return samples
|
|
|
|
def convert_clip2diffuser(clip):
|
|
|
|
from transformers import (
|
|
CLIPTextModel,CLIPTextModelWithProjection,
|
|
CLIPTextConfig,
|
|
)
|
|
from contextlib import nullcontext
|
|
|
|
try:
|
|
from accelerate import init_empty_weights
|
|
from accelerate.utils import set_module_tensor_to_device
|
|
is_accelerate_available = True
|
|
except:
|
|
pass
|
|
|
|
import comfy.model_management as mm
|
|
|
|
offload_device=mm.unet_offload_device()
|
|
|
|
text_encoder_config=CLIPTextConfig.from_pretrained(os.path.join(cur_path, 'local_repo/text_encoder'), local_files_only=True)
|
|
ctx = init_empty_weights if is_accelerate_available else nullcontext
|
|
with ctx():
|
|
text_encoder = CLIPTextModel(text_encoder_config)
|
|
text_encoder_sd = clip.get_sd()
|
|
text_encoder_2_sd = clip.get_sd()
|
|
|
|
from diffusers.pipelines.stable_diffusion.convert_from_ckpt import convert_open_clip_checkpoint,convert_ldm_clip_checkpoint
|
|
text_encoder = convert_ldm_clip_checkpoint(text_encoder_sd, text_encoder=text_encoder)
|
|
text_encoder=None
|
|
text_encoder_2 = convert_open_clip_checkpoint(text_encoder_2_sd,config_name=os.path.join(cur_path, 'local_repo/text_encoder_2'))
|
|
|
|
return text_encoder,text_encoder_2
|
|
|
|
|
|
|
|
def convert_cfvae2diffuser(VAE,use_flux=False):
|
|
from diffusers import AutoencoderKL
|
|
from diffusers.pipelines.stable_diffusion.convert_from_ckpt import convert_ldm_vae_checkpoint
|
|
if use_flux:
|
|
vae_config = os.path.join(cur_path, "config/FLUX.1-dev/vae/config.json")
|
|
else:
|
|
vae_config = os.path.join(cur_path, "local_repo/vae/config.json")
|
|
vae_state_dict=VAE.get_sd()
|
|
ae_config = AutoencoderKL.load_config(vae_config)
|
|
|
|
if not use_flux:
|
|
AE = AutoencoderKL.from_config(ae_config).to(device, torch.float16)
|
|
cf_state_dict = convert_ldm_vae_checkpoint(vae_state_dict, AE.config)
|
|
AE.load_state_dict(cf_state_dict, strict=False)
|
|
del cf_state_dict,vae_state_dict
|
|
else:
|
|
AE = AutoencoderKL.from_config(ae_config).to(device, torch.bfloat16)
|
|
AE.load_state_dict(vae_state_dict, strict=False)
|
|
del vae_state_dict
|
|
torch.cuda.empty_cache()
|
|
return AE
|
|
|
|
|
|
def Loader_storydiffusion(cf_model,photomake_ckpt_path,VAE,UNET=None):
|
|
sdxl_repo = os.path.join(cur_path, "local_repo")
|
|
vae_config=OmegaConf.load(os.path.join(cur_path,"local_repo/vae/config.json"))
|
|
|
|
from .utils.pipeline_wrapper import StableDiffusionXLPipelineWrapper
|
|
from .utils.pipeline_img import PhotoMakerStableDiffusionXLPipeline as PhotoMakerStableDiffusionXLPipelineWrapper
|
|
from .utils.pipeline_v2_warrper import PhotoMakerStableDiffusionXLPipeline as PhotoMakerStableDiffusionXLPipelineV2
|
|
if cf_model is not None:
|
|
Unet=convert_cf2diffuser(cf_model.model)
|
|
if photomake_ckpt_path is not None: # guess img2img
|
|
if VAE is None:
|
|
raise "need link a SDXL vae when use img2img!"
|
|
vae = convert_cfvae2diffuser(VAE)
|
|
if "v1" in photomake_ckpt_path:
|
|
model = PhotoMakerStableDiffusionXLPipelineWrapper.from_pretrained(sdxl_repo, unet=Unet, vae=vae,text_encoder=None,text_encoder_2=None,
|
|
torch_dtype=torch.float16, use_safetensors=True)
|
|
model.load_photomaker_adapter(
|
|
photomake_ckpt_path,
|
|
subfolder="",
|
|
weight_name="photomaker-v1.bin",
|
|
trigger_word="img" # define the trigger word
|
|
)
|
|
|
|
else:
|
|
model = PhotoMakerStableDiffusionXLPipelineV2.from_pretrained(sdxl_repo, unet=Unet, vae=vae,text_encoder=None,text_encoder_2=None,
|
|
torch_dtype=torch.float16, use_safetensors=True)
|
|
model.load_photomaker_adapter(
|
|
photomake_ckpt_path,
|
|
subfolder="",
|
|
weight_name="photomaker-v2.bin",
|
|
trigger_word="img",
|
|
pm_version='v2',
|
|
)
|
|
else:
|
|
model = StableDiffusionXLPipelineWrapper.from_pretrained(sdxl_repo, unet=Unet, vae_config=vae_config,
|
|
torch_dtype=torch.float16, use_safetensors=True)
|
|
elif UNET is not None:
|
|
model = StableDiffusionXLPipelineWrapper.from_pretrained(sdxl_repo, unet=UNET.unet, vae_config=vae_config,
|
|
torch_dtype=torch.float16, use_safetensors=True)
|
|
else:
|
|
raise "need link a comfyUI checkpoint model!"
|
|
|
|
model.to(device)
|
|
return model
|
|
|
|
|
|
|
|
|
|
def Loader_story_maker(cf_model,ipadapter_ckpt_path,VAE,low_vram,lora_scale,controlnet=None,UNET=None):
|
|
sdxl_repo = os.path.join(cur_path, "local_repo")
|
|
#vae_config = OmegaConf.load(vae_config)
|
|
if cf_model is not None:
|
|
Unet = convert_cf2diffuser(cf_model.model)
|
|
AE=convert_cfvae2diffuser(VAE)
|
|
from .StoryMaker.pipeline_sdxl_storymaker_wrapper import StableDiffusionXLStoryMakerPipeline as StableDiffusionXLStoryMakerPipeline_wapper
|
|
model = StableDiffusionXLStoryMakerPipeline_wapper.from_pretrained(sdxl_repo, vae=AE,unet=Unet,text_encoder=None,text_encoder_2=None,
|
|
torch_dtype=torch.float16, use_safetensors=True)
|
|
elif UNET is not None:
|
|
AE=convert_cfvae2diffuser(VAE)
|
|
from .StoryMaker.pipeline_sdxl_storymaker_wrapper import StableDiffusionXLStoryMakerPipeline as StableDiffusionXLStoryMakerPipeline_wapper
|
|
model = StableDiffusionXLStoryMakerPipeline_wapper.from_pretrained(sdxl_repo, vae=AE,unet=UNET.unet,text_encoder=None,text_encoder_2=None,
|
|
torch_dtype=torch.float16, use_safetensors=True)
|
|
else:
|
|
raise "need link a comfyUI checkpoint model!"
|
|
|
|
|
|
if device != "mps":
|
|
if not low_vram:
|
|
model.cuda()
|
|
|
|
model.load_storymaker_adapter(ipadapter_ckpt_path, scale=0.8, lora_scale=lora_scale, controlnet=controlnet)
|
|
#model.scheduler = UniPCMultistepScheduler.from_config(model.scheduler.config)
|
|
return model
|
|
|
|
|
|
def Loader_Flux_Pulid(model,cf_model, ipadapter_ckpt_path,quantized_mode,aggressive_offload,offload,if_repo,clip_vision_path):
|
|
|
|
logging.info("start flux-pulid processing...")
|
|
from .PuLID.app_flux import FluxGenerator
|
|
|
|
pipe = FluxGenerator(model, "cuda:0", offload,aggressive_offload, pretrained_model=ipadapter_ckpt_path,
|
|
quantized_mode=quantized_mode, if_repo=if_repo,clip_vision_path=clip_vision_path)
|
|
|
|
return pipe
|
|
|
|
def Loader_InfiniteYou(extra_info,VAE,quantize_mode):
|
|
logging.info("start InfiniteYou mode processing...")
|
|
from .pipelines.pipeline_infu_flux import InfUFluxPipeline
|
|
from diffusers import BitsAndBytesConfig as DiffusersBitsAndBytesConfig,FluxTransformer2DModel
|
|
from transformers import BitsAndBytesConfig as TransformersBitsAndBytesConfig,T5EncoderModel
|
|
from .pipelines.resampler import Resampler
|
|
|
|
|
|
# quantize T5
|
|
# quant_config = TransformersBitsAndBytesConfig(
|
|
# load_in_4bit=True,
|
|
# bnb_4bit_quant_type="nf4",
|
|
# )
|
|
|
|
# text_encoder_2_4bit = T5EncoderModel.from_pretrained(
|
|
# repo_id,
|
|
# subfolder="text_encoder_2",
|
|
# quantization_config=quant_config,
|
|
# torch_dtype=torch.bfloat16,
|
|
# ) # init nf4
|
|
repo_list=[i for i in [extra_info.get("repo1_path") , extra_info.get("repo2_path")] if i is not None]
|
|
assert repo_list is not [] ,"repo_list is None"
|
|
find_infusenet=[i for i in repo_list if "sim_stage1" in i or "aes_stage2" in i]
|
|
|
|
assert len(find_infusenet)>=1 ,"no infusenet"
|
|
|
|
repo_id=os.path.join(cur_path, "config/FLUX.1-dev")
|
|
use_svdq=False
|
|
if extra_info.get("svdq_repo") is not None:
|
|
print("use svdq quantization")
|
|
from nunchaku import NunchakuFluxTransformer2dModel
|
|
use_svdq=True
|
|
transformer = NunchakuFluxTransformer2dModel.from_pretrained(extra_info.get("svdq_repo"),offload=True)
|
|
try:
|
|
transformer.set_attention_impl("nunchaku-fp16")
|
|
except:
|
|
pass
|
|
vae=convert_cfvae2diffuser(VAE,use_flux=True)
|
|
elif extra_info.get("gguf_path") is not None:
|
|
print("use gguf quantization")
|
|
from diffusers import GGUFQuantizationConfig
|
|
transformer = FluxTransformer2DModel.from_single_file(
|
|
extra_info.get("gguf_path"),
|
|
config=os.path.join(repo_id, "transformer"),
|
|
quantization_config=GGUFQuantizationConfig(compute_dtype=torch.bfloat16),
|
|
torch_dtype=torch.bfloat16,
|
|
)
|
|
vae=convert_cfvae2diffuser(VAE,use_flux=True)
|
|
elif extra_info.get("unet_path") is not None:
|
|
|
|
if quantize_mode=="fp8":
|
|
transformer = FluxTransformer2DModel.from_single_file(extra_info.get("unet_path"), config=os.path.join(repo_id,"transformer/config.json"),
|
|
torch_dtype=torch.bfloat16)
|
|
else:
|
|
from safetensors.torch import load_file
|
|
t_state_dict=load_file(extra_info.get("unet_path"))
|
|
unet_config = FluxTransformer2DModel.load_config(os.path.join(repo_id,"transformer/config.json"))
|
|
transformer = FluxTransformer2DModel.from_config(unet_config).to(torch.bfloat16)
|
|
transformer.load_state_dict(t_state_dict, strict=False)
|
|
del t_state_dict
|
|
gc_cleanup()
|
|
else:
|
|
vae = None
|
|
find_flux=[i for i in repo_list if "flux" in i ]
|
|
if not find_flux:
|
|
raise "need fill flux repo in EasyFunction_Lite repo1 or repo2!"
|
|
print("use nf4 quantization")
|
|
if quantize_mode=="fp8":
|
|
transformer = FluxTransformer2DModel.from_pretrained(
|
|
find_flux[0],
|
|
subfolder="transformer",
|
|
quantization_config=DiffusersBitsAndBytesConfig(load_in_8bit=True,),
|
|
torch_dtype=torch.bfloat16,
|
|
)
|
|
|
|
elif quantize_mode=="nf4": #nf4
|
|
transformer = FluxTransformer2DModel.from_pretrained(
|
|
find_flux[0],
|
|
subfolder="transformer",
|
|
quantization_config=DiffusersBitsAndBytesConfig(
|
|
load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16
|
|
),
|
|
torch_dtype=torch.bfloat16,)
|
|
else:
|
|
transformer = FluxTransformer2DModel.from_pretrained(
|
|
find_flux[0],
|
|
subfolder="transformer",
|
|
torch_dtype=torch.bfloat16,
|
|
)
|
|
|
|
|
|
infusenet_path = os.path.join(find_infusenet[0], 'InfuseNetModel')
|
|
|
|
|
|
pipe = InfUFluxPipeline(
|
|
repo_id,infusenet_path,
|
|
text_encoder_2=None,
|
|
text_encoder=None,
|
|
transformer=transformer,
|
|
vae=vae,
|
|
use_svdq=use_svdq,
|
|
)
|
|
# Load image proj model
|
|
num_tokens = 8 # image_proj_num_tokens
|
|
image_emb_dim = 512
|
|
image_proj_model = Resampler(
|
|
dim=1280,
|
|
depth=4,
|
|
dim_head=64,
|
|
heads=20,
|
|
num_queries=num_tokens,
|
|
embedding_dim=image_emb_dim,
|
|
output_dim=4096,
|
|
ff_mult=4,
|
|
)
|
|
image_proj_model_path = os.path.join(find_infusenet[0], 'image_proj_model.bin')
|
|
ipm_state_dict = torch.load(image_proj_model_path, map_location="cpu",weights_only=False)
|
|
image_proj_model.load_state_dict(ipm_state_dict['image_proj'])
|
|
del ipm_state_dict
|
|
torch.cuda.empty_cache()
|
|
image_proj_model.to('cuda', torch.bfloat16)
|
|
image_proj_model.eval()
|
|
|
|
pipe.pipe.enable_model_cpu_offload()
|
|
return pipe,image_proj_model
|
|
|
|
def load_pipeline_consistory(cf_model,VAE):
|
|
from .consistory.consistory_unet_sdxl import ConsistorySDXLUNet2DConditionModel
|
|
from .consistory.consistory_pipeline_wapper import ConsistoryExtendAttnSDXLPipeline as ConsistoryExtendAttnSDXLPipeline_wapper
|
|
float_type = torch.float16
|
|
|
|
device = torch.device(f'cuda:0') if torch.cuda.is_available() else torch.device('cpu')
|
|
|
|
if cf_model is not None:
|
|
config_file = os.path.join(cur_path,"local_repo/unet/config.json")
|
|
sdxl_repo = os.path.join(cur_path, "local_repo")
|
|
|
|
from diffusers.pipelines.stable_diffusion.convert_from_ckpt import convert_ldm_unet_checkpoint
|
|
cf_state_dict = cf_model.model.diffusion_model.state_dict()
|
|
unet_state_dict = cf_model.model.model_config.process_unet_state_dict_for_saving(cf_state_dict)
|
|
|
|
unet_config = ConsistorySDXLUNet2DConditionModel.load_config(config_file)
|
|
Unet = ConsistorySDXLUNet2DConditionModel.from_config(unet_config).to(torch.float16)
|
|
cf_state_dict = convert_ldm_unet_checkpoint(unet_state_dict, Unet.config)
|
|
Unet.load_state_dict(cf_state_dict, strict=False)
|
|
del cf_state_dict,unet_state_dict
|
|
gc_cleanup()
|
|
|
|
AE=convert_cfvae2diffuser(VAE)
|
|
|
|
story_pipeline=ConsistoryExtendAttnSDXLPipeline_wapper.from_pretrained(sdxl_repo,unet=Unet,vae=AE,text_encoder=None,text_encoder_2=None, torch_dtype=float_type, )
|
|
story_pipeline.to(device)
|
|
else:
|
|
raise "need link a sdxl checkpoints"
|
|
|
|
story_pipeline.enable_freeu(s1=0.6, s2=0.4, b1=1.1, b2=1.2)
|
|
return story_pipeline
|
|
|
|
def Loader_KOLOR(repo_id,clip_vision_path,face_ckpt,):
|
|
|
|
from .kolors.models.modeling_chatglm import ChatGLMModel
|
|
from .kolors.models.tokenization_chatglm import ChatGLMTokenizer
|
|
from transformers import CLIPVisionModelWithProjection,CLIPImageProcessor
|
|
from diffusers import EulerDiscreteScheduler, UNet2DConditionModel, AutoencoderKL
|
|
|
|
logging.info("loader kolor processing...")
|
|
|
|
# text_encoder = ChatGLMModel.from_pretrained(
|
|
# f'{repo_id}/text_encoder', torch_dtype=torch.float16).half()
|
|
vae = AutoencoderKL.from_pretrained(f"{repo_id}/vae", revision=None).half()
|
|
#tokenizer = ChatGLMTokenizer.from_pretrained(f'{repo_id}/text_encoder')
|
|
scheduler = EulerDiscreteScheduler.from_pretrained(f"{repo_id}/scheduler")
|
|
|
|
|
|
from .kolors.pipelines.pipeline_stable_diffusion_xl_chatglm_256_ipadapter_FaceID import \
|
|
StableDiffusionXLPipeline as StableDiffusionXLPipelineFaceID
|
|
unet = UNet2DConditionModel.from_pretrained(f'{repo_id}/unet', revision=None).half()
|
|
|
|
if clip_vision_path:
|
|
|
|
clip_image_encoder = clip_vision_path
|
|
clip_image_processor = CLIPImageProcessor(size=224, crop_size=224)
|
|
use_singel_clip = True
|
|
else:
|
|
clip_image_encoder = CLIPVisionModelWithProjection.from_pretrained(
|
|
f'{repo_id}/clip-vit-large-patch14-336', ignore_mismatched_sizes=True)
|
|
clip_image_encoder.to("cuda")
|
|
clip_image_processor = CLIPImageProcessor(size=336, crop_size=336)
|
|
use_singel_clip = False
|
|
|
|
pipe = StableDiffusionXLPipelineFaceID(
|
|
vae=vae,
|
|
text_encoder=None,
|
|
tokenizer=None,
|
|
unet=unet,
|
|
scheduler=scheduler,
|
|
face_clip_encoder=clip_image_encoder,
|
|
face_clip_processor=clip_image_processor,
|
|
force_zeros_for_empty_prompt=False,
|
|
use_single_clip=use_singel_clip,
|
|
)
|
|
pipe = pipe.to("cuda")
|
|
pipe.load_ip_adapter_faceid_plus(face_ckpt, device="cuda")
|
|
pipe.set_face_fidelity_scale(0.8)
|
|
return pipe
|
|
|
|
def GLM_clip(cur_path,chatglm3_path): #https://github.com/kijai/ComfyUI-KwaiKolorsWrapper/blob/main/nodes.py @kijai
|
|
from .kolors.models.modeling_chatglm import ChatGLMModel, ChatGLMConfig
|
|
from contextlib import nullcontext
|
|
from comfy.utils import ProgressBar, load_torch_file
|
|
try:
|
|
from accelerate import init_empty_weights
|
|
from accelerate.utils import set_module_tensor_to_device
|
|
is_accelerate_available = True
|
|
except:
|
|
pass
|
|
|
|
import comfy.model_management as mm
|
|
import json
|
|
offload_device=mm.unet_offload_device()
|
|
text_encoder_config = os.path.join(cur_path, 'config/glm/text_encoder_config.json')
|
|
with open(text_encoder_config, 'r') as file:
|
|
config = json.load(file)
|
|
|
|
text_encoder_config = ChatGLMConfig(**config)
|
|
with (init_empty_weights() if is_accelerate_available else nullcontext()):
|
|
text_encoder = ChatGLMModel(text_encoder_config)
|
|
if '4bit' in chatglm3_path:
|
|
text_encoder.quantize(4)
|
|
elif '8bit' in chatglm3_path:
|
|
text_encoder.quantize(8)
|
|
|
|
text_encoder_sd = load_torch_file(chatglm3_path)
|
|
if is_accelerate_available:
|
|
for key in text_encoder_sd:
|
|
set_module_tensor_to_device(text_encoder, key, device=offload_device, value=text_encoder_sd[key])
|
|
else:
|
|
text_encoder.load_state_dict()
|
|
|
|
|
|
return text_encoder
|
|
|
|
def glm_single_encode(chatglm3_model, prompt_list,role_list, negative_prompt_, num_images_per_prompt,nc_mode=False): #https://github.com/kijai/ComfyUI-KwaiKolorsWrapper/blob/main/nodes.py @kijai
|
|
import comfy.model_management as mm
|
|
device = mm.get_torch_device()
|
|
offload_device = mm.unet_offload_device()
|
|
mm.unload_all_models()
|
|
mm.soft_empty_cache()
|
|
# Function to randomly select an option from the brackets
|
|
# def choose_random_option(match):
|
|
# options = match.group(1).split('|')
|
|
# return random.choice(options)
|
|
|
|
# # Randomly choose between options in brackets for prompt and negative_prompt
|
|
# prompt = re.sub(r'\{([^{}]*)\}', choose_random_option, prompt)
|
|
# negative_prompt = re.sub(r'\{([^{}]*)\}', choose_random_option, negative_prompt)
|
|
|
|
# if "|" in prompt:
|
|
# prompt = prompt.split("|")
|
|
# negative_prompt = [negative_prompt] * len(prompt) # Replicate negative_prompt to match length of prompt list
|
|
|
|
do_classifier_free_guidance = True
|
|
#print(prompt)
|
|
if nc_mode:
|
|
only_role_emb,only_role_emb_ne = [],[]
|
|
for prompt,negative_prompt in zip(prompt_list,[negative_prompt_]*len(prompt_list)):
|
|
|
|
if prompt is not None and isinstance(prompt, str):
|
|
batch_size = 1
|
|
elif prompt is not None and isinstance(prompt, list):
|
|
batch_size = len(prompt)
|
|
|
|
# Define tokenizers and text encoders
|
|
tokenizer = chatglm3_model['tokenizer']
|
|
text_encoder = chatglm3_model['text_encoder']
|
|
|
|
text_encoder.to(device)
|
|
|
|
text_inputs = tokenizer(
|
|
prompt,
|
|
padding="max_length",
|
|
max_length=256,
|
|
truncation=True,
|
|
return_tensors="pt",
|
|
).to(device)
|
|
|
|
output = text_encoder(
|
|
input_ids=text_inputs['input_ids'] ,
|
|
attention_mask=text_inputs['attention_mask'],
|
|
position_ids=text_inputs['position_ids'],
|
|
output_hidden_states=True)
|
|
|
|
prompt_embeds = output.hidden_states[-2].permute(1, 0, 2).clone() # [batch_size, 77, 4096]
|
|
text_proj = output.hidden_states[-1][-1, :, :].clone() # [batch_size, 4096]
|
|
bs_embed, seq_len, _ = prompt_embeds.shape
|
|
prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1)
|
|
prompt_embeds = prompt_embeds.view(bs_embed * num_images_per_prompt, seq_len, -1)
|
|
|
|
|
|
if do_classifier_free_guidance:
|
|
uncond_tokens = []
|
|
if negative_prompt is None:
|
|
uncond_tokens = [""] * batch_size
|
|
elif prompt is not None and type(prompt) is not type(negative_prompt):
|
|
raise TypeError(
|
|
f"`negative_prompt` should be the same type to `prompt`, but got {type(negative_prompt)} !="
|
|
f" {type(prompt)}."
|
|
)
|
|
elif isinstance(negative_prompt, str):
|
|
uncond_tokens = [negative_prompt]
|
|
elif batch_size != len(negative_prompt):
|
|
raise ValueError(
|
|
f"`negative_prompt`: {negative_prompt} has batch size {len(negative_prompt)}, but `prompt`:"
|
|
f" {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches"
|
|
" the batch size of `prompt`."
|
|
)
|
|
else:
|
|
uncond_tokens = negative_prompt
|
|
|
|
|
|
max_length = prompt_embeds.shape[1]
|
|
uncond_input = tokenizer(
|
|
uncond_tokens,
|
|
padding="max_length",
|
|
max_length=max_length,
|
|
truncation=True,
|
|
return_tensors="pt",
|
|
).to(device)
|
|
output = text_encoder(
|
|
input_ids=uncond_input['input_ids'] ,
|
|
attention_mask=uncond_input['attention_mask'],
|
|
position_ids=uncond_input['position_ids'],
|
|
output_hidden_states=True)
|
|
negative_prompt_embeds = output.hidden_states[-2].permute(1, 0, 2).clone() # [batch_size, 77, 4096]
|
|
negative_text_proj = output.hidden_states[-1][-1, :, :].clone() # [batch_size, 4096]
|
|
|
|
if do_classifier_free_guidance:
|
|
# duplicate unconditional embeddings for each generation per prompt, using mps friendly method
|
|
seq_len = negative_prompt_embeds.shape[1]
|
|
|
|
negative_prompt_embeds = negative_prompt_embeds.to(dtype=text_encoder.dtype, device=device)
|
|
|
|
negative_prompt_embeds = negative_prompt_embeds.repeat(1, num_images_per_prompt, 1)
|
|
negative_prompt_embeds = negative_prompt_embeds.view(
|
|
batch_size * num_images_per_prompt, seq_len, -1
|
|
)
|
|
|
|
bs_embed = text_proj.shape[0]
|
|
text_proj = text_proj.repeat(1, num_images_per_prompt).view(
|
|
bs_embed * num_images_per_prompt, -1
|
|
)
|
|
negative_text_proj = negative_text_proj.repeat(1, num_images_per_prompt).view(
|
|
bs_embed * num_images_per_prompt, -1
|
|
)
|
|
math_emb=[prompt_embeds,text_proj]
|
|
only_role_emb.append(math_emb)
|
|
math_emb_n=[negative_prompt_embeds,negative_text_proj]
|
|
only_role_emb_ne.append(math_emb_n)
|
|
|
|
text_encoder.to(offload_device)
|
|
mm.soft_empty_cache()
|
|
gc.collect()
|
|
return only_role_emb,only_role_emb_ne
|
|
else:
|
|
role_emb_dict={}
|
|
for key,prompts in zip(role_list,prompt_list):
|
|
only_role_emb,only_role_emb_ne = [],[]
|
|
for prompt,negative_prompt in zip(prompts,[negative_prompt_]*len(prompts)):
|
|
|
|
if prompt is not None and isinstance(prompt, str):
|
|
batch_size = 1
|
|
elif prompt is not None and isinstance(prompt, list):
|
|
batch_size = len(prompt)
|
|
|
|
# Define tokenizers and text encoders
|
|
tokenizer = chatglm3_model['tokenizer']
|
|
text_encoder = chatglm3_model['text_encoder']
|
|
|
|
text_encoder.to(device)
|
|
|
|
text_inputs = tokenizer(
|
|
prompt,
|
|
padding="max_length",
|
|
max_length=256,
|
|
truncation=True,
|
|
return_tensors="pt",
|
|
).to(device)
|
|
|
|
output = text_encoder(
|
|
input_ids=text_inputs['input_ids'] ,
|
|
attention_mask=text_inputs['attention_mask'],
|
|
position_ids=text_inputs['position_ids'],
|
|
output_hidden_states=True)
|
|
|
|
prompt_embeds = output.hidden_states[-2].permute(1, 0, 2).clone() # [batch_size, 77, 4096]
|
|
text_proj = output.hidden_states[-1][-1, :, :].clone() # [batch_size, 4096]
|
|
bs_embed, seq_len, _ = prompt_embeds.shape
|
|
prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1)
|
|
prompt_embeds = prompt_embeds.view(bs_embed * num_images_per_prompt, seq_len, -1)
|
|
|
|
|
|
if do_classifier_free_guidance:
|
|
uncond_tokens = []
|
|
if negative_prompt is None:
|
|
uncond_tokens = [""] * batch_size
|
|
elif prompt is not None and type(prompt) is not type(negative_prompt):
|
|
raise TypeError(
|
|
f"`negative_prompt` should be the same type to `prompt`, but got {type(negative_prompt)} !="
|
|
f" {type(prompt)}."
|
|
)
|
|
elif isinstance(negative_prompt, str):
|
|
uncond_tokens = [negative_prompt]
|
|
elif batch_size != len(negative_prompt):
|
|
raise ValueError(
|
|
f"`negative_prompt`: {negative_prompt} has batch size {len(negative_prompt)}, but `prompt`:"
|
|
f" {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches"
|
|
" the batch size of `prompt`."
|
|
)
|
|
else:
|
|
uncond_tokens = negative_prompt
|
|
|
|
|
|
max_length = prompt_embeds.shape[1]
|
|
uncond_input = tokenizer(
|
|
uncond_tokens,
|
|
padding="max_length",
|
|
max_length=max_length,
|
|
truncation=True,
|
|
return_tensors="pt",
|
|
).to(device)
|
|
output = text_encoder(
|
|
input_ids=uncond_input['input_ids'] ,
|
|
attention_mask=uncond_input['attention_mask'],
|
|
position_ids=uncond_input['position_ids'],
|
|
output_hidden_states=True)
|
|
negative_prompt_embeds = output.hidden_states[-2].permute(1, 0, 2).clone() # [batch_size, 77, 4096]
|
|
negative_text_proj = output.hidden_states[-1][-1, :, :].clone() # [batch_size, 4096]
|
|
|
|
if do_classifier_free_guidance:
|
|
# duplicate unconditional embeddings for each generation per prompt, using mps friendly method
|
|
seq_len = negative_prompt_embeds.shape[1]
|
|
|
|
negative_prompt_embeds = negative_prompt_embeds.to(dtype=text_encoder.dtype, device=device)
|
|
|
|
negative_prompt_embeds = negative_prompt_embeds.repeat(1, num_images_per_prompt, 1)
|
|
negative_prompt_embeds = negative_prompt_embeds.view(
|
|
batch_size * num_images_per_prompt, seq_len, -1
|
|
)
|
|
|
|
bs_embed = text_proj.shape[0]
|
|
text_proj = text_proj.repeat(1, num_images_per_prompt).view(
|
|
bs_embed * num_images_per_prompt, -1
|
|
)
|
|
negative_text_proj = negative_text_proj.repeat(1, num_images_per_prompt).view(
|
|
bs_embed * num_images_per_prompt, -1
|
|
)
|
|
math_emb=[prompt_embeds,text_proj]
|
|
only_role_emb.append(math_emb)
|
|
math_emb_n=[negative_prompt_embeds,negative_text_proj]
|
|
only_role_emb_ne.append(math_emb_n)
|
|
role_emb_dict[key]=only_role_emb
|
|
|
|
text_encoder.to(offload_device)
|
|
mm.soft_empty_cache()
|
|
gc.collect()
|
|
|
|
return role_emb_dict,only_role_emb_ne
|
|
|
|
|
|
def encode_prompt_with_trigger_word(clip,model_,
|
|
prompt: str,
|
|
prompt_2 = None,
|
|
num_id_images= 1,
|
|
device = None,
|
|
prompt_embeds = None,
|
|
pooled_prompt_embeds= None,
|
|
class_tokens_mask = None,
|
|
nc_flag: bool = False,
|
|
trigger_word="img",
|
|
unet_dtype=torch.float16
|
|
):
|
|
device = device
|
|
tokenizer_2=model_.tokenizer_2
|
|
tokenizer=model_.tokenizer
|
|
#text_encoders=clip.encode_from_tokens
|
|
|
|
if prompt is not None and isinstance(prompt, str):
|
|
batch_size = 1
|
|
elif prompt is not None and isinstance(prompt, list):
|
|
batch_size = len(prompt)
|
|
else:
|
|
batch_size = prompt_embeds.shape[0]
|
|
|
|
# Find the token id of the trigger word
|
|
image_token_id = tokenizer_2.convert_tokens_to_ids(trigger_word)
|
|
|
|
# Define tokenizers and text encoders
|
|
tokenizers = [tokenizer, tokenizer_2] if tokenizer is not None else [tokenizer_2]
|
|
# text_encoders = (
|
|
# [self.text_encoder, self.text_encoder_2] if self.text_encoder is not None else [self.text_encoder_2]
|
|
# )
|
|
|
|
if prompt_embeds is None:
|
|
prompt_2 = prompt_2 or prompt
|
|
prompt_embeds_list = []
|
|
prompts = [prompt, prompt_2]
|
|
|
|
for prompt, tokenizer,text_en in zip(prompts, tokenizers,[clip.cond_stage_model.clip_l.encode_token_weights,clip.cond_stage_model.clip_g.encode_token_weights]):
|
|
input_ids = tokenizer.encode(prompt) # TODO: batch encode
|
|
clean_index = 0
|
|
clean_input_ids = []
|
|
class_token_index = []
|
|
# Find out the corresponding class word token based on the newly added trigger word token
|
|
for i, token_id in enumerate(input_ids):
|
|
if token_id == image_token_id:
|
|
class_token_index.append(clean_index - 1)
|
|
else:
|
|
clean_input_ids.append(token_id)
|
|
clean_index += 1
|
|
if nc_flag:
|
|
return None, None, None
|
|
if len(class_token_index) > 1:
|
|
raise ValueError(
|
|
f"PhotoMaker currently does not support multiple trigger words in a single prompt.\
|
|
Trigger word: {trigger_word}, Prompt: {prompt}."
|
|
)
|
|
elif len(class_token_index) == 0 and not nc_flag:
|
|
raise ValueError(
|
|
f"PhotoMaker currently does not support multiple trigger words in a single prompt.\
|
|
Trigger word: {trigger_word}, Prompt: {prompt}."
|
|
)
|
|
class_token_index = class_token_index[0]
|
|
|
|
# Expand the class word token and corresponding mask
|
|
class_token = clean_input_ids[class_token_index]
|
|
clean_input_ids = clean_input_ids[:class_token_index] + [class_token] * num_id_images + \
|
|
clean_input_ids[class_token_index+1:]
|
|
|
|
# Truncation or padding
|
|
max_len = tokenizer.model_max_length
|
|
if len(clean_input_ids) > max_len:
|
|
clean_input_ids = clean_input_ids[:max_len]
|
|
else:
|
|
clean_input_ids = clean_input_ids + [tokenizer.pad_token_id] * (
|
|
max_len - len(clean_input_ids)
|
|
)
|
|
|
|
class_tokens_mask = [True if class_token_index <= i < class_token_index+num_id_images else False \
|
|
for i in range(len(clean_input_ids))]
|
|
#print(clean_input_ids)
|
|
#print(max_len)
|
|
|
|
clean_input_ids=clean_input_ids[:77] if len(clean_input_ids) > 77 else clean_input_ids + [0]*(77 - len(clean_input_ids))
|
|
|
|
clean_input_ids_1=[(i,1.0) for i in clean_input_ids]
|
|
clean_input_ids_2=[(i,1.0) for i in clean_input_ids if i==clean_input_ids[0] or i==49407 ]
|
|
clean_input_ids_2=clean_input_ids_2[:77] if len(clean_input_ids_2) > 77 else clean_input_ids_2 + [(0,1.0)]*(77 - len(clean_input_ids_2))
|
|
clean_input_ids_list=[clean_input_ids_1,clean_input_ids_2]
|
|
#print(clean_input_ids)
|
|
#clean_input_ids_ = torch.tensor(clean_input_ids, dtype=torch.long).unsqueeze(0)
|
|
class_tokens_mask = torch.tensor(class_tokens_mask, dtype=torch.bool).unsqueeze(0)
|
|
|
|
|
|
prompt_embeds, pooled_prompt_embeds = text_en(clean_input_ids_list) # print(prompt_embeds.shape,pooled_prompt_embeds.shape)torch.Size([1, 154, 768]) torch.Size([1, 768]) torch.Size([1, 154, 1280]) torch.Size([1, 1280])
|
|
|
|
|
|
prompt_embeds_list.append((prompt_embeds,pooled_prompt_embeds))
|
|
|
|
#prompt_embeds = torch.concat(prompt_embeds_list, dim=-1)
|
|
#prompt_embeds=prompt_embeds_list[1]
|
|
pooled_prompt_embeds=prompt_embeds_list[1][1]
|
|
# cut_to = min(prompt_embeds_list[0][0].shape[1], prompt_embeds_list[1][0].shape[1])
|
|
# print(cut_to)
|
|
cut_to=77
|
|
prompt_embeds= torch.cat([prompt_embeds_list[0][0][:,:cut_to], prompt_embeds_list[1][0][:,:cut_to]], dim=-1)
|
|
prompt_embeds = prompt_embeds.to(device=device,dtype=unet_dtype, )
|
|
class_tokens_mask = class_tokens_mask.to(device=device) # TODO: ignoring two-prompt case
|
|
|
|
return prompt_embeds, pooled_prompt_embeds, class_tokens_mask
|
|
|
|
def cf_clip_single(clip,prompt,
|
|
prompt_2,
|
|
device,
|
|
num_images_per_prompt,
|
|
do_classifier_free_guidance,
|
|
negative_prompt,
|
|
negative_prompt_2,
|
|
prompt_embeds,
|
|
negative_prompt_embeds,
|
|
pooled_prompt_embeds,
|
|
negative_pooled_prompt_embeds,):
|
|
|
|
tokens_p = clip.tokenize(prompt)
|
|
output_p = clip.encode_from_tokens(tokens_p, return_dict=True) # {"pooled_output":tensor}
|
|
cond_p = output_p.pop("cond")
|
|
if cond_p.shape[1] / 77 > 1 :
|
|
logging.warning("prompt'tokens length is abvoe 77,split it")
|
|
cond_p = torch.chunk(cond_p, cond_p.shape[1] // 77, dim=1)[0]
|
|
pool_p=output_p.pop("pooled_output")
|
|
|
|
tokens_n = clip.tokenize(negative_prompt)
|
|
output_n = clip.encode_from_tokens(tokens_n, return_dict=True) # {"pooled_output":tensor}
|
|
cond_n = output_n.pop("cond")
|
|
if cond_n.shape[1] / 77 > 1 :
|
|
logging.warning("prompt'tokens length is abvoe 77,split it")
|
|
cond_n = torch.chunk(cond_n, cond_n.shape[1] // 77, dim=1)[0]
|
|
pool_n = output_n.pop("pooled_output")
|
|
|
|
return cond_p,cond_n,pool_p,pool_n # TODO: replace the pooled_prompt_embeds with text only prompt
|
|
|
|
|
|
|
|
def photomaker_clip(clip,model_,prompt_list,negative_prompt,input_id_images,trigger_word="img",num_images_per_prompt=1,nc_flag=False,prompt_2=None,prompt_embeds=None,pooled_prompt_embeds=None,class_tokens_mask=None,prompt_embeds_text_only=None,negative_pooled_prompt_embeds=None):
|
|
id_encoder=model_.id_encoder
|
|
id_image_processor=model_.id_image_processor
|
|
|
|
tokenizer=model_.tokenizer_2
|
|
batch_size=len(prompt_list)
|
|
|
|
num_id_images = len(input_id_images) #双角色时,须确保输入的图片是列表,且跟prompt对应上
|
|
prompt_arr = prompt_list
|
|
negative_prompt_embeds_arr = []
|
|
prompt_embeds_text_only_arr = []
|
|
prompt_embeds_arr = []
|
|
latents_arr = []
|
|
add_time_ids_arr = []
|
|
negative_pooled_prompt_embeds_arr = []
|
|
pooled_prompt_embeds_text_only_arr = []
|
|
pooled_prompt_embeds_arr = []
|
|
for prompt in prompt_arr:
|
|
(
|
|
prompt_embeds,
|
|
pooled_prompt_embeds,
|
|
class_tokens_mask,
|
|
) = encode_prompt_with_trigger_word(clip,model_,
|
|
prompt=prompt,
|
|
prompt_2=prompt_2,
|
|
device=device,
|
|
num_id_images=num_id_images,
|
|
prompt_embeds=prompt_embeds,
|
|
pooled_prompt_embeds=pooled_prompt_embeds,
|
|
class_tokens_mask=class_tokens_mask,
|
|
nc_flag = nc_flag,
|
|
)
|
|
|
|
# 4. Encode input prompt without the trigger word for delayed conditioning
|
|
# encode, remove trigger word token, then decode
|
|
tokens_text_only = tokenizer.encode(prompt, add_special_tokens=False)
|
|
trigger_word_token = tokenizer.convert_tokens_to_ids(trigger_word)
|
|
if not nc_flag:
|
|
tokens_text_only.remove(trigger_word_token)
|
|
prompt_text_only = tokenizer.decode(tokens_text_only, add_special_tokens=False)
|
|
#print(prompt_text_only)
|
|
(
|
|
prompt_embeds_text_only,
|
|
negative_prompt_embeds,
|
|
pooled_prompt_embeds_text_only, # TODO: replace the pooled_prompt_embeds with text only prompt
|
|
negative_pooled_prompt_embeds,
|
|
) = cf_clip_single(clip,
|
|
prompt=prompt_text_only,
|
|
prompt_2=prompt_2,
|
|
device=device,
|
|
num_images_per_prompt=num_images_per_prompt,
|
|
do_classifier_free_guidance=True,
|
|
negative_prompt=negative_prompt,
|
|
negative_prompt_2=None,
|
|
prompt_embeds=prompt_embeds_text_only,
|
|
negative_prompt_embeds=None,
|
|
pooled_prompt_embeds=None,
|
|
negative_pooled_prompt_embeds=negative_pooled_prompt_embeds,
|
|
)
|
|
|
|
# 5. Prepare the input ID images
|
|
dtype = next(id_encoder.parameters()).dtype
|
|
if not isinstance(input_id_images[0], torch.Tensor):
|
|
id_pixel_values = id_image_processor(input_id_images, return_tensors="pt").pixel_values
|
|
|
|
id_pixel_values = id_pixel_values.unsqueeze(0).to(device=device, dtype=dtype) # TODO: multiple prompts
|
|
|
|
# duplicate text embeddings for each generation per prompt, using mps friendly method
|
|
if not nc_flag:
|
|
# 6. Get the update text embedding with the stacked ID embedding
|
|
prompt_embeds = id_encoder(id_pixel_values, prompt_embeds, class_tokens_mask)
|
|
|
|
bs_embed, seq_len, _ = prompt_embeds.shape
|
|
prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1)
|
|
prompt_embeds = prompt_embeds.view(bs_embed * num_images_per_prompt, seq_len, -1)
|
|
pooled_prompt_embeds = pooled_prompt_embeds.repeat(1, num_images_per_prompt).view(
|
|
bs_embed * num_images_per_prompt, -1
|
|
)
|
|
pooled_prompt_embeds_arr.append(pooled_prompt_embeds)
|
|
pooled_prompt_embeds = None
|
|
|
|
negative_prompt_embeds_arr.append(negative_prompt_embeds)
|
|
negative_prompt_embeds = None
|
|
negative_pooled_prompt_embeds_arr.append(negative_pooled_prompt_embeds)
|
|
negative_pooled_prompt_embeds = None
|
|
prompt_embeds_text_only_arr.append(prompt_embeds_text_only)
|
|
prompt_embeds_text_only = None
|
|
prompt_embeds_arr.append(prompt_embeds)
|
|
prompt_embeds = None
|
|
pooled_prompt_embeds_text_only_arr.append(pooled_prompt_embeds_text_only)
|
|
pooled_prompt_embeds_text_only = None
|
|
|
|
emb_dict={"negative_prompt_embeds_arr":negative_prompt_embeds_arr,
|
|
"negative_prompt_embeds":negative_prompt_embeds,
|
|
"negative_pooled_prompt_embeds_arr":negative_pooled_prompt_embeds_arr,
|
|
"negative_pooled_prompt_embeds":negative_pooled_prompt_embeds,
|
|
"prompt_embeds_text_only_arr":prompt_embeds_text_only_arr,
|
|
"prompt_embeds_text_only":prompt_embeds_text_only,
|
|
"prompt_embeds_arr":prompt_embeds_arr,
|
|
"prompt_embeds":prompt_embeds,
|
|
"pooled_prompt_embeds":pooled_prompt_embeds,
|
|
"pooled_prompt_embeds_text_only_arr":pooled_prompt_embeds_text_only_arr,
|
|
"pooled_prompt_embeds_text_only":pooled_prompt_embeds_text_only,
|
|
"pooled_prompt_embeds_arr":pooled_prompt_embeds_arr,
|
|
"batch_size":batch_size,
|
|
}
|
|
return emb_dict
|
|
|
|
|
|
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
|
def encode_prompt_with_trigger_word_v2(clip,model_,
|
|
prompt: str,
|
|
prompt_2 = None,
|
|
num_id_images= 1,
|
|
device = None,
|
|
prompt_embeds = None,
|
|
negative_prompt = None,
|
|
negative_prompt_2 = None,
|
|
negative_prompt_embeds= None,
|
|
pooled_prompt_embeds= None,
|
|
negative_pooled_prompt_embeds= None,
|
|
num_images_per_prompt=1,
|
|
class_tokens_mask = None,
|
|
nc_flag: bool = False,
|
|
trigger_word="img",
|
|
unet_dtype=torch.float16,
|
|
clip_skip=None,
|
|
lora_scale=None,
|
|
do_classifier_free_guidance=True,
|
|
force_zeros_for_empty_prompts=False,
|
|
num_tokens=2,
|
|
|
|
):
|
|
|
|
device = device
|
|
tokenizer_2=model_.tokenizer_2
|
|
tokenizer=model_.tokenizer
|
|
#text_encoders=clip.encode_from_tokens
|
|
|
|
if prompt is not None and isinstance(prompt, str):
|
|
batch_size = 1
|
|
elif prompt is not None and isinstance(prompt, list):
|
|
batch_size = len(prompt)
|
|
else:
|
|
batch_size = prompt_embeds.shape[0]
|
|
|
|
# Find the token id of the trigger word
|
|
image_token_id = tokenizer_2.convert_tokens_to_ids(trigger_word)
|
|
|
|
# Define tokenizers and text encoders
|
|
tokenizers = [tokenizer, tokenizer_2] if tokenizer is not None else [tokenizer_2]
|
|
# text_encoders = (
|
|
# [self.text_encoder, self.text_encoder_2] if self.text_encoder is not None else [self.text_encoder_2]
|
|
# )
|
|
|
|
if prompt_embeds is None:
|
|
prompt_2 = prompt_2 or prompt
|
|
prompt_embeds_list = []
|
|
prompts = [prompt, prompt_2]
|
|
|
|
for prompt, tokenizer,text_en in zip(prompts, tokenizers,[clip.cond_stage_model.clip_l.encode_token_weights,clip.cond_stage_model.clip_g.encode_token_weights]):
|
|
# if isinstance(self, TextualInversionLoaderMixin):
|
|
# prompt = self.maybe_convert_prompt(prompt, tokenizer)
|
|
|
|
input_ids = tokenizer.encode(prompt) # TODO: batch encode
|
|
clean_index = 0
|
|
clean_input_ids = []
|
|
class_token_index = []
|
|
# Find out the corresponding class word token based on the newly added trigger word token
|
|
for i, token_id in enumerate(input_ids):
|
|
if token_id == image_token_id:
|
|
class_token_index.append(clean_index - 1)
|
|
else:
|
|
clean_input_ids.append(token_id)
|
|
clean_index += 1
|
|
if nc_flag:
|
|
return None, None, None,None, None
|
|
if len(class_token_index) > 1:
|
|
raise ValueError(
|
|
f"PhotoMaker currently does not support multiple trigger words in a single prompt.\
|
|
Trigger word: {trigger_word}, Prompt: {prompt}."
|
|
)
|
|
elif len(class_token_index) == 0 and not nc_flag:
|
|
raise ValueError(
|
|
f"PhotoMaker currently does not support multiple trigger words in a single prompt.\
|
|
Trigger word: {trigger_word}, Prompt: {prompt}."
|
|
)
|
|
class_token_index = class_token_index[0]
|
|
|
|
# Expand the class word token and corresponding mask
|
|
class_token = clean_input_ids[class_token_index]
|
|
clean_input_ids = clean_input_ids[:class_token_index] + [class_token] * num_id_images * num_tokens + clean_input_ids[class_token_index + 1:]
|
|
# Truncation or padding
|
|
max_len = tokenizer.model_max_length
|
|
if len(clean_input_ids) > max_len:
|
|
clean_input_ids = clean_input_ids[:max_len]
|
|
else:
|
|
clean_input_ids = clean_input_ids + [tokenizer.pad_token_id] * (
|
|
max_len - len(clean_input_ids)
|
|
)
|
|
|
|
#class_tokens_mask = [True if class_token_index <= i < class_token_index+num_id_images else False for i in range(len(clean_input_ids))]
|
|
class_tokens_mask = [True if class_token_index <= i < class_token_index + (num_id_images * num_tokens) else False for i in range(len(clean_input_ids))]
|
|
#print(clean_input_ids)
|
|
#print(max_len)
|
|
|
|
clean_input_ids=clean_input_ids[:77] if len(clean_input_ids) > 77 else clean_input_ids + [0]*(77 - len(clean_input_ids))
|
|
|
|
clean_input_ids_1=[(i,1.0) for i in clean_input_ids]
|
|
clean_input_ids_2=[(i,1.0) for i in clean_input_ids if i==clean_input_ids[0] or i==49407 ]
|
|
clean_input_ids_2=clean_input_ids_2[:77] if len(clean_input_ids_2) > 77 else clean_input_ids_2 + [(0,1.0)]*(77 - len(clean_input_ids_2))
|
|
clean_input_ids_list=[clean_input_ids_1,clean_input_ids_2]
|
|
#print(clean_input_ids)
|
|
#clean_input_ids_ = torch.tensor(clean_input_ids, dtype=torch.long).unsqueeze(0)
|
|
class_tokens_mask = torch.tensor(class_tokens_mask, dtype=torch.bool).unsqueeze(0)
|
|
|
|
|
|
prompt_embeds, pooled_prompt_embeds = text_en(clean_input_ids_list) # print(prompt_embeds.shape,pooled_prompt_embeds.shape)torch.Size([1, 154, 768]) torch.Size([1, 768]) torch.Size([1, 154, 1280]) torch.Size([1, 1280])
|
|
|
|
|
|
prompt_embeds_list.append((prompt_embeds,pooled_prompt_embeds))
|
|
|
|
#prompt_embeds = torch.concat(prompt_embeds_list, dim=-1)
|
|
#prompt_embeds=prompt_embeds_list[1]
|
|
pooled_prompt_embeds=prompt_embeds_list[1][1]
|
|
# cut_to = min(prompt_embeds_list[0][0].shape[1], prompt_embeds_list[1][0].shape[1])
|
|
# print(cut_to)
|
|
cut_to=77
|
|
prompt_embeds= torch.cat([prompt_embeds_list[0][0][:,:cut_to], prompt_embeds_list[1][0][:,:cut_to]], dim=-1)
|
|
prompt_embeds = prompt_embeds.to(device=device,dtype=unet_dtype, )
|
|
class_tokens_mask = class_tokens_mask.to(device=device) # TODO: ignoring two-prompt case
|
|
# get unconditional embeddings for classifier free guidance
|
|
zero_out_negative_prompt = negative_prompt is None and force_zeros_for_empty_prompts
|
|
if do_classifier_free_guidance and negative_prompt_embeds is None and zero_out_negative_prompt:
|
|
negative_prompt_embeds = torch.zeros_like(prompt_embeds)
|
|
negative_pooled_prompt_embeds = torch.zeros_like(pooled_prompt_embeds)
|
|
elif do_classifier_free_guidance and negative_prompt_embeds is None:
|
|
negative_prompt = negative_prompt or ""
|
|
negative_prompt_2 = negative_prompt_2 or negative_prompt
|
|
|
|
# normalize str to list
|
|
#negative_prompt = batch_size * [negative_prompt] if isinstance(negative_prompt, str) else negative_prompt
|
|
negative_prompt = str(negative_prompt) if isinstance(negative_prompt, list) else negative_prompt
|
|
negative_prompt_2 = (
|
|
batch_size * [negative_prompt_2] if isinstance(negative_prompt_2, str) else negative_prompt_2
|
|
)
|
|
|
|
uncond_tokens: List[str]
|
|
if prompt is not None and type(prompt) is not type(negative_prompt):
|
|
raise TypeError(
|
|
f"`negative_prompt` should be the same type to `prompt`, but got {type(negative_prompt)} !="
|
|
f" {type(prompt)}."
|
|
)
|
|
# elif batch_size != len(negative_prompt):
|
|
# raise ValueError(
|
|
# f"`negative_prompt`: {negative_prompt} has batch size {len(negative_prompt)}, but `prompt`:"
|
|
# f" {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches"
|
|
# " the batch size of `prompt`."
|
|
# )
|
|
else:
|
|
uncond_tokens = [negative_prompt, negative_prompt_2]
|
|
# ng 没用上
|
|
negative_prompt_embeds_list = []
|
|
for negative_prompt, tokenizer, text_encoder in zip(uncond_tokens, tokenizers, [clip.cond_stage_model.clip_l.encode_token_weights,clip.cond_stage_model.clip_g.encode_token_weights]):
|
|
# if isinstance(self, TextualInversionLoaderMixin):
|
|
# negative_prompt = self.maybe_convert_prompt(negative_prompt, tokenizer)
|
|
|
|
max_length = prompt_embeds.shape[1]
|
|
uncond_input=tokenizer.encode(negative_prompt)
|
|
# uncond_input = tokenizer(
|
|
# negative_prompt,
|
|
# padding="max_length",
|
|
# max_length=max_length,
|
|
# truncation=True,
|
|
# return_tensors="pt",
|
|
# )
|
|
# negative_prompt_embeds = text_encoder(
|
|
# uncond_input.input_ids.to(device),
|
|
# output_hidden_states=True,
|
|
# )
|
|
clean_input_ids_n=uncond_input[:77] if len(uncond_input) > 77 else uncond_input + [0]*(77 - len(uncond_input))
|
|
|
|
clean_input_ids_1_=[(i,1.0) for i in clean_input_ids_n]
|
|
clean_input_ids_2_=[(i,1.0) for i in clean_input_ids_n if i==clean_input_ids_n[0] or i==49407 ]
|
|
clean_input_ids_2_=clean_input_ids_2_[:77] if len(clean_input_ids_2_) > 77 else clean_input_ids_2_ + [(0,1.0)]*(77 - len(clean_input_ids_2_))
|
|
clean_input_ids_list_n=[clean_input_ids_1_,clean_input_ids_2_]
|
|
prompt_embeds_n, pooled_prompt_embeds_n = text_en(clean_input_ids_list_n)
|
|
negative_prompt_embeds_list.append((prompt_embeds_n,pooled_prompt_embeds_n))
|
|
|
|
|
|
|
|
# We are only ALWAYS interested in the pooled output of the final text encoder
|
|
# negative_pooled_prompt_embeds = negative_prompt_embeds[0]
|
|
# negative_prompt_embeds = negative_prompt_embeds.hidden_states[-2]
|
|
|
|
# negative_prompt_embeds_list.append(negative_prompt_embeds)
|
|
|
|
negative_pooled_prompt_embeds=negative_prompt_embeds_list[1][1]
|
|
#negative_prompt_embeds = torch.concat(negative_prompt_embeds_list, dim=-1)
|
|
cut_to=77
|
|
negative_prompt_embeds= torch.cat([negative_prompt_embeds_list[0][0][:,:cut_to], negative_prompt_embeds_list[1][0][:,:cut_to]], dim=-1)
|
|
negative_prompt_embeds = negative_prompt_embeds.to(device=device,dtype=unet_dtype, )
|
|
|
|
|
|
bs_embed, seq_len, _ = prompt_embeds.shape
|
|
|
|
if do_classifier_free_guidance:
|
|
# duplicate unconditional embeddings for each generation per prompt, using mps friendly method
|
|
seq_len = negative_prompt_embeds.shape[1]
|
|
|
|
|
|
negative_prompt_embeds = negative_prompt_embeds.to(dtype=unet_dtype, device=device)
|
|
|
|
negative_prompt_embeds = negative_prompt_embeds.repeat(1, num_images_per_prompt, 1)
|
|
negative_prompt_embeds = negative_prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1)
|
|
|
|
pooled_prompt_embeds = pooled_prompt_embeds.repeat(1, num_images_per_prompt).view(
|
|
bs_embed * num_images_per_prompt, -1
|
|
)
|
|
if do_classifier_free_guidance:
|
|
negative_pooled_prompt_embeds = negative_pooled_prompt_embeds.repeat(1, num_images_per_prompt).view(
|
|
bs_embed * num_images_per_prompt, -1
|
|
)
|
|
|
|
|
|
return prompt_embeds, negative_prompt_embeds,pooled_prompt_embeds, negative_pooled_prompt_embeds, class_tokens_mask
|
|
|
|
|
|
def photomaker_clip_v2(clip,model_,prompt_list,negative_prompt,input_id_images,id_embeds,trigger_word="img",num_images_per_prompt=1,nc_flag=False,prompt_2=None,prompt_embeds=None,pooled_prompt_embeds=None,class_tokens_mask=None,prompt_embeds_text_only=None,negative_pooled_prompt_embeds=None):
|
|
id_encoder=model_.id_encoder
|
|
id_image_processor=model_.id_image_processor
|
|
|
|
tokenizer=model_.tokenizer_2
|
|
batch_size=len(prompt_list)
|
|
|
|
num_id_images = len(input_id_images) #双角色时,须确保输入的图片是列表,且跟prompt对应上
|
|
prompt_arr = prompt_list
|
|
negative_prompt_embeds_arr = []
|
|
prompt_embeds_text_only_arr = []
|
|
prompt_embeds_arr = []
|
|
latents_arr = []
|
|
add_time_ids_arr = []
|
|
negative_pooled_prompt_embeds_arr = []
|
|
pooled_prompt_embeds_text_only_arr = []
|
|
pooled_prompt_embeds_arr = []
|
|
for prompt in prompt_arr:
|
|
(
|
|
prompt_embeds,
|
|
_,
|
|
pooled_prompt_embeds,
|
|
_,
|
|
class_tokens_mask
|
|
) = encode_prompt_with_trigger_word_v2(clip,model_,
|
|
prompt=prompt,
|
|
prompt_2=prompt_2,
|
|
device=device,
|
|
num_id_images=num_id_images,
|
|
class_tokens_mask=class_tokens_mask,
|
|
num_images_per_prompt=num_images_per_prompt,
|
|
do_classifier_free_guidance=True,
|
|
negative_prompt=negative_prompt,
|
|
negative_prompt_2=None,
|
|
prompt_embeds=None,
|
|
negative_prompt_embeds=None,
|
|
pooled_prompt_embeds=None,
|
|
negative_pooled_prompt_embeds=None,
|
|
nc_flag=nc_flag,
|
|
)
|
|
|
|
# 4. Encode input prompt without the trigger word for delayed conditioning
|
|
# encode, remove trigger word token, then decode
|
|
tokens_text_only = tokenizer.encode(prompt, add_special_tokens=False)
|
|
trigger_word_token = tokenizer.convert_tokens_to_ids(trigger_word)
|
|
if not nc_flag:
|
|
tokens_text_only.remove(trigger_word_token)
|
|
prompt_text_only = tokenizer.decode(tokens_text_only, add_special_tokens=False)
|
|
#print(prompt_text_only)
|
|
(
|
|
prompt_embeds_text_only,
|
|
negative_prompt_embeds,
|
|
pooled_prompt_embeds_text_only, # TODO: replace the pooled_prompt_embeds with text only prompt
|
|
negative_pooled_prompt_embeds,
|
|
) = cf_clip_single(clip,
|
|
prompt=prompt_text_only,
|
|
prompt_2=prompt_2,
|
|
device=device,
|
|
num_images_per_prompt=num_images_per_prompt,
|
|
do_classifier_free_guidance=True,
|
|
negative_prompt=negative_prompt,
|
|
negative_prompt_2=None,
|
|
prompt_embeds=prompt_embeds_text_only,
|
|
negative_prompt_embeds=None,
|
|
pooled_prompt_embeds=None,
|
|
negative_pooled_prompt_embeds=negative_pooled_prompt_embeds,
|
|
)
|
|
|
|
# 5. Prepare the input ID images
|
|
dtype = next(id_encoder.parameters()).dtype
|
|
if not isinstance(input_id_images[0], torch.Tensor):
|
|
id_pixel_values = id_image_processor(input_id_images, return_tensors="pt").pixel_values
|
|
|
|
id_pixel_values = id_pixel_values.unsqueeze(0).to(device=device, dtype=dtype) # TODO: multiple prompts
|
|
|
|
# duplicate text embeddings for each generation per prompt, using mps friendly method
|
|
if not nc_flag:
|
|
if id_embeds is not None:
|
|
id_embeds = id_embeds.unsqueeze(0).to(device=device, dtype=dtype)
|
|
|
|
prompt_embeds = id_encoder(id_pixel_values, prompt_embeds, class_tokens_mask, id_embeds) #torch.Size([1, 1, 3, 224, 224]) torch.Size([1, 77, 2048]) torch.Size([1, 77]) torch.Size([1, 512])
|
|
else:
|
|
prompt_embeds = id_encoder(id_pixel_values, prompt_embeds, class_tokens_mask)
|
|
# 6. Get the update text embedding with the stacked ID embedding
|
|
|
|
bs_embed, seq_len, _ = prompt_embeds.shape
|
|
prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1)
|
|
prompt_embeds = prompt_embeds.view(bs_embed * num_images_per_prompt, seq_len, -1)
|
|
pooled_prompt_embeds = pooled_prompt_embeds.repeat(1, num_images_per_prompt).view(
|
|
bs_embed * num_images_per_prompt, -1
|
|
)
|
|
pooled_prompt_embeds_arr.append(pooled_prompt_embeds)
|
|
pooled_prompt_embeds = None
|
|
|
|
negative_prompt_embeds_arr.append(negative_prompt_embeds)
|
|
negative_prompt_embeds = None
|
|
negative_pooled_prompt_embeds_arr.append(negative_pooled_prompt_embeds)
|
|
negative_pooled_prompt_embeds = None
|
|
prompt_embeds_text_only_arr.append(prompt_embeds_text_only)
|
|
prompt_embeds_text_only = None
|
|
prompt_embeds_arr.append(prompt_embeds)
|
|
prompt_embeds = None
|
|
pooled_prompt_embeds_text_only_arr.append(pooled_prompt_embeds_text_only)
|
|
pooled_prompt_embeds_text_only = None
|
|
|
|
emb_dict={"negative_prompt_embeds_arr":negative_prompt_embeds_arr,
|
|
"negative_prompt_embeds":negative_prompt_embeds,
|
|
"negative_pooled_prompt_embeds_arr":negative_pooled_prompt_embeds_arr,
|
|
"negative_pooled_prompt_embeds":negative_pooled_prompt_embeds,
|
|
"prompt_embeds_text_only_arr":prompt_embeds_text_only_arr,
|
|
"prompt_embeds_text_only":prompt_embeds_text_only,
|
|
"prompt_embeds_arr":prompt_embeds_arr,
|
|
"prompt_embeds":prompt_embeds,
|
|
"pooled_prompt_embeds":pooled_prompt_embeds,
|
|
"pooled_prompt_embeds_text_only_arr":pooled_prompt_embeds_text_only_arr,
|
|
"pooled_prompt_embeds_text_only":pooled_prompt_embeds_text_only,
|
|
"pooled_prompt_embeds_arr":pooled_prompt_embeds_arr,
|
|
"batch_size":batch_size,
|
|
}
|
|
return emb_dict
|
|
|
|
def Loader_UNO(extra_info,offload,quantize_mode,save_quantezed,lora_rank):
|
|
from .UNO.uno.flux.pipeline import UNOPipeline
|
|
from accelerate import Accelerator
|
|
accelerator = Accelerator()
|
|
device= accelerator.device
|
|
if extra_info is not None:
|
|
model_path = extra_info["unet_path"]
|
|
lora_list=[i for i in [extra_info.get("lora1_path"),extra_info.get("lora2_path")] if i is not None]
|
|
if lora_list:
|
|
lora_ckpt_path=lora_list[0]
|
|
if "dit_lora" not in lora_ckpt_path.lower():
|
|
raise ValueError("lora_ckpt_path must be a dit_lora checkpoint")
|
|
only_lora=True
|
|
else:
|
|
only_lora=False
|
|
|
|
|
|
use_fp8=True if quantize_mode=="fp8" else False
|
|
|
|
if "schnell" in model_path:
|
|
model_type="flux-schnell"
|
|
else:
|
|
model_type="flux-dev"
|
|
|
|
|
|
if "schnell" in model_path:
|
|
model_type="flux-schnell"
|
|
else:
|
|
model_type="flux-dev"
|
|
|
|
if only_lora :
|
|
from .UNO.uno.flux.util import load_flow_model_only_lora
|
|
model_ = load_flow_model_only_lora(
|
|
model_type,model_path,lora_ckpt_path,use_fp8, device="cpu" if offload else device,save_quantezed=save_quantezed, lora_rank=lora_rank)
|
|
else:
|
|
from .UNO.uno.flux.util import load_flow_model
|
|
model_ = load_flow_model(model_type,model_path,use_fp8, device="cpu" if offload else device,save_quantezed=save_quantezed)
|
|
model = UNOPipeline(
|
|
model_,
|
|
device,
|
|
offload,
|
|
use_fp8,
|
|
)
|
|
else:
|
|
raise ValueError("must use EasyFunction_Lite node")
|
|
return model
|
|
|
|
def load_pipeline_realcustom(cf_model,realcustom_checkpoint):
|
|
from .RealCustom.models.unet_2d_condition_custom import UNet2DConditionModel as UNet2DConditionModelDiffusers
|
|
if "shallow" in realcustom_checkpoint.lower():
|
|
unet_config_path=os.path.join(cur_path, "RealCustom/configs/realcustom_sigdino_highres_shallow.json")
|
|
else:
|
|
unet_config_path=os.path.join(cur_path, "RealCustom/configs/realcustom_sigdino_highres.json")
|
|
with open(unet_config_path, 'r') as f:
|
|
unet_config = json.load(f)
|
|
|
|
# Settings for image encoder
|
|
vision_model_config = unet_config.pop("vision_model_config", None)
|
|
vision_model_config_ar = vision_model_config.pop("vision_model_config", None)
|
|
|
|
|
|
|
|
unet_type = unet_config.pop("type", None)
|
|
unet_model = UNet2DConditionModelDiffusers(**unet_config)
|
|
unet_model.eval()
|
|
|
|
#cf_state_dict=torch.load(unet_checkpoint,weights_only=False, map_location="cpu")
|
|
cf_state_dict = cf_model.model.diffusion_model.state_dict()
|
|
unet_state_dict = cf_model.model.model_config.process_unet_state_dict_for_saving(cf_state_dict)
|
|
|
|
from diffusers.pipelines.stable_diffusion.convert_from_ckpt import convert_ldm_unet_checkpoint
|
|
cf_state_dict = convert_ldm_unet_checkpoint(unet_state_dict, unet_model.config)
|
|
unet_model.load_state_dict(cf_state_dict, strict=False)
|
|
|
|
realcustom_DICT=torch.load(realcustom_checkpoint,weights_only=False, map_location="cpu")
|
|
unet_model.load_state_dict(realcustom_DICT, strict=False)
|
|
unet_model = torch.compile(unet_model, disable=True)# CHECK THIS
|
|
|
|
del cf_state_dict,realcustom_DICT
|
|
gc.collect()
|
|
torch.cuda.empty_cache()
|
|
print("loading unet base model finished.")
|
|
|
|
return unet_model,vision_model_config_ar,unet_type
|
|
|
|
|
|
|
|
|
|
def load_clip_clipvsion(clip_paths,token_paths,dino_path,siglip_path,vision_model_config):
|
|
from .RealCustom.models.text import TextModel
|
|
from transformers import CLIPTextModel,CLIPTextConfig
|
|
from .RealCustom.utils import instantiate_from_config
|
|
|
|
# text_encoder=CLIPTextModel.from_pretrained(clip_paths[0], local_files_only=True)
|
|
# text_encoder_2=CLIPTextModel.from_pretrained(clip_paths[1], local_files_only=True)
|
|
|
|
from safetensors.torch import load_file
|
|
text_encoder_config=CLIPTextConfig.from_pretrained(token_paths[0], local_files_only=True)
|
|
text_encoder = CLIPTextModel(text_encoder_config)
|
|
text_encoder_sd_1 = load_file(clip_paths[0])
|
|
text_encoder.load_state_dict(text_encoder_sd_1, strict=False)
|
|
|
|
|
|
text_encoder_config_2=CLIPTextConfig.from_pretrained(token_paths[1], local_files_only=True)
|
|
text_encoder_2 = CLIPTextModel(text_encoder_config_2)
|
|
text_encoder_sd_2 = load_file(clip_paths[1])
|
|
text_encoder_2.load_state_dict(text_encoder_sd_2, strict=False)
|
|
del text_encoder_sd_2,text_encoder_sd_1
|
|
gc.collect()
|
|
|
|
|
|
text_model = TextModel([text_encoder,text_encoder_2],token_paths,["penultimate_nonorm"])
|
|
text_model.eval()
|
|
|
|
siglip_path = os.path.normpath(siglip_path).replace("\\", "/")
|
|
dino_path = os.path.normpath(dino_path).replace("\\", "/")
|
|
|
|
vision_model_config["params"]["siglip_path"] = siglip_path
|
|
vision_model_config["params"]["dino_path"] = dino_path
|
|
|
|
vision_model = instantiate_from_config(vision_model_config)
|
|
vision_model = vision_model.eval().to(device)
|
|
|
|
print("loading image model and text_model finished.")
|
|
return text_model,vision_model
|
|
|
|
|
|
|
|
|
|
def realcustom_clip_emb(text_model,vision_model,vae_config,vae_downsample_factor,positive_prompt,negative_prompt,positive_image,target_phrase,width,height,device,samples_per_prompt,guidance_weight=7.5):
|
|
from .RealCustom.inference.inference_utils import find_phrase_positions_in_text
|
|
import torchvision
|
|
with torch.no_grad():
|
|
image_metadata_validate = torch.tensor(
|
|
data=[
|
|
width, # original_height
|
|
height, # original_width
|
|
0, # coordinate top
|
|
0, # coordinate left
|
|
width, # target_height
|
|
height, # target_width
|
|
],
|
|
device=device,
|
|
dtype=torch.float32
|
|
).view(1, -1).repeat(samples_per_prompt, 1)
|
|
|
|
if guidance_weight != 1:
|
|
text_negative_output = text_model(negative_prompt)
|
|
# Compute target phrases
|
|
target_token = torch.zeros(1, 77).to(device)
|
|
|
|
positions = find_phrase_positions_in_text(positive_prompt, target_phrase)
|
|
for position in positions:
|
|
prompt_before = positive_prompt[:position] # NOTE We do not need -1 here because the SDXL text encoder does not encode the trailing space.
|
|
prompt_include = positive_prompt[:position+len(target_phrase)]
|
|
#print("prompt before: ", prompt_before, ", prompt_include: ", prompt_include)
|
|
prompt_before_length = text_model.get_vaild_token_length(prompt_before) + 1
|
|
prompt_include_length = text_model.get_vaild_token_length(prompt_include) + 1
|
|
# print("prompt_before_length: ", prompt_before_length, ", prompt_include_length: ", prompt_include_length)
|
|
target_token[:, prompt_before_length:prompt_include_length] = 1
|
|
|
|
# Text used for progress bar
|
|
pbar_text = positive_prompt[:40]
|
|
|
|
# Compute text embeddings
|
|
text_positive_output = text_model(positive_prompt)
|
|
text_positive_embeddings = text_positive_output.embeddings.repeat_interleave(samples_per_prompt, dim=0)
|
|
text_positive_pooled = text_positive_output.pooled[-1].repeat_interleave(samples_per_prompt, dim=0)
|
|
if guidance_weight != 1:
|
|
text_negative_embeddings = text_negative_output.embeddings.repeat_interleave(samples_per_prompt, dim=0)
|
|
text_negative_pooled = text_negative_output.pooled[-1].repeat_interleave(samples_per_prompt, dim=0)
|
|
|
|
# Compute image embeddings
|
|
# positive_image = Image.open(positive_promt_image_path).convert("RGB")
|
|
positive_image = torchvision.transforms.ToTensor()(positive_image)
|
|
|
|
positive_image = positive_image.unsqueeze(0).repeat_interleave(samples_per_prompt, dim=0)
|
|
positive_image = torch.nn.functional.interpolate(
|
|
positive_image,
|
|
size=(768, 768),
|
|
mode="bilinear",
|
|
align_corners=False
|
|
)
|
|
negative_image = torch.zeros_like(positive_image)
|
|
#print("positive_image:",positive_image.size(), negative_image.size()) #torch.Size([1, 3, 768, 768]) torch.Size([1, 3, 768, 768])
|
|
positive_image = positive_image.to(device)
|
|
negative_image = negative_image.to(device)
|
|
|
|
positive_image_dict = {"image_ref": positive_image}
|
|
positive_image_output = vision_model(positive_image_dict, device=device)
|
|
#positive_image_output = daul_encoder(vision_model,positive_image_dict)
|
|
|
|
negative_image_dict = {"image_ref": negative_image}
|
|
negative_image_output = vision_model(negative_image_dict, device=device)
|
|
#negative_image_output=daul_encoder(vision_model,negative_image_dict)
|
|
# Initialize latent with input latent + noise (i2i) / pure noise (t2i)
|
|
# latent = torch.randn(
|
|
# size=[
|
|
# samples_per_prompt,
|
|
# vae_config["latent_channels"],
|
|
# height // vae_downsample_factor,
|
|
# width // vae_downsample_factor
|
|
# ],
|
|
# device=device,
|
|
# generator=torch.Generator(device).manual_seed(seed))
|
|
target_h = (height // vae_downsample_factor) // 2
|
|
target_w = (width // vae_downsample_factor) // 2
|
|
|
|
emb_dict={
|
|
"text_positive_embeddings":text_positive_embeddings.to(device),
|
|
"text_positive_pooled":text_positive_pooled.to(device),
|
|
"image_metadata_validate":image_metadata_validate.to(device),
|
|
"positive_image_output":positive_image_output,
|
|
"text_negative_embeddings":text_negative_embeddings.to(device),
|
|
"text_negative_pooled":text_negative_pooled.to(device),
|
|
"negative_image_output":negative_image_output,
|
|
}
|
|
|
|
latent_dict={
|
|
"vae_config":vae_config,
|
|
"vae_downsample_factor":vae_downsample_factor,
|
|
"target_h":target_h,
|
|
"target_w":target_w,
|
|
"pbar_text":pbar_text,
|
|
"guidance_weight":guidance_weight,
|
|
"target_token":target_token,
|
|
|
|
}
|
|
|
|
return emb_dict,latent_dict
|
|
|
|
|
|
def realcustom_infer(unet_model,sample_steps,mask_reused_step,emb_dict,latent_dict,mask_scope,mask_strategy,guidance_weight,height,width,seed,
|
|
device,schedule_type="squared_linear",schedule_shift_snr=1.0,scheduler_type="ddim",unet_prediction="epsilon"):
|
|
from .RealCustom.schedulers.ddim import DDIMScheduler
|
|
from .RealCustom.schedulers.dpm_s import DPMSolverSingleStepScheduler
|
|
from .RealCustom.schedulers.utils import get_betas
|
|
from .RealCustom.inference.mask_generation import mask_generation
|
|
from .RealCustom.inference.inference_utils import classifier_free_guidance_image_prompt_cascade
|
|
from torchvision.transforms.functional import to_pil_image
|
|
from tqdm import tqdm
|
|
# Initialize ddim scheduler
|
|
ddim_train_steps = 1000
|
|
ddim_betas = get_betas(name=schedule_type, num_steps=ddim_train_steps, shift_snr=schedule_shift_snr, terminal_pure_noise=False)
|
|
scheduler_class = DPMSolverSingleStepScheduler if scheduler_type == 'dpm' else DDIMScheduler
|
|
scheduler = scheduler_class(betas=ddim_betas, num_train_timesteps=ddim_train_steps, num_inference_timesteps=sample_steps, device=device)
|
|
infer_timesteps = scheduler.timesteps
|
|
#print(infer_timesteps)
|
|
# Real Reverse diffusion process.
|
|
text2image_crossmap_2d_all_timesteps_list = []
|
|
current_step = 0
|
|
pbar_text = latent_dict.get("pbar_text")
|
|
#guidance_weight = latent_dict.get("guidance_weight")
|
|
unet_model.to(device)
|
|
latent=latent_dict.get("latent")
|
|
latent = torch.randn(
|
|
size=[
|
|
1,
|
|
latent_dict.get("vae_config")["latent_channels"],
|
|
height // latent_dict.get("vae_downsample_factor"),
|
|
width // latent_dict.get("vae_downsample_factor")
|
|
],
|
|
device=device,
|
|
generator=torch.Generator(device).manual_seed(seed))
|
|
|
|
|
|
with torch.no_grad():
|
|
for timestep in tqdm(iterable=infer_timesteps, desc=f"[{pbar_text}]", dynamic_ncols=True):
|
|
|
|
if current_step < mask_reused_step:
|
|
pred_cond, pred_cond_dict = unet_model(
|
|
sample=latent,
|
|
timestep=timestep,
|
|
encoder_hidden_states=emb_dict.get("text_positive_embeddings"),
|
|
encoder_attention_mask=None,
|
|
added_cond_kwargs=dict(
|
|
text_embeds=emb_dict.get("text_positive_pooled"),
|
|
time_ids=emb_dict.get("image_metadata_validate")
|
|
),
|
|
vision_input_dict=None,
|
|
vision_guided_mask=None,
|
|
return_as_origin=False,
|
|
return_text2image_mask=True,
|
|
)
|
|
|
|
crossmap_2d_avg = mask_generation(
|
|
crossmap_2d_list=pred_cond_dict.get("text2image_crossmap_2d",[]), selfmap_2d_list=pred_cond_dict.get("self_attention_map", []),
|
|
target_token=latent_dict.get("target_token"), mask_scope=mask_scope,
|
|
mask_target_h=latent_dict.get("target_h"), mask_target_w=latent_dict.get("target_w"), mask_mode=mask_strategy,
|
|
)
|
|
else:
|
|
# using previous step's mask
|
|
crossmap_2d_avg = text2image_crossmap_2d_all_timesteps_list[-1].squeeze(1)
|
|
if crossmap_2d_avg.dim() == 5: # Means that each layer uses a separate mask weight.
|
|
text2image_crossmap_2d_all_timesteps_list.append(crossmap_2d_avg.mean(dim=2).unsqueeze(1))
|
|
else:
|
|
text2image_crossmap_2d_all_timesteps_list.append(crossmap_2d_avg.unsqueeze(1))
|
|
|
|
pred_cond, pred_cond_dict = unet_model(
|
|
sample=latent,
|
|
timestep=timestep,
|
|
encoder_hidden_states=emb_dict.get("text_positive_embeddings"),
|
|
encoder_attention_mask=None,
|
|
added_cond_kwargs=dict(
|
|
text_embeds=emb_dict.get("text_positive_pooled"),
|
|
time_ids=emb_dict.get("image_metadata_validate")
|
|
),
|
|
vision_input_dict=emb_dict.get("positive_image_output"),
|
|
vision_guided_mask=crossmap_2d_avg,
|
|
return_as_origin=False,
|
|
return_text2image_mask=True,
|
|
multiple_reference_image=False
|
|
)
|
|
|
|
crossmap_2d_avg_neg = crossmap_2d_avg.mean(dim=1, keepdim=True)
|
|
pred_negative, pred_negative_dict = unet_model(
|
|
sample=latent,
|
|
timestep=timestep,
|
|
encoder_hidden_states=emb_dict.get("text_negative_embeddings"),
|
|
encoder_attention_mask=None,
|
|
added_cond_kwargs=dict(
|
|
text_embeds=emb_dict.get("text_negative_pooled"),
|
|
time_ids=emb_dict.get("image_metadata_validate")
|
|
),
|
|
vision_input_dict=emb_dict.get("negative_image_output"),
|
|
vision_guided_mask=crossmap_2d_avg,
|
|
return_as_origin=False,
|
|
return_text2image_mask=True,
|
|
multiple_reference_image=False
|
|
)
|
|
|
|
pred = classifier_free_guidance_image_prompt_cascade(
|
|
pred_t_cond=None, pred_ti_cond=pred_cond, pred_uncond=pred_negative,
|
|
guidance_weight_t=guidance_weight, guidance_weight_i=guidance_weight,
|
|
guidance_stdev_rescale_factor=0, cfg_rescale_mode="naive_global_direct"
|
|
)
|
|
step = scheduler.step(
|
|
model_output=pred,
|
|
model_output_type=unet_prediction,
|
|
timestep=timestep,
|
|
sample=latent,
|
|
)
|
|
|
|
latent = step.prev_sample
|
|
|
|
current_step += 1
|
|
|
|
sample=step.pred_original_sample/0.13025
|
|
|
|
return sample
|
|
|
|
|
|
def load_realcustom_vae(vae,device):
|
|
from .RealCustom.models.vae import AutoencoderKL
|
|
from diffusers.pipelines.stable_diffusion.convert_from_ckpt import convert_ldm_vae_checkpoint
|
|
|
|
vae_config_path = os.path.join(cur_path, "local_repo/vae/config.json")
|
|
# Initialize vae model
|
|
with open(vae_config_path, 'r') as vae_config_file:
|
|
vae_config = json.load(vae_config_file)
|
|
vae_downsample_factor = 2 ** (len(vae_config["block_out_channels"]) - 1) # 2 ** 3 = 8
|
|
vae_model = AutoencoderKL(**vae_config)
|
|
vae_model.eval().to(device)
|
|
|
|
vae_state_dict=vae.get_sd()
|
|
#AE = AutoencoderKL.from_config(ae_config).to(device, torch.float16)
|
|
cf_state_dict = convert_ldm_vae_checkpoint(vae_state_dict, vae_model.config)
|
|
vae_model.load_state_dict(cf_state_dict, strict=False)
|
|
del cf_state_dict,vae_state_dict
|
|
|
|
|
|
#vae_decoder = torch.compile(lambda x: vae_model.decode(x / vae_model.scaling_factor).sample.clip(-1, 1), disable=True)
|
|
vae_encoder = torch.compile(lambda x: vae_model.encode(x).latent_dist.mode().mul_(vae_model.scaling_factor), disable=True)
|
|
print("loading vae finished.")
|
|
return vae_encoder,vae_downsample_factor,vae_config
|
|
|
|
|
|
|
|
def load_pipeline_instant_character(extra_info,ip_adapter_path,VAE,quantize_mode):
|
|
from .InstantCharacter.pipeline_wrapper import InstantCharacterFluxPipeline
|
|
from diffusers import BitsAndBytesConfig as DiffusersBitsAndBytesConfig,FluxTransformer2DModel
|
|
if extra_info.get("svdq_repo") is not None:
|
|
print("use svdq")
|
|
from nunchaku import NunchakuFluxTransformer2dModel
|
|
transformer = NunchakuFluxTransformer2dModel.from_pretrained(extra_info.get("svdq_repo"),offload=True)
|
|
try:
|
|
transformer.set_attention_impl("nunchaku-fp16")
|
|
except:
|
|
pass
|
|
vae=convert_cfvae2diffuser(VAE,use_flux=True)
|
|
pipe = InstantCharacterFluxPipeline.from_pretrained(os.path.join(cur_path,"config/FLUX.1-dev"),vae=vae,transformer=transformer,text_encoder=None,text_encoder_2=None, torch_dtype=torch.bfloat16)
|
|
elif extra_info.get("gguf_path") is not None:
|
|
print("use gguf quantization")
|
|
from diffusers import GGUFQuantizationConfig
|
|
transformer = FluxTransformer2DModel.from_single_file(
|
|
extra_info.get("gguf_path") ,
|
|
config=os.path.join(cur_path, "config/FLUX.1-dev/transformer"),
|
|
quantization_config=GGUFQuantizationConfig(compute_dtype=torch.bfloat16),
|
|
torch_dtype=torch.bfloat16,
|
|
)
|
|
vae=convert_cfvae2diffuser(VAE,use_flux=True)
|
|
pipe = InstantCharacterFluxPipeline.from_pretrained(os.path.join(cur_path,"config/FLUX.1-dev"),vae=vae,transformer=transformer,text_encoder=None,text_encoder_2=None, torch_dtype=torch.bfloat16)
|
|
elif extra_info.get("unet_path"):
|
|
|
|
if quantize_mode=="fp8":
|
|
transformer = FluxTransformer2DModel.from_single_file(extra_info.get("unet_path"), config=os.path.join(cur_path,"config/FLUX.1-dev/transformer/config.json"),
|
|
torch_dtype=torch.bfloat16)
|
|
else:
|
|
from safetensors.torch import load_file
|
|
t_state_dict=load_file(extra_info.get("unet_path"))
|
|
unet_config = FluxTransformer2DModel.load_config(os.path.join(cur_path,"config/FLUX.1-dev/transformer/config.json"))
|
|
transformer = FluxTransformer2DModel.from_config(unet_config).to(torch.bfloat16)
|
|
transformer.load_state_dict(t_state_dict, strict=False)
|
|
del t_state_dict
|
|
gc_cleanup()
|
|
vae=convert_cfvae2diffuser(VAE,use_flux=True)
|
|
pipe = InstantCharacterFluxPipeline.from_pretrained(os.path.join(cur_path,"config/FLUX.1-dev"),transformer=transformer,vae=vae,text_encoder=None,text_encoder_2=None, torch_dtype=torch.bfloat16)
|
|
else:
|
|
print(f"use {quantize_mode} quantization")
|
|
find_flux=[i for i in [extra_info.get("repo1_path") , extra_info.get("repo2_path")] if "flux" in i ]
|
|
if not find_flux:
|
|
raise ValueError("can not find flux repo")
|
|
if quantize_mode=="fp8":
|
|
transformer = FluxTransformer2DModel.from_pretrained(
|
|
find_flux[0],
|
|
subfolder="transformer",
|
|
quantization_config=DiffusersBitsAndBytesConfig(load_in_8bit=True,),
|
|
torch_dtype=torch.bfloat16,
|
|
)
|
|
|
|
elif quantize_mode=="nf4": #nf4
|
|
transformer = FluxTransformer2DModel.from_pretrained(
|
|
find_flux[0],
|
|
subfolder="transformer",
|
|
quantization_config=DiffusersBitsAndBytesConfig(
|
|
load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16
|
|
),
|
|
torch_dtype=torch.bfloat16,)
|
|
else:
|
|
transformer = FluxTransformer2DModel.from_pretrained(
|
|
find_flux[0],
|
|
subfolder="transformer",
|
|
torch_dtype=torch.bfloat16,
|
|
)
|
|
|
|
pipe = InstantCharacterFluxPipeline.from_pretrained(find_flux[0],transformer=transformer,text_encoder=None,text_encoder_2=None, torch_dtype=torch.bfloat16)
|
|
# if not cf_model.get("use_svdq"):
|
|
# pipe.enable_model_cpu_offload()
|
|
pipe.to(device)
|
|
pipe.init_adapter(
|
|
subject_ipadapter_cfg=dict(subject_ip_adapter_path=ip_adapter_path, nb_token=1024),
|
|
)
|
|
|
|
return pipe
|
|
|
|
def cf_flux_prompt_clip(cf_clip,prompt):
|
|
tokens = cf_clip.tokenize(prompt)
|
|
tokens["t5xxl"] = cf_clip.tokenize(prompt)["t5xxl"]
|
|
prompt_embeds = cf_clip.encode_from_tokens(tokens, return_pooled=True, return_dict=True).pop("cond")
|
|
tokens["l"] = cf_clip.tokenize(prompt)["l"]
|
|
pooled_prompt_embeds = cf_clip.encode_from_tokens(tokens, return_dict=True).pop("pooled_output")
|
|
prompt_embeds=prompt_embeds.to(device,torch.bfloat16)
|
|
pooled_prompt_embeds=pooled_prompt_embeds.to(device,torch.bfloat16)
|
|
text_ids = torch.zeros(prompt_embeds.shape[1], 3).to(device=device, dtype=torch.bfloat16)
|
|
return prompt_embeds,pooled_prompt_embeds,text_ids
|
|
|
|
|
|
def load_dual_clip(image_encoder_path,image_encoder_2_path,device,dtype):
|
|
from transformers import SiglipVisionModel, SiglipImageProcessor, AutoModel, AutoImageProcessor
|
|
# image encoder
|
|
print(f"=> loading image_encoder_1: {image_encoder_path}")
|
|
image_encoder = SiglipVisionModel.from_pretrained(image_encoder_path)
|
|
image_processor = SiglipImageProcessor.from_pretrained(image_encoder_path)
|
|
image_encoder.eval()
|
|
image_encoder.to(device, dtype=dtype)
|
|
|
|
|
|
# image encoder 2
|
|
print(f"=> loading image_encoder_2: {image_encoder_2_path}")
|
|
image_encoder_2 = AutoModel.from_pretrained(image_encoder_2_path)
|
|
image_processor_2 = AutoImageProcessor.from_pretrained(image_encoder_2_path)
|
|
image_encoder_2.eval()
|
|
image_encoder_2.to(device, dtype=dtype)
|
|
image_processor_2.crop_size = dict(height=384, width=384)
|
|
image_processor_2.size = dict(shortest_edge=384)
|
|
|
|
|
|
return image_encoder,image_processor,image_encoder_2,image_processor_2
|
|
|
|
|
|
|
|
@torch.inference_mode()
|
|
def instant_character_id_clip(subject_image,siglip_image_encoder,siglip_image_processor,dino_image_encoder_2,dino_image_processor_2,device,dtype):
|
|
|
|
|
|
from einops import rearrange
|
|
|
|
def encode_siglip_image_emb(siglip_image, device, dtype):
|
|
siglip_image = siglip_image.to(device, dtype=dtype)
|
|
res = siglip_image_encoder(siglip_image, output_hidden_states=True)
|
|
|
|
siglip_image_embeds = res.last_hidden_state
|
|
|
|
siglip_image_shallow_embeds = torch.cat([res.hidden_states[i] for i in [7, 13, 26]], dim=1)
|
|
|
|
return siglip_image_embeds, siglip_image_shallow_embeds
|
|
|
|
|
|
def encode_dinov2_image_emb(dinov2_image, device, dtype):
|
|
dinov2_image = dinov2_image.to(device, dtype=dtype)
|
|
res = dino_image_encoder_2(dinov2_image, output_hidden_states=True)
|
|
|
|
dinov2_image_embeds = res.last_hidden_state[:, 1:]
|
|
|
|
dinov2_image_shallow_embeds = torch.cat([res.hidden_states[i][:, 1:] for i in [9, 19, 29]], dim=1)
|
|
|
|
return dinov2_image_embeds, dinov2_image_shallow_embeds
|
|
|
|
|
|
|
|
def encode_image_emb(siglip_image, device, dtype):
|
|
object_image_pil = siglip_image
|
|
object_image_pil_low_res = [object_image_pil.resize((384, 384))]
|
|
object_image_pil_high_res = object_image_pil.resize((768, 768))
|
|
object_image_pil_high_res = [
|
|
object_image_pil_high_res.crop((0, 0, 384, 384)),
|
|
object_image_pil_high_res.crop((384, 0, 768, 384)),
|
|
object_image_pil_high_res.crop((0, 384, 384, 768)),
|
|
object_image_pil_high_res.crop((384, 384, 768, 768)),
|
|
]
|
|
nb_split_image = len(object_image_pil_high_res)
|
|
|
|
siglip_image_embeds = encode_siglip_image_emb(
|
|
siglip_image_processor(images=object_image_pil_low_res, return_tensors="pt").pixel_values,
|
|
device,
|
|
dtype
|
|
)
|
|
dinov2_image_embeds = encode_dinov2_image_emb(
|
|
dino_image_processor_2(images=object_image_pil_low_res, return_tensors="pt").pixel_values,
|
|
device,
|
|
dtype
|
|
)
|
|
|
|
image_embeds_low_res_deep = torch.cat([siglip_image_embeds[0], dinov2_image_embeds[0]], dim=2)
|
|
image_embeds_low_res_shallow = torch.cat([siglip_image_embeds[1], dinov2_image_embeds[1]], dim=2)
|
|
|
|
siglip_image_high_res = siglip_image_processor(images=object_image_pil_high_res, return_tensors="pt").pixel_values
|
|
siglip_image_high_res = siglip_image_high_res[None]
|
|
siglip_image_high_res = rearrange(siglip_image_high_res, 'b n c h w -> (b n) c h w')
|
|
siglip_image_high_res_embeds = encode_siglip_image_emb(siglip_image_high_res, device, dtype)
|
|
siglip_image_high_res_deep = rearrange(siglip_image_high_res_embeds[0], '(b n) l c -> b (n l) c', n=nb_split_image)
|
|
dinov2_image_high_res = dino_image_processor_2(images=object_image_pil_high_res, return_tensors="pt").pixel_values
|
|
dinov2_image_high_res = dinov2_image_high_res[None]
|
|
dinov2_image_high_res = rearrange(dinov2_image_high_res, 'b n c h w -> (b n) c h w')
|
|
dinov2_image_high_res_embeds = encode_dinov2_image_emb(dinov2_image_high_res, device, dtype)
|
|
dinov2_image_high_res_deep = rearrange(dinov2_image_high_res_embeds[0], '(b n) l c -> b (n l) c', n=nb_split_image)
|
|
image_embeds_high_res_deep = torch.cat([siglip_image_high_res_deep, dinov2_image_high_res_deep], dim=2)
|
|
|
|
image_embeds_dict = dict(
|
|
image_embeds_low_res_shallow=image_embeds_low_res_shallow.to(device),
|
|
image_embeds_low_res_deep=image_embeds_low_res_deep.to(device),
|
|
image_embeds_high_res_deep=image_embeds_high_res_deep.to(device),
|
|
)
|
|
|
|
return image_embeds_dict
|
|
|
|
subject_image = subject_image.resize((max(subject_image.size), max(subject_image.size)))
|
|
subject_image_embeds_dict = encode_image_emb(subject_image, device, dtype)
|
|
|
|
return subject_image_embeds_dict
|
|
|
|
def Loader_Dreamo(extra_info,VAE,quantize_mode,dreamo_lora_path,cfg_distill_path,Turbo_path,device,dreamo_version):
|
|
from .DreamO.dreamo.dreamo_pipeline import DreamOPipeline
|
|
from diffusers import BitsAndBytesConfig as DiffusersBitsAndBytesConfig,FluxTransformer2DModel
|
|
#vae=convert_cfvae2diffuser(VAE,use_flux=True)
|
|
repo_list=[i for i in [extra_info.get("repo1_path"),extra_info.get("repo2_path")] if i is not None]
|
|
if repo_list:
|
|
flux_repo=[i for i in repo_list if "flux" in i.lower()]
|
|
else:
|
|
raise "you must fill a flux repo"
|
|
if extra_info.get("gguf_path") is not None: #get error
|
|
print("use gguf quantization")
|
|
from diffusers import GGUFQuantizationConfig
|
|
transformer = FluxTransformer2DModel.from_single_file(
|
|
extra_info.get("gguf_path"),
|
|
config=os.path.join(cur_path, "config/FLUX.1-dev/transformer"),
|
|
quantization_config=GGUFQuantizationConfig(compute_dtype=torch.bfloat16),
|
|
torch_dtype=torch.bfloat16,
|
|
)
|
|
dreamo_pipeline = DreamOPipeline.from_pretrained(flux_repo, transformer=transformer,torch_dtype=torch.bfloat16)
|
|
dreamo_pipeline.load_dreamo_model(device,dreamo_lora_path,cfg_distill_path,Turbo_path, use_turbo=True,dreamo_version=dreamo_version)
|
|
elif extra_info.get("svdq_repo") is not None:#get error
|
|
print("use svdq")
|
|
from nunchaku import NunchakuFluxTransformer2dModel
|
|
transformer = NunchakuFluxTransformer2dModel.from_pretrained(extra_info.get("svdq_repo"),offload=True)
|
|
try:
|
|
transformer.set_attention_impl("nunchaku-fp16")
|
|
except:
|
|
pass
|
|
dreamo_pipeline = DreamOPipeline.from_pretrained(flux_repo, transformer=transformer,torch_dtype=torch.bfloat16)
|
|
dreamo_pipeline.load_dreamo_model(device,dreamo_lora_path,cfg_distill_path,Turbo_path, use_turbo=True,use_svdq=True,dreamo_version=dreamo_version)
|
|
elif extra_info.get("unet_path") is not None:
|
|
print("use single unet")
|
|
if quantize_mode=="fp8":
|
|
transformer = FluxTransformer2DModel.from_single_file(extra_info.get("unet_path"), config=os.path.join(cur_path,"config/FLUX.1-dev/transformer/config.json"),
|
|
torch_dtype=torch.bfloat16)
|
|
else:
|
|
from safetensors.torch import load_file
|
|
t_state_dict=load_file(extra_info.get("unet_path"))
|
|
unet_config = FluxTransformer2DModel.load_config(os.path.join(cur_path,"config/FLUX.1-dev/transformer/config.json"))
|
|
transformer = FluxTransformer2DModel.from_config(unet_config).to(torch.bfloat16)
|
|
transformer.load_state_dict(t_state_dict, strict=False)
|
|
del t_state_dict
|
|
gc_cleanup()
|
|
dreamo_pipeline = DreamOPipeline.from_pretrained(flux_repo, transformer=transformer,torch_dtype=torch.bfloat16)
|
|
dreamo_pipeline.load_dreamo_model(device,dreamo_lora_path,cfg_distill_path,Turbo_path, use_turbo=True,dreamo_version=dreamo_version)
|
|
else:
|
|
print(f"use {quantize_mode} quantization")
|
|
if quantize_mode=="fp8":
|
|
# transformer = FluxTransformer2DModel.from_pretrained(
|
|
# cf_model.get("extra_repo"),
|
|
# subfolder="transformer",
|
|
# quantization_config=DiffusersBitsAndBytesConfig(load_in_8bit=True,),
|
|
# torch_dtype=torch.bfloat16,
|
|
# )
|
|
dreamo_pipeline = DreamOPipeline.from_pretrained(flux_repo, torch_dtype=torch.bfloat16)
|
|
dreamo_pipeline.load_dreamo_model(device,dreamo_lora_path,cfg_distill_path,Turbo_path, use_turbo=True,dreamo_version=dreamo_version)
|
|
from optimum.quanto import freeze, qint8, quantize
|
|
quantize(dreamo_pipeline.transformer, qint8)
|
|
freeze(dreamo_pipeline.transformer)
|
|
quantize(dreamo_pipeline.text_encoder_2, qint8)
|
|
freeze(dreamo_pipeline.text_encoder_2)
|
|
|
|
else:
|
|
if quantize_mode=="nf4": #nf4
|
|
transformer = FluxTransformer2DModel.from_pretrained(
|
|
flux_repo,
|
|
subfolder="transformer",
|
|
quantization_config=DiffusersBitsAndBytesConfig(
|
|
load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16
|
|
),
|
|
torch_dtype=torch.bfloat16,)
|
|
else:
|
|
transformer = FluxTransformer2DModel.from_pretrained(
|
|
flux_repo,
|
|
subfolder="transformer",
|
|
torch_dtype=torch.bfloat16,
|
|
)
|
|
dreamo_pipeline = DreamOPipeline.from_pretrained(flux_repo, transformer=transformer,torch_dtype=torch.bfloat16)
|
|
dreamo_pipeline.load_dreamo_model(device,dreamo_lora_path,cfg_distill_path,Turbo_path, use_turbo=True,dreamo_version=dreamo_version)
|
|
dreamo_pipeline.enable_model_cpu_offload()
|
|
return dreamo_pipeline
|
|
|
|
@torch.no_grad()
|
|
def get_align_face(img,face_helper):
|
|
from .DreamO.dreamo.utils import img2tensor,tensor2img
|
|
from torchvision.transforms.functional import normalize
|
|
# the face preprocessing code is same as PuLID
|
|
face_helper.clean_all()
|
|
image_bgr = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
|
|
face_helper.read_image(image_bgr)
|
|
face_helper.get_face_landmarks_5(only_center_face=True)
|
|
face_helper.align_warp_face()
|
|
if len(face_helper.cropped_faces) == 0:
|
|
return None
|
|
align_face = face_helper.cropped_faces[0]
|
|
|
|
input = img2tensor(align_face, bgr2rgb=True).unsqueeze(0) / 255.0
|
|
input = input.to(torch.device("cuda"))
|
|
parsing_out = face_helper.face_parse(normalize(input, [0.485, 0.456, 0.406], [0.229, 0.224, 0.225]))[0]
|
|
parsing_out = parsing_out.argmax(dim=1, keepdim=True)
|
|
bg_label = [0, 16, 18, 7, 8, 9, 14, 15]
|
|
bg = sum(parsing_out == i for i in bg_label).bool()
|
|
white_image = torch.ones_like(input)
|
|
# only keep the face features
|
|
face_features_image = torch.where(bg, white_image, input)
|
|
face_features_image = tensor2img(face_features_image, rgb2bgr=False)
|
|
|
|
return face_features_image
|
|
|
|
def Dreamo_image_encoder(BEN2_path,ref_image1,ref_image2,ref_task1,ref_task2,ref_res):
|
|
from .DreamO.dreamo.utils import img2tensor, resize_numpy_image_area
|
|
ref_conds = []
|
|
#debug_images = []
|
|
|
|
ref_images = [ref_image1, ref_image2]
|
|
ref_tasks = [ref_task1, ref_task2]
|
|
|
|
for idx, (ref_image, ref_task) in enumerate(zip(ref_images, ref_tasks)):
|
|
if ref_image is not None:
|
|
if ref_task == "id":
|
|
from facexlib.utils.face_restoration_helper import FaceRestoreHelper
|
|
face_helper = FaceRestoreHelper(
|
|
upscale_factor=1,
|
|
face_size=512,
|
|
crop_ratio=(1, 1),
|
|
det_model='retinaface_resnet50',
|
|
save_ext='png',
|
|
device=device,
|
|
)
|
|
ref_image = resize_numpy_image_long(ref_image, 1024)
|
|
ref_image = get_align_face(ref_image,face_helper)
|
|
elif ref_task != "style": # ip
|
|
from .DreamO.tools import BEN2
|
|
bg_rm_model = BEN2.BEN_Base().to(device).eval() #TODO 反复加载 需要修复
|
|
#hf_hub_download(repo_id='PramaLLC/BEN2', filename='BEN2_Base.pth', local_dir='models')
|
|
bg_rm_model.loadcheckpoints(BEN2_path)
|
|
ref_image = bg_rm_model.inference(ref_image) #NEED TO CHECK
|
|
bg_rm_model.to(torch.device('cpu'))
|
|
if ref_task != "id":
|
|
ref_image = resize_numpy_image_area(np.array(ref_image), ref_res * ref_res)
|
|
|
|
#debug_images.append(ref_image)
|
|
ref_image = img2tensor(ref_image, bgr2rgb=False).unsqueeze(0) / 255.0
|
|
ref_image = 2 * ref_image - 1.0
|
|
ref_conds.append(
|
|
{
|
|
'img': ref_image,
|
|
'task': ref_task,
|
|
'idx': idx + 1,
|
|
}
|
|
)
|
|
return ref_conds
|
|
|
|
def Loader_Flux_Diffuser(extra_info,omi_lora_path,VAE,quantize_mode):
|
|
from .OmniConsistency.src_inference.pipeline import FluxPipeline as FluxPipeline_dif
|
|
from .OmniConsistency.src_inference.pipeline_ import FluxPipeline as FluxPipeline_dif_original
|
|
from diffusers import BitsAndBytesConfig as DiffusersBitsAndBytesConfig,FluxTransformer2DModel
|
|
from .OmniConsistency.src_inference.lora_helper import set_single_lora
|
|
flux_dif_repo=os.path.join(cur_path,"config/FLUX.1-dev")
|
|
if extra_info.get("gguf_path") is not None:
|
|
vae=convert_cfvae2diffuser(VAE,use_flux=True)
|
|
print("use gguf quantization")
|
|
|
|
from diffusers import GGUFQuantizationConfig
|
|
transformer = FluxTransformer2DModel.from_single_file(
|
|
extra_info.get("gguf_path"),
|
|
config=os.path.join(cur_path, "config/FLUX.1-dev/transformer"),
|
|
quantization_config=GGUFQuantizationConfig(compute_dtype=torch.bfloat16),
|
|
torch_dtype=torch.bfloat16,
|
|
)
|
|
pipeline = FluxPipeline_dif.from_pretrained(flux_dif_repo, vae=vae,transformer=transformer,text_encoder=None,text_encoder_2=None,torch_dtype=torch.bfloat16)
|
|
|
|
elif extra_info.get("svdq_repo") is not None:
|
|
print("use svdq")
|
|
vae=convert_cfvae2diffuser(VAE,use_flux=True)
|
|
from nunchaku import NunchakuFluxTransformer2dModel
|
|
transformer = NunchakuFluxTransformer2dModel.from_pretrained(extra_info.get("svdq_repo"),offload=True)
|
|
try:
|
|
transformer.set_attention_impl("nunchaku-fp16")
|
|
except:
|
|
pass
|
|
pipeline = FluxPipeline_dif.from_pretrained(flux_dif_repo, vae=vae,transformer=transformer,text_encoder=None,text_encoder_2=None,torch_dtype=torch.bfloat16)
|
|
|
|
elif extra_info.get("unet_path") is not None:
|
|
print("use single unet")
|
|
vae=convert_cfvae2diffuser(VAE,use_flux=True)
|
|
if quantize_mode=="fp8":
|
|
transformer = FluxTransformer2DModel.from_single_file(extra_info.get("unet_path"), config=os.path.join(cur_path,"config/FLUX.1-dev/transformer/config.json"),
|
|
torch_dtype=torch.bfloat16)
|
|
else:
|
|
from safetensors.torch import load_file
|
|
t_state_dict=load_file(extra_info.get("unet_path"))
|
|
unet_config = FluxTransformer2DModel.load_config(os.path.join(cur_path,"config/FLUX.1-dev/transformer/config.json"))
|
|
transformer = FluxTransformer2DModel.from_config(unet_config).to(torch.bfloat16)
|
|
transformer.load_state_dict(t_state_dict, strict=False)
|
|
del t_state_dict
|
|
gc_cleanup()
|
|
pipeline = FluxPipeline_dif.from_pretrained(flux_dif_repo, vae=vae,transformer=transformer,text_encoder=None,text_encoder_2=None,torch_dtype=torch.bfloat16)
|
|
|
|
else:
|
|
print(f"use {quantize_mode} quantization")
|
|
repo_list=[i for i in [extra_info.get("repo1_path"),extra_info.get("repo2_path")] if i is not None]
|
|
if repo_list:
|
|
flux_repo=[i for i in repo_list if "flux" in i.lower()]
|
|
else:
|
|
raise "you must fill a flux repo"
|
|
if quantize_mode=="nf4": #nf4
|
|
transformer = FluxTransformer2DModel.from_pretrained(
|
|
flux_repo[0],
|
|
subfolder="transformer",
|
|
quantization_config=DiffusersBitsAndBytesConfig(
|
|
load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16
|
|
),
|
|
torch_dtype=torch.bfloat16,)
|
|
elif quantize_mode=="fp8":
|
|
transformer = FluxTransformer2DModel.from_pretrained(
|
|
flux_repo[0],
|
|
subfolder="transformer",
|
|
quantization_config=DiffusersBitsAndBytesConfig(load_in_8bit=True,),
|
|
torch_dtype=torch.bfloat16,
|
|
)
|
|
else:
|
|
transformer = FluxTransformer2DModel.from_pretrained(
|
|
flux_repo[0],
|
|
subfolder="transformer",
|
|
torch_dtype=torch.bfloat16,
|
|
)
|
|
pipeline = FluxPipeline_dif_original.from_pretrained(flux_repo[0],transformer=transformer,torch_dtype=torch.bfloat16)
|
|
|
|
multi_lora=[]
|
|
if extra_info.get("lora1_path") is not None:
|
|
multi_lora.append(extra_info.get("lora1_path"))
|
|
if extra_info.get("lora2_path") is not None:
|
|
multi_lora.append(extra_info.get("lora2_path"))
|
|
if multi_lora:
|
|
if len(multi_lora)==1:
|
|
set_single_lora(pipeline.transformer, omi_lora_path, lora_weights=[1], cond_size=512)
|
|
pipeline.unload_lora_weights()
|
|
pipeline.load_lora_weights(os.path.dirname(multi_lora[0]),weight_name=os.path.basename(multi_lora[0]))
|
|
|
|
else:
|
|
from .OmniConsistency.src_inference.lora_helper import set_multi_lora
|
|
lora_weights = [1]*len(multi_lora)
|
|
set_multi_lora(pipeline.transformer, multi_lora, lora_weights=[[lora_weights]], cond_size=512)
|
|
pipeline.unload_lora_weights()
|
|
else:
|
|
raise "need chocie a lora model in EasyFunction_Lite!"
|
|
pipeline.enable_model_cpu_offload()
|
|
return pipeline
|
|
|
|
|
|
|
|
def load_lora_for_unet_only(pipeline, lora_path, adapter_name="default", lora_scale=1.0):
|
|
|
|
try:
|
|
|
|
pipeline.load_lora_weights(lora_path, adapter_name=adapter_name)
|
|
pipeline.set_adapters(adapter_name, adapter_weights=lora_scale)
|
|
print(f"成功加载LoRA权重: {adapter_name} (scale: {lora_scale})")
|
|
except Exception as e:
|
|
print(f"加载LoRA权重失败: {e}")
|
|
|
|
return pipeline
|