Buckets:

krea-community/krea-2 / sampling.py
mrfakename's picture
download
raw
4.94 kB
"""Functional flow-matching sampler for the K2 MMDiT (no Scheduler class)."""
import math
import torch
from einops import rearrange, repeat
from PIL import Image
def roundup(value, multiple, name):
"""Round `value` up to the nearest multiple, logging when padding is applied."""
aligned = ((value + multiple - 1) // multiple) * multiple
if aligned != value:
print(
f"[sample] {name}={value} is not a multiple of {multiple}; padding to {aligned}"
)
return aligned
def prepare(img, txtlen, patch, txtmask):
"""Patchify the latent and build the combined text+image position / mask tensors.
Returns (img_tokens, pos, mask).
"""
b, _, h, w = img.shape
h_, w_ = h // patch, w // patch
imgids = torch.zeros((h_, w_, 3), device=img.device)
imgids[..., 1] = torch.arange(h_, device=img.device)[:, None]
imgids[..., 2] = torch.arange(w_, device=img.device)[None, :]
imgpos = repeat(imgids, "h w three -> b (h w) three", b=b, three=3)
imgmask = torch.ones(b, h_ * w_, device=img.device, dtype=torch.bool)
img = rearrange(img, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=patch, pw=patch)
txtpos = torch.zeros(b, txtlen, 3, device=img.device)
mask = torch.cat((txtmask, imgmask), dim=1)
pos = torch.cat((txtpos, imgpos), dim=1)
return img, pos, mask
def timesteps(seq_len, steps, x1, x2, y1=0.5, y2=1.15, sigma=1.0, mu=None):
"""Resolution-aware flow-matching timestep schedule (t: 1 -> 0).
`mu` is interpolated linearly in image-sequence length between (x1,y1) and
(x2,y2), then used to time-shift a uniform 1->0 grid. Pass an explicit `mu`
to pin a constant shift regardless of resolution (used by the distilled
checkpoint, which was trained at a fixed mu=1.15).
"""
ts = torch.linspace(1, 0, steps + 1)
if mu is None:
slope = (y2 - y1) / (x2 - x1)
mu = slope * seq_len + (y1 - slope * x1)
ts = math.exp(mu) / (math.exp(mu) + (1.0 / ts - 1.0) ** sigma)
return ts.tolist()
@torch.no_grad()
def sample(
model,
ae,
encoder,
prompts,
*,
negative_prompts=None,
device="cuda",
dtype=torch.bfloat16,
width=1024,
height=1024,
steps=28,
guidance=4.5,
seed=0,
minres=256,
maxres=1280,
y1=0.5,
y2=1.15,
mu=None,
):
"""End-to-end text-to-image sampling: encode -> euler+CFG denoise -> decode."""
patch = model.config.patch
# The latent grid (dim // ae.compression) is patchified in `patch`-sized blocks,
# so width/height must be multiples of ae.compression * patch. Pad up otherwise.
align = ae.compression * patch
width, height = roundup(width, align, "width"), roundup(height, align, "height")
n = len(prompts)
cfg = guidance > 0
if negative_prompts is None:
negative_prompts = [""] * n
# Per-prompt seeded gaussian latent noise.
noise = torch.cat(
[
torch.randn(
1,
ae.channels,
height // ae.compression,
width // ae.compression,
device=device,
dtype=dtype,
generator=torch.Generator(device=device).manual_seed(seed + i),
)
for i in range(n)
],
dim=0,
)
# Positive (conditional) text conditioning.
txt, txtmask = encoder(prompts)
x, pos, mask = prepare(noise, txt.shape[1], patch, txtmask)
# The unconditional branch is only used for CFG; skip encoding/prep entirely
# when guidance is disabled.
if cfg:
untxt, untxtmask = encoder(negative_prompts)
_, unpos, unmask = prepare(noise, untxt.shape[1], patch, untxtmask)
# min_res/max_res define the (x1,y1)-(x2,y2) interpolation endpoints for `mu`.
x1 = (minres // (ae.compression * patch)) ** 2
x2 = (maxres // (ae.compression * patch)) ** 2
ts = timesteps(x.shape[1], steps, x1, x2, y1=y1, y2=y2, mu=mu)
# Euler integration of the flow ODE with CFG.
img = x
for tcurr, tprev in zip(ts[:-1], ts[1:]):
t = torch.full((len(img),), tcurr, dtype=img.dtype, device=img.device)
cond = model(img=img, context=txt, t=t, pos=pos, mask=mask)
if cfg:
uncond = model(img=img, context=untxt, t=t, pos=unpos, mask=unmask)
v = cond + guidance * (cond - uncond)
else:
v = cond
img = img + (tprev - tcurr) * v
# Unpatchify back to a latent and decode to pixels.
img = rearrange(
img,
"b (h w) (c ph pw) -> b c (h ph) (w pw)",
ph=patch,
pw=patch,
h=height // (ae.compression * patch),
w=width // (ae.compression * patch),
)
img = ae.decode(img.to(torch.bfloat16))
img = img.clamp(-1, 1) * 0.5 + 0.5
img = rearrange(img * 255.0, "b c h w -> b h w c").cpu().byte().numpy()
return [Image.fromarray(img[i]) for i in range(len(img))]

Xet Storage Details

Size:
4.94 kB
·
Xet hash:
69057286a749de442d8120406f627d54f50ced504a796c0b425483b30297d082

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.