| """ |
| File handling utilities for CompI project. |
| """ |
|
|
| import json |
| import yaml |
| from pathlib import Path |
| from typing import Dict, Any, Union |
| from PIL import Image |
| import soundfile as sf |
| import numpy as np |
|
|
| from src.config import OUTPUTS_DIR |
|
|
| def save_image(image: Image.Image, filename: str, subfolder: str = "images") -> Path: |
| """ |
| Save a PIL Image to the outputs directory. |
| |
| Args: |
| image: PIL Image to save |
| filename: Name of the file (with extension) |
| subfolder: Subfolder within outputs directory |
| |
| Returns: |
| Path to saved file |
| """ |
| output_dir = OUTPUTS_DIR / subfolder |
| output_dir.mkdir(parents=True, exist_ok=True) |
| |
| file_path = output_dir / filename |
| image.save(file_path) |
| |
| return file_path |
|
|
| def save_audio(audio_data: np.ndarray, filename: str, |
| sample_rate: int = 22050, subfolder: str = "audio") -> Path: |
| """ |
| Save audio data to the outputs directory. |
| |
| Args: |
| audio_data: Audio data as numpy array |
| filename: Name of the file (with extension) |
| sample_rate: Audio sample rate |
| subfolder: Subfolder within outputs directory |
| |
| Returns: |
| Path to saved file |
| """ |
| output_dir = OUTPUTS_DIR / subfolder |
| output_dir.mkdir(parents=True, exist_ok=True) |
| |
| file_path = output_dir / filename |
| sf.write(file_path, audio_data, sample_rate) |
| |
| return file_path |
|
|
| def load_config(config_path: Union[str, Path]) -> Dict[str, Any]: |
| """ |
| Load configuration from JSON or YAML file. |
| |
| Args: |
| config_path: Path to configuration file |
| |
| Returns: |
| Configuration dictionary |
| """ |
| config_path = Path(config_path) |
| |
| if not config_path.exists(): |
| raise FileNotFoundError(f"Configuration file not found: {config_path}") |
| |
| with open(config_path, 'r') as f: |
| if config_path.suffix.lower() in ['.yml', '.yaml']: |
| return yaml.safe_load(f) |
| elif config_path.suffix.lower() == '.json': |
| return json.load(f) |
| else: |
| raise ValueError(f"Unsupported config file format: {config_path.suffix}") |
|
|
| def ensure_dir(path: Union[str, Path]) -> Path: |
| """ |
| Ensure directory exists, create if it doesn't. |
| |
| Args: |
| path: Directory path |
| |
| Returns: |
| Path object |
| """ |
| path = Path(path) |
| path.mkdir(parents=True, exist_ok=True) |
| return path |
|
|
| def ensure_directory_exists(path: Union[str, Path]) -> Path: |
| """ |
| Alias for ensure_dir for backward compatibility. |
| |
| Args: |
| path: Directory path |
| |
| Returns: |
| Path object |
| """ |
| return ensure_dir(path) |
|
|
| def generate_filename(prompt: str, style: str = "", mood: str = "", |
| seed: int = 0, variation: int = 1, |
| has_audio: bool = False, max_length: int = 100) -> str: |
| """ |
| Generate a descriptive filename for generated images. |
| |
| Args: |
| prompt: Text prompt used for generation |
| style: Art style |
| mood: Mood/atmosphere |
| seed: Random seed used |
| variation: Variation number |
| has_audio: Whether audio was used in generation |
| max_length: Maximum filename length |
| |
| Returns: |
| Generated filename (without extension) |
| """ |
| import re |
| from datetime import datetime |
|
|
| |
| prompt_clean = re.sub(r'[^\w\s-]', '', prompt.lower()) |
| prompt_slug = "_".join(prompt_clean.split()[:6])[:30] |
|
|
| |
| style_slug = re.sub(r'[^\w]', '', style.lower())[:10] if style else "" |
| mood_slug = re.sub(r'[^\w]', '', mood.lower())[:10] if mood else "" |
|
|
| |
| timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") |
|
|
| |
| audio_tag = "_audio" if has_audio else "" |
|
|
| |
| parts = [prompt_slug, style_slug, mood_slug, timestamp, f"seed{seed}", f"v{variation}"] |
| filename = "_".join(filter(None, parts)) + audio_tag |
|
|
| |
| if len(filename) > max_length: |
| filename = filename[:max_length] |
|
|
| return filename |
|
|