#!/opt/homebrew/bin/python3.11 """ CLIP Logo Identity Integration Script Purpose: 1. Download logo image 2. Extract CLIP ViT-L/14 embedding (768-dim) 3. Store embedding to reference_data JSONB 4. Register Logo Identity to PostgreSQL database Test Object: Accusys Storage Logo https://www.accusys.com.tw/wp-content/uploads/2023/03/Accusys-Orange-2017.png Usage: python3 scripts/clip_logo_integration.py --logo-url "URL" --name "Logo Name" python3 scripts/clip_logo_integration.py --test-accusys """ import os import sys import json import argparse import requests import psycopg2 from pathlib import Path from datetime import datetime import numpy as np DATABASE_URL = os.getenv("DATABASE_URL", "postgres://accusys@localhost:5432/momentry?options=-c%20search_path=dev") TEMP_DIR = Path("data/logo_images") TEMP_DIR.mkdir(parents=True, exist_ok=True) def download_image(image_url: str, save_path: Path) -> bool: """Download image from URL""" try: resp = requests.get(image_url, timeout=30) resp.raise_for_status() save_path.parent.mkdir(parents=True, exist_ok=True) with open(save_path, "wb") as f: f.write(resp.content) print(f"āœ… Downloaded: {save_path.name} ({len(resp.content)} bytes)") return True except Exception as e: print(f"āŒ Download failed: {e}") return False def load_clip_model(): """Load CLIP ViT-L/14 model""" try: import torch from transformers import CLIPModel, CLIPProcessor device = torch.device("mps" if torch.backends.mps.is_available() else "cpu") print(f"šŸ”§ Loading CLIP ViT-L/14 on {device}...") model = CLIPModel.from_pretrained("openai/clip-vit-large-patch14").to(device) processor = CLIPProcessor.from_pretrained("openai/clip-vit-large-patch14") print(f"āœ… CLIP model loaded on {device}") return model, processor, device except Exception as e: print(f"āŒ Failed to load CLIP: {e}") return None, None, None def extract_clip_embedding(model, processor, device, image_path: Path) -> list[float] | None: """Extract CLIP ViT-L/14 embedding (768-dim)""" try: from PIL import Image import torch image = Image.open(image_path).convert("RGB") inputs = processor(images=image, return_tensors="pt").to(device) with torch.no_grad(): embedding = model.get_image_features(**inputs) embedding = embedding.cpu().numpy().flatten().tolist() print(f"āœ… Extracted embedding: {len(embedding)}-dim") return embedding except Exception as e: print(f"āŒ Extraction failed: {e}") return None def test_mps_performance(model, processor, device, image_path: Path, iterations: int = 100): """Test MPS vs CPU performance""" try: from PIL import Image import torch import time from transformers import CLIPModel image = Image.open(image_path).convert("RGB") print(f"\nšŸ”§ Performance test: {iterations} iterations...") # MPS performance inputs_mps = processor(images=image, return_tensors="pt").to(device) start_time = time.time() for i in range(iterations): with torch.no_grad(): embedding = model.get_image_features(**inputs_mps) mps_time = time.time() - start_time print(f" MPS: {mps_time:.3f}s ({iterations} iterations)") print(f" MPS: {mps_time/iterations:.4f}s per image") # CPU performance cpu_device = torch.device("cpu") model_cpu = CLIPModel.from_pretrained("openai/clip-vit-large-patch14").to(cpu_device) inputs_cpu = processor(images=image, return_tensors="pt").to(cpu_device) start_time = time.time() for i in range(iterations): with torch.no_grad(): embedding = model_cpu.get_image_features(**inputs_cpu) cpu_time = time.time() - start_time print(f" CPU: {cpu_time:.3f}s ({iterations} iterations)") print(f" CPU: {cpu_time/iterations:.4f}s per image") speedup = cpu_time / mps_time if mps_time > 0 else 1.0 print(f" Speedup: {speedup:.2f}x") return { "mps_time": mps_time / iterations, "cpu_time": cpu_time / iterations, "speedup": speedup, } except Exception as e: print(f"āŒ Performance test failed: {e}") return None def register_logo_identity_to_db( name: str, logo_url: str, embedding: list[float], schema: str = "dev", ) -> str | None: """Register Logo Identity to PostgreSQL""" conn = psycopg2.connect(DATABASE_URL) cur = conn.cursor() try: reference_data = { "identity_embeddings": [ { "embedding": embedding, "source": "logo_image", "image_url": logo_url, "context": "brand_logo", "created_at": datetime.now().isoformat(), } ], "image_urls": [logo_url], } sql = f""" UPDATE {schema}.identities SET identity_embedding = %s, reference_data = %s, status = 'confirmed', updated_at = NOW() WHERE name = %s RETURNING uuid; """ embedding_str = "[" + ",".join(str(x) for x in embedding) + "]" cur.execute( sql, ( embedding_str, json.dumps(reference_data), name, ), ) result = cur.fetchone() if result: uuid = result[0] conn.commit() print(f"āœ… Logo Identity updated: {name} (UUID: {uuid})") return uuid else: print(f"āš ļø Identity '{name}' not found, creating new...") sql = f""" INSERT INTO {schema}.identities ( name, identity_type, source, status, identity_embedding, reference_data, created_at, updated_at ) VALUES ( %s, %s, %s, %s, %s, %s, NOW(), NOW() ) RETURNING uuid; """ cur.execute( sql, ( name, "logo", "manual", "confirmed", embedding_str, json.dumps(reference_data), ), ) uuid = cur.fetchone()[0] conn.commit() print(f"āœ… Logo Identity created: {name} (UUID: {uuid})") return uuid except Exception as e: print(f"āŒ Database error: {e}") conn.rollback() return None finally: cur.close() conn.close() def test_similarity_search( identity_uuid: str, test_embeddings: list[list[float]], threshold: float = 0.85, schema: str = "dev", ) -> list[dict]: """Test similarity search against Identity""" conn = psycopg2.connect(DATABASE_URL) cur = conn.cursor() try: cur.execute(f""" SELECT identity_embedding FROM {schema}.identities WHERE uuid = %s; """, (identity_uuid,)) result = cur.fetchone() if not result or not result[0]: print("āš ļø Identity embedding not found") return [] stored_embedding_raw = result[0] if isinstance(stored_embedding_raw, str): stored_embedding_raw = json.loads(stored_embedding_raw) stored_embedding = np.array(stored_embedding_raw, dtype=np.float64) matches = [] for i, test_emb in enumerate(test_embeddings): test_emb_array = np.array(test_emb) similarity = np.dot(stored_embedding, test_emb_array) / ( np.linalg.norm(stored_embedding) * np.linalg.norm(test_emb_array) ) is_match = similarity >= threshold matches.append({ "test_index": i, "similarity": float(similarity), "is_match": is_match, }) print(f" Test {i+1}: similarity={similarity:.4f}, match={is_match}") return matches except Exception as e: print(f"āŒ Similarity search failed: {e}") return [] finally: cur.close() conn.close() def main(): parser = argparse.ArgumentParser(description="CLIP Logo Identity Integration") parser.add_argument("--logo-url", help="Logo image URL") parser.add_argument("--name", help="Logo name") parser.add_argument("--schema", default="dev", help="Database schema") parser.add_argument("--test-accusys", action="store_true", help="Test Accusys Logo") parser.add_argument("--performance", action="store_true", help="Run performance test") args = parser.parse_args() if args.test_accusys: logo_url = "https://www.accusys.com.tw/wp-content/uploads/2023/03/Accusys-Orange-2017.png" name = "Accusys Storage Logo" elif args.logo_url and args.name: logo_url = args.logo_url name = args.name else: print("āŒ Please provide --logo-url and --name, or use --test-accusys") sys.exit(1) print("=" * 60) print("CLIP Logo Identity Integration") print("=" * 60) print(f"Logo: {name}") print(f"URL: {logo_url}") print(f"Schema: {args.schema}") print("=" * 60) logo_path = TEMP_DIR / f"{name.replace(' ', '_')}.png" if not logo_path.exists(): print("\nšŸ”§ Downloading logo...") if not download_image(logo_url, logo_path): sys.exit(1) model, processor, device = load_clip_model() if not model: sys.exit(1) if args.performance: perf_result = test_mps_performance(model, processor, device, logo_path, iterations=10) if perf_result: print("\nšŸ“Š Performance Summary:") print(f" MPS: {perf_result['mps_time']:.4f}s/img") print(f" CPU: {perf_result['cpu_time']:.4f}s/img") print(f" Speedup: {perf_result['speedup']:.2f}x") print("\nšŸ”§ Extracting CLIP embedding...") embedding = extract_clip_embedding(model, processor, device, logo_path) if not embedding: sys.exit(1) print("\nšŸ”§ Registering to database...") uuid = register_logo_identity_to_db( name=name, logo_url=logo_url, embedding=embedding, schema=args.schema, ) if uuid: print("\nšŸŽ‰ Integration completed!") print(f" Identity: {name}") print(f" UUID: {uuid}") print(f" Embedding: {len(embedding)}-dim") print(f" URL: {logo_url}") print("\nšŸ”§ Testing similarity search...") test_embeddings = [ embedding, [0.1] * 768, ] matches = test_similarity_search(uuid, test_embeddings, threshold=0.85, schema=args.schema) if matches: print("\nāœ… Similarity search test passed") else: print("\nāŒ Integration failed") sys.exit(1) if __name__ == "__main__": main()