import os import json from pathlib import Path import asyncio import base64 from openai import AsyncOpenAI from dotenv import load_dotenv load_dotenv() VISION_MODEL = "gpt-4o-2024-08-06" client = AsyncOpenAI() async def analyze_screenshot(design_id: str, design_path: Path): """Analyze screenshots and return description, categories, and visual characteristics""" try: # Check files exist metadata_path = design_path / "metadata.json" desktop_img = design_path / "screenshot_desktop.png" mobile_img = design_path / "screenshot_mobile.png" if not all(f.exists() for f in [metadata_path, desktop_img, mobile_img]): print(f"Missing required files for design {design_id}") return design_id, None, None, None # Load existing metadata with open(metadata_path, "r") as f: metadata = json.load(f) # Read both images try: with open(desktop_img, "rb") as f: desktop_base64 = base64.b64encode(f.read()).decode('utf-8') with open(mobile_img, "rb") as f: mobile_base64 = base64.b64encode(f.read()).decode('utf-8') except Exception as e: print(f"Error reading images for design {design_id}: {str(e)}") return design_id, None, None, None print(f"Analyzing design {design_id}...") # Get response first response = await client.chat.completions.create( model=VISION_MODEL, messages=[ { "role": "system", "content": """You are an expert graphic designer analyzing print and web designs for aesthetics, functionality, audience appeal, and potential applications. The design should be considered from a visual standpoint. Use chain of thought to consider color palette, visual layout, typography, artistic style, mood, and potential applications. Consider gradients, texture, background effects, and the use of images. Treat all text content as placeholder Lorem Ipsum. Provide analysis in clean JSON format with these exact keys: { "description": "A one-paragraph summary highlighting exceptional features of the design", "categories": ["category1", "category2"], "visual_characteristics": ["characteristic1", "characteristic2"] } Provide 4-6 categories and 4-6 visual characteristics most relevant to the style and feel of the design. Do not reference css or web design directly because this analysis is primarily about design. These lists should describe the design to another LLM that will use this data to generate a UI. """ }, { "role": "user", "content": [ { "type": "text", "text": "Analyze this visual design. Output only the JSON object." }, { "type": "image_url", "image_url": { "url": f"data:image/png;base64,{desktop_base64}", "detail": "high" } }, { "type": "image_url", "image_url": { "url": f"data:image/png;base64,{mobile_base64}", "detail": "high" } } ] } ], max_tokens=1000 ) # Then get the content response_content = response.choices[0].message.content.strip() # Ensure the response is not empty if not response_content: print(f"Empty response for design {design_id}") return design_id, None, None, None # Extract JSON content from markdown code block if "```json" in response_content: # Remove the ```json prefix and ``` suffix response_content = response_content.split("```json")[1].split("```")[0].strip() # Parse the JSON response try: analysis = json.loads(response_content) # Update metadata with all fields metadata.update(analysis) # Save updated metadata with open(metadata_path, "w") as f: json.dump(metadata, f, indent=2) print(f"Successfully analyzed design {design_id}") # Return visual_characteristics as fourth element return design_id, analysis["description"], analysis["categories"], analysis["visual_characteristics"] except json.JSONDecodeError as e: print(f"Error parsing JSON response for design {design_id}: {str(e)}") return design_id, None, None, None except Exception as e: print(f"Error processing design {design_id}: {str(e)}") return design_id, None, None, None async def main(): designs_dir = Path("designs") if not designs_dir.exists(): print("Designs directory not found!") return # Get all design directories design_dirs = [d for d in designs_dir.iterdir() if d.is_dir()] if not design_dirs: print("No design directories found!") return print(f"Found {len(design_dirs)} designs to analyze") # Create list of design IDs to analyze (001-050) design_ids = [f"{i:03d}" for i in range(1, 51)] # Analyze all designs tasks = [] for design_dir in design_dirs: design_id = design_dir.name tasks.append(analyze_screenshot(design_id, design_dir)) # Run analyses concurrently results = await asyncio.gather(*tasks) # Print summary successful = 0 for design_id, desc, cats, _ in results: if desc is not None: successful += 1 print(f"\nDesign {design_id}:") print(f"Description: {desc}") print(f"Categories: {', '.join(cats)}") print(f"\nSuccessfully analyzed {successful} out of {len(design_dirs)} designs") if __name__ == "__main__": asyncio.run(main())