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.
177 lines
5.8 KiB
Python
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())
|