Files
MIDIFoundationModel/dllm/assets/logo.py
2025-11-27 15:44:17 +08:00

120 lines
3.6 KiB
Python

import numpy as np
from PIL import Image, ImageDraw, ImageFont
import os
# ---------- Configuration (smaller size) ----------
W, H = 480, 210 # lower resolution
TOTAL_DURATION = 3.0
FPS = 15 # lower fps
TEXT = "dLLM"
# TEXT_COLOR = (235, 235, 235)
TEXT_COLOR = (0, 0, 0)
OUTPUT = "logo.gif"
LAST_FRAME_PNG = "logo.png"
DIFFUSION_PORTION = 0.3 # fewer diffusion frames
SEED = 8
# ---------- Auto font size ----------
def load_font_auto_size(text, w, h, target_width_ratio=0.95, target_height_ratio=0.95):
lo, hi = 10, 2000
best_font, best_size = None, lo
while lo <= hi:
mid = (lo + hi) // 2
try:
font = ImageFont.truetype(
"/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", size=mid
)
except:
font = ImageFont.load_default()
dummy = Image.new("L", (w, h), 0)
d = ImageDraw.Draw(dummy)
bbox = d.textbbox((0, 0), text, font=font)
tw, th = bbox[2] - bbox[0], bbox[3] - bbox[1]
width_ok = tw <= w * target_width_ratio
height_ok = th <= h * target_height_ratio
if width_ok and height_ok:
best_font, best_size = font, mid
lo = mid + 1
else:
hi = mid - 1
return best_font if best_font is not None else font
# ---------- Text rendering ----------
def render_text_mask(w, h, text, font):
img = Image.new("L", (w, h), 0)
d = ImageDraw.Draw(img)
bbox = d.textbbox((0, 0), text, font=font)
tw, th = bbox[2] - bbox[0], bbox[3] - bbox[1]
x = (w - tw) // 2 - bbox[0]
y = (h - th) // 2 - bbox[1]
d.text((x, y), text, font=font, fill=255)
return np.asarray(img, np.float32) / 255.0
# ---------- Initialization ----------
font = load_font_auto_size(TEXT, W, H)
mask = render_text_mask(W, H, TEXT, font)
num_frames = int(TOTAL_DURATION * FPS)
diffusion_frames = max(1, int(num_frames * DIFFUSION_PORTION))
hold_ms = int((TOTAL_DURATION - diffusion_frames / FPS) * 1000)
rng = np.random.default_rng(SEED)
frames = []
# ---------- Diffusion stage ----------
for i in range(diffusion_frames):
t = i / max(1, diffusion_frames - 1)
progress = t**0.9
noise_sigma = (1.0 - progress) ** 2.2
noise = rng.standard_normal((H, W, 1)).astype(np.float32)
noise_img = 1.0 - noise_sigma * 0.5 * np.abs(noise)
np.clip(noise_img, 0.0, 1.0, out=noise_img)
alpha = progress**2.0
alpha_map = (mask * alpha).astype(np.float32)[..., None]
text_rgb = np.zeros((H, W, 3), dtype=np.float32)
for c in range(3):
text_rgb[..., c] = (mask > 0).astype(np.float32) * (TEXT_COLOR[c] / 255.0)
frame = (1.0 - alpha_map) * noise_img + alpha_map * text_rgb
frame = (np.clip(frame, 0.0, 1.0) * 255).astype(np.uint8)
frames.append(Image.fromarray(frame, mode="RGB"))
# ---------- Last frame ----------
final_frame = frames[-1]
# ---------- Save last frame as PNG ----------
final_frame.save(LAST_FRAME_PNG)
print(f"🖼️ Last frame saved as: {LAST_FRAME_PNG}")
# ---------- Quantization (reduce size) ----------
pal_frames = [f.convert("P", palette=Image.ADAPTIVE, colors=64) for f in frames]
pal_final = final_frame.convert("P", palette=Image.ADAPTIVE, colors=64)
# ---------- Save GIF ----------
normal_ms = int(1000 / FPS)
durations = [normal_ms] * len(pal_frames) + [hold_ms]
pal_frames[0].save(
OUTPUT,
save_all=True,
append_images=pal_frames[1:] + [pal_final],
duration=durations,
loop=0,
optimize=True,
)
print(f"✅ GIF saved: {OUTPUT}")
print(
f"Frames (diffusion only): {len(pal_frames)} at {FPS} FPS, final hold {hold_ms} ms, resolution {W}x{H}"
)