From 00f3aeb0087a1850fa73bd9fa11ae43843548ff7 Mon Sep 17 00:00:00 2001 From: Ferdinand Date: Tue, 7 Apr 2026 13:43:02 +0200 Subject: [PATCH] feat: Claude Vision AI analysis integration Co-Authored-By: Claude Sonnet 4.6 --- analyzer.py | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/analyzer.py b/analyzer.py index 9654746..73ab5b3 100644 --- a/analyzer.py +++ b/analyzer.py @@ -3,6 +3,7 @@ import numpy as np from PIL import Image import imagehash import os +import base64 from typing import List, Optional @@ -69,6 +70,58 @@ def find_duplicates(paths: List[str], threshold: int = 8) -> List[List[str]]: SUPPORTED_EXTENSIONS = {".jpg", ".jpeg", ".png"} +def _analyze_with_ai(paths: List[str], api_key: str) -> dict: + """ + Sendet Bilder an Claude Vision API zur Qualitaetsanalyse. + Gibt {path: [reasons]} zurueck. Bei Fehler wird der Pfad uebersprungen. + """ + import anthropic + + client = anthropic.Anthropic(api_key=api_key) + ai_results: dict = {path: [] for path in paths} + + PROMPT = ( + "Analysiere dieses Foto auf Qualitaetsprobleme fuer einen professionellen Fotografen. " + "Antworte NUR mit einer kommagetrennten Liste von Problemen aus diesen Kategorien: " + "unscharf, ueberbelichtet, unterbelichtet, schlechter Bildausschnitt, stoerende Elemente, " + "schlechter Weissabgleich. Wenn das Bild in Ordnung ist, antworte mit 'ok'." + ) + + for path in paths: + try: + with open(path, "rb") as f: + img_data = base64.standard_b64encode(f.read()).decode("utf-8") + ext = os.path.splitext(path)[1].lower().lstrip(".") + media_type = "image/jpeg" if ext in ("jpg", "jpeg") else "image/png" + + response = client.messages.create( + model="claude-opus-4-6", + max_tokens=100, + messages=[{ + "role": "user", + "content": [ + { + "type": "image", + "source": { + "type": "base64", + "media_type": media_type, + "data": img_data, + }, + }, + {"type": "text", "text": PROMPT}, + ], + }], + ) + answer = response.content[0].text.strip().lower() + if answer != "ok": + reasons = [r.strip() for r in answer.split(",") if r.strip()] + ai_results[path].extend(reasons) + except Exception: + continue + + return ai_results + + def analyze_folder( folder: str, blur_threshold: float = 100.0,