rijal028's picture
Update app.py
3d0e83d verified
Raw History Blame Contribute Delete
5.38 kB
import io
import cv2
import numpy as np
import torch
from PIL import Image
import spaces
from transformers import AutoImageProcessor, AutoModelForImageClassification
import gradio as gr
# 1. Inisialisasi Model & Processor
RESNET_MODEL_ID = "prithivMLmods/Deep-Fake-Detector-Model"
print(f"[*] Inisialisasi arsitektur forensik: {RESNET_MODEL_ID}")
processor = AutoImageProcessor.from_pretrained(RESNET_MODEL_ID)
resnet_model = AutoModelForImageClassification.from_pretrained(RESNET_MODEL_ID).eval()
# 2. Analisis Spektral 2D FFT (CPU)
def analyze_phase_spectrum(img_pil: Image.Image) -> float:
gray_img = img_pil.convert("L")
w, h = gray_img.size
crop_size = 256
if w >= crop_size and h >= crop_size:
left = (w - crop_size) // 2
top = (h - crop_size) // 2
gray_crop = gray_img.crop((left, top, left + crop_size, top + crop_size))
gray = np.array(gray_crop, dtype=np.float32) / 255.0
else:
gray = np.array(gray_img.resize((256, 256)), dtype=np.float32) / 255.0
f = np.fft.fft2(gray)
fshift = np.fft.fftshift(f)
mag = np.abs(fshift)
cy, cx = 128, 128
y, x = np.ogrid[:256, :256]
low_freq_mask = ((x - cx)**2 + (y - cy)**2) <= (32**2)
low_energy = float(np.mean(mag[low_freq_mask]) + 1e-5)
high_energy = float(np.mean(mag[~low_freq_mask]) + 1e-5)
spectral_ratio = high_energy / low_energy
score = 1.0 / (1.0 + np.exp(-40.0 * (spectral_ratio - 0.045)))
return float(np.clip(score, 0.05, 0.95))
# 3. Pipeline Forensik Lengkap (Terdaftar Langsung di ZeroGPU)
@spaces.GPU
def predict_forensic(input_image):
if input_image is None:
return {
"verdict": "INCONCLUSIVE",
"calibrated_confidence": 0.0,
"reason": "Payload gambar kosong."
}
pil_img = Image.fromarray(input_image).convert("RGB")
# Profiler Degradasi
gray_np = np.array(pil_img.convert("L"))
laplacian_var = float(cv2.Laplacian(gray_np, cv2.CV_64F).var())
w, h = pil_img.size
is_downscaled = bool((w * h) < (720 * 1280))
quality_score = float(np.clip(laplacian_var / 100.0, 0.05, 1.0))
if laplacian_var < 5.0 or (laplacian_var < 35.0 and is_downscaled):
return {
"verdict": "INCONCLUSIVE",
"calibrated_confidence": 0.50,
"reason": "Severe physical degradation outside safe forensic envelope.",
"metrics": {"sharpness": round(laplacian_var, 2), "dimensions": f"{w}x{h}", "quality": quality_score}
}
# Inferensi Akselerasi ZeroGPU (A100)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
resnet_model.to(device)
inputs = processor(images=pil_img, return_tensors="pt").to(device)
with torch.inference_mode():
outputs = resnet_model(**inputs)
probs = torch.softmax(outputs.logits, dim=-1)[0]
prob_real = float(probs[0].item())
score_visual = float(np.clip(1.0 - prob_real, 0.01, 0.99))
score_phase = analyze_phase_spectrum(pil_img)
# Kalibrasi Fusi Bukti
w_visual = max(0.4, 0.8 * quality_score)
w_phase = 0.2
combined_score = (score_visual * w_visual + score_phase * w_phase) / (w_visual + w_phase)
disagreement = abs(score_visual - score_phase)
if combined_score <= 0.35 and score_visual < 0.15:
verdict = "LIKELY NATURAL CAPTURE"
confidence = round(1.0 - combined_score, 3)
reason = "Optical sensor texture and visual residuals indicate natural capture."
elif disagreement > 0.55 and quality_score < 0.25 and combined_score > 0.35:
verdict = "INCONCLUSIVE"
confidence = 0.50
reason = "Visual channel and spectral phase exhibit acute divergence under heavy compression."
elif combined_score >= 0.55:
verdict = "LIKELY AI-GENERATED"
confidence = round(min(combined_score, 0.70 + (0.28 * quality_score)), 3)
reason = "Forensic visual patterns and high-frequency spectral phase reflect synthetic artifacts."
elif combined_score <= 0.45:
verdict = "LIKELY NATURAL CAPTURE"
confidence = round(1.0 - combined_score, 3)
reason = "Visual features and phase distributions remain consistent with standard optical capture."
else:
verdict = "INCONCLUSIVE"
confidence = 0.50
reason = "Technical ambiguity margin (0.45 - 0.55)."
return {
"verdict": verdict,
"calibrated_confidence": round(confidence, 3),
"reason": reason,
"metrics": {
"dimensions": f"{w}x{h}",
"sharpness": round(laplacian_var, 2),
"quality_index": round(quality_score, 2),
"score_visual": round(score_visual, 3),
"score_phase": round(score_phase, 3),
"combined_score": round(combined_score, 3)
}
}
# 4. Antarmuka Gradio Resmi (Memenuhi Spesifikasi ZeroGPU HF)
with gr.Blocks(title="SecurePixel Engine") as demo:
gr.Markdown("# 🛡️ SecurePixel Forensics Engine (ZeroGPU)")
with gr.Row():
image_input = gr.Image(type="numpy", label="Input Frame")
result_output = gr.JSON(label="Forensic Result")
btn_predict = gr.Button("Analyze Frame", variant="primary")
btn_predict.click(
fn=predict_forensic,
inputs=image_input,
outputs=result_output,
api_name="predict"
)
demo.launch()