ouzhang57's picture
Upload folder using huggingface_hub (part 10)
dd35111 verified
Raw
History Blame Contribute Delete
17.9 kB
# HiDream-O1-Image Pipeline for DiffSynth-Studio.
import torch, math
from typing import Optional, Union
from tqdm import tqdm
from PIL import Image
from ..core.device.npu_compatible_device import get_device_type
from ..diffusion import FlowMatchScheduler
from ..core import ModelConfig
from ..diffusion.base_pipeline import BasePipeline, PipelineUnit
from ..models.hidream_o1_image_dit import HiDreamO1ImageModel
from ..models.hidream_common import (
add_special_tokens, get_rope_index_fix_point, patchify, unpatchify, PATCH_SIZE,
resize_pilimage, calculate_dimensions, create_layout_reference_images
)
class HiDreamO1ImagePipeline(BasePipeline):
def __init__(self, device=get_device_type(), torch_dtype=torch.bfloat16):
super().__init__(
device=device, torch_dtype=torch_dtype,
height_division_factor=PATCH_SIZE, width_division_factor=PATCH_SIZE,
)
self.scheduler = FlowMatchScheduler("HiDream-O1-Image")
self.dit: HiDreamO1ImageModel = None
self.processor = None
self.in_iteration_models = ("dit",)
self.units = [
HiDreamO1ImageUnit_ShapeChecker(),
HiDreamO1ImageUnit_RefImageEmbedder(),
HiDreamO1ImageUnit_PromptTokenizer(),
HiDreamO1ImageUnit_NoiseInitializer(),
HiDreamO1ImageUnit_InputImageEmbedder(),
]
self.model_fn = model_fn_hidream_o1_image
@staticmethod
def from_pretrained(
torch_dtype: torch.dtype = torch.bfloat16,
device: Union[str, torch.device] = get_device_type(),
model_configs: list[ModelConfig] = [],
processor_config: ModelConfig = ModelConfig(model_id="HiDream-ai/HiDream-O1-Image-Dev", origin_file_pattern="./"),
vram_limit: float = None,
):
pipe = HiDreamO1ImagePipeline(device=device, torch_dtype=torch_dtype)
model_pool = pipe.download_and_load_models(model_configs, vram_limit)
pipe.dit = model_pool.fetch_model("hidream_o1_image_dit")
if processor_config is not None:
from transformers import AutoProcessor
processor_config.download_if_necessary()
pipe.processor = AutoProcessor.from_pretrained(processor_config.path)
add_special_tokens(pipe.processor.tokenizer)
pipe.vram_management_enabled = pipe.check_vram_management_state()
return pipe
@torch.no_grad()
def __call__(
self,
prompt: str,
negative_prompt: str = " ",
cfg_scale: float = 4.0,
height: int = 2048,
width: int = 2048,
seed: int = None,
rand_device: str = "cpu",
num_inference_steps: int = 50,
model_type: str = "full",
shift: float = 3.0,
noise_scale: float = 8.0,
edit_image: Union[Image.Image, list[Image.Image]] = None,
keep_original_aspect: bool = True,
layout_bboxes: list[list[float]] = None,
# LoRA
lora = None,
negative_lora = None,
progress_bar_cmd=tqdm,
):
# Scheduler
self.scheduler.set_timesteps(num_inference_steps, shift=shift, special_case=model_type)
# Parameters
inputs_posi = {"prompt": prompt}
inputs_nega = {"negative_prompt": negative_prompt}
inputs_shared = {
"cfg_scale": cfg_scale,
"height": height, "width": width,
"seed": seed, "rand_device": rand_device,
"noise_scale": noise_scale,
"edit_image": edit_image, "keep_original_aspect": keep_original_aspect, "layout_bboxes": layout_bboxes,
"positive_only_lora": lora,
"negative_only_lora": negative_lora,
}
# Units
for unit in self.units:
inputs_shared, inputs_posi, inputs_nega = self.unit_runner(unit, self, inputs_shared, inputs_posi, inputs_nega)
# Denoise
self.load_models_to_device(self.in_iteration_models)
models = {name: getattr(self, name) for name in self.in_iteration_models}
for progress_id, timestep in enumerate(progress_bar_cmd(self.scheduler.timesteps)):
timestep = timestep.unsqueeze(0).to(dtype=self.torch_dtype, device=self.device)
noise_pred = self.cfg_guided_model_fn(
self.model_fn, cfg_scale,
inputs_shared, inputs_posi, inputs_nega,
**models, timestep=timestep, progress_id=progress_id
)
inputs_shared["latents"] = self.step(self.scheduler, progress_id=progress_id, noise_pred=noise_pred, **inputs_shared)
image = self.vae_output_to_image(inputs_shared["latents"])
return image
class HiDreamO1ImageUnit_InputImageEmbedder(PipelineUnit):
def __init__(self):
super().__init__(
input_params=("input_image",),
output_params=("input_latents",),
)
def process(self, pipe: HiDreamO1ImagePipeline, input_image):
if input_image is None or not pipe.scheduler.training:
return {}
img_tensor = pipe.preprocess_image(input_image).to(device=pipe.device, dtype=pipe.torch_dtype)
return {"input_latents": img_tensor}
class HiDreamO1ImageUnit_NoiseInitializer(PipelineUnit):
def __init__(self):
super().__init__(
input_params=("height", "width", "seed", "rand_device", "noise_scale"),
output_params=("latents",),
)
def prepare_inputs(self, inputs_shared, inputs_posi, inputs_nega):
return inputs_shared, inputs_posi, inputs_nega
def process(self, pipe: HiDreamO1ImagePipeline, height=None, width=None, seed=None, rand_device=None, noise_scale=None):
noise = pipe.generate_noise((1, 3, height, width), seed=seed, rand_device=rand_device, rand_torch_dtype=pipe.torch_dtype)
noise = noise_scale * noise
return {"latents": noise}
class HiDreamO1ImageUnit_ShapeChecker(PipelineUnit):
def __init__(self):
super().__init__(
input_params=("height", "width"),
output_params=("height", "width"),
)
def process(self, pipe: HiDreamO1ImagePipeline, height, width):
height, width = pipe.check_resize_height_width(height, width)
return {"height": height, "width": width}
class HiDreamO1ImageUnit_RefImageEmbedder(PipelineUnit):
def __init__(self):
super().__init__(
input_params=("edit_image", "height", "width", "keep_original_aspect", "layout_bboxes"),
output_params=("ref_for_prompt_tokenizer", "height", "width", "ref_patches"),
)
def get_sizes(self, K, height, width):
if K == 1: max_size = max(height, width)
elif K == 2: max_size = max(height, width) * 48 // 64
elif K <= 4: max_size = max(height, width) // 2
elif K <= 8: max_size = max(height, width) * 24 // 64
else: max_size = max(height, width) // 4
CONDITION_IMAGE_SIZE = 384
if K <= 4: cond_img_size = CONDITION_IMAGE_SIZE
elif K <= 8: cond_img_size = CONDITION_IMAGE_SIZE * 48 // 64
else: cond_img_size = CONDITION_IMAGE_SIZE // 2
return max_size, cond_img_size
def process(self, pipe: HiDreamO1ImagePipeline, edit_image, height, width, keep_original_aspect, layout_bboxes):
if edit_image is None:
return {}
if isinstance(edit_image, Image.Image):
edit_image = [edit_image]
if keep_original_aspect and len(edit_image) == 1:
edit_image = [resize_pilimage(pil, 2048) for pil in edit_image]
width, height = edit_image[0].size
if layout_bboxes is not None:
edit_image = create_layout_reference_images(edit_image, layout_bboxes, width, height)
max_size, cond_img_size = self.get_sizes(len(edit_image), height, width)
ref_image_tensors, ref_pils_vlm = [], []
image_grid_thw_tgt = torch.tensor([1, height // PATCH_SIZE, width // PATCH_SIZE], dtype=torch.int64).unsqueeze(0)
image_grid_thw_ref = torch.zeros((len(edit_image), 3), dtype=torch.int64)
for i, pil in enumerate(edit_image):
pil_r = pil if keep_original_aspect and len(edit_image) == 1 else resize_pilimage(pil, max_size)
cond_w, cond_h = calculate_dimensions(cond_img_size, pil_r.width / pil_r.height)
ref_pils_vlm.append(pil_r.resize((cond_w, cond_h), resample=Image.LANCZOS))
image_grid_thw_ref[i] = torch.tensor([1, pil_r.height // PATCH_SIZE, pil_r.width // PATCH_SIZE], dtype=torch.int64)
x = pipe.preprocess_image(pil_r)
x = patchify(x)
ref_image_tensors.append(x)
ref_image_lens = [img.shape[1] for img in ref_image_tensors]
ref_patches = torch.cat(ref_image_tensors, dim=1).to(pipe.device, pipe.torch_dtype)
return {
"ref_for_prompt_tokenizer": {
"ref_image_lens": ref_image_lens,
"ref_pils_vlm": ref_pils_vlm,
"image_grid_thw_tgt": image_grid_thw_tgt,
"image_grid_thw_ref": image_grid_thw_ref,
"tgt_image_len": (height // PATCH_SIZE) * (width // PATCH_SIZE),
},
"height": height,
"width": width,
"ref_patches": ref_patches,
}
class HiDreamO1ImageUnit_PromptTokenizer(PipelineUnit):
def __init__(self):
super().__init__(
seperate_cfg=True,
input_params=("height", "width", "ref_for_prompt_tokenizer"),
input_params_posi={"prompt": "prompt"},
input_params_nega={"prompt": "negative_prompt"},
output_params=("input_ids", "position_ids", "token_types", "vinput_mask",
"pixel_values", "image_grid_thw"),
)
def process(self, pipe: HiDreamO1ImagePipeline, prompt, height, width, ref_for_prompt_tokenizer=None):
# T2I path
if ref_for_prompt_tokenizer is None:
return self.build_text_sample(
prompt=prompt,
height=height, width=width,
tokenizer=pipe.processor.tokenizer, processor=pipe.processor,
model_config=pipe.dit.config, device=pipe.device,
)
# I2I path
return self.build_i2i_sample(
prompt=prompt,
height=height, width=width,
ref_for_prompt_tokenizer=ref_for_prompt_tokenizer,
tokenizer=pipe.processor.tokenizer, processor=pipe.processor,
model_config=pipe.dit.config, device=pipe.device,
torch_dtype=pipe.torch_dtype,
)
def build_text_sample(self, prompt, height, width, tokenizer, processor, model_config, device):
TIMESTEP_TOKEN_NUM = 1
image_token_id = model_config.image_token_id
video_token_id = model_config.video_token_id
vision_start_token_id = model_config.vision_start_token_id
image_len = (height // PATCH_SIZE) * (width // PATCH_SIZE)
boi_token = getattr(tokenizer, "boi_token", "<|boi_token|>")
tms_token = getattr(tokenizer, "tms_token", "<|tms_token|>")
messages = [{"role": "user", "content": prompt}]
template_caption = (
processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
+ boi_token
+ tms_token * TIMESTEP_TOKEN_NUM
)
input_ids = tokenizer.encode(template_caption, return_tensors="pt", add_special_tokens=False)
image_grid_thw = torch.tensor(
[1, height // PATCH_SIZE, width // PATCH_SIZE], dtype=torch.int64
).unsqueeze(0)
vision_tokens = torch.zeros((1, image_len), dtype=input_ids.dtype) + image_token_id
vision_tokens[0, 0] = vision_start_token_id
input_ids_pad = torch.cat([input_ids, vision_tokens], dim=-1)
position_ids, _ = get_rope_index_fix_point(
spatial_merge_size=1,
image_token_id=image_token_id,
video_token_id=video_token_id,
vision_start_token_id=vision_start_token_id,
input_ids=input_ids_pad,
image_grid_thw=image_grid_thw,
video_grid_thw=None,
attention_mask=None,
skip_vision_start_token=[1],
)
txt_seq_len = input_ids.shape[-1]
all_seq_len = position_ids.shape[-1]
token_types = torch.zeros((1, all_seq_len), dtype=input_ids.dtype)
bgn = txt_seq_len - TIMESTEP_TOKEN_NUM
token_types[0, bgn: bgn + image_len + TIMESTEP_TOKEN_NUM] = 1
token_types[0, txt_seq_len - TIMESTEP_TOKEN_NUM: txt_seq_len] = 3
vinput_mask = (token_types == 1)
token_types_bin = (token_types > 0).to(token_types.dtype)
return {
'input_ids': input_ids.to(device),
'position_ids': position_ids.to(device),
'token_types': token_types_bin.to(device),
'vinput_mask': vinput_mask.to(device),
}
def build_i2i_sample(self, prompt, height, width, ref_for_prompt_tokenizer,
tokenizer, processor, model_config, device, torch_dtype):
TIMESTEP_TOKEN_NUM = 1
image_token_id = model_config.image_token_id
video_token_id = model_config.video_token_id
vision_start_token_id = model_config.vision_start_token_id
spatial_merge_size = model_config.vision_config.spatial_merge_size
ref_pils_vlm = ref_for_prompt_tokenizer["ref_pils_vlm"]
image_grid_thw_tgt = ref_for_prompt_tokenizer["image_grid_thw_tgt"]
ref_image_lens = ref_for_prompt_tokenizer["ref_image_lens"]
image_grid_thw_ref = ref_for_prompt_tokenizer["image_grid_thw_ref"]
tgt_image_len = ref_for_prompt_tokenizer["tgt_image_len"]
K = len(ref_pils_vlm)
# processor call
boi_token = getattr(tokenizer, "boi_token", "<|boi_token|>")
tms_token = getattr(tokenizer, "tms_token", "<|tms_token|>")
content = [{"type": "image"} for _ in range(K)]
content.append({"type": "text", "text": prompt})
template_caption = processor.apply_chat_template([{"role": "user", "content": content}], tokenize=False, add_generation_prompt=True)
proc = processor(text=[template_caption], images=ref_pils_vlm, padding="longest", return_tensors="pt")
input_ids_2 = tokenizer.encode(boi_token + tms_token * TIMESTEP_TOKEN_NUM, return_tensors="pt", add_special_tokens=False)
input_ids = torch.cat([proc.input_ids, input_ids_2], dim=-1)
# image_grid_thw combine
igthw_cond = proc.image_grid_thw.clone()
for i in range(K):
igthw_cond[i, 1] //= spatial_merge_size
igthw_cond[i, 2] //= spatial_merge_size
igthw_all = torch.cat([igthw_cond, image_grid_thw_tgt, image_grid_thw_ref], dim=0)
# vision tokens
vision_tokens_list = []
vt_tgt = torch.full((1, tgt_image_len), image_token_id, dtype=input_ids.dtype)
vt_tgt[0, 0] = vision_start_token_id
vision_tokens_list.append(vt_tgt)
for rl in ref_image_lens:
vt_ref = torch.full((1, rl), image_token_id, dtype=input_ids.dtype)
vt_ref[0, 0] = vision_start_token_id
vision_tokens_list.append(vt_ref)
vision_tokens = torch.cat(vision_tokens_list, dim=1)
input_ids_pad = torch.cat([input_ids, vision_tokens], dim=-1)
# position_ids
position_ids, _ = get_rope_index_fix_point(
1, image_token_id, video_token_id, vision_start_token_id,
input_ids=input_ids_pad, image_grid_thw=igthw_all,
video_grid_thw=None, attention_mask=None,
skip_vision_start_token=[0] * K + [1] + [1] * K,
)
txt_seq_len = input_ids.shape[-1]
all_seq_len = position_ids.shape[-1]
# token_types / vinput_mask
token_types_raw = torch.zeros((1, all_seq_len), dtype=input_ids.dtype)
bgn = txt_seq_len - TIMESTEP_TOKEN_NUM
end = bgn + tgt_image_len + TIMESTEP_TOKEN_NUM
token_types_raw[0, bgn:end] = 1 # target
token_types_raw[0, end: end + sum(ref_image_lens)] = 2 # ref
token_types_raw[0, txt_seq_len - TIMESTEP_TOKEN_NUM: txt_seq_len] = 3 # TMS
vinput_mask = torch.logical_or(token_types_raw == 1, token_types_raw == 2)
token_types_bin = (token_types_raw > 0).to(token_types_raw.dtype)
return {
'input_ids': input_ids.to(device),
'position_ids': position_ids.to(device),
'token_types': token_types_bin.to(device),
'vinput_mask': vinput_mask.to(device),
'pixel_values': proc.pixel_values.to(device, torch_dtype),
'image_grid_thw': proc.image_grid_thw.to(device),
}
def model_fn_hidream_o1_image(
dit,
latents,
timestep,
input_ids,
position_ids,
token_types,
vinput_mask,
pixel_values=None,
image_grid_thw=None,
ref_patches=None,
use_gradient_checkpointing: bool = False,
use_gradient_checkpointing_offload: bool = False,
**kwargs,
):
b, c, h, w = latents.shape
x = patchify(latents)
img_seq_len = x.shape[1]
if ref_patches is not None:
x = torch.cat([x, ref_patches], dim=1)
timestep = timestep / 1000.
outputs = dit(
input_ids=input_ids,
position_ids=position_ids,
vinputs=x,
timestep=(1 - timestep).reshape(-1),
token_types=token_types,
pixel_values=pixel_values,
image_grid_thw=image_grid_thw,
use_gradient_checkpointing=use_gradient_checkpointing,
use_gradient_checkpointing_offload=use_gradient_checkpointing_offload,
)
x_pred = outputs.x_pred[0, vinput_mask[0]][:img_seq_len].unsqueeze(0)
x_pred = unpatchify(x_pred, h, w)
v_pred = (latents - x_pred) / timestep
return v_pred