| """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] = [] |
| |
| 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) |
|
|