Spaces:
Sleeping
Sleeping
File size: 4,284 Bytes
d581b00 dda3973 d581b00 dda3973 d581b00 ff9e377 d581b00 dda3973 d581b00 ff9e377 d581b00 ff9e377 dda3973 ff9e377 d581b00 ff9e377 d581b00 ff9e377 dda3973 d581b00 ff9e377 d581b00 ff9e377 d581b00 ff9e377 dda3973 e7ddfc7 dda3973 ff9e377 e7ddfc7 d581b00 ff9e377 d581b00 ff9e377 d581b00 ff9e377 d581b00 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | import gradio as gr
import torch
import base64
import io
from PIL import Image
from src.models.inference import load_model, predict, predict_generator, load_generator_model
from src.services.metadata_checker import get_metadata
from src.services.gradcam import generate_gradcam
import tempfile
import os
# Load both models once at startup β singleton pattern
# Models stay in memory for the lifetime of the app
# Avoids 2-3 second reload penalty on every user request
model = load_model()
generator_model = load_generator_model()
def analyze_image(image):
"""
Main function called by Gradio when user submits an image.
Flow:
1. Save PIL image to a temp file (inference functions require file paths)
2. Run binary prediction (real vs fake)
3. Run generator type prediction (Real/GAN/Diffusion/Other)
4. Extract image metadata and EXIF
5. Generate Grad-CAM heatmap
6. Build formatted markdown result string
7. Delete temp file
8. Return (result_text, cam_image) β matches Gradio output components
Why temp file:
Gradio passes images as PIL objects but our inference functions
expect file paths. We save temporarily and clean up after.
"""
if image is None:
return "Please upload an image.", None
# Save PIL image to temp file with unique name
with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tmp:
image.save(tmp.name)
tmp_path = tmp.name
try:
# Run all analysis pipelines
prediction = predict(tmp_path, model=model)
metadata = get_metadata(tmp_path)
cam_array = generate_gradcam(tmp_path, model=model)
cam_image = Image.fromarray(cam_array) # convert numpy array to PIL for Gradio
generator_result = predict_generator(tmp_path, model=generator_model)
finally:
# Always clean up temp file regardless of success or failure
os.remove(tmp_path)
label = prediction["label"]
confidence = prediction["confidence"]
# Build markdown-formatted result string for Gradio Markdown component
result_text = f"**{label}** β Confidence: {confidence}%\n\n"
result_text += f"β οΈ This is a model-based estimate, not definitive proof.\n\n"
result_text += f"π **Note:** Model performs best on Stable Diffusion, StyleGAN, and DDPM images. "
result_text += f"Performance drops on unseen generators (DALL-E, MidJourney).\n\n"
# Generator type section β only shown when image is predicted AI-Generated
# No point showing generator type if image is classified as real
if label == "AI-Generated":
gen_type = generator_result["generator_type"]
gen_conf = generator_result["confidence"]
result_text += f"**Generator Type:** {gen_type} ({gen_conf}%)\n\n"
result_text += "**Class Probabilities:**\n"
for cls, prob in generator_result["class_probabilities"].items():
result_text += f"- {cls}: {prob}%\n"
result_text += "\n"
# Metadata section
result_text += f"**Metadata:**\n"
result_text += f"- Format: {metadata['format']}\n"
result_text += f"- Dimensions: {metadata['dimensions']}\n"
result_text += f"- File Size: {metadata['file_size_kb']} KB\n"
result_text += f"- EXIF Data: {'Yes' if metadata['has_exif'] else 'No'}\n"
# Show EXIF note in italics β honest disclaimer about what missing EXIF means
if not metadata["has_exif"]:
result_text += f"\n_{metadata['exif_note']}_"
# Gradio expects return values matching the outputs list order:
# outputs[0] = Markdown β result_text
# outputs[1] = Image β cam_image
return result_text, cam_image
# Gradio Interface β simplest Gradio pattern
# fn: function to call on submit
# inputs: single image upload component
# outputs: markdown text + image side by side
demo = gr.Interface(
fn=analyze_image,
inputs=gr.Image(type="pil", label="Upload Image"),
outputs=[
gr.Markdown(label="Result"),
gr.Image(type="pil", label="Grad-CAM Explanation")
],
title="π ImageTrust-AI",
description="Upload an image to check if it's real or AI-generated. Powered by ResNet18 trained on ArtiFact dataset.",
examples=[],
)
if __name__ == "__main__":
demo.launch() |