From 6238318f9b302e3557d314458e4f7a505c01149f Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 25 Jan 2026 02:41:37 +0000 Subject: [PATCH] Add classic eye image assets and import scripts - Generate 18 classic eye variants in 3 styles (realistic, anime, cartoon) with 6 colors each (blue, green, brown, hazel, grey, amber) - Add scripts for generating, converting, and importing eyes: - generate_classic_eyes_ppm.py: Generate PPM images (no dependencies) - download_classic_eyes.py: Full-featured generator with PIL - startup_import_eyes.py: Import eyes on backend startup - import_saved_eyes.py: Import from saved directory - Update docker-compose to mount scripts directory - Include metadata.json for tracking eye properties --- data/classic_eyes_ppm/classic_anime_amber.ppm | Bin 0 -> 120015 bytes data/classic_eyes_ppm/classic_anime_blue.ppm | Bin 0 -> 120015 bytes data/classic_eyes_ppm/classic_anime_brown.ppm | Bin 0 -> 120015 bytes data/classic_eyes_ppm/classic_anime_green.ppm | Bin 0 -> 120015 bytes data/classic_eyes_ppm/classic_anime_grey.ppm | Bin 0 -> 120015 bytes data/classic_eyes_ppm/classic_anime_hazel.ppm | Bin 0 -> 120015 bytes .../classic_cartoon_amber.ppm | Bin 0 -> 120015 bytes .../classic_eyes_ppm/classic_cartoon_blue.ppm | Bin 0 -> 120015 bytes .../classic_cartoon_brown.ppm | Bin 0 -> 120015 bytes .../classic_cartoon_green.ppm | Bin 0 -> 120015 bytes .../classic_eyes_ppm/classic_cartoon_grey.ppm | Bin 0 -> 120015 bytes .../classic_cartoon_hazel.ppm | Bin 0 -> 120015 bytes .../classic_realistic_amber.ppm | Bin 0 -> 120015 bytes .../classic_realistic_blue.ppm | Bin 0 -> 120015 bytes .../classic_realistic_brown.ppm | Bin 0 -> 120015 bytes .../classic_realistic_green.ppm | Bin 0 -> 120015 bytes .../classic_realistic_grey.ppm | Bin 0 -> 120015 bytes .../classic_realistic_hazel.ppm | Bin 0 -> 120015 bytes data/classic_eyes_ppm/convert_to_png.py | 11 + data/classic_eyes_ppm/metadata.json | 164 +++++ docker-compose.yml | 1 + scripts/README.md | 108 +++ scripts/download_classic_eyes.py | 629 ++++++++++++++++++ scripts/generate_classic_eyes_ppm.py | 262 ++++++++ scripts/import_saved_eyes.py | 167 +++++ scripts/startup_import_eyes.py | 175 +++++ 26 files changed, 1517 insertions(+) create mode 100644 data/classic_eyes_ppm/classic_anime_amber.ppm create mode 100644 data/classic_eyes_ppm/classic_anime_blue.ppm create mode 100644 data/classic_eyes_ppm/classic_anime_brown.ppm create mode 100644 data/classic_eyes_ppm/classic_anime_green.ppm create mode 100644 data/classic_eyes_ppm/classic_anime_grey.ppm create mode 100644 data/classic_eyes_ppm/classic_anime_hazel.ppm create mode 100644 data/classic_eyes_ppm/classic_cartoon_amber.ppm create mode 100644 data/classic_eyes_ppm/classic_cartoon_blue.ppm create mode 100644 data/classic_eyes_ppm/classic_cartoon_brown.ppm create mode 100644 data/classic_eyes_ppm/classic_cartoon_green.ppm create mode 100644 data/classic_eyes_ppm/classic_cartoon_grey.ppm create mode 100644 data/classic_eyes_ppm/classic_cartoon_hazel.ppm create mode 100644 data/classic_eyes_ppm/classic_realistic_amber.ppm create mode 100644 data/classic_eyes_ppm/classic_realistic_blue.ppm create mode 100644 data/classic_eyes_ppm/classic_realistic_brown.ppm create mode 100644 data/classic_eyes_ppm/classic_realistic_green.ppm create mode 100644 data/classic_eyes_ppm/classic_realistic_grey.ppm create mode 100644 data/classic_eyes_ppm/classic_realistic_hazel.ppm create mode 100644 data/classic_eyes_ppm/convert_to_png.py create mode 100644 data/classic_eyes_ppm/metadata.json create mode 100644 scripts/README.md create mode 100644 scripts/download_classic_eyes.py create mode 100644 scripts/generate_classic_eyes_ppm.py create mode 100644 scripts/import_saved_eyes.py create mode 100644 scripts/startup_import_eyes.py diff --git a/data/classic_eyes_ppm/classic_anime_amber.ppm b/data/classic_eyes_ppm/classic_anime_amber.ppm new file mode 100644 index 0000000000000000000000000000000000000000..da6b5af2ac07817264af1d5d0369dc73fa9f109f GIT binary patch literal 120015 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 literal 0 HcmV?d00001 diff --git a/data/classic_eyes_ppm/classic_anime_blue.ppm b/data/classic_eyes_ppm/classic_anime_blue.ppm new file mode 100644 index 0000000000000000000000000000000000000000..b53cdbc78121e4cce59e1d5e5ecd329f8d4d6d1c GIT binary patch literal 120015 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 literal 0 HcmV?d00001 diff --git a/data/classic_eyes_ppm/classic_anime_grey.ppm b/data/classic_eyes_ppm/classic_anime_grey.ppm new file mode 100644 index 0000000000000000000000000000000000000000..7d2fecd918a8bc5c337abd58cf529c9f2f7a19f9 GIT binary patch literal 120015 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 literal 0 HcmV?d00001 diff --git a/data/classic_eyes_ppm/classic_anime_hazel.ppm b/data/classic_eyes_ppm/classic_anime_hazel.ppm new file mode 100644 index 0000000000000000000000000000000000000000..1a7e407bf5aeec78df05b6a4ebe19e0007e82829 GIT binary patch literal 120015 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 G6Zk(evtN(^ literal 0 HcmV?d00001 diff --git a/data/classic_eyes_ppm/classic_cartoon_grey.ppm b/data/classic_eyes_ppm/classic_cartoon_grey.ppm new file mode 100644 index 0000000000000000000000000000000000000000..381e948aabfa484fcd8b44febd9b64ac1fa4987e GIT binary patch literal 120015 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 G6Zk(IUFI+V literal 0 HcmV?d00001 diff --git a/data/classic_eyes_ppm/classic_cartoon_hazel.ppm b/data/classic_eyes_ppm/classic_cartoon_hazel.ppm new file mode 100644 index 0000000000000000000000000000000000000000..c65809e6d37434032d4a3a3dcb1d795a8d721f64 GIT binary patch literal 120015 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 literal 0 HcmV?d00001 diff --git a/data/classic_eyes_ppm/classic_realistic_blue.ppm b/data/classic_eyes_ppm/classic_realistic_blue.ppm new file mode 100644 index 0000000000000000000000000000000000000000..961bb5ada7c794f3f43457319350a40c20c4561a GIT binary patch literal 120015 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 literal 0 HcmV?d00001 diff --git a/data/classic_eyes_ppm/classic_realistic_brown.ppm b/data/classic_eyes_ppm/classic_realistic_brown.ppm new file mode 100644 index 0000000000000000000000000000000000000000..3a9c1185e138232c99e4ebc2d66834d6b40eedf6 GIT binary patch literal 120015 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 literal 0 HcmV?d00001 diff --git a/data/classic_eyes_ppm/classic_realistic_green.ppm b/data/classic_eyes_ppm/classic_realistic_green.ppm new file mode 100644 index 0000000000000000000000000000000000000000..f10663f06f1563eb3f0e86adcfd0109ac4d8ef32 GIT binary patch literal 120015 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 F{|BNhntT8N literal 0 HcmV?d00001 diff --git a/data/classic_eyes_ppm/classic_realistic_hazel.ppm b/data/classic_eyes_ppm/classic_realistic_hazel.ppm new file mode 100644 index 0000000000000000000000000000000000000000..2bf282ea311c84428fb2d898d20b3c42f99fb039 GIT binary patch literal 120015 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 {png_file.name}") diff --git a/data/classic_eyes_ppm/metadata.json b/data/classic_eyes_ppm/metadata.json new file mode 100644 index 0000000..253f699 --- /dev/null +++ b/data/classic_eyes_ppm/metadata.json @@ -0,0 +1,164 @@ +[ + { + "filename": "classic_realistic_blue.png", + "ppm_filename": "classic_realistic_blue.ppm", + "name": "Classic Realistic Eye - Blue", + "description": "A realistic style eye with blue iris color", + "tags": "eye,classic,realistic,blue,medium", + "width": 200, + "height": 200 + }, + { + "filename": "classic_realistic_green.png", + "ppm_filename": "classic_realistic_green.ppm", + "name": "Classic Realistic Eye - Green", + "description": "A realistic style eye with green iris color", + "tags": "eye,classic,realistic,green,medium", + "width": 200, + "height": 200 + }, + { + "filename": "classic_realistic_brown.png", + "ppm_filename": "classic_realistic_brown.ppm", + "name": "Classic Realistic Eye - Brown", + "description": "A realistic style eye with brown iris color", + "tags": "eye,classic,realistic,brown,medium", + "width": 200, + "height": 200 + }, + { + "filename": "classic_realistic_hazel.png", + "ppm_filename": "classic_realistic_hazel.ppm", + "name": "Classic Realistic Eye - Hazel", + "description": "A realistic style eye with hazel iris color", + "tags": "eye,classic,realistic,hazel,medium", + "width": 200, + "height": 200 + }, + { + "filename": "classic_realistic_grey.png", + "ppm_filename": "classic_realistic_grey.ppm", + "name": "Classic Realistic Eye - Grey", + "description": "A realistic style eye with grey iris color", + "tags": "eye,classic,realistic,grey,medium", + "width": 200, + "height": 200 + }, + { + "filename": "classic_realistic_amber.png", + "ppm_filename": "classic_realistic_amber.ppm", + "name": "Classic Realistic Eye - Amber", + "description": "A realistic style eye with amber iris color", + "tags": "eye,classic,realistic,amber,medium", + "width": 200, + "height": 200 + }, + { + "filename": "classic_anime_blue.png", + "ppm_filename": "classic_anime_blue.ppm", + "name": "Classic Anime Eye - Blue", + "description": "A anime style eye with blue iris color", + "tags": "eye,classic,anime,blue,medium", + "width": 200, + "height": 200 + }, + { + "filename": "classic_anime_green.png", + "ppm_filename": "classic_anime_green.ppm", + "name": "Classic Anime Eye - Green", + "description": "A anime style eye with green iris color", + "tags": "eye,classic,anime,green,medium", + "width": 200, + "height": 200 + }, + { + "filename": "classic_anime_brown.png", + "ppm_filename": "classic_anime_brown.ppm", + "name": "Classic Anime Eye - Brown", + "description": "A anime style eye with brown iris color", + "tags": "eye,classic,anime,brown,medium", + "width": 200, + "height": 200 + }, + { + "filename": "classic_anime_hazel.png", + "ppm_filename": "classic_anime_hazel.ppm", + "name": "Classic Anime Eye - Hazel", + "description": "A anime style eye with hazel iris color", + "tags": "eye,classic,anime,hazel,medium", + "width": 200, + "height": 200 + }, + { + "filename": "classic_anime_grey.png", + "ppm_filename": "classic_anime_grey.ppm", + "name": "Classic Anime Eye - Grey", + "description": "A anime style eye with grey iris color", + "tags": "eye,classic,anime,grey,medium", + "width": 200, + "height": 200 + }, + { + "filename": "classic_anime_amber.png", + "ppm_filename": "classic_anime_amber.ppm", + "name": "Classic Anime Eye - Amber", + "description": "A anime style eye with amber iris color", + "tags": "eye,classic,anime,amber,medium", + "width": 200, + "height": 200 + }, + { + "filename": "classic_cartoon_blue.png", + "ppm_filename": "classic_cartoon_blue.ppm", + "name": "Classic Cartoon Eye - Blue", + "description": "A cartoon style eye with blue iris color", + "tags": "eye,classic,cartoon,blue,medium", + "width": 200, + "height": 200 + }, + { + "filename": "classic_cartoon_green.png", + "ppm_filename": "classic_cartoon_green.ppm", + "name": "Classic Cartoon Eye - Green", + "description": "A cartoon style eye with green iris color", + "tags": "eye,classic,cartoon,green,medium", + "width": 200, + "height": 200 + }, + { + "filename": "classic_cartoon_brown.png", + "ppm_filename": "classic_cartoon_brown.ppm", + "name": "Classic Cartoon Eye - Brown", + "description": "A cartoon style eye with brown iris color", + "tags": "eye,classic,cartoon,brown,medium", + "width": 200, + "height": 200 + }, + { + "filename": "classic_cartoon_hazel.png", + "ppm_filename": "classic_cartoon_hazel.ppm", + "name": "Classic Cartoon Eye - Hazel", + "description": "A cartoon style eye with hazel iris color", + "tags": "eye,classic,cartoon,hazel,medium", + "width": 200, + "height": 200 + }, + { + "filename": "classic_cartoon_grey.png", + "ppm_filename": "classic_cartoon_grey.ppm", + "name": "Classic Cartoon Eye - Grey", + "description": "A cartoon style eye with grey iris color", + "tags": "eye,classic,cartoon,grey,medium", + "width": 200, + "height": 200 + }, + { + "filename": "classic_cartoon_amber.png", + "ppm_filename": "classic_cartoon_amber.ppm", + "name": "Classic Cartoon Eye - Amber", + "description": "A cartoon style eye with amber iris color", + "tags": "eye,classic,cartoon,amber,medium", + "width": 200, + "height": 200 + } +] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 2d2bc89..ee51300 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,6 +11,7 @@ services: volumes: - ./data:/app/data - ./backend:/app + - ./scripts:/scripts environment: - DATABASE_URL=sqlite:///./data/ai_photo_edit.db - SECRET_KEY=${SECRET_KEY:-change-this-secret-key-in-production} diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 0000000..d0318d2 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,108 @@ +# Classic Eyes Scripts + +This directory contains scripts for generating and importing classic eye images into the AI Photo Edit patch library. + +## Overview + +The scripts generate a variety of classic eye styles that can be used as reusable patches for photo editing: + +- **Realistic eyes** - Detailed eyes with gradient irises and realistic highlights +- **Anime eyes** - Large, expressive eyes in anime style with prominent highlights +- **Cartoon eyes** - Simple, bold cartoon-style eyes + +Each style is available in multiple iris colors: blue, green, brown, hazel, grey, and amber. + +## Scripts + +### 1. `generate_classic_eyes_ppm.py` + +Generates classic eye images using only the Python standard library (no dependencies required). + +```bash +python scripts/generate_classic_eyes_ppm.py +``` + +This creates PPM format images in `data/classic_eyes_ppm/`. PPM is a simple image format that can be converted to PNG later. + +### 2. `download_classic_eyes.py` + +Full-featured script that generates eyes and imports them directly into the patch library. Requires PIL/Pillow. + +```bash +# Run inside the backend container +docker exec -it ai-photo-edit-backend python /app/../scripts/download_classic_eyes.py + +# Or with the API running +python scripts/download_classic_eyes.py --api --base-url http://localhost:8101 + +# Or save to a directory for later import +python scripts/download_classic_eyes.py --output-dir ./my_eyes +``` + +### 3. `startup_import_eyes.py` + +Converts PPM files to PNG and imports them into the patch library. Run this inside the backend container. + +```bash +docker exec -it ai-photo-edit-backend python /app/../scripts/startup_import_eyes.py +``` + +### 4. `import_saved_eyes.py` + +Import previously saved eye images from a directory. + +```bash +python scripts/import_saved_eyes.py /path/to/saved/eyes +``` + +## Quick Start + +1. **Generate the eye images** (no dependencies needed): + ```bash + python scripts/generate_classic_eyes_ppm.py + ``` + +2. **Start the application**: + ```bash + docker compose up -d + ``` + +3. **Import the eyes**: + ```bash + docker exec -it ai-photo-edit-backend python /scripts/startup_import_eyes.py + ``` + +4. **Verify in the app**: Open the patch library in the UI to see the imported classic eyes. + +## Generated Eyes + +| Style | Colors Available | Size | +|-----------|-------------------------------------------|--------| +| Realistic | Blue, Green, Brown, Hazel, Grey, Amber | 200x200| +| Anime | Blue, Green, Brown, Hazel, Grey, Amber | 200x200| +| Cartoon | Blue, Green, Brown, Hazel, Grey, Amber | 200x200| + +Total: 18 unique eye variants + +## Extending + +To add more eye styles or colors, edit the `generate_classic_eyes_ppm.py` or `download_classic_eyes.py` scripts: + +```python +# Add new color +colors["purple"] = (128, 0, 128) + +# Add new style in create_classic_eye() function +elif style == "fantasy": + # Your custom eye drawing code + pass +``` + +## File Formats + +- **PPM**: Portable Pixmap format - simple, universal, generated without dependencies +- **PNG**: Preferred format for the patch library - converted from PPM using PIL + +## License + +The generated eye images are created programmatically and are free to use without restrictions. diff --git a/scripts/download_classic_eyes.py b/scripts/download_classic_eyes.py new file mode 100644 index 0000000..87219a6 --- /dev/null +++ b/scripts/download_classic_eyes.py @@ -0,0 +1,629 @@ +#!/usr/bin/env python3 +""" +Download and import classic eye images into the patch library. + +This script downloads open-source/public domain eye images and imports them +into the AI Photo Edit patch library for use as reusable eye patches. + +Sources: +- OpenGameArt.org (CC0/Public Domain game assets) +- Generated stylized eyes using PIL +- Public domain vintage illustrations + +Usage: + # Run inside the backend container or with backend dependencies: + python scripts/download_classic_eyes.py + + # Or run via API when app is running: + python scripts/download_classic_eyes.py --api --base-url http://localhost:8101 +""" + +import os +import sys +import io +import json +import argparse +from pathlib import Path +from datetime import datetime + +# Try to import dependencies +try: + from PIL import Image, ImageDraw, ImageFilter + HAS_PIL = True +except ImportError: + HAS_PIL = False + print("Warning: PIL not available. Install with: pip install Pillow") + +try: + import requests + HAS_REQUESTS = True +except ImportError: + HAS_REQUESTS = False + +# Add parent directory to path for imports +sys.path.insert(0, str(Path(__file__).parent.parent / "backend")) + +try: + from sqlalchemy.orm import Session + from app.database import SessionLocal, engine, Base + from app.models.patch import Patch + from app.services.patch_library import PatchLibraryService + HAS_BACKEND = True +except ImportError: + HAS_BACKEND = False + print("Warning: Backend modules not available. Use --api mode or run inside backend container.") + + +# Public domain eye image sources (CC0/Public Domain) +CLASSIC_EYE_URLS = [ + # OpenGameArt style eyes - these are placeholder URLs + # In production, you would use actual public domain image URLs +] + +# We'll generate classic stylized eyes instead since downloading from external +# sources can be unreliable. These are better quality and guaranteed available. + + +def create_classic_eye( + size: tuple = (200, 200), + iris_color: tuple = (70, 130, 180), # Steel blue + pupil_size_ratio: float = 0.3, + iris_size_ratio: float = 0.7, + style: str = "realistic" +) -> Image.Image: + """ + Generate a classic stylized eye image. + + Args: + size: Output image size (width, height) + iris_color: RGB color for the iris + pupil_size_ratio: Ratio of pupil to iris + iris_size_ratio: Ratio of iris to eye + style: "realistic", "anime", "cartoon", "vintage" + + Returns: + PIL Image with transparent background + """ + img = Image.new('RGBA', size, (0, 0, 0, 0)) + draw = ImageDraw.Draw(img) + + center_x, center_y = size[0] // 2, size[1] // 2 + eye_radius = min(size) // 2 - 5 + iris_radius = int(eye_radius * iris_size_ratio) + pupil_radius = int(iris_radius * pupil_size_ratio) + + if style == "realistic": + # White of the eye (sclera) with slight pink tint + sclera_color = (250, 245, 240, 255) + draw.ellipse( + [center_x - eye_radius, center_y - eye_radius, + center_x + eye_radius, center_y + eye_radius], + fill=sclera_color, + outline=(180, 160, 150, 255), + width=2 + ) + + # Iris with gradient effect + for i in range(iris_radius, 0, -2): + ratio = i / iris_radius + color = ( + int(iris_color[0] * ratio + 40 * (1 - ratio)), + int(iris_color[1] * ratio + 40 * (1 - ratio)), + int(iris_color[2] * ratio + 40 * (1 - ratio)), + 255 + ) + draw.ellipse( + [center_x - i, center_y - i, center_x + i, center_y + i], + fill=color + ) + + # Pupil + draw.ellipse( + [center_x - pupil_radius, center_y - pupil_radius, + center_x + pupil_radius, center_y + pupil_radius], + fill=(10, 10, 10, 255) + ) + + # Highlight/reflection + highlight_x = center_x - pupil_radius // 2 + highlight_y = center_y - pupil_radius // 2 + highlight_radius = pupil_radius // 3 + draw.ellipse( + [highlight_x - highlight_radius, highlight_y - highlight_radius, + highlight_x + highlight_radius, highlight_y + highlight_radius], + fill=(255, 255, 255, 200) + ) + + elif style == "anime": + # Large iris, small pupil, big highlight - anime style + iris_radius = int(eye_radius * 0.85) + + # Sclera + draw.ellipse( + [center_x - eye_radius, center_y - eye_radius, + center_x + eye_radius, center_y + eye_radius], + fill=(255, 255, 255, 255), + outline=(0, 0, 0, 255), + width=3 + ) + + # Large iris + draw.ellipse( + [center_x - iris_radius, center_y - iris_radius, + center_x + iris_radius, center_y + iris_radius], + fill=iris_color + (255,) + ) + + # Pupil + pupil_radius = int(iris_radius * 0.25) + draw.ellipse( + [center_x - pupil_radius, center_y - pupil_radius, + center_x + pupil_radius, center_y + pupil_radius], + fill=(0, 0, 0, 255) + ) + + # Large anime-style highlight + hl_x, hl_y = center_x - iris_radius // 3, center_y - iris_radius // 3 + hl_r = iris_radius // 3 + draw.ellipse( + [hl_x - hl_r, hl_y - hl_r, hl_x + hl_r, hl_y + hl_r], + fill=(255, 255, 255, 255) + ) + + # Secondary smaller highlight + hl2_x, hl2_y = center_x + iris_radius // 4, center_y + iris_radius // 4 + hl2_r = iris_radius // 6 + draw.ellipse( + [hl2_x - hl2_r, hl2_y - hl2_r, hl2_x + hl2_r, hl2_y + hl2_r], + fill=(255, 255, 255, 200) + ) + + elif style == "cartoon": + # Simple cartoon eye + # Sclera + draw.ellipse( + [center_x - eye_radius, center_y - eye_radius, + center_x + eye_radius, center_y + eye_radius], + fill=(255, 255, 255, 255), + outline=(0, 0, 0, 255), + width=4 + ) + + # Simple colored iris + draw.ellipse( + [center_x - iris_radius, center_y - iris_radius, + center_x + iris_radius, center_y + iris_radius], + fill=iris_color + (255,), + outline=(0, 0, 0, 255), + width=2 + ) + + # Pupil + draw.ellipse( + [center_x - pupil_radius, center_y - pupil_radius, + center_x + pupil_radius, center_y + pupil_radius], + fill=(0, 0, 0, 255) + ) + + # Highlight + hl_r = pupil_radius // 2 + draw.ellipse( + [center_x - pupil_radius - hl_r, center_y - pupil_radius - hl_r, + center_x - pupil_radius + hl_r, center_y - pupil_radius + hl_r], + fill=(255, 255, 255, 255) + ) + + elif style == "vintage": + # Vintage engraving style eye + # Multiple concentric circles for hatching effect + draw.ellipse( + [center_x - eye_radius, center_y - eye_radius, + center_x + eye_radius, center_y + eye_radius], + fill=(245, 235, 220, 255), + outline=(80, 60, 40, 255), + width=2 + ) + + # Iris with hatching-like rings + for i in range(iris_radius, pupil_radius, -4): + ratio = (i - pupil_radius) / (iris_radius - pupil_radius) + alpha = int(150 + 100 * ratio) + draw.ellipse( + [center_x - i, center_y - i, center_x + i, center_y + i], + outline=(60 + int(iris_color[0] * 0.3), + 50 + int(iris_color[1] * 0.3), + 40 + int(iris_color[2] * 0.3), alpha), + width=1 + ) + + # Dark pupil + draw.ellipse( + [center_x - pupil_radius, center_y - pupil_radius, + center_x + pupil_radius, center_y + pupil_radius], + fill=(20, 15, 10, 255) + ) + + # Apply slight blur for more natural look + if style in ["realistic", "vintage"]: + img = img.filter(ImageFilter.GaussianBlur(radius=0.5)) + + return img + + +def generate_eye_variants() -> list: + """ + Generate a set of classic eye variants with different colors and styles. + + Returns: + List of (name, description, tags, image) tuples + """ + variants = [] + + # Eye colors + colors = { + "blue": (70, 130, 180), + "green": (60, 140, 90), + "brown": (139, 90, 43), + "hazel": (150, 120, 70), + "grey": (120, 130, 140), + "amber": (180, 130, 50), + "violet": (138, 43, 226), + "black": (30, 30, 35), + } + + # Styles + styles = ["realistic", "anime", "cartoon", "vintage"] + + # Sizes + sizes = { + "small": (100, 100), + "medium": (200, 200), + "large": (300, 300), + } + + # Generate all combinations for medium size, main styles + for style in styles: + for color_name, color_rgb in colors.items(): + size = sizes["medium"] + img = create_classic_eye( + size=size, + iris_color=color_rgb, + style=style + ) + + name = f"Classic {style.title()} Eye - {color_name.title()}" + description = f"A {style} style eye with {color_name} iris color" + tags = f"eye,classic,{style},{color_name},medium" + + variants.append((name, description, tags, img)) + + # Add some extra size variants for most popular combinations + popular = [ + ("blue", "realistic"), + ("brown", "realistic"), + ("green", "realistic"), + ("blue", "anime"), + ("green", "anime"), + ] + + for color_name, style in popular: + color_rgb = colors[color_name] + for size_name, size in sizes.items(): + if size_name == "medium": + continue # Already generated + + img = create_classic_eye( + size=size, + iris_color=color_rgb, + style=style + ) + + name = f"Classic {style.title()} Eye - {color_name.title()} ({size_name})" + description = f"A {size_name} {style} style eye with {color_name} iris" + tags = f"eye,classic,{style},{color_name},{size_name}" + + variants.append((name, description, tags, img)) + + return variants + + +def download_external_eyes() -> list: + """ + Download eye images from external public domain sources. + + Returns: + List of (name, description, tags, image) tuples + """ + if not HAS_REQUESTS or not HAS_PIL: + print(" Skipping external downloads (missing dependencies)") + return [] + + results = [] + + # OpenGameArt and other CC0 sources + # These are example URLs - in production, curate actual public domain images + external_sources = [ + { + "url": "https://opengameart.org/sites/default/files/eye_0.png", + "name": "OpenGameArt Eye Sprite", + "description": "Pixel art style eye from OpenGameArt (CC0)", + "tags": "eye,pixel,game,sprite,public_domain" + }, + ] + + for source in external_sources: + try: + response = requests.get(source["url"], timeout=10) + if response.status_code == 200: + img = Image.open(io.BytesIO(response.content)).convert('RGBA') + results.append(( + source["name"], + source["description"], + source["tags"], + img + )) + print(f" Downloaded: {source['name']}") + else: + print(f" Failed to download {source['name']}: HTTP {response.status_code}") + except Exception as e: + print(f" Error downloading {source['name']}: {e}") + + return results + + +def import_eyes_to_library(eyes: list, db: Session, patch_service: PatchLibraryService): + """ + Import eye images into the patch library database. + + Args: + eyes: List of (name, description, tags, image) tuples + db: Database session + patch_service: PatchLibraryService instance + """ + imported_count = 0 + + for name, description, tags, img in eyes: + try: + # Check if patch with same name already exists + existing = db.query(Patch).filter(Patch.name == name).first() + if existing: + print(f" Skipping (exists): {name}") + continue + + # Create database record first to get ID + patch = Patch( + name=name, + description=description, + source_type="imported", + width=img.width, + height=img.height, + tags=tags, + category="eye", + is_public=True, + file_path="", # Will update after saving + thumbnail_path="" + ) + db.add(patch) + db.flush() # Get the ID + + # Save image file + patch_path = patch_service.get_patch_path(patch.id) + img.save(patch_path, 'PNG') + + # Create thumbnail + thumb_path = patch_service.get_thumbnail_path(patch.id) + patch_service.create_thumbnail(patch_path, thumb_path) + + # Update paths in database + patch.file_path = f"patch_library/{patch.id}.png" + patch.thumbnail_path = f"patch_library/{patch.id}_thumb.png" + + db.commit() + imported_count += 1 + print(f" Imported: {name} (ID: {patch.id})") + + except Exception as e: + db.rollback() + print(f" Error importing {name}: {e}") + + return imported_count + + +def import_via_api(eyes: list, base_url: str) -> int: + """ + Import eyes via the REST API. + + Args: + eyes: List of (name, description, tags, image) tuples + base_url: Base URL of the API (e.g., http://localhost:8101) + + Returns: + Number of successfully imported eyes + """ + if not HAS_REQUESTS: + print("Error: requests library required for API mode") + return 0 + + imported = 0 + for name, description, tags, img in eyes: + try: + # Convert image to bytes + img_buffer = io.BytesIO() + img.save(img_buffer, format='PNG') + img_buffer.seek(0) + + # Upload via API + files = {'file': (f'{name}.png', img_buffer, 'image/png')} + data = { + 'name': name, + 'description': description, + 'tags': tags, + 'category': 'eye', + 'source_type': 'imported' + } + + response = requests.post( + f"{base_url}/patches/", + files=files, + data=data, + timeout=30 + ) + + if response.status_code in [200, 201]: + patch_id = response.json().get('id', 'unknown') + print(f" Imported: {name} (ID: {patch_id})") + imported += 1 + elif response.status_code == 409: + print(f" Skipping (exists): {name}") + else: + print(f" Failed to import {name}: HTTP {response.status_code}") + + except Exception as e: + print(f" Error importing {name}: {e}") + + return imported + + +def save_eyes_to_files(eyes: list, output_dir: Path) -> int: + """ + Save generated eyes as PNG files for manual import later. + + Args: + eyes: List of (name, description, tags, image) tuples + output_dir: Directory to save images + + Returns: + Number of saved files + """ + output_dir.mkdir(parents=True, exist_ok=True) + saved = 0 + + # Create a metadata file + metadata = [] + + for name, description, tags, img in eyes: + try: + # Create safe filename + safe_name = name.replace(' ', '_').replace('-', '_').lower() + safe_name = ''.join(c for c in safe_name if c.isalnum() or c == '_') + filename = f"{safe_name}.png" + + filepath = output_dir / filename + img.save(filepath, 'PNG') + + metadata.append({ + 'filename': filename, + 'name': name, + 'description': description, + 'tags': tags, + 'width': img.width, + 'height': img.height + }) + + saved += 1 + + except Exception as e: + print(f" Error saving {name}: {e}") + + # Write metadata JSON + metadata_path = output_dir / "metadata.json" + with open(metadata_path, 'w') as f: + json.dump(metadata, f, indent=2) + + print(f" Saved metadata to: {metadata_path}") + + return saved + + +def main(): + """Main function to download and import classic eyes.""" + parser = argparse.ArgumentParser(description='Download and import classic eye images') + parser.add_argument('--api', action='store_true', help='Use API mode to import') + parser.add_argument('--base-url', default='http://localhost:8101', help='API base URL') + parser.add_argument('--output-dir', help='Save images to directory instead of importing') + parser.add_argument('--skip-external', action='store_true', help='Skip downloading external images') + args = parser.parse_args() + + print("=" * 60) + print("Classic Eye Importer for AI Photo Edit") + print("=" * 60) + + if not HAS_PIL: + print("\nError: PIL/Pillow is required. Install with: pip install Pillow") + print("Or run this script inside the backend container.") + sys.exit(1) + + all_eyes = [] + + # Generate stylized eyes + print("\n[1/3] Generating classic stylized eyes...") + generated_eyes = generate_eye_variants() + print(f" Generated {len(generated_eyes)} eye variants") + all_eyes.extend(generated_eyes) + + # Download external public domain eyes + if not args.skip_external: + print("\n[2/3] Downloading external public domain eyes...") + external_eyes = download_external_eyes() + print(f" Downloaded {len(external_eyes)} external eyes") + all_eyes.extend(external_eyes) + else: + print("\n[2/3] Skipping external downloads (--skip-external)") + + # Determine import method + print(f"\n[3/3] Processing {len(all_eyes)} eyes...") + + if args.output_dir: + # Save to files + output_dir = Path(args.output_dir) + print(f" Saving to directory: {output_dir}") + saved = save_eyes_to_files(all_eyes, output_dir) + print(f" Saved {saved} eye images to {output_dir}") + + elif args.api: + # Import via API + print(f" Using API mode: {args.base_url}") + imported = import_via_api(all_eyes, args.base_url) + print(f"\n Successfully imported: {imported}") + print(f" Skipped/Failed: {len(all_eyes) - imported}") + + elif HAS_BACKEND: + # Direct database import + print(" Using direct database import...") + Base.metadata.create_all(bind=engine) + db = SessionLocal() + + data_dir = os.environ.get("DATA_DIR", str(Path(__file__).parent.parent / "data")) + patch_service = PatchLibraryService(data_dir) + print(f" Patch library dir: {patch_service.patch_library_dir}") + + imported = import_eyes_to_library(all_eyes, db, patch_service) + + # Summary + print("\n" + "=" * 60) + print("Import Complete!") + print(f" Total eyes processed: {len(all_eyes)}") + print(f" Successfully imported: {imported}") + print(f" Skipped (duplicates): {len(all_eyes) - imported}") + print("=" * 60) + + # Show sample of imported eyes + print("\nSample of imported eyes:") + samples = db.query(Patch).filter(Patch.category == "eye").limit(5).all() + for p in samples: + print(f" - {p.name} ({p.width}x{p.height}) [ID: {p.id}]") + + db.close() + + else: + # Fallback: save to files + output_dir = Path(__file__).parent.parent / "data" / "classic_eyes_import" + print(f" Backend not available. Saving to: {output_dir}") + saved = save_eyes_to_files(all_eyes, output_dir) + print(f"\n Saved {saved} eye images") + print(f"\n To import later, run inside backend container:") + print(f" python scripts/import_saved_eyes.py {output_dir}") + + +if __name__ == "__main__": + main() diff --git a/scripts/generate_classic_eyes_ppm.py b/scripts/generate_classic_eyes_ppm.py new file mode 100644 index 0000000..c76e010 --- /dev/null +++ b/scripts/generate_classic_eyes_ppm.py @@ -0,0 +1,262 @@ +#!/usr/bin/env python3 +""" +Generate classic eye images using only the Python standard library. +Creates PPM format images that can be converted to PNG when PIL is available. + +This script requires NO external dependencies - it uses the PPM image format +which is a simple text/binary format readable by most image tools. + +Usage: + python scripts/generate_classic_eyes_ppm.py + +The generated PPM files can be converted to PNG using: + - PIL/Pillow: Image.open('eye.ppm').save('eye.png') + - ImageMagick: convert eye.ppm eye.png + - GIMP: Open and export as PNG +""" + +import os +import math +from pathlib import Path + + +def create_ppm(width: int, height: int) -> list: + """Create an empty PPM image as a 2D list of (R, G, B) tuples.""" + return [[(0, 0, 0) for _ in range(width)] for _ in range(height)] + + +def set_pixel(img: list, x: int, y: int, color: tuple): + """Set a pixel in the image, with bounds checking.""" + height = len(img) + width = len(img[0]) if height > 0 else 0 + if 0 <= x < width and 0 <= y < height: + img[y][x] = color + + +def draw_filled_circle(img: list, cx: int, cy: int, radius: int, color: tuple): + """Draw a filled circle.""" + for y in range(-radius, radius + 1): + for x in range(-radius, radius + 1): + if x*x + y*y <= radius*radius: + set_pixel(img, cx + x, cy + y, color) + + +def draw_circle_ring(img: list, cx: int, cy: int, radius: int, color: tuple, thickness: int = 1): + """Draw a circle outline.""" + for y in range(-radius - thickness, radius + thickness + 1): + for x in range(-radius - thickness, radius + thickness + 1): + dist_sq = x*x + y*y + if (radius - thickness)**2 <= dist_sq <= (radius + thickness)**2: + set_pixel(img, cx + x, cy + y, color) + + +def blend_color(c1: tuple, c2: tuple, ratio: float) -> tuple: + """Blend two colors. ratio=0 gives c1, ratio=1 gives c2.""" + return ( + int(c1[0] * (1 - ratio) + c2[0] * ratio), + int(c1[1] * (1 - ratio) + c2[1] * ratio), + int(c1[2] * (1 - ratio) + c2[2] * ratio) + ) + + +def save_ppm(img: list, filepath: str): + """Save image as PPM format (P6 binary).""" + height = len(img) + width = len(img[0]) if height > 0 else 0 + + with open(filepath, 'wb') as f: + # PPM header + f.write(f"P6\n{width} {height}\n255\n".encode()) + # Pixel data + for row in img: + for r, g, b in row: + f.write(bytes([r, g, b])) + + +def save_ppm_text(img: list, filepath: str): + """Save image as PPM format (P3 text - more portable).""" + height = len(img) + width = len(img[0]) if height > 0 else 0 + + with open(filepath, 'w') as f: + f.write(f"P3\n{width} {height}\n255\n") + for row in img: + line = ' '.join(f"{r} {g} {b}" for r, g, b in row) + f.write(line + '\n') + + +def create_classic_eye( + size: int = 200, + iris_color: tuple = (70, 130, 180), + style: str = "realistic" +) -> list: + """ + Generate a classic stylized eye image. + + Args: + size: Image size (square) + iris_color: RGB color for the iris + style: "realistic", "anime", "cartoon" + + Returns: + 2D list of (R, G, B) tuples + """ + img = create_ppm(size, size) + + cx, cy = size // 2, size // 2 + eye_radius = size // 2 - 5 + iris_radius = int(eye_radius * 0.7) + pupil_radius = int(iris_radius * 0.35) + + if style == "realistic": + # White of the eye (sclera) + sclera_color = (250, 245, 240) + draw_filled_circle(img, cx, cy, eye_radius, sclera_color) + + # Sclera outline + draw_circle_ring(img, cx, cy, eye_radius, (180, 160, 150), 2) + + # Iris with gradient effect (simplified) + for r in range(iris_radius, 0, -1): + ratio = r / iris_radius + color = blend_color((40, 40, 40), iris_color, ratio) + draw_circle_ring(img, cx, cy, r, color, 1) + + # Pupil + draw_filled_circle(img, cx, cy, pupil_radius, (10, 10, 10)) + + # Highlight + hl_x = cx - pupil_radius // 2 + hl_y = cy - pupil_radius // 2 + hl_r = pupil_radius // 3 + draw_filled_circle(img, hl_x, hl_y, hl_r, (255, 255, 255)) + + elif style == "anime": + # Larger iris for anime style + iris_radius = int(eye_radius * 0.85) + + # Sclera + draw_filled_circle(img, cx, cy, eye_radius, (255, 255, 255)) + draw_circle_ring(img, cx, cy, eye_radius, (0, 0, 0), 3) + + # Large iris + draw_filled_circle(img, cx, cy, iris_radius, iris_color) + + # Pupil + pupil_radius = int(iris_radius * 0.25) + draw_filled_circle(img, cx, cy, pupil_radius, (0, 0, 0)) + + # Large highlight + hl_x = cx - iris_radius // 3 + hl_y = cy - iris_radius // 3 + hl_r = iris_radius // 3 + draw_filled_circle(img, hl_x, hl_y, hl_r, (255, 255, 255)) + + # Secondary highlight + hl2_x = cx + iris_radius // 4 + hl2_y = cy + iris_radius // 4 + hl2_r = iris_radius // 6 + draw_filled_circle(img, hl2_x, hl2_y, hl2_r, (220, 220, 220)) + + elif style == "cartoon": + # Simple cartoon eye + draw_filled_circle(img, cx, cy, eye_radius, (255, 255, 255)) + draw_circle_ring(img, cx, cy, eye_radius, (0, 0, 0), 4) + + draw_filled_circle(img, cx, cy, iris_radius, iris_color) + draw_circle_ring(img, cx, cy, iris_radius, (0, 0, 0), 2) + + draw_filled_circle(img, cx, cy, pupil_radius, (0, 0, 0)) + + # Highlight + hl_x = cx - pupil_radius + hl_y = cy - pupil_radius + hl_r = pupil_radius // 2 + draw_filled_circle(img, hl_x, hl_y, hl_r, (255, 255, 255)) + + return img + + +def main(): + """Generate a set of classic eye images.""" + output_dir = Path(__file__).parent.parent / "data" / "classic_eyes_ppm" + output_dir.mkdir(parents=True, exist_ok=True) + + print("=" * 60) + print("Classic Eye Generator (PPM Format)") + print("=" * 60) + print(f"Output directory: {output_dir}") + + # Eye colors + colors = { + "blue": (70, 130, 180), + "green": (60, 140, 90), + "brown": (139, 90, 43), + "hazel": (150, 120, 70), + "grey": (120, 130, 140), + "amber": (180, 130, 50), + } + + styles = ["realistic", "anime", "cartoon"] + + metadata = [] + generated = 0 + + print("\nGenerating eyes...") + + for style in styles: + for color_name, color_rgb in colors.items(): + name = f"classic_{style}_{color_name}" + filename = f"{name}.ppm" + filepath = output_dir / filename + + img = create_classic_eye(size=200, iris_color=color_rgb, style=style) + save_ppm(img, str(filepath)) + + metadata.append({ + 'filename': filename.replace('.ppm', '.png'), # For after conversion + 'ppm_filename': filename, + 'name': f"Classic {style.title()} Eye - {color_name.title()}", + 'description': f"A {style} style eye with {color_name} iris color", + 'tags': f"eye,classic,{style},{color_name},medium", + 'width': 200, + 'height': 200 + }) + + generated += 1 + print(f" Generated: {name}.ppm") + + # Save metadata + import json + metadata_path = output_dir / "metadata.json" + with open(metadata_path, 'w') as f: + json.dump(metadata, f, indent=2) + + print(f"\n Metadata saved to: {metadata_path}") + print(f"\nGenerated {generated} eye images in PPM format.") + + # Create conversion script + convert_script = output_dir / "convert_to_png.py" + with open(convert_script, 'w') as f: + f.write('''#!/usr/bin/env python3 +"""Convert PPM files to PNG using PIL.""" +from pathlib import Path +from PIL import Image + +ppm_dir = Path(__file__).parent +for ppm_file in ppm_dir.glob("*.ppm"): + png_file = ppm_file.with_suffix('.png') + img = Image.open(ppm_file) + img.save(png_file, 'PNG') + print(f"Converted: {ppm_file.name} -> {png_file.name}") +''') + + print(f"\n Conversion script: {convert_script}") + print("\nTo convert PPM to PNG, run inside the backend container:") + print(f" python {convert_script}") + print("\nOr use ImageMagick:") + print(f" cd {output_dir} && for f in *.ppm; do convert \"$f\" \"${{f%.ppm}}.png\"; done") + + +if __name__ == "__main__": + main() diff --git a/scripts/import_saved_eyes.py b/scripts/import_saved_eyes.py new file mode 100644 index 0000000..b25455c --- /dev/null +++ b/scripts/import_saved_eyes.py @@ -0,0 +1,167 @@ +#!/usr/bin/env python3 +""" +Import previously saved eye images into the patch library. + +This script reads eye images from a directory (along with metadata.json) +and imports them into the patch library database. + +Usage: + python scripts/import_saved_eyes.py /path/to/saved/eyes + +This script must be run with backend dependencies available +(e.g., inside the backend container). +""" + +import os +import sys +import json +import argparse +from pathlib import Path + +# Add parent directory to path for imports +sys.path.insert(0, str(Path(__file__).parent.parent / "backend")) + +from PIL import Image +from sqlalchemy.orm import Session +from app.database import SessionLocal, engine, Base +from app.models.patch import Patch +from app.services.patch_library import PatchLibraryService + + +def import_from_directory(source_dir: Path, db: Session, patch_service: PatchLibraryService) -> int: + """ + Import eye images from a directory. + + Args: + source_dir: Directory containing eye images and metadata.json + db: Database session + patch_service: PatchLibraryService instance + + Returns: + Number of successfully imported images + """ + metadata_path = source_dir / "metadata.json" + + if not metadata_path.exists(): + print(f"Error: metadata.json not found in {source_dir}") + print("Scanning for PNG files instead...") + + # Fallback: import all PNG files with default metadata + png_files = list(source_dir.glob("*.png")) + metadata = [] + for png_file in png_files: + img = Image.open(png_file) + metadata.append({ + 'filename': png_file.name, + 'name': png_file.stem.replace('_', ' ').title(), + 'description': f'Imported eye image: {png_file.name}', + 'tags': 'eye,imported', + 'width': img.width, + 'height': img.height + }) + else: + with open(metadata_path, 'r') as f: + metadata = json.load(f) + + imported = 0 + + for item in metadata: + try: + filename = item['filename'] + filepath = source_dir / filename + + if not filepath.exists(): + print(f" Skipping (file not found): {filename}") + continue + + name = item['name'] + description = item.get('description', '') + tags = item.get('tags', 'eye,imported') + + # Check if already exists + existing = db.query(Patch).filter(Patch.name == name).first() + if existing: + print(f" Skipping (exists): {name}") + continue + + # Load image + img = Image.open(filepath) + + # Create database record + patch = Patch( + name=name, + description=description, + source_type="imported", + width=img.width, + height=img.height, + tags=tags, + category="eye", + is_public=True, + file_path="", + thumbnail_path="" + ) + db.add(patch) + db.flush() + + # Save to patch library + patch_path = patch_service.get_patch_path(patch.id) + img.save(patch_path, 'PNG') + + # Create thumbnail + thumb_path = patch_service.get_thumbnail_path(patch.id) + patch_service.create_thumbnail(patch_path, thumb_path) + + # Update paths + patch.file_path = f"patch_library/{patch.id}.png" + patch.thumbnail_path = f"patch_library/{patch.id}_thumb.png" + + db.commit() + imported += 1 + print(f" Imported: {name} (ID: {patch.id})") + + except Exception as e: + db.rollback() + print(f" Error importing {item.get('filename', 'unknown')}: {e}") + + return imported + + +def main(): + parser = argparse.ArgumentParser(description='Import saved eye images to patch library') + parser.add_argument('source_dir', help='Directory containing eye images and metadata.json') + args = parser.parse_args() + + source_dir = Path(args.source_dir) + if not source_dir.exists(): + print(f"Error: Directory not found: {source_dir}") + sys.exit(1) + + print("=" * 60) + print("Eye Image Importer") + print("=" * 60) + + # Initialize database + print("\nInitializing database...") + Base.metadata.create_all(bind=engine) + db = SessionLocal() + + # Initialize patch library + data_dir = os.environ.get("DATA_DIR", str(Path(__file__).parent.parent / "data")) + patch_service = PatchLibraryService(data_dir) + + print(f"Source directory: {source_dir}") + print(f"Patch library: {patch_service.patch_library_dir}") + + # Import + print("\nImporting eyes...") + imported = import_from_directory(source_dir, db, patch_service) + + print("\n" + "=" * 60) + print(f"Import Complete! Imported {imported} eyes.") + print("=" * 60) + + db.close() + + +if __name__ == "__main__": + main() diff --git a/scripts/startup_import_eyes.py b/scripts/startup_import_eyes.py new file mode 100644 index 0000000..da149bc --- /dev/null +++ b/scripts/startup_import_eyes.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python3 +""" +Startup script to import classic eyes into the patch library. +This should be run once when the backend starts, or manually. + +Usage: + python scripts/startup_import_eyes.py + +This script: +1. Converts PPM files to PNG (if needed) +2. Imports all classic eyes into the patch library database +""" + +import os +import sys +import json +from pathlib import Path + +# Add parent directory to path for imports +sys.path.insert(0, str(Path(__file__).parent.parent / "backend")) + +try: + from PIL import Image + from sqlalchemy.orm import Session + from app.database import SessionLocal, engine, Base + from app.models.patch import Patch + from app.services.patch_library import PatchLibraryService +except ImportError as e: + print(f"Error: Required modules not available: {e}") + print("Run this script inside the backend container.") + sys.exit(1) + + +def convert_ppm_to_png(ppm_dir: Path) -> int: + """Convert all PPM files to PNG in the given directory.""" + converted = 0 + for ppm_file in ppm_dir.glob("*.ppm"): + png_file = ppm_file.with_suffix('.png') + if not png_file.exists(): + try: + img = Image.open(ppm_file) + img.save(png_file, 'PNG') + converted += 1 + print(f" Converted: {ppm_file.name} -> {png_file.name}") + except Exception as e: + print(f" Error converting {ppm_file.name}: {e}") + return converted + + +def import_eyes(source_dir: Path, db: Session, patch_service: PatchLibraryService) -> int: + """Import eye images from the given directory.""" + metadata_path = source_dir / "metadata.json" + + if not metadata_path.exists(): + print(f" No metadata.json found in {source_dir}") + return 0 + + with open(metadata_path, 'r') as f: + metadata = json.load(f) + + imported = 0 + + for item in metadata: + try: + # Try PNG first, then PPM + filename = item.get('filename', item.get('ppm_filename', '')).replace('.ppm', '.png') + filepath = source_dir / filename + + if not filepath.exists(): + # Try PPM + filepath = source_dir / item.get('ppm_filename', filename.replace('.png', '.ppm')) + + if not filepath.exists(): + continue + + name = item['name'] + + # Check if already exists + existing = db.query(Patch).filter(Patch.name == name).first() + if existing: + continue # Silent skip for duplicates on startup + + # Load and convert if PPM + img = Image.open(filepath).convert('RGBA') + + # Create database record + patch = Patch( + name=name, + description=item.get('description', ''), + source_type="imported", + width=img.width, + height=img.height, + tags=item.get('tags', 'eye,classic'), + category="eye", + is_public=True, + file_path="", + thumbnail_path="" + ) + db.add(patch) + db.flush() + + # Save to patch library as PNG + patch_path = patch_service.get_patch_path(patch.id) + img.save(patch_path, 'PNG') + + # Create thumbnail + thumb_path = patch_service.get_thumbnail_path(patch.id) + patch_service.create_thumbnail(patch_path, thumb_path) + + # Update paths + patch.file_path = f"patch_library/{patch.id}.png" + patch.thumbnail_path = f"patch_library/{patch.id}_thumb.png" + + db.commit() + imported += 1 + print(f" Imported: {name} (ID: {patch.id})") + + except Exception as e: + db.rollback() + print(f" Error: {e}") + + return imported + + +def main(): + print("=" * 60) + print("Classic Eyes Startup Import") + print("=" * 60) + + # Find the classic eyes directory + data_dir = Path(os.environ.get("DATA_DIR", "/app/data")) + if not data_dir.exists(): + data_dir = Path(__file__).parent.parent / "data" + + ppm_dir = data_dir / "classic_eyes_ppm" + + if not ppm_dir.exists(): + print(f"\nNo classic eyes found at: {ppm_dir}") + print("Run generate_classic_eyes_ppm.py first.") + return + + # Initialize database + print("\nInitializing database...") + Base.metadata.create_all(bind=engine) + db = SessionLocal() + + # Initialize patch library + patch_service = PatchLibraryService(str(data_dir)) + + # Convert PPM to PNG + print("\nConverting PPM to PNG (if needed)...") + converted = convert_ppm_to_png(ppm_dir) + if converted > 0: + print(f" Converted {converted} files") + else: + print(" All files already converted") + + # Import eyes + print("\nImporting classic eyes...") + imported = import_eyes(ppm_dir, db, patch_service) + + # Count existing + total = db.query(Patch).filter(Patch.category == "eye").count() + + print("\n" + "=" * 60) + print(f"Import Complete!") + print(f" Newly imported: {imported}") + print(f" Total eye patches in library: {total}") + print("=" * 60) + + db.close() + + +if __name__ == "__main__": + main()