radai-api / test.py
RageCoder2006's picture
Clean deployment for Hugging Face
879d39c
Raw
History Blame Contribute Delete
4.77 kB
import torch
import torch.nn as nn
from torchvision import transforms, models
import open_clip
from PIL import Image, ImageFilter
import numpy as np
import os
# --- 1. SETUP & DEVICE ---
DEVICE = torch.device("mps") if torch.backends.mps.is_available() else torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"Using device: {DEVICE}")
# --- 2. LOAD MODELS ---
# A. Load openclip (ViT-L-14)
print("Loading openclip...")
openclip_model, _, openclip_preprocess = open_clip.create_model_and_transforms(
'ViT-L-14', pretrained='datacomp_xl_s13b_b90k'
)
openclip_model.to(DEVICE)
# Define your openclip Forensic Head Architecture (matches your training)
class openclipHead(nn.Module):
def __init__(self, input_dim):
super().__init__()
self.net = nn.Sequential(
nn.Linear(input_dim, 512),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(512, 1)
)
def forward(self, x): return self.net(x)
# Load openclip Weights
openclip_head = openclipHead(input_dim=768).to(DEVICE)
openclip_head.load_state_dict(torch.load('models/openclip_forensic_head.pth', map_location=DEVICE))
openclip_head.eval()
# B. Load ConvNeXt-Base
print("Loading ConvNeXt...")
cn_backbone = models.convnext_base(weights=None) # Architecture only
cn_backbone.to(DEVICE)
cn_backbone.eval()
class ConvNextHead(nn.Module):
def __init__(self, input_dim):
super().__init__()
self.net = nn.Sequential(
nn.Linear(input_dim, 512),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(512, 1)
)
def forward(self, x): return self.net(x)
cn_head = ConvNextHead(input_dim=1024).to(DEVICE)
cn_head.load_state_dict(torch.load('models/convnext_forensic_head.pth', map_location=DEVICE))
cn_head.eval()
# ConvNext Preprocessing (Standard ImageNet)
cn_preprocess = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
# --- 3. FEATURE EXTRACTION (Heuristics) ---
def extract_simple_features(image_path):
img = Image.open(image_path).convert('RGB')
img_array = np.array(img) / 255.0
edges = np.abs(np.diff(np.mean(img_array, axis=2), axis=0)).mean() + \
np.abs(np.diff(np.mean(img_array, axis=2), axis=1)).mean()
img_smooth = np.array(img.filter(ImageFilter.GaussianBlur(2))) / 255.0
noise = np.mean((img_array - img_smooth) ** 2) * 1000
return {
'noise_level': noise,
'edge_density': edges,
'is_too_clean': (noise < 0.05 and edges < 0.12) # Adjusted thresholds
}
# --- 4. THE ENSEMBLE INFERENCE ---
def run_ensemble(image_path):
img = Image.open(image_path).convert('RGB')
# openclip Score
img_openclip = openclip_preprocess(img).unsqueeze(0).to(DEVICE)
with torch.no_grad():
sig_feat = openclip_model.encode_image(img_openclip)
sig_feat /= sig_feat.norm(dim=-1, keepdim=True)
sig_logit = openclip_head(sig_feat)
prob_openclip = torch.sigmoid(sig_logit).item()
# ConvNeXt Score
img_cn = cn_preprocess(img).unsqueeze(0).to(DEVICE)
with torch.no_grad():
feat = cn_backbone.features(img_cn)
feat = cn_backbone.avgpool(feat)
feat = torch.flatten(feat, 1)
cn_logit = cn_head(feat)
prob_cn = torch.sigmoid(cn_logit).item()
# Average the two for the "Raw Ensemble Score"
raw_ensemble_score = (prob_openclip + prob_cn) / 2
# Calibration
features = extract_simple_features(image_path)
if features['is_too_clean']:
calibrated_score = raw_ensemble_score * 0.55 # 45% discount for product shots
reason = "Clean product-shot detected. Reducing probability."
else:
calibrated_score = raw_ensemble_score
reason = "Standard analysis applied."
return {
'openclip_score': prob_openclip,
'convnext_score': prob_cn,
'raw_ensemble': raw_ensemble_score,
'calibrated': min(calibrated_score, 0.95),
'reason': reason,
'features': features
}
# --- 5. TEST IT ---
test_image = "/Users/rishitbaitule/Downloads/b.jpg" # Update this path!
if os.path.exists(test_image):
results = run_ensemble(test_image)
print("-" * 30)
print(f"Individual openclip: {results['openclip_score']:.2%}")
print(f"Individual ConvNeXt: {results['convnext_score']:.2%}")
print("-" * 30)
print(f"ENSEMBLE RAW SCORE: {results['raw_ensemble']:.2%}")
print(f"CALIBRATED SCORE: {results['calibrated']:.2%}")
print(f"REASON: {results['reason']}")
print("-" * 30)
else:
print("Image not found. Please check test_image path.")