Skip to content

Improve how we convert emojis to images #20

@maxencefaldor

Description

@maxencefaldor
import os
import requests
import numpy as np
from PIL import Image, ImageFont, ImageDraw

# Hardcoded path to ensure consistency within the repo
FONT_PATH = ".cache/fonts/NotoColorEmoji.ttf"
FONT_URL = "https://github.com/googlefonts/noto-emoji/raw/main/fonts/NotoColorEmoji.ttf"

def load_emoji_font() -> ImageFont.FreeTypeFont:
    """Loads the font, downloading it only if necessary."""
    if not os.path.exists(FONT_PATH):
        os.makedirs(os.path.dirname(FONT_PATH), exist_ok=True)
        print(f"Downloading Noto Emoji to {FONT_PATH}...")
        with open(FONT_PATH, "wb") as f:
            f.write(requests.get(FONT_URL).content)

    # We load at 109px (native bitmap size) to avoid OSError
    return ImageFont.truetype(FONT_PATH, 109)

def emoji_to_tensor(emoji: str, grid_size: int = 40, target_size: int = 28) -> np.ndarray:
    """
    Renders an emoji to a numpy array, handling downsampling automatically.
    
    Args:
        emoji: Unicode character (e.g. "🦎").
        grid_size: The full size of the simulation grid.
        target_size: The actual size of the emoji within the grid.
    """
    font = load_emoji_font()

    # 1. Render High-Res (Native 109px)
    # We use a large canvas to safely compute the bounding box
    canvas_size = 150 
    img = Image.new("RGBA", (canvas_size, canvas_size), (0, 0, 0, 0))
    draw = ImageDraw.Draw(img)

    # Center and draw
    bbox = draw.textbbox((0, 0), emoji, font=font)
    w, h = bbox[2] - bbox[0], bbox[3] - bbox[1]
    draw.text(((canvas_size - w) // 2, (canvas_size - h) // 2), emoji, font=font, embedded_color=True)

    # 2. Crop to content and Resize High-Quality
    # This gets us a clean 'target_size' emoji without empty space
    img = img.crop(img.getbbox())
    img.thumbnail((target_size, target_size), Image.Resampling.LANCZOS)

    # 3. Paste into final grid
    final_grid = Image.new("RGBA", (grid_size, grid_size), (0, 0, 0, 0))
    offset = ((grid_size - img.width) // 2, (grid_size - img.height) // 2)
    final_grid.paste(img, offset)

    # 4. Normalize and Premultiply Alpha
    arr = np.array(final_grid, dtype=np.float32) / 255.0
    arr[..., :3] *= arr[..., 3:4] # Multiply RGB by Alpha

    return arr

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions