Translator / utils /avatar_manager.py
nihun's picture
Upload 19 files
85c18a5 verified
"""
Avatar Manager Module
=====================
Handles avatar discovery, creation, and management.
Functions:
- ensure_sample_avatar: Create default sample avatar
- list_avatars: Get list of available avatars
- get_avatar_preview: Get preview image of an avatar
"""
from PIL import Image, ImageDraw
from pathlib import Path
from typing import List, Optional
import numpy as np
def ensure_sample_avatar(avatars_dir: Path) -> None:
"""
Create a sample avatar if none exists.
Generates a simple animated avatar with:
- Base face image
- Three mouth positions (closed, medium, open)
Args:
avatars_dir: Base directory for avatars
Example:
>>> ensure_sample_avatar(Path("./avatars"))
# Creates ./avatars/sample/ with base.png and mouth_*.png
Note:
This creates a basic placeholder avatar. For better results,
create custom avatars with proper artwork.
"""
sample_dir = avatars_dir / "sample"
# Check if sample already exists with content
if sample_dir.exists() and any(sample_dir.iterdir()):
return
# Create directory
sample_dir.mkdir(parents=True, exist_ok=True)
# Image dimensions
width, height = 512, 512
# Create base image (simple face background)
base = Image.new("RGBA", (width, height), (255, 220, 200, 255))
draw_base = ImageDraw.Draw(base)
# Draw simple face features on base
# Face circle
draw_base.ellipse([56, 56, 456, 456], fill=(255, 230, 210, 255), outline=(200, 150, 130, 255), width=3)
# Eyes
draw_base.ellipse([150, 180, 200, 230], fill=(255, 255, 255, 255), outline=(0, 0, 0, 255), width=2)
draw_base.ellipse([312, 180, 362, 230], fill=(255, 255, 255, 255), outline=(0, 0, 0, 255), width=2)
# Pupils
draw_base.ellipse([165, 195, 185, 215], fill=(50, 50, 50, 255))
draw_base.ellipse([327, 195, 347, 215], fill=(50, 50, 50, 255))
# Eyebrows
draw_base.arc([140, 150, 210, 190], start=200, end=340, fill=(100, 70, 50, 255), width=3)
draw_base.arc([302, 150, 372, 190], start=200, end=340, fill=(100, 70, 50, 255), width=3)
# Nose
draw_base.polygon([(256, 250), (240, 310), (272, 310)], fill=(240, 200, 180, 255))
# Hair (simple)
draw_base.arc([40, 20, 472, 300], start=180, end=360, fill=(80, 50, 30, 255), width=30)
base.save(sample_dir / "base.png")
# Create mouth frames (transparent overlays)
mouth_positions = [
# (y_offset, height) - Mouth closed to open
(0, 8), # mouth_0: Nearly closed
(0, 20), # mouth_1: Slightly open
(0, 35), # mouth_2: Wide open
]
for i, (y_off, mouth_height) in enumerate(mouth_positions):
# Create transparent image for mouth overlay
mouth_img = Image.new("RGBA", (width, height), (0, 0, 0, 0))
draw_mouth = ImageDraw.Draw(mouth_img)
# Calculate mouth position
mouth_y = 340 + y_off
mouth_left = 200
mouth_right = 312
# Draw mouth (ellipse shape)
draw_mouth.ellipse(
[mouth_left, mouth_y, mouth_right, mouth_y + mouth_height],
fill=(180, 80, 80, 255),
outline=(120, 50, 50, 255),
width=2
)
# Add inner mouth detail for open mouths
if mouth_height > 15:
inner_offset = 5
draw_mouth.ellipse(
[mouth_left + inner_offset, mouth_y + inner_offset,
mouth_right - inner_offset, mouth_y + mouth_height - inner_offset],
fill=(100, 40, 40, 255)
)
mouth_img.save(sample_dir / f"mouth_{i}.png")
def list_avatars(avatars_dir: Path) -> List[str]:
"""
Get list of available avatar names.
Scans the avatars directory for valid avatar folders
(containing base.png and mouth_*.png files).
Args:
avatars_dir: Base directory containing avatar folders
Returns:
List of avatar folder names
Example:
>>> avatars = list_avatars(Path("./avatars"))
>>> print(avatars)
['sample', 'anime_girl', 'anime_boy']
"""
# Ensure sample avatar exists
ensure_sample_avatar(avatars_dir)
# Find all valid avatar directories
avatars = []
if avatars_dir.exists():
for path in avatars_dir.iterdir():
if path.is_dir():
# Check for required files
has_base = (path / "base.png").exists()
has_mouth = any(path.glob("mouth_*.png"))
if has_base and has_mouth:
avatars.append(path.name)
return sorted(avatars)
def get_avatar_preview(avatar_name: str, avatars_dir: Path) -> Optional[Image.Image]:
"""
Get a preview image of an avatar.
Composites the base image with the first mouth frame
to show what the avatar looks like.
Args:
avatar_name: Name of the avatar folder
avatars_dir: Base directory containing avatar folders
Returns:
PIL Image object or None if avatar not found
Example:
>>> preview = get_avatar_preview("sample", Path("./avatars"))
>>> preview.show()
"""
avatar_folder = avatars_dir / avatar_name
base_path = avatar_folder / "base.png"
if not base_path.exists():
return None
# Load base image
base = Image.open(base_path).convert("RGBA")
# Find first mouth frame
mouth_frames = sorted(avatar_folder.glob("mouth_*.png"))
if mouth_frames:
mouth = Image.open(mouth_frames[0]).convert("RGBA").resize(base.size)
# Composite mouth onto base
preview = Image.alpha_composite(base, mouth)
else:
preview = base
return preview