import spaces import gradio as gr import torch import tempfile import os from PIL import Image from transformers import pipeline # ── Model registry ─────────────────────────────────────────────────────────── MODELS = { ("fast", "kodf"): "KoreaPeter/ms-eff-gcvit-deepfake-b0-kodf", ("fast", "celeb-df-v2"): "KoreaPeter/ms-eff-gcvit-deepfake-b0-celeb-df-v2", ("fast", "ff++"): "KoreaPeter/ms-eff-gcvit-deepfake-b0-ff-plus-plus", ("pro", "kodf"): "KoreaPeter/ms-eff-gcvit-deepfake-b5-kodf", ("pro", "celeb-df-v2"): "KoreaPeter/ms-eff-gcvit-deepfake-b5-celeb-df-v2", ("pro", "ff++"): "KoreaPeter/ms-eff-gcvit-deepfake-b5-ff-plus-plus", } _cache: dict = {} def _get_pipeline(variant: str, dataset: str): key = (variant, dataset) if key not in _cache: device = "cuda" if torch.cuda.is_available() else "cpu" _cache[key] = pipeline( "image-classification", model=MODELS[key], trust_remote_code=True, device=device, ) return _cache[key] # ── Inference ───────────────────────────────────────────────────────────────── @spaces.GPU def predict( image: Image.Image, variant: str, dataset: str, margin_ratio: float, conf_thres: float, min_face_ratio: float, tta_hflip: float, top_k: int, ): if image is None: return None, "⚠️ Please upload an image." with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as f: image.save(f.name) tmp_path = f.name try: pipe = _get_pipeline(variant, dataset) results = pipe( tmp_path, margin_ratio=margin_ratio, conf_thres=conf_thres, min_face_ratio=min_face_ratio, tta_hflip=tta_hflip, top_k=top_k, ) except Exception as e: return None, f"❌ Pipeline error: {e}" finally: os.unlink(tmp_path) if not results: return None, "⚠️ No result returned." top = results[0] if top.get("label") == "no_face_detected": return ( {"REAL": 0.0, "FAKE": 0.0}, "😶 No face detected.\nPlease use an image with a clearly visible face.", ) scores = {r["label"].upper(): round(r["score"], 4) for r in results if r.get("label") in ("fake", "real")} fake_prob = scores.get("FAKE", 0.0) if fake_prob >= 0.7: verdict = f"🔴 FAKE ({fake_prob * 100:.1f}% confidence)" elif fake_prob >= 0.4: verdict = f"🟡 UNCERTAIN (FAKE {fake_prob * 100:.1f}%)" else: verdict = f"🟢 REAL ({(1 - fake_prob) * 100:.1f}% confidence)" return scores, verdict # ── Hero banner ─────────────────────────────────────────────────────────────── HERO = """
🕵️

DeepGuard

Image Deepfake Detection

Deepfake detection powered by MS-EffGCViT  ·  6 domain-specific models  ·  Asian · Celebrity · Western faces

🔗 GitHub 🤗 HuggingFace 🎬 Video Detection 🔬 DeepFake XAI
""" # ── Theme & CSS ─────────────────────────────────────────────────────────────── THEME = gr.themes.Base( primary_hue="pink", neutral_hue="slate", font=gr.themes.GoogleFont("Inter"), ).set( body_background_fill="#1a0010", body_text_color="#fce4ec", block_background_fill="#2d0020", block_border_color="#ad1457", input_background_fill="#1a0010", button_primary_background_fill="#c2185b", button_primary_text_color="#fce4ec", button_primary_border_color="#e91e63", button_primary_background_fill_hover="#e91e63", button_primary_text_color_hover="#ffffff", ) CSS = """ .result-box textarea { font-size:1.1rem !important; font-weight:bold !important; text-align:center; } footer { display:none !important; } """ # ── Build UI ────────────────────────────────────────────────────────────────── with gr.Blocks(title="DeepGuard Image") as demo: gr.HTML(HERO) with gr.Row(): with gr.Column(scale=1, min_width=300): image_input = gr.Image(type="pil", label="Upload Image") with gr.Accordion("🖼️ Example Images — (click to expand)", open=False): gr.Examples( examples=[ "example1.JPG", "example2.JPG", "example3.JPG", "example4.JPG", ], inputs=image_input, label="All samples are FAKE", ) gr.Markdown("### ⚡ Model Variant") variant_radio = gr.Radio( choices=["fast", "pro"], value="fast", label=None, info="Fast (b0): 8.7M params | Pro (b5): 50.3M params", ) gr.Markdown("### 🌍 Training Domain") dataset_radio = gr.Radio( choices=[ ("🇰🇷 KoDF — East Asian faces", "kodf"), ("🌟 Celeb-DF-v2 — Celebrity faces", "celeb-df-v2"), ("🌍 FF++ — Western faces", "ff++"), ], value="ff++", label=None, ) run_btn = gr.Button("🔍 Analyze", variant="primary", size="lg") with gr.Column(scale=1, min_width=300): verdict_output = gr.Textbox( label="Result", interactive=False, lines=2, elem_classes=["result-box"], ) score_output = gr.Label(label="Class Probabilities", num_top_classes=2) gr.Markdown(""" --- **Model Guide** | | ⚡ Fast (b0) | 🔥 Pro (b5) | |---|---|---| | **Params** | 8.7M | 50.3M | | **Resolution** | 224×224 | 384×384 | | **Speed** | Faster | Slower | | **Accuracy** | High | Higher | **Domain Guide** | Domain | Training Data | Best For | |---|---|---| | KoDF | 62K real / 175K fake (Korean) | East Asian faces | | Celeb-DF-v2 | 890 real / 5.6K fake (Celebrities) | Celebrity faces | | FF++ | 1K real / 6K fake (4 manipulation types) | Western faces | """) with gr.Accordion("⚙️ Advanced Settings", open=True): gr.Markdown("Adjust inference parameters. Defaults are recommended for most cases.") with gr.Row(): with gr.Column(): margin_ratio_slider = gr.Slider( minimum=0.0, maximum=0.5, value=0.2, step=0.05, label="Margin Ratio", info="Margin around the detected face bounding box", ) conf_thres_slider = gr.Slider( minimum=0.1, maximum=0.9, value=0.5, step=0.05, label="Face Detection Confidence", info="YOLO face detection confidence threshold", ) with gr.Column(): min_face_ratio_slider = gr.Slider( minimum=0.001, maximum=0.1, value=0.01, step=0.001, label="Min Face Ratio", info="Minimum face-to-frame area ratio to process", ) tta_hflip_slider = gr.Slider( minimum=0.0, maximum=1.0, value=0.0, step=0.1, label="TTA Horizontal Flip", info="⚠️ > 0.0 doubles inference time", ) top_k_slider = gr.Slider( minimum=1, maximum=2, value=2, step=1, label="Top K", info="Number of top predictions to return", ) run_btn.click( fn=predict, inputs=[ image_input, variant_radio, dataset_radio, margin_ratio_slider, conf_thres_slider, min_face_ratio_slider, tta_hflip_slider, top_k_slider, ], outputs=[score_output, verdict_output], ) image_input.change( fn=lambda img, v, d, mr, ct, mfr, tta, tk: ( predict(img, v, d, mr, ct, mfr, tta, tk) if img is not None else (None, "") ), inputs=[ image_input, variant_radio, dataset_radio, margin_ratio_slider, conf_thres_slider, min_face_ratio_slider, tta_hflip_slider, top_k_slider, ], outputs=[score_output, verdict_output], ) if __name__ == "__main__": demo.launch(theme=THEME, css=CSS)