Rubli's picture
Update app.py
b83e63d verified
Raw History Blame Contribute Delete
16 kB
import os
import sys
import gc
from functools import lru_cache
import gradio as gr
import torch
import torch.nn as nn
import numpy as np
import spaces
import librosa
import torchaudio
import torchaudio.transforms as T
from PIL import Image
from transformers import (
pipeline,
AutoModelForAudioClassification,
AutoFeatureExtractor,
)
from huggingface_hub import hf_hub_download
import importlib.util
# ============================================================
# CONFIGURATION
# ============================================================
_DEVICE = "cpu"
IMAGE_MODEL_REPO = "xRayon/convnext-ai-images-detector"
IMAGE_CHECKPOINT = "AI Images Detector/checkpoints/checkpoint_phase2.pth"
IMAGE_ARCHITECTURE = "AI Images Detector/model/convnext.py"
IMAGE_TRANSFORMS = "AI Images Detector/transforms.py"
TEXT_MODEL_ID = "Rubli/intent_classifier"
URDU_AUDIO_MODEL = (
"JuniKhan123/urdu-deepfake-detector-unified_for_gradio_app"
)
ENGLISH_AUDIO_MODEL = "mo-thecreator/Deepfake-audio-detection"
@spaces.GPU
def _dummy_gpu_check():
return {"status": "ok"}
# ============================================================
# 1. IMAGE DETECTION (PRETRAINED CONVNEXT MODEL)
# ============================================================
@lru_cache(maxsize=1)
def _load_image_detector():
"""
Load the existing pretrained ConvNeXt model once per Space process.
This does not train or fine-tune the model.
"""
architecture_path = hf_hub_download(
repo_id=IMAGE_MODEL_REPO,
filename=IMAGE_ARCHITECTURE,
)
checkpoint_path = hf_hub_download(
repo_id=IMAGE_MODEL_REPO,
filename=IMAGE_CHECKPOINT,
)
transforms_path = hf_hub_download(
repo_id=IMAGE_MODEL_REPO,
filename=IMAGE_TRANSFORMS,
)
# Load the architecture file from the model repository.
spec = importlib.util.spec_from_file_location(
"sentinelzone_convnext_architecture",
architecture_path,
)
if spec is None or spec.loader is None:
raise RuntimeError("Could not load the ConvNeXt architecture file.")
architecture_module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = architecture_module
spec.loader.exec_module(architecture_module)
build_model = getattr(architecture_module, "build_model", None)
if build_model is None:
raise RuntimeError(
"The downloaded convnext.py does not define build_model()."
)
model = build_model()
checkpoint = torch.load(
checkpoint_path,
map_location=torch.device(_DEVICE),
weights_only=False,
)
if not isinstance(checkpoint, dict) or "model" not in checkpoint:
raise RuntimeError(
"Unexpected checkpoint format: expected a dictionary with a 'model' key."
)
model.load_state_dict(checkpoint["model"])
model.to(torch.device(_DEVICE))
model.eval()
# Import the author's own test transforms so preprocessing stays exact.
transform_spec = importlib.util.spec_from_file_location(
"sentinelzone_convnext_transforms",
transforms_path,
)
if transform_spec is None or transform_spec.loader is None:
raise RuntimeError("Could not load the ConvNeXt preprocessing file.")
transform_module = importlib.util.module_from_spec(transform_spec)
sys.modules[transform_spec.name] = transform_module
transform_spec.loader.exec_module(transform_module)
test_transforms = getattr(transform_module, "test_transforms", None)
if test_transforms is None:
raise RuntimeError(
"The downloaded transforms.py does not define test_transforms()."
)
transform = test_transforms()
return model, transform
def detect_image(image):
if image is None:
return {
"verdict": "No image",
"confidence": 0.0,
}
try:
model, transform = _load_image_detector()
image = image.convert("RGB")
image_tensor = transform(image).unsqueeze(0).to(_DEVICE)
with torch.inference_mode():
logits = model(image_tensor)
probabilities = torch.softmax(logits.float(), dim=1)[0]
# Model card/dataset mapping: class 0 = real, class 1 = fake.
real_probability = float(probabilities[0].item())
fake_probability = float(probabilities[1].item())
verdict = (
"AI-Generated"
if fake_probability >= 0.50
else "Real"
)
confidence = (
fake_probability if verdict == "AI-Generated"
else real_probability
) * 100
return {
"verdict": verdict,
"confidence": round(confidence, 2),
"ai_probability": round(fake_probability * 100, 2),
"real_probability": round(real_probability * 100, 2),
"model": IMAGE_MODEL_REPO,
"predicted_label": "fake" if verdict == "AI-Generated" else "real",
}
except Exception as e:
print(f"ConvNeXt image detection error: {type(e).__name__}: {e}")
return {
"verdict": "Error",
"confidence": 0.0,
"error": f"{type(e).__name__}: {str(e)}",
"model": IMAGE_MODEL_REPO,
}
# ============================================================
# 2. TEXT HARASSMENT DETECTION
# ============================================================
@lru_cache(maxsize=1)
def _load_text_classifier():
return pipeline(
"text-classification",
model=TEXT_MODEL_ID,
device=-1,
)
def detect_text(text):
if not text or not text.strip():
return {
"label": "Safe",
"score": 0.0,
"risk": "Low",
}
text_lower = text.lower().strip()
SAFE_WORDS = [
"hello", "hi", "hey", "ok", "okay",
"thanks", "thank you", "friend", "bhai",
"bro", "sister", "dost", "kya haal hai",
"kasy ho", "kasi ho",
]
# Exact phrase matching avoids some substring false positives.
if (
len(text_lower.split()) <= 4
and text_lower in SAFE_WORDS
):
return {
"label": "Safe",
"score": 0.0,
"risk": "Low",
}
BAD_WORDS = [
"harami", "haramkhor", "gandi", "gndi",
"gaali", "kamini", "kutta", "kutty", "bc",
"mc", "madarchod", "bhenchod", "behanchod",
"randi", "raand", "sex", "sexy", "mazy",
"nudes", "nangi", "besharam", "tharki",
"lund", "phudi", "chut", "gashti", "kanjar",
"kanjri", "jahil", "badtameez", "pagal",
"zaleel", "mot",
]
THREAT_WORDS = [
"maar dunga", "jaan se", "kill", "murder",
"tabah", "barbaad", "dekh lunga",
"chhor dunga", "chhod dunga", "destroy",
"jaan", "shadi ni krny",
"mnh dehkany k qabal ni",
]
has_bad = any(
word in text_lower for word in BAD_WORDS
)
has_threat = any(
word in text_lower for word in THREAT_WORDS
)
if has_bad or has_threat:
return {
"label": "Harassment",
"score": 90.0,
"risk": "High",
}
try:
classifier = _load_text_classifier()
result = classifier(text)[0]
label = str(result["label"])
score_percent = float(result["score"]) * 100
# Confirm the model's label mapping before production use.
is_harassment = label.upper() == "LABEL_1"
if is_harassment:
if score_percent >= 85:
risk = "High"
elif score_percent >= 50:
risk = "Medium"
else:
risk = "Low"
return {
"label": (
"Harassment"
if score_percent >= 50
else "Safe"
),
"score": round(score_percent, 2),
"risk": risk,
}
return {
"label": "Safe",
"score": round(score_percent, 2),
"risk": "Low",
}
except Exception as e:
print(f"Text detection error: {e}")
return {
"label": "Error",
"score": 0.0,
"risk": "Unknown",
"error": str(e),
}
# ============================================================
# 3. AUDIO DEEPFAKE DETECTION (URDU & ENGLISH)
# ============================================================
@lru_cache(maxsize=2)
def _load_audio_model(model_id):
"""
Cache the feature extractor and model.
Check for uninitialized meta parameters before inference.
"""
feature_extractor = AutoFeatureExtractor.from_pretrained(
model_id
)
model = AutoModelForAudioClassification.from_pretrained(
model_id,
low_cpu_mem_usage=False,
device_map=None,
torch_dtype=torch.float32,
)
meta_params = [
name
for name, param in model.named_parameters()
if param.is_meta
]
meta_buffers = [
name
for name, buffer in model.named_buffers()
if buffer.is_meta
]
if meta_params or meta_buffers:
examples = (meta_params + meta_buffers)[:8]
raise RuntimeError(
"Audio model has uninitialized meta tensors. "
f"Examples: {examples}. "
"Check Transformers, Accelerate, PyTorch, and "
"checkpoint compatibility in requirements.txt."
)
model = model.to(torch.device("cpu"))
model.eval()
return feature_extractor, model
def _is_fake_label(label):
label = str(label).strip().lower().replace("_", "-")
return any(
word in label
for word in (
"fake",
"deepfake",
"synthetic",
"ai-generated",
"ai generated",
)
)
def _is_real_label(label):
label = str(label).strip().lower().replace("_", "-")
return any(
word in label
for word in (
"real",
"genuine",
"bonafide",
"bona-fide",
"human",
)
)
def detect_audio(audio_path, language="Urdu"):
if not audio_path:
return {
"verdict": "No audio",
"confidence": 0.0,
}
model_id = (
URDU_AUDIO_MODEL
if language == "Urdu"
else ENGLISH_AUDIO_MODEL
)
try:
import soundfile as sf
# Read and convert audio to mono.
try:
waveform, sr = sf.read(
audio_path,
dtype="float32",
)
if waveform.ndim > 1:
waveform = np.mean(
waveform,
axis=1,
dtype=np.float32,
)
if waveform.size == 0:
return {
"verdict": "Error",
"confidence": 0.0,
"error": "The uploaded audio file is empty.",
}
if sr != 16000:
waveform = librosa.resample(
waveform,
orig_sr=sr,
target_sr=16000,
)
except Exception as read_error:
print(
"SoundFile read failed; trying librosa:",
read_error,
)
waveform, sr = librosa.load(
audio_path,
sr=16000,
mono=True,
)
waveform = np.asarray(
waveform,
dtype=np.float32,
).reshape(-1)
if waveform.size == 0:
return {
"verdict": "Error",
"confidence": 0.0,
"error": (
"Audio is empty or could not be decoded."
),
}
feature_extractor, model = _load_audio_model(
model_id
)
inputs = feature_extractor(
waveform,
sampling_rate=16000,
return_tensors="pt",
padding=True,
)
inputs = {
key: value.to("cpu")
for key, value in inputs.items()
}
with torch.inference_mode():
outputs = model(**inputs)
logits = outputs.logits
probs = torch.softmax(
logits.float(),
dim=-1,
)[0]
pred_idx = int(torch.argmax(probs).item())
confidence = float(
probs[pred_idx].item() * 100
)
id2label = (
getattr(model.config, "id2label", {}) or {}
)
label = id2label.get(
pred_idx,
id2label.get(
str(pred_idx),
f"LABEL_{pred_idx}",
),
)
if _is_fake_label(label):
verdict = "Fake (AI-Generated)"
elif _is_real_label(label):
verdict = "Real"
else:
# Fallback only for generic labels.
# Verify the Urdu checkpoint's class mapping
# against its model card/configuration.
if language == "English":
verdict = (
"Fake (AI-Generated)"
if pred_idx == 0
else "Real"
)
else:
verdict = (
"Fake (AI-Generated)"
if pred_idx == 1
else "Real"
)
return {
"verdict": verdict,
"confidence": round(confidence, 2),
"model": model_id,
"predicted_label": str(label),
}
except Exception as e:
print(
f"Audio detection error ({model_id}): "
f"{type(e).__name__}: {e}"
)
return {
"verdict": "Error",
"confidence": 0.0,
"error": f"{type(e).__name__}: {str(e)}",
"model": model_id,
}
# ============================================================
# 4. GRADIO INTERFACE
# ============================================================
with gr.Blocks(title="SentinelZone AI") as demo:
gr.Markdown(
"# SentinelZone - AI Detection API\n"
"Image, text harassment, and audio deepfake detection."
)
# IMAGE TAB
with gr.Tab("Image Detection"):
img_input = gr.Image(
type="pil",
label="Upload Image",
)
img_output = gr.JSON(
label="Image Analysis Result"
)
img_btn = gr.Button("Analyze Image")
img_btn.click(
fn=detect_image,
inputs=img_input,
outputs=img_output,
api_name="predict_image",
)
# TEXT TAB
with gr.Tab("Text Harassment"):
txt_input = gr.Textbox(
label="Enter Text",
placeholder="Enter the message to analyze...",
lines=4,
)
txt_output = gr.JSON(
label="Text Analysis Result"
)
txt_btn = gr.Button("Analyze Text")
txt_btn.click(
fn=detect_text,
inputs=txt_input,
outputs=txt_output,
api_name="predict_text",
)
# AUDIO TAB
with gr.Tab("Audio Deepfake"):
aud_input = gr.Audio(
type="filepath",
label="Upload Audio File",
)
aud_lang = gr.Radio(
choices=["Urdu", "English"],
label="Select Language",
value="Urdu",
)
aud_output = gr.JSON(
label="Audio Analysis Result"
)
aud_btn = gr.Button("Analyze Audio")
aud_btn.click(
fn=detect_audio,
inputs=[aud_input, aud_lang],
outputs=aud_output,
api_name="predict_audio",
)
if __name__ == "__main__":
demo.launch()