feat: Claude Vision AI analysis integration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
53
analyzer.py
53
analyzer.py
@@ -3,6 +3,7 @@ import numpy as np
|
|||||||
from PIL import Image
|
from PIL import Image
|
||||||
import imagehash
|
import imagehash
|
||||||
import os
|
import os
|
||||||
|
import base64
|
||||||
from typing import List, Optional
|
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"}
|
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(
|
def analyze_folder(
|
||||||
folder: str,
|
folder: str,
|
||||||
blur_threshold: float = 100.0,
|
blur_threshold: float = 100.0,
|
||||||
|
|||||||
Reference in New Issue
Block a user