Spaces:
Sleeping
Sleeping
| # === FASTAPI BACKEND (main.py) === | |
| from fastapi import FastAPI, UploadFile, File, HTTPException | |
| from fastapi.responses import JSONResponse | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from transformers import pipeline | |
| from PIL import Image | |
| import io | |
| import torch | |
| import numpy as np | |
| import cv2 | |
| import base64 | |
| app = FastAPI() | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| device = 0 if torch.cuda.is_available() else -1 | |
| MODELS_CONFIG = { | |
| "SwinV2 Based": {"path": "haywoodsloan/ai-image-detector-deploy", "weight": 0.15}, | |
| "ViT Based": {"path": "Heem2/AI-vs-Real-Image-Detection", "weight": 0.15}, | |
| "SDXL Dataset": {"path": "Organika/sdxl-detector", "weight": 0.15}, | |
| "SDXL + FLUX": {"path": "cmckinle/sdxl-flux-detector_v1.1", "weight": 0.15}, | |
| "DeepFake v2": {"path": "prithivMLmods/Deep-Fake-Detector-v2-Model", "weight": 0.15}, | |
| "Midjourney/SDXL": {"path": "ideepankarsharma2003/AI_ImageClassification_MidjourneyV6_SDXL", "weight": 0.10}, | |
| "ViT v4": {"path": "date3k2/vit-real-fake-classification-v4", "weight": 0.15}, | |
| } | |
| models = {} | |
| for name, config in MODELS_CONFIG.items(): | |
| try: | |
| models[name] = pipeline("image-classification", model=config["path"], device=device) | |
| except Exception as e: | |
| print(f"Failed to load model {name}: {e}") | |
| def pil_to_base64(image): | |
| buffered = io.BytesIO() | |
| image.save(buffered, format="JPEG") | |
| return "data:image/jpeg;base64," + base64.b64encode(buffered.getvalue()).decode("utf-8") | |
| def gen_ela(img_array, quality=90): | |
| if img_array.shape[2] == 4: | |
| img_array = cv2.cvtColor(img_array, cv2.COLOR_RGBA2RGB) | |
| encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), quality] | |
| _, buffer = cv2.imencode('.jpg', img_array, encode_param) | |
| compressed_img = cv2.imdecode(buffer, cv2.IMREAD_COLOR) | |
| ela_img = cv2.absdiff(img_array, compressed_img) | |
| ela_img = cv2.convertScaleAbs(ela_img, alpha=10) | |
| return Image.fromarray(cv2.cvtColor(ela_img, cv2.COLOR_BGR2RGB)) | |
| def gradient_processing(image_array): | |
| gray_img = cv2.cvtColor(image_array, cv2.COLOR_BGR2GRAY) | |
| dx = cv2.Sobel(gray_img, cv2.CV_64F, 1, 0, ksize=3) | |
| dy = cv2.Sobel(gray_img, cv2.CV_64F, 0, 1, ksize=3) | |
| gradient_magnitude = cv2.magnitude(dx, dy) | |
| gradient_img = cv2.normalize(gradient_magnitude, None, 0, 255, cv2.NORM_MINMAX, dtype=cv2.CV_8U) | |
| return Image.fromarray(gradient_img) | |
| async def detect(image: UploadFile = File(...)): | |
| try: | |
| import time | |
| start_time = time.time() | |
| image_bytes = await image.read() | |
| input_image = Image.open(io.BytesIO(image_bytes)).convert("RGB") | |
| img_np = np.array(input_image) | |
| img_bgr = cv2.cvtColor(img_np, cv2.COLOR_RGB2BGR) | |
| individual_results = [] | |
| weighted_ai_score = 0 | |
| total_weight = 0 | |
| aiModels = [] | |
| colors = ["bg-red-500", "bg-orange-500", "bg-yellow-500", "bg-green-500", "bg-blue-500", "bg-purple-500", "bg-pink-500"] | |
| for i, (name, model_pipeline) in enumerate(models.items()): | |
| model_weight = MODELS_CONFIG[name]["weight"] | |
| predictions = model_pipeline(input_image) | |
| confidence = {p['label'].lower(): p['score'] for p in predictions} | |
| artificial_score = ( | |
| confidence.get('artificial', 0) or confidence.get('ai image', 0) or | |
| confidence.get('ai', 0) or confidence.get('deepfake', 0) or | |
| confidence.get('ai_gen', 0) or confidence.get('fake', 0) | |
| ) | |
| real_score = ( | |
| confidence.get('real', 0) or confidence.get('real image', 0) or | |
| confidence.get('human', 0) or confidence.get('realism', 0) | |
| ) | |
| if artificial_score > 0 and real_score == 0: | |
| real_score = 1.0 - artificial_score | |
| elif real_score > 0 and artificial_score == 0: | |
| artificial_score = 1.0 - real_score | |
| weighted_ai_score += artificial_score * model_weight | |
| total_weight += model_weight | |
| aiModels.append({ | |
| "name": name, | |
| "percentage": round(artificial_score * 100, 2), | |
| "color": colors[i % len(colors)] | |
| }) | |
| final_score = (weighted_ai_score / total_weight) * 100 if total_weight > 0 else 0 | |
| verdict = final_score > 50 | |
| processing_time = int((time.time() - start_time) * 1000) | |
| # Forensics | |
| ela_img = gen_ela(img_bgr) | |
| gradient_img = gradient_processing(img_bgr) | |
| return JSONResponse({ | |
| "filename": image.filename, | |
| "isDeepfake": verdict, | |
| "confidence": round(final_score, 2), | |
| "aiModels": aiModels, | |
| "processingTime": processing_time, | |
| "forensics": { | |
| "original": pil_to_base64(input_image), | |
| "ela": pil_to_base64(ela_img), | |
| "gradient": pil_to_base64(gradient_img) | |
| }, | |
| "verdictMessage": f"Consensus: {'Likely AI-Generated' if verdict else 'Likely Human-Made (Real)'}" | |
| }) | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| # === Add this new code to the bottom of your deepfake_api.py file === | |
| import os | |
| import google.generativeai as genai | |
| from pydantic import BaseModel, Field | |
| from typing import List, Dict, Any | |
| # Configure the Gemini API with the key from Hugging Face secrets | |
| gemini_api_key = os.getenv("GOOGLE_API_KEY") | |
| if gemini_api_key: | |
| genai.configure(api_key=gemini_api_key) | |
| # Define Pydantic models to validate the incoming data structure | |
| class AIModelResult(BaseModel): | |
| name: str | |
| percentage: float | |
| class ForensicResult(BaseModel): | |
| original: str | |
| ela: str | |
| gradient: str | |
| class DetectionResult(BaseModel): | |
| filename: str | |
| isDeepfake: bool | |
| confidence: float | |
| aiModels: List[AIModelResult] | |
| processingTime: int | |
| forensics: ForensicResult | |
| verdictMessage: str | |
| # New endpoint to generate the report | |
| async def generate_report(result: DetectionResult): | |
| if not gemini_api_key: | |
| raise HTTPException(status_code=500, detail="Google API key is not configured.") | |
| try: | |
| model = genai.GenerativeModel('gemini-1.5-flash') | |
| # Create a detailed prompt for the AI | |
| prompt = f""" | |
| You are an AI image forensics analyst. Your task is to generate a professional report based on the JSON data from a deepfake detection scan. | |
| The user has scanned the file: "{result.filename}". | |
| Here is the JSON data from the scan: | |
| {{ | |
| "isDeepfake": {result.isDeepfake}, | |
| "overall_confidence_score": {result.confidence}%, | |
| "verdict": "{result.verdictMessage}", | |
| "model_analysis": { {model.name: f"{model.percentage}%" for model in result.aiModels} } | |
| }} | |
| Please generate a report with the following structure using Markdown: | |
| ## Forensic Analysis Report: {result.filename} | |
| ### **Executive Summary** | |
| Provide a brief, high-level summary of the findings. State the final verdict clearly and the overall confidence score. | |
| ### **Detailed Model Analysis** | |
| Analyze the results from the individual AI models. Mention the models with the highest confidence scores (e.g., Midjourney/SDXL, SDXL Dataset) and explain what their findings imply. | |
| ### **Conclusion & Recommendation** | |
| Provide a final conclusion based on the combined evidence. Recommend next steps, such as whether the image can be trusted or if further manual analysis is required. | |
| """ | |
| # Generate the report | |
| response = model.generate_content(prompt) | |
| return {"report": response.text} | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=f"Failed to generate report: {str(e)}") | |