"""Analysis routes — image_analysis / metadata / forensics / ocr / objects / scene / nsfw / ai_detection / embedding / forensic_metadata.""" from __future__ import annotations from typing import List, Optional from fastapi import APIRouter, Depends from pydantic import BaseModel from api.deps import ( get_analysis_service, get_forensic_metadata_service, get_object_intelligence_service, get_location_intelligence_service, get_correlation_engine_service, ) from models.jobs import JobKind, JobRequest from services.analysis_service import AnalysisService from services.forensic_metadata_service import ForensicMetadataService from services.object_intelligence_service import ObjectIntelligenceService from services.location_intelligence_service import LocationIntelligenceService from services.correlation_engine_service import CorrelationEngineService router = APIRouter() class AnalysisRequest(BaseModel): image_url: Optional[str] = None image_base64: Optional[str] = None providers: List[str] = [] class LocationRequest(BaseModel): image_url: Optional[str] = None image_base64: Optional[str] = None providers: List[str] = [] # Optional pre-computed evidence (caller can pass OCR text, plate text, etc.) ocr_text: Optional[str] = None plate_text: Optional[str] = None scene_label: Optional[str] = None objects: Optional[List[dict]] = None @router.post("/image") async def analyze_image(req: AnalysisRequest, svc: AnalysisService = Depends(get_analysis_service)): """Run image-quality + properties analysis.""" job_req = JobRequest( kind=JobKind.IMAGE_ANALYSIS, image_url=req.image_url, image_base64=req.image_base64, providers=req.providers, ) return await svc.analyze(job_req) @router.post("/metadata") async def extract_metadata(req: AnalysisRequest, svc: AnalysisService = Depends(get_analysis_service)): """Extract EXIF / XMP / IPTC metadata.""" job_req = JobRequest( kind=JobKind.METADATA, image_url=req.image_url, image_base64=req.image_base64, providers=req.providers, ) return await svc.analyze(job_req) @router.post("/forensics") async def forensics_analysis(req: AnalysisRequest, svc: AnalysisService = Depends(get_analysis_service)): """Run forensics: integrity, duplicate detection, ELA, manipulation analysis.""" job_req = JobRequest( kind=JobKind.FORENSICS, image_url=req.image_url, image_base64=req.image_base64, providers=req.providers, ) return await svc.analyze(job_req) @router.post("/ocr") async def ocr_analysis(req: AnalysisRequest, svc: AnalysisService = Depends(get_analysis_service)): """Run OCR text extraction.""" job_req = JobRequest( kind=JobKind.OCR, image_url=req.image_url, image_base64=req.image_base64, providers=req.providers, ) return await svc.analyze(job_req) @router.post("/objects") async def object_detection(req: AnalysisRequest, svc: AnalysisService = Depends(get_analysis_service)): """Run object detection (YOLOv8).""" job_req = JobRequest( kind=JobKind.OBJECT_DETECTION, image_url=req.image_url, image_base64=req.image_base64, providers=req.providers, ) return await svc.analyze(job_req) @router.post("/scene") async def scene_classification(req: AnalysisRequest, svc: AnalysisService = Depends(get_analysis_service)): """Run scene classification (Places365).""" job_req = JobRequest( kind=JobKind.SCENE_RECOGNITION, image_url=req.image_url, image_base64=req.image_base64, providers=req.providers, ) return await svc.analyze(job_req) @router.post("/nsfw") async def nsfw_detection(req: AnalysisRequest, svc: AnalysisService = Depends(get_analysis_service)): """Run NSFW detection (NudeNet).""" job_req = JobRequest( kind=JobKind.NSFW_DETECTION, image_url=req.image_url, image_base64=req.image_base64, providers=req.providers, ) return await svc.analyze(job_req) @router.post("/ai-detection") async def ai_image_detection(req: AnalysisRequest, svc: AnalysisService = Depends(get_analysis_service)): """Run AI-generated image detection (LAID).""" job_req = JobRequest( kind=JobKind.AI_IMAGE_DETECTION, image_url=req.image_url, image_base64=req.image_base64, providers=req.providers, ) return await svc.analyze(job_req) @router.post("/embedding") async def image_embedding(req: AnalysisRequest, svc: AnalysisService = Depends(get_analysis_service)): """Generate image embedding (EfficientNet-Lite).""" job_req = JobRequest( kind=JobKind.EMBEDDING, image_url=req.image_url, image_base64=req.image_base64, providers=req.providers, ) return await svc.analyze(job_req) @router.post("/forensic-metadata") async def forensic_metadata(req: AnalysisRequest, svc: ForensicMetadataService = Depends(get_forensic_metadata_service)): """Extract comprehensive forensic metadata: EXIF, GPS, ICC, thumbnail, editing history, camera fingerprint, compression analysis.""" job_req = JobRequest( kind=JobKind.METADATA, image_url=req.image_url, image_base64=req.image_base64, providers=req.providers, ) return await svc.analyze(job_req) @router.post("/object-intelligence") async def object_intelligence( req: AnalysisRequest, svc: ObjectIntelligenceService = Depends(get_object_intelligence_service), ): """Run object intelligence: YOLO + QR codes + barcodes with crops + classification.""" job_req = JobRequest( kind=JobKind.OBJECT_DETECTION, image_url=req.image_url, image_base64=req.image_base64, providers=req.providers, ) return await svc.analyze(job_req) @router.post("/location") async def location_intelligence( req: LocationRequest, svc: LocationIntelligenceService = Depends(get_location_intelligence_service), ): """Estimate image capture location from GPS + OCR + language + scene + objects.""" options: dict = {} if req.ocr_text: options["ocr_text"] = req.ocr_text if req.plate_text: options["plate_text"] = req.plate_text if req.scene_label: options["scene_label"] = req.scene_label if req.objects: options["objects"] = req.objects job_req = JobRequest( kind=None, image_url=req.image_url, image_base64=req.image_base64, providers=req.providers, options=options, ) return await svc.analyze(job_req) class CorrelateRequest(BaseModel): """A pre-computed UnifiedFaceReport to correlate.""" report: dict @router.post("/correlate") async def correlate( req: CorrelateRequest, svc: CorrelationEngineService = Depends(get_correlation_engine_service), ): """Correlate evidence in a pre-computed report into a relationship graph. Accepts a UnifiedFaceReport (the output of any analysis job) and builds a deterministic correlation graph of relationships between faces, objects, locations, metadata, hashes, and embeddings. """ return await svc.correlate(req.report)