Files
PaintPlus/backend/scripts/seed_eye_catalog.py
T
Claude 3fe70fa663 Add public domain eye catalog system for CNC wood carving
Added complete system for populating eye catalog with classical carved eyes:

Features:
- Eye import script (import_eyes.py) for batch/single eye imports
- Public domain source guide (museums: Met, Smithsonian, Getty, etc.)
- Seed catalog system for pre-populating database
- Organized by emotion (serene, fierce, wise, peaceful, etc.)
- Organized by style (Greek, Roman, Egyptian, Renaissance)
- Organized by side (left, right, both)
- Auto-generates thumbnails and metadata
- CNC-ready tagging system

Workflow:
1. Download classical sculpture photos from public domain museums
2. Crop eyes in any image editor
3. Run import script with metadata
4. Eyes saved to catalog with proper tags
5. Apply to colored photos (pure image compositing, no AI regeneration)
6. Convert result to grayscale for CNC carving

Documentation:
- PUBLIC_DOMAIN_EYE_SOURCES.md: Where to find carved eyes
- scripts/README.md: How to import eyes
- Includes recommended starting collection (10 essential eyes)

Benefits for wood carving:
- Build library from master sculptors (2000+ years of proven designs)
- Reusable across all projects
- Consistent emotional weight in carvings
- No AI regeneration - just intelligent copy/paste/blend
- Perfect for CNC workflow (colored preview → grayscale → carve)

The patch library system uses PIL/OpenCV for image compositing,
NOT AI regeneration, so it preserves exact carved geometry.
2026-01-25 02:06:40 +00:00

177 lines
5.8 KiB
Python

"""
Seed the patch library with classical carved eyes from public domain sources
This script helps pre-populate the eye catalog with examples from:
- Greek statues (Metropolitan Museum, Louvre)
- Roman sculptures (Smithsonian, British Museum)
- Renaissance carvings
- Ancient Egyptian carved eyes
All images should be public domain (CC0, Public Domain Mark)
"""
import asyncio
import httpx
from pathlib import Path
from PIL import Image
from io import BytesIO
# Public domain eye examples to seed the catalog
# These are examples - you would add actual URLs from museum APIs
CLASSICAL_EYES = [
{
"name": "Greek Statue - Serene Left Eye",
"description": "Classical Greek marble carving, convex eyeball, defined upper lid, deep socket. Perfect for serene expressions.",
"category": "carved_eye",
"tags": "greek, serene, left, marble, classical, convex, deep-socket",
"style": "greek_classical",
"emotion": "serene",
"side": "left",
"source_url": "https://images.metmuseum.org/...", # Example
"source": "Metropolitan Museum of Art - Public Domain"
},
{
"name": "Roman Sculpture - Fierce Right Eye",
"description": "Roman marble, prominent brow ridge, intense gaze, sharp eyelid definition.",
"category": "carved_eye",
"tags": "roman, fierce, right, marble, intense, sharp-detail",
"style": "roman_classical",
"emotion": "fierce",
"side": "right",
"source_url": "https://...",
"source": "Smithsonian - CC0"
},
{
"name": "Greek Kouros - Peaceful Left Eye",
"description": "Archaic Greek style, almond-shaped, subtle carving, peaceful expression.",
"category": "carved_eye",
"tags": "greek, peaceful, left, archaic, almond-shaped, subtle",
"style": "greek_archaic",
"emotion": "peaceful",
"side": "left",
"source_url": "https://...",
"source": "Getty Museum - Public Domain"
},
{
"name": "Roman Portrait - Wise Right Eye",
"description": "Late Roman period, detailed eyelids, slight downward gaze, wisdom and age.",
"category": "carved_eye",
"tags": "roman, wise, right, portrait, detailed, aged",
"style": "roman_portrait",
"emotion": "wise",
"side": "right",
"source_url": "https://...",
"source": "British Museum - CC0"
},
]
async def download_image(url: str) -> bytes:
"""Download image from URL"""
async with httpx.AsyncClient() as client:
response = await client.get(url)
response.raise_for_status()
return response.content
async def crop_eye_from_statue(image_bytes: bytes, crop_box: tuple) -> bytes:
"""
Crop just the eye from a full statue photo
Args:
image_bytes: Full statue image
crop_box: (left, top, right, bottom) coordinates
Returns:
Cropped eye image bytes
"""
img = Image.open(BytesIO(image_bytes))
eye = img.crop(crop_box)
# Save as PNG
buffer = BytesIO()
eye.save(buffer, format='PNG')
return buffer.getvalue()
async def seed_eye_catalog():
"""
Seed the patch library with classical carved eyes
NOTE: This is a template. You need to:
1. Get actual public domain image URLs
2. Manually crop the eyes (or provide crop coordinates)
3. Run this to populate the catalog
"""
from app.database import SessionLocal
from app.models.patch import Patch
from app.services.patch_library import PatchLibraryService
db = SessionLocal()
patch_service = PatchLibraryService()
print("Seeding eye catalog with classical carved eyes...")
for eye_data in CLASSICAL_EYES:
print(f"\nAdding: {eye_data['name']}")
# Create patch record
patch = Patch(
name=eye_data['name'],
description=eye_data['description'],
source_type="imported",
category=eye_data['category'],
tags=eye_data['tags'],
width=0, # Will be set after image save
height=0,
user_id=None,
file_path=""
)
db.add(patch)
db.commit()
db.refresh(patch)
# Download and save image
# NOTE: You need to manually download/crop these first
# This is just the structure
try:
# image_bytes = await download_image(eye_data['source_url'])
# cropped_eye = await crop_eye_from_statue(image_bytes, crop_box)
# For now, you would manually place images in:
# ./seed_data/eyes/greek_serene_left.png
# ./seed_data/eyes/roman_fierce_right.png
# etc.
seed_image_path = Path(__file__).parent / "seed_data" / "eyes" / f"{eye_data['style']}_{eye_data['emotion']}_{eye_data['side']}.png"
if seed_image_path.exists():
file_path = patch_service.save_patch_from_file(
patch.id,
str(seed_image_path),
create_thumb=True
)
# Get dimensions
width, height = patch_service.get_patch_size(patch.id)
patch.width = width
patch.height = height
patch.file_path = file_path
patch.thumbnail_path = str(patch_service.get_thumbnail_path(patch.id))
db.commit()
print(f"✅ Added {eye_data['name']}")
else:
print(f"⚠️ Image not found: {seed_image_path}")
print(f" Please download and crop eye from: {eye_data['source']}")
except Exception as e:
print(f"❌ Error adding {eye_data['name']}: {e}")
db.rollback()
db.close()
print("\n✅ Eye catalog seeding complete!")
if __name__ == "__main__":
asyncio.run(seed_eye_catalog())