Ayan515 commited on
Commit
46fccfe
·
verified ·
1 Parent(s): 7e4b5b6

Upload 6 files

Browse files
Files changed (6) hide show
  1. .gitattributes +35 -35
  2. .gitignore +10 -0
  3. README.md +20 -12
  4. app.backup.py +364 -0
  5. app_mcp.py +614 -0
  6. requirements.txt +18 -0
.gitattributes CHANGED
@@ -1,35 +1,35 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ .env
2
+ *goat.py
3
+ .vscode
4
+ *onnx.py
5
+ ./models/*
6
+ forensics/__pycache__/*
7
+ *.cpython-311.pyc
8
+ *.cpython-312.pyc
9
+ test.ipynb
10
+ models/*
README.md CHANGED
@@ -1,12 +1,20 @@
1
- ---
2
- title: Deepfake
3
- emoji: 👀
4
- colorFrom: indigo
5
- colorTo: purple
6
- sdk: gradio
7
- sdk_version: 5.35.0
8
- app_file: app.py
9
- pinned: false
10
- ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: MCP Toolkit - Re-Thinking Deepfake Detection & Forensics
3
+ description: MCP Server for Project OpenSight's Deepfake Detection & Digital Forensics Tools
4
+ emoji: 🚑
5
+ colorFrom: yellow
6
+ colorTo: yellow
7
+ sdk: gradio
8
+ sdk_version: 5.33.0
9
+ app_file: app_mcp.py
10
+ pinned: true
11
+ models:
12
+ - aiwithoutborders-xyz/OpenSight-CommunityForensics-Deepfake-ViT
13
+ - Heem2/AI-vs-Real-Image-Detection
14
+ - haywoodsloan/ai-image-detector-deploy
15
+ - cmckinle/sdxl-flux-detector
16
+ - Organika/sdxl-detector
17
+ license: mit
18
+ ---
19
+
20
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.backup.py ADDED
@@ -0,0 +1,364 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from typing import Literal
3
+ import spaces
4
+ import gradio as gr
5
+ import modelscope_studio.components.antd as antd
6
+ import modelscope_studio.components.antdx as antdx
7
+ import modelscope_studio.components.base as ms
8
+ from transformers import pipeline, AutoImageProcessor, SwinForImageClassification, Swinv2ForImageClassification, AutoFeatureExtractor, AutoModelForImageClassification
9
+ from torchvision import transforms
10
+ import torch
11
+ from PIL import Image
12
+ import numpy as np
13
+ import io
14
+ import logging
15
+ from utils.utils import softmax, augment_image, convert_pil_to_bytes
16
+ from utils.gradient import gradient_processing
17
+ from utils.minmax import preprocess as minmax_preprocess
18
+ from utils.ela import genELA as ELA
19
+ from utils.wavelet import wavelet_blocking_noise_estimation
20
+
21
+ # Configure logging
22
+ logging.basicConfig(level=logging.INFO)
23
+ logger = logging.getLogger(__name__)
24
+
25
+
26
+ # Ensure using GPU if available
27
+ device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
28
+
29
+ header_style = {
30
+ "textAlign": 'center',
31
+ "color": '#fff',
32
+ "height": 64,
33
+ "paddingInline": 48,
34
+ "lineHeight": '64px',
35
+ "backgroundColor": '#4096ff',
36
+ }
37
+
38
+ content_style = {
39
+ "textAlign": 'center',
40
+ "minHeight": 120,
41
+ "lineHeight": '120px',
42
+ "color": '#fff',
43
+ "backgroundColor": '#0958d9',
44
+ }
45
+
46
+ sider_style = {
47
+ "textAlign": 'center',
48
+ "lineHeight": '120px',
49
+ "color": '#fff',
50
+ "backgroundColor": '#1677ff',
51
+ }
52
+
53
+ footer_style = {
54
+ "textAlign": 'center',
55
+ "color": '#fff',
56
+ "backgroundColor": '#4096ff',
57
+ }
58
+
59
+ layout_style = {
60
+ "borderRadius": 8,
61
+ "overflow": 'hidden',
62
+ "width": 'calc(100% - 8px)',
63
+ "maxWidth": 'calc(100% - 8px)',
64
+ }
65
+ # Model paths and class names
66
+ MODEL_PATHS = {
67
+ "model_1": "haywoodsloan/ai-image-detector-deploy",
68
+ "model_2": "Heem2/AI-vs-Real-Image-Detection",
69
+ "model_3": "Organika/sdxl-detector",
70
+ "model_4": "cmckinle/sdxl-flux-detector_v1.1",
71
+ "model_5": "prithivMLmods/Deep-Fake-Detector-v2-Model",
72
+ "model_5b": "prithivMLmods/Deepfake-Detection-Exp-02-22",
73
+ "model_6": "ideepankarsharma2003/AI_ImageClassification_MidjourneyV6_SDXL",
74
+ "model_7": "date3k2/vit-real-fake-classification-v4"
75
+ }
76
+
77
+ CLASS_NAMES = {
78
+ "model_1": ['artificial', 'real'],
79
+ "model_2": ['AI Image', 'Real Image'],
80
+ "model_3": ['AI', 'Real'],
81
+ "model_4": ['AI', 'Real'],
82
+ "model_5": ['Realism', 'Deepfake'],
83
+ "model_5b": ['Real', 'Deepfake'],
84
+ "model_6": ['ai_gen', 'human'],
85
+ "model_7": ['Fake', 'Real'],
86
+
87
+ }
88
+
89
+ # Load models and processors
90
+ def load_models():
91
+ image_processor_1 = AutoImageProcessor.from_pretrained(MODEL_PATHS["model_1"], use_fast=True)
92
+ model_1 = Swinv2ForImageClassification.from_pretrained(MODEL_PATHS["model_1"])
93
+ model_1 = model_1.to(device)
94
+ clf_1 = pipeline(model=model_1, task="image-classification", image_processor=image_processor_1, device=device)
95
+
96
+ clf_2 = pipeline("image-classification", model=MODEL_PATHS["model_2"], device=device)
97
+
98
+ feature_extractor_3 = AutoFeatureExtractor.from_pretrained(MODEL_PATHS["model_3"], device=device)
99
+ model_3 = AutoModelForImageClassification.from_pretrained(MODEL_PATHS["model_3"]).to(device)
100
+
101
+ feature_extractor_4 = AutoFeatureExtractor.from_pretrained(MODEL_PATHS["model_4"], device=device)
102
+ model_4 = AutoModelForImageClassification.from_pretrained(MODEL_PATHS["model_4"]).to(device)
103
+
104
+ clf_5 = pipeline("image-classification", model=MODEL_PATHS["model_5"], device=device)
105
+ clf_5b = pipeline("image-classification", model=MODEL_PATHS["model_5b"], device=device)
106
+
107
+ image_processor_6 = AutoImageProcessor.from_pretrained(MODEL_PATHS["model_6"], use_fast=True)
108
+ model_6 = SwinForImageClassification.from_pretrained(MODEL_PATHS["model_6"]).to(device)
109
+ clf_6 = pipeline(model=model_6, task="image-classification", image_processor=image_processor_6, device=device)
110
+
111
+ image_processor_7 = AutoImageProcessor.from_pretrained(MODEL_PATHS["model_7"], use_fast=True)
112
+ model_7 = AutoModelForImageClassification.from_pretrained(MODEL_PATHS["model_7"]).to(device)
113
+ clf_7 = pipeline(model=model_7, task="image-classification", image_processor=image_processor_7, device=device)
114
+
115
+ return clf_1, clf_2, feature_extractor_3, model_3, feature_extractor_4, model_4, clf_5, clf_5b, clf_6, model_7, clf_7
116
+
117
+ clf_1, clf_2, feature_extractor_3, model_3, feature_extractor_4, model_4, clf_5, clf_5b, clf_6, model_7, clf_7 = load_models()
118
+
119
+ @spaces.GPU(duration=10)
120
+ def predict_with_model(img_pil, clf, class_names, confidence_threshold, model_name, model_id, feature_extractor=None):
121
+ try:
122
+ if feature_extractor:
123
+ inputs = feature_extractor(img_pil, return_tensors="pt").to(device)
124
+ with torch.no_grad():
125
+ outputs = clf(**inputs)
126
+ logits = outputs.logits
127
+ probabilities = softmax(logits.cpu().numpy()[0])
128
+ result = {class_names[i]: probabilities[i] for i in range(len(class_names))}
129
+ else:
130
+ prediction = clf(img_pil)
131
+ result = {pred['label']: pred['score'] for pred in prediction}
132
+
133
+ result_output = [model_id, model_name, result.get(class_names[1], 0.0), result.get(class_names[0], 0.0)]
134
+ logger.info(result_output)
135
+ for class_name in class_names:
136
+ if class_name not in result:
137
+ result[class_name] = 0.0
138
+ if result[class_names[0]] >= confidence_threshold:
139
+ label = f"AI, Confidence: {result[class_names[0]]:.4f}"
140
+ result_output.append('AI')
141
+ elif result[class_names[1]] >= confidence_threshold:
142
+ label = f"Real, Confidence: {result[class_names[1]]:.4f}"
143
+ result_output.append('REAL')
144
+ else:
145
+ label = "Uncertain Classification"
146
+ result_output.append('UNCERTAIN')
147
+ except Exception as e:
148
+ label = f"Error: {str(e)}"
149
+ result_output = [model_id, model_name, 0.0, 0.0, 'ERROR'] # Ensure result_output is assigned in case of error
150
+ return label, result_output
151
+
152
+ @spaces.GPU(duration=10)
153
+ def predict_image(img, confidence_threshold):
154
+ if not isinstance(img, Image.Image):
155
+ raise ValueError(f"Expected a PIL Image, but got {type(img)}")
156
+ if img.mode != 'RGB':
157
+ img_pil = img.convert('RGB')
158
+ else:
159
+ img_pil = img
160
+ img_pil = transforms.Resize((256, 256))(img_pil)
161
+ img_pilvits = transforms.Resize((224, 224))(img_pil)
162
+
163
+ label_1, result_1output = predict_with_model(img_pil, clf_1, CLASS_NAMES["model_1"], confidence_threshold, "SwinV2-base", 1)
164
+ label_2, result_2output = predict_with_model(img_pilvits, clf_2, CLASS_NAMES["model_2"], confidence_threshold, "ViT-base Classifier", 2)
165
+ label_3, result_3output = predict_with_model(img_pil, model_3, CLASS_NAMES["model_3"], confidence_threshold, "SDXL-Trained", 3, feature_extractor_3)
166
+ label_4, result_4output = predict_with_model(img_pil, model_4, CLASS_NAMES["model_4"], confidence_threshold, "SDXL + FLUX", 4, feature_extractor_4)
167
+ label_5, result_5output = predict_with_model(img_pilvits, clf_5, CLASS_NAMES["model_5"], confidence_threshold, "ViT-base Newcomer", 5)
168
+ label_5b, result_5boutput = predict_with_model(img_pilvits, clf_5b, CLASS_NAMES["model_5b"], confidence_threshold, "ViT-base Newcomer", 6)
169
+ label_6, result_6output = predict_with_model(img_pilvits, clf_6, CLASS_NAMES["model_6"], confidence_threshold, "Swin Midjourney/SDXL", 7)
170
+ label_7, result_7output = predict_with_model(img_pilvits, clf_7, CLASS_NAMES["model_7"], confidence_threshold, "Vit", 7)
171
+
172
+ combined_results = {
173
+ "SwinV2/detect": label_1,
174
+ "ViT/AI-vs-Real": label_2,
175
+ "Swin/SDXL": label_3,
176
+ "Swin/SDXL-FLUX": label_4,
177
+ "prithivMLmods": label_5,
178
+ "prithivMLmods-2-22": label_5b,
179
+ "SwinMidSDXL": label_6,
180
+ "Vit": label_7
181
+ }
182
+ print(combined_results)
183
+
184
+ combined_outputs = [result_1output, result_2output, result_3output, result_4output, result_5output, result_5boutput, result_6output, result_7output]
185
+ return img_pil, combined_outputs
186
+ # Define a function to generate the HTML content
187
+
188
+ def generate_results_html(results):
189
+ def get_header_color(label):
190
+ if label == 'AI':
191
+ return 'bg-red-500 text-red-700', 'bg-red-400', 'bg-red-100', 'bg-red-700 text-red-700', 'bg-red-200'
192
+ elif label == 'REAL':
193
+ return 'bg-green-500 text-green-700', 'bg-green-400', 'bg-green-100', 'bg-green-700 text-green-700', 'bg-green-200'
194
+ elif label == 'UNCERTAIN':
195
+ return 'bg-yellow-500 text-yellow-700 bg-yellow-100', 'bg-yellow-400', 'bg-yellow-100', 'bg-yellow-700 text-yellow-700', 'bg-yellow-200'
196
+ elif label == 'MAINTENANCE':
197
+ return 'bg-blue-500 text-blue-700', 'bg-blue-400', 'bg-blue-100', 'bg-blue-700 text-blue-700', 'bg-blue-200'
198
+ else:
199
+ return 'bg-gray-300 text-gray-700', 'bg-gray-400', 'bg-gray-100', 'bg-gray-700 text-gray-700', 'bg-gray-200'
200
+
201
+ def generate_tile_html(index, result, model_name, contributor, model_path):
202
+ label = result[-1]
203
+ header_colors = get_header_color(label)
204
+ real_conf = result[2]
205
+ ai_conf = result[3]
206
+ return f"""
207
+ <div
208
+ class="flex flex-col bg-gray-800 rounded-sm p-4 m-1 border border-gray-800 shadow-xs transition hover:shadow-lg dark:shadow-gray-700/25">
209
+ <div
210
+ class="-m-4 h-24 {header_colors[0]} rounded-sm rounded-b-none transition border group-hover:border-gray-100 group-hover:shadow-lg group-hover:{header_colors[4]}">
211
+ <span class="text-gray-300 font-mono tracking-widest p-4 pb-3 block text-xs text-center">MODEL {index + 1}:</span>
212
+ <span
213
+ class="flex w-30 mx-auto tracking-wide items-center justify-center rounded-full {header_colors[2]} px-1 py-0.5 {header_colors[3]}"
214
+ >
215
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="3" stroke="currentColor" class="w-4 h-4 mr-2 -ml-3 group-hover:animate group-hover:animate-pulse">
216
+ {'<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />' if label == 'REAL' else '<path stroke-linecap="round" stroke-linejoin="round" d="m9.75 9.75 4.5 4.5m0-4.5-4.5 4.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />'}
217
+ </svg>
218
+ <p class="text-base whitespace-nowrap leading-normal font-bold text-center self-center align-middle py-px">{label}</p>
219
+ </span>
220
+ </div>
221
+ <div>
222
+ <div class="mt-4 relative -mx-4 bg-gray-800">
223
+ <div class="w-full bg-gray-400 rounded-none h-8">
224
+ <div class="inline-flex whitespace-nowrap bg-green-400 h-full rounded-none" style="width: {real_conf * 100:.2f}%;">
225
+ <p class="p-2 px-4 text-xs self-center align-middle">Conf:
226
+ <span class="ml-1 font-medium font-mono">{real_conf:.4f}</span>
227
+ </p>
228
+ </div>
229
+ </div>
230
+ </div>
231
+ <div class="relative -mx-4 bg-gray-800">
232
+ <div class="w-full bg-gray-400 rounded-none h-8">
233
+ <div class="inline-flex whitespace-nowrap bg-red-400 h-full rounded-none" style="width: {ai_conf * 100:.2f}%;">
234
+ <p class="p-2 px-4 text-xs self-center align-middle">Conf:
235
+ <span class="ml-1 font-medium font-mono">{ai_conf:.4f}</span>
236
+ </p>
237
+ </div>
238
+ </div>
239
+ </div>
240
+ </div>
241
+ <div class="flex flex-col items-start">
242
+ <h4 class="mt-4 text-sm font-semibold tracking-wide">{model_name}</h4>
243
+ <div class="text-xs font-mono">Real: {real_conf:.4f}, AI: {ai_conf:.4f}</div>
244
+ <div class="card-footer">
245
+ <a href="https://huggingface.co/{model_path}" target="_blank" class="mt-2 text-xs text-nowrap nowrap" style="font-size:0.66rem !important;">by @{contributor}</a>
246
+ </div>
247
+ </div>
248
+ </div>
249
+ """
250
+
251
+ html_content = f"""
252
+ <link href="https://unpkg.com/[email protected]/dist/tailwind.min.css" rel="stylesheet">
253
+ <div class="container mx-auto">
254
+ <div class="grid xl:grid-cols-4 md:grid-cols-4 grid-cols-1 gap-1">
255
+ {generate_tile_html(0, results[0], "SwinV2 Based", "haywoodsloan", MODEL_PATHS["model_1"])}
256
+ {generate_tile_html(1, results[1], "ViT Based", "Heem2", MODEL_PATHS["model_2"])}
257
+ {generate_tile_html(2, results[2], "SDXL Dataset", "Organika", MODEL_PATHS["model_3"])}
258
+ {generate_tile_html(3, results[3], "SDXL + FLUX", "cmckinle", MODEL_PATHS["model_4"])}
259
+ {generate_tile_html(4, results[4], "Vit Based", "prithivMLmods", MODEL_PATHS["model_5"])}
260
+ {generate_tile_html(5, results[5], "Vit Based, Newer Dataset", "prithivMLmods", MODEL_PATHS["model_5b"])}
261
+ {generate_tile_html(6, results[6], "Swin, Midj + SDXL", "ideepankarsharma2003", MODEL_PATHS["model_6"])}
262
+ {generate_tile_html(7, results[7], "ViT", "temp", MODEL_PATHS["model_7"])}
263
+ </div>
264
+ </div>
265
+ """
266
+ return html_content
267
+
268
+
269
+ def predict_image_with_html(img, confidence_threshold, augment_methods, rotate_degrees, noise_level, sharpen_strength):
270
+ if augment_methods:
271
+ img_pil, _ = augment_image(img, augment_methods, rotate_degrees, noise_level, sharpen_strength)
272
+ else:
273
+ img_pil = img
274
+ img_pil, results = predict_image(img_pil, confidence_threshold)
275
+ img_np = np.array(img_pil) # Convert PIL Image to NumPy array
276
+ img_np_og = np.array(img) # Convert PIL Image to NumPy array
277
+
278
+ gradient_image = gradient_processing(img_np) # Added gradient processing
279
+ minmax_image = minmax_preprocess(img_np) # Added MinMax processing
280
+
281
+ # First pass - standard analysis
282
+ ela1 = ELA(img_np_og, quality=75, scale=50, contrast=20, linear=False, grayscale=True)
283
+
284
+ # Second pass - enhanced visibility
285
+ ela2 = ELA(img_np_og, quality=75, scale=75, contrast=25, linear=False, grayscale=True)
286
+ ela3 = ELA(img_np_og, quality=75, scale=75, contrast=25, linear=False, grayscale=False)
287
+
288
+ forensics_images = [img_pil, ela1, ela2, ela3, gradient_image, minmax_image]
289
+
290
+ html_content = generate_results_html(results)
291
+ return img_pil, forensics_images, html_content
292
+
293
+ with gr.Blocks(css="#post-gallery { overflow: hidden !important;} .grid-wrap{ overflow-y: hidden !important;} .ms-gr-ant-welcome-icon{ height:unset !important;} .tabs{margin-top:10px;}") as iface:
294
+ with ms.Application() as app:
295
+ with antd.ConfigProvider():
296
+ antdx.Welcome(
297
+ icon=
298
+ "https://cdn-avatars.huggingface.co/v1/production/uploads/639daf827270667011153fbc/WpeSFhuB81DY-1TjNUmV_.png",
299
+ title="Welcome to Project OpenSight",
300
+ description=
301
+ "The OpenSight aims to be an open-source SOTA generated image detection model. This HF Space is not only an introduction but a educational playground for the public to evaluate and challenge current open source models. **Space will be upgraded shortly; inference on all 6 models should take about 1.2~ seconds.** "
302
+ )
303
+ with gr.Tab("👀 Detection Models Eval / Playground"):
304
+ gr.Markdown("# Open Source Detection Models Found on the Hub\n\n - **Space will be upgraded shortly;** inference on all 6 models should take about 1.2~ seconds once we're back on CUDA.\n - The **Community Forensics** mother of all detection models is now available for inference, head to the middle tab above this.\n - Lots of exciting things coming up, stay tuned!")
305
+
306
+ with gr.Row():
307
+ with gr.Column(scale=1):
308
+ image_input = gr.Image(label="Upload Image to Analyze", sources=['upload', 'webcam'], type='pil')
309
+ with gr.Accordion("Settings (Optional)", open=False, elem_id="settings_accordion"):
310
+ augment_checkboxgroup = gr.CheckboxGroup(["rotate", "add_noise", "sharpen"], label="Augmentation Methods")
311
+ rotate_slider = gr.Slider(0, 45, value=2, step=1, label="Rotate Degrees", visible=False)
312
+ noise_slider = gr.Slider(0, 50, value=4, step=1, label="Noise Level", visible=False)
313
+ sharpen_slider = gr.Slider(0, 50, value=11, step=1, label="Sharpen Strength", visible=False)
314
+ confidence_slider = gr.Slider(0.0, 1.0, value=0.75, step=0.05, label="Confidence Threshold")
315
+ inputs = [image_input, confidence_slider, augment_checkboxgroup, rotate_slider, noise_slider, sharpen_slider]
316
+ predict_button = gr.Button("Predict")
317
+ augment_button = gr.Button("Augment & Predict")
318
+ image_output = gr.Image(label="Processed Image", visible=False)
319
+
320
+
321
+ with gr.Column(scale=2):
322
+ # Custom HTML component to display results in 5 columns
323
+ results_html = gr.HTML(label="Model Predictions")
324
+ forensics_gallery = gr.Gallery(label="Post Processed Images", visible=True, columns=[4], rows=[2], container=False, height="auto", object_fit="contain", elem_id="post-gallery")
325
+
326
+ outputs = [image_output, forensics_gallery, results_html]
327
+
328
+ # Show/hide rotate slider based on selected augmentation method
329
+ augment_checkboxgroup.change(lambda methods: gr.update(visible="rotate" in methods), inputs=[augment_checkboxgroup], outputs=[rotate_slider])
330
+ augment_checkboxgroup.change(lambda methods: gr.update(visible="add_noise" in methods), inputs=[augment_checkboxgroup], outputs=[noise_slider])
331
+ augment_checkboxgroup.change(lambda methods: gr.update(visible="sharpen" in methods), inputs=[augment_checkboxgroup], outputs=[sharpen_slider])
332
+
333
+ predict_button.click(
334
+ fn=predict_image_with_html,
335
+ inputs=inputs,
336
+ outputs=outputs
337
+ )
338
+ augment_button.click( # Connect Augment button to the function
339
+ fn=predict_image_with_html,
340
+ inputs=[
341
+ image_input,
342
+ confidence_slider,
343
+ gr.CheckboxGroup(["rotate", "add_noise", "sharpen"], value=["rotate", "add_noise", "sharpen"], visible=False), # Default values
344
+ rotate_slider,
345
+ noise_slider,
346
+ sharpen_slider
347
+ ],
348
+ outputs=outputs
349
+ )
350
+ predict_button.click(
351
+ fn=None,
352
+ js="() => {document.getElementById('project_accordion').open = false;}", # Close the project accordion
353
+ inputs=[],
354
+ outputs=[]
355
+ )
356
+ with gr.Tab("👑 Community Forensics Preview"):
357
+ temp_space = gr.load("aiwithoutborders-xyz/OpenSight-Community-Forensics-Preview", src="spaces")
358
+ # preview # no idea if this will work
359
+ with gr.Tab("🥇 Leaderboard"):
360
+ gr.Markdown("# AI Generated / Deepfake Detection Models Leaderboard: Soon™")
361
+
362
+
363
+ # Launch the interface
364
+ iface.launch()
app_mcp.py ADDED
@@ -0,0 +1,614 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import time
3
+ from typing import Literal
4
+ import spaces
5
+ import gradio as gr
6
+ import modelscope_studio.components.antd as antd
7
+ import modelscope_studio.components.antdx as antdx
8
+ import modelscope_studio.components.base as ms
9
+ from transformers import pipeline, AutoImageProcessor, SwinForImageClassification, Swinv2ForImageClassification, AutoFeatureExtractor, AutoModelForImageClassification
10
+ from torchvision import transforms
11
+ import torch
12
+ from PIL import Image
13
+ import numpy as np
14
+ import io
15
+ import logging
16
+ from utils.utils import softmax, augment_image, convert_pil_to_bytes
17
+ from utils.gradient import gradient_processing
18
+ from utils.minmax import preprocess as minmax_preprocess
19
+ from utils.ela import genELA as ELA
20
+ from utils.wavelet import wavelet_blocking_noise_estimation
21
+ from utils.bitplane import bit_plane_extractor
22
+ from utils.hf_logger import log_inference_data
23
+ from utils.text_content import QUICK_INTRO, IMPLEMENTATION
24
+ from agents.monitoring_agents import EnsembleMonitorAgent, WeightOptimizationAgent, SystemHealthAgent
25
+ from agents.smart_agents import ContextualIntelligenceAgent, ForensicAnomalyDetectionAgent
26
+
27
+ from forensics.registry import register_model, MODEL_REGISTRY, ModelEntry
28
+ from agents.weight_management import ModelWeightManager
29
+ from dotenv import load_dotenv
30
+ import json
31
+ from huggingface_hub import CommitScheduler
32
+
33
+ # Configure logging
34
+ logging.basicConfig(level=logging.INFO)
35
+ logger = logging.getLogger(__name__)
36
+ os.environ['HF_HUB_CACHE'] = './models'
37
+
38
+ LOCAL_LOG_DIR = "./hf_inference_logs"
39
+ HF_DATASET_NAME="degentic_rd0"
40
+ load_dotenv()
41
+ # print(os.getenv("HF_HUB_CACHE"))
42
+
43
+ # Custom JSON Encoder to handle numpy types
44
+ class NumpyEncoder(json.JSONEncoder):
45
+ def default(self, obj):
46
+ if isinstance(obj, np.float32):
47
+ return float(obj)
48
+ return json.JSONEncoder.default(self, obj)
49
+
50
+ # Ensure using GPU if available
51
+ device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
52
+
53
+ header_style = {
54
+ "textAlign": 'center',
55
+ "color": '#fff',
56
+ "height": 64,
57
+ "paddingInline": 48,
58
+ "lineHeight": '64px',
59
+ "backgroundColor": '#4096ff',
60
+ }
61
+
62
+ content_style = {
63
+ "textAlign": 'center',
64
+ "minHeight": 120,
65
+ "lineHeight": '120px',
66
+ "color": '#fff',
67
+ "backgroundColor": '#0958d9',
68
+ }
69
+
70
+ sider_style = {
71
+ "textAlign": 'center',
72
+ "lineHeight": '120px',
73
+ "color": '#fff',
74
+ "backgroundColor": '#1677ff',
75
+ }
76
+
77
+ footer_style = {
78
+ "textAlign": 'center',
79
+ "color": '#fff',
80
+ "backgroundColor": '#4096ff',
81
+ }
82
+
83
+ layout_style = {
84
+ "borderRadius": 8,
85
+ "overflow": 'hidden',
86
+ "width": 'calc(100% - 8px)',
87
+ "maxWidth": 'calc(100% - 8px)',
88
+ }
89
+ # Model paths and class names
90
+ MODEL_PATHS = {
91
+ "model_1": "haywoodsloan/ai-image-detector-deploy",
92
+ "model_2": "Heem2/AI-vs-Real-Image-Detection",
93
+ "model_3": "Organika/sdxl-detector",
94
+ "model_4": "cmckinle/sdxl-flux-detector_v1.1",
95
+ "model_5": "prithivMLmods/Deep-Fake-Detector-v2-Model",
96
+ "model_5b": "prithivMLmods/Deepfake-Detection-Exp-02-22",
97
+ "model_6": "ideepankarsharma2003/AI_ImageClassification_MidjourneyV6_SDXL",
98
+ "model_7": "date3k2/vit-real-fake-classification-v4"
99
+ }
100
+
101
+ CLASS_NAMES = {
102
+ "model_1": ['artificial', 'real'],
103
+ "model_2": ['AI Image', 'Real Image'],
104
+ "model_3": ['AI', 'Real'],
105
+ "model_4": ['AI', 'Real'],
106
+ "model_5": ['Realism', 'Deepfake'],
107
+ "model_5b": ['Real', 'Deepfake'],
108
+ "model_6": ['ai_gen', 'human'],
109
+ "model_7": ['Fake', 'Real'],
110
+
111
+ }
112
+
113
+ def preprocess_resize_256(image):
114
+ if image.mode != 'RGB':
115
+ image = image.convert('RGB')
116
+ return transforms.Resize((256, 256))(image)
117
+
118
+ def preprocess_resize_224(image):
119
+ if image.mode != 'RGB':
120
+ image = image.convert('RGB')
121
+ return transforms.Resize((224, 224))(image)
122
+
123
+ def postprocess_pipeline(prediction, class_names):
124
+ # Assumes HuggingFace pipeline output
125
+ return {pred['label']: pred['score'] for pred in prediction}
126
+
127
+ def postprocess_logits(outputs, class_names):
128
+ # Assumes model output with logits
129
+ logits = outputs.logits.cpu().numpy()[0]
130
+ probabilities = softmax(logits)
131
+ return {class_names[i]: probabilities[i] for i in range(len(class_names))}
132
+
133
+ # Expand ModelEntry to include metadata
134
+ # (Assume ModelEntry is updated in registry.py to accept display_name, contributor, model_path)
135
+ # If not, we will update registry.py accordingly after this.
136
+
137
+ def register_model_with_metadata(model_id, model, preprocess, postprocess, class_names, display_name, contributor, model_path):
138
+ entry = ModelEntry(model, preprocess, postprocess, class_names)
139
+ entry.display_name = display_name
140
+ entry.contributor = contributor
141
+ entry.model_path = model_path
142
+ MODEL_REGISTRY[model_id] = entry
143
+
144
+ # Load and register models (example for two models)
145
+ image_processor_1 = AutoImageProcessor.from_pretrained(MODEL_PATHS["model_1"], use_fast=True)
146
+ model_1 = Swinv2ForImageClassification.from_pretrained(MODEL_PATHS["model_1"]).to(device)
147
+ clf_1 = pipeline(model=model_1, task="image-classification", image_processor=image_processor_1, device=device)
148
+ register_model_with_metadata(
149
+ "model_1", clf_1, preprocess_resize_256, postprocess_pipeline, CLASS_NAMES["model_1"],
150
+ display_name="SwinV2 Based", contributor="haywoodsloan", model_path=MODEL_PATHS["model_1"]
151
+ )
152
+
153
+ clf_2 = pipeline("image-classification", model=MODEL_PATHS["model_2"], device=device)
154
+ register_model_with_metadata(
155
+ "model_2", clf_2, preprocess_resize_224, postprocess_pipeline, CLASS_NAMES["model_2"],
156
+ display_name="ViT Based", contributor="Heem2", model_path=MODEL_PATHS["model_2"]
157
+ )
158
+
159
+ # Register remaining models
160
+ feature_extractor_3 = AutoFeatureExtractor.from_pretrained(MODEL_PATHS["model_3"], device=device)
161
+ model_3 = AutoModelForImageClassification.from_pretrained(MODEL_PATHS["model_3"]).to(device)
162
+ def preprocess_256(image):
163
+ if image.mode != 'RGB':
164
+ image = image.convert('RGB')
165
+ return transforms.Resize((256, 256))(image)
166
+ def postprocess_logits_model3(outputs, class_names):
167
+ logits = outputs.logits.cpu().numpy()[0]
168
+ probabilities = softmax(logits)
169
+ return {class_names[i]: probabilities[i] for i in range(len(class_names))}
170
+ def model3_infer(image):
171
+ inputs = feature_extractor_3(image, return_tensors="pt").to(device)
172
+ with torch.no_grad():
173
+ outputs = model_3(**inputs)
174
+ return outputs
175
+ register_model_with_metadata(
176
+ "model_3", model3_infer, preprocess_256, postprocess_logits_model3, CLASS_NAMES["model_3"],
177
+ display_name="SDXL Dataset", contributor="Organika", model_path=MODEL_PATHS["model_3"]
178
+ )
179
+
180
+ feature_extractor_4 = AutoFeatureExtractor.from_pretrained(MODEL_PATHS["model_4"], device=device)
181
+ model_4 = AutoModelForImageClassification.from_pretrained(MODEL_PATHS["model_4"]).to(device)
182
+ def model4_infer(image):
183
+ inputs = feature_extractor_4(image, return_tensors="pt").to(device)
184
+ with torch.no_grad():
185
+ outputs = model_4(**inputs)
186
+ return outputs
187
+ def postprocess_logits_model4(outputs, class_names):
188
+ logits = outputs.logits.cpu().numpy()[0]
189
+ probabilities = softmax(logits)
190
+ return {class_names[i]: probabilities[i] for i in range(len(class_names))}
191
+ register_model_with_metadata(
192
+ "model_4", model4_infer, preprocess_256, postprocess_logits_model4, CLASS_NAMES["model_4"],
193
+ display_name="SDXL + FLUX", contributor="cmckinle", model_path=MODEL_PATHS["model_4"]
194
+ )
195
+
196
+ clf_5 = pipeline("image-classification", model=MODEL_PATHS["model_5"], device=device)
197
+ register_model_with_metadata(
198
+ "model_5", clf_5, preprocess_resize_224, postprocess_pipeline, CLASS_NAMES["model_5"],
199
+ display_name="Vit Based", contributor="prithivMLmods", model_path=MODEL_PATHS["model_5"]
200
+ )
201
+
202
+ clf_5b = pipeline("image-classification", model=MODEL_PATHS["model_5b"], device=device)
203
+ register_model_with_metadata(
204
+ "model_5b", clf_5b, preprocess_resize_224, postprocess_pipeline, CLASS_NAMES["model_5b"],
205
+ display_name="Vit Based, Newer Dataset", contributor="prithivMLmods", model_path=MODEL_PATHS["model_5b"]
206
+ )
207
+
208
+ image_processor_6 = AutoImageProcessor.from_pretrained(MODEL_PATHS["model_6"], use_fast=True)
209
+ model_6 = SwinForImageClassification.from_pretrained(MODEL_PATHS["model_6"]).to(device)
210
+ clf_6 = pipeline(model=model_6, task="image-classification", image_processor=image_processor_6, device=device)
211
+ register_model_with_metadata(
212
+ "model_6", clf_6, preprocess_resize_224, postprocess_pipeline, CLASS_NAMES["model_6"],
213
+ display_name="Swin, Midj + SDXL", contributor="ideepankarsharma2003", model_path=MODEL_PATHS["model_6"]
214
+ )
215
+
216
+ image_processor_7 = AutoImageProcessor.from_pretrained(MODEL_PATHS["model_7"], use_fast=True)
217
+ model_7 = AutoModelForImageClassification.from_pretrained(MODEL_PATHS["model_7"]).to(device)
218
+ clf_7 = pipeline(model=model_7, task="image-classification", image_processor=image_processor_7, device=device)
219
+ register_model_with_metadata(
220
+ "model_7", clf_7, preprocess_resize_224, postprocess_pipeline, CLASS_NAMES["model_7"],
221
+ display_name="ViT", contributor="temp", model_path=MODEL_PATHS["model_7"]
222
+ )
223
+
224
+ # Generic inference function
225
+
226
+ def infer(image: Image.Image, model_id: str, confidence_threshold: float = 0.75) -> dict:
227
+ entry = MODEL_REGISTRY[model_id]
228
+ img = entry.preprocess(image)
229
+ try:
230
+ result = entry.model(img)
231
+ scores = entry.postprocess(result, entry.class_names)
232
+ # Flatten output for Dataframe: include metadata and both class scores
233
+ ai_score = float(scores.get(entry.class_names[0], 0.0))
234
+ real_score = float(scores.get(entry.class_names[1], 0.0))
235
+ label = "AI" if ai_score >= confidence_threshold else ("REAL" if real_score >= confidence_threshold else "UNCERTAIN")
236
+ return {
237
+ "Model": entry.display_name,
238
+ "Contributor": entry.contributor,
239
+ "HF Model Path": entry.model_path,
240
+ "AI Score": ai_score,
241
+ "Real Score": real_score,
242
+ "Label": label
243
+ }
244
+ except Exception as e:
245
+ return {
246
+ "Model": entry.display_name,
247
+ "Contributor": entry.contributor,
248
+ "HF Model Path": entry.model_path,
249
+ "AI Score": 0.0, # Ensure it's a float even on error
250
+ "Real Score": 0.0, # Ensure it's a float even on error
251
+ "Label": f"Error: {str(e)}"
252
+ }
253
+
254
+ # Update predict_image to use all registered models in order
255
+
256
+ def predict_image(img, confidence_threshold):
257
+ model_ids = [
258
+ "model_1", "model_2", "model_3", "model_4", "model_5", "model_5b", "model_6", "model_7"
259
+ ]
260
+ results = [infer(img, model_id, confidence_threshold) for model_id in model_ids]
261
+ return img, results
262
+
263
+ def get_consensus_label(results):
264
+ labels = [r[4] for r in results if len(r) > 4]
265
+ if not labels:
266
+ return "No results"
267
+ consensus = max(set(labels), key=labels.count)
268
+ color = {"AI": "red", "REAL": "green", "UNCERTAIN": "orange"}.get(consensus, "gray")
269
+ return f"<b><span style='color:{color}'>{consensus}</span></b>"
270
+
271
+ # Update predict_image_with_json to return consensus label
272
+
273
+ def predict_image_with_json(img, confidence_threshold, augment_methods, rotate_degrees, noise_level, sharpen_strength):
274
+ # Ensure img is a PIL Image (if it's not already)
275
+ if not isinstance(img, Image.Image):
276
+ try:
277
+ # If it's a numpy array, convert it
278
+ img = Image.fromarray(img)
279
+ except Exception as e:
280
+ logger.error(f"Error converting input image to PIL: {e}")
281
+ # If conversion fails, it's a critical error for the whole process
282
+ raise ValueError("Input image could not be converted to PIL Image.")
283
+
284
+ # Initialize agents
285
+ monitor_agent = EnsembleMonitorAgent()
286
+ weight_manager = ModelWeightManager()
287
+ optimization_agent = WeightOptimizationAgent(weight_manager)
288
+ health_agent = SystemHealthAgent()
289
+ # New smart agents
290
+ context_agent = ContextualIntelligenceAgent()
291
+ anomaly_agent = ForensicAnomalyDetectionAgent()
292
+
293
+ # Monitor system health
294
+ health_agent.monitor_system_health()
295
+
296
+ if augment_methods:
297
+ img_pil, _ = augment_image(img, augment_methods, rotate_degrees, noise_level, sharpen_strength)
298
+ else:
299
+ img_pil = img
300
+ img_np_og = np.array(img) # Convert PIL Image to NumPy array
301
+
302
+ # 1. Get initial predictions from all models
303
+ model_predictions_raw = {}
304
+ confidence_scores = {}
305
+ results = [] # To store the results for the DataFrame
306
+
307
+ for model_id in MODEL_REGISTRY:
308
+ model_start = time.time()
309
+ result = infer(img_pil, model_id, confidence_threshold)
310
+ model_end = time.time()
311
+
312
+ # Monitor individual model performance
313
+ monitor_agent.monitor_prediction(
314
+ model_id,
315
+ result["Label"],
316
+ max(result.get("AI Score", 0.0), result.get("Real Score", 0.0)),
317
+ model_end - model_start
318
+ )
319
+
320
+ model_predictions_raw[model_id] = result # Store the full result dictionary
321
+ confidence_scores[model_id] = max(result.get("AI Score", 0.0), result.get("Real Score", 0.0))
322
+ results.append(result) # Add individual model result to the list
323
+
324
+ # 2. Infer context tags using ContextualIntelligenceAgent
325
+ image_data_for_context = {
326
+ "width": img.width,
327
+ "height": img.height,
328
+ "mode": img.mode,
329
+ # Add more features like EXIF data if exif_full_dump is used
330
+ }
331
+ detected_context_tags = context_agent.infer_context_tags(image_data_for_context, model_predictions_raw)
332
+ logger.info(f"Detected context tags: {detected_context_tags}")
333
+
334
+ # 3. Get adjusted weights, passing context tags
335
+ adjusted_weights = weight_manager.adjust_weights(model_predictions_raw, confidence_scores, context_tags=detected_context_tags)
336
+
337
+ # 4. Optimize weights if needed
338
+ # `final_prediction_label` is determined AFTER weighted consensus, so analyze_performance will be called later
339
+
340
+ # 5. Calculate weighted consensus
341
+ weighted_predictions = {
342
+ "AI": 0.0,
343
+ "REAL": 0.0,
344
+ "UNCERTAIN": 0.0
345
+ }
346
+
347
+ for model_id, prediction in model_predictions_raw.items(): # Use raw predictions for weighting
348
+ # Ensure the prediction label is valid for weighted_predictions
349
+ prediction_label = prediction.get("Label") # Extract the label
350
+ if prediction_label in weighted_predictions:
351
+ weighted_predictions[prediction_label] += adjusted_weights[model_id]
352
+ else:
353
+ # Handle cases where prediction might be an error or unexpected label
354
+ logger.warning(f"Unexpected prediction label '{prediction_label}' from model '{model_id}'. Skipping its weight in consensus.")
355
+
356
+ final_prediction_label = "UNCERTAIN"
357
+ if weighted_predictions["AI"] > weighted_predictions["REAL"] and weighted_predictions["AI"] > weighted_predictions["UNCERTAIN"]:
358
+ final_prediction_label = "AI"
359
+ elif weighted_predictions["REAL"] > weighted_predictions["AI"] and weighted_predictions["REAL"] > weighted_predictions["UNCERTAIN"]:
360
+ final_prediction_label = "REAL"
361
+
362
+ # Call analyze_performance after final_prediction_label is known
363
+ optimization_agent.analyze_performance(final_prediction_label, None)
364
+
365
+ # 6. Perform forensic processing
366
+ gradient_image = gradient_processing(img_np_og) # Added gradient processing
367
+ minmax_image = minmax_preprocess(img_np_og) # Added MinMax processing
368
+
369
+ # First pass - standard analysis
370
+ ela1 = ELA(img_np_og, quality=75, scale=50, contrast=20, linear=False, grayscale=True)
371
+
372
+ # Second pass - enhanced visibility
373
+ ela2 = ELA(img_np_og, quality=75, scale=75, contrast=25, linear=False, grayscale=True)
374
+ ela3 = ELA(img_np_og, quality=75, scale=75, contrast=25, linear=False, grayscale=False)
375
+
376
+ forensics_images = [img_pil, ela1, ela2, ela3, gradient_image, minmax_image]
377
+
378
+ # 7. Generate boilerplate descriptions for forensic outputs for anomaly agent
379
+ forensic_output_descriptions = [
380
+ f"Original augmented image (PIL): {img_pil.width}x{img_pil.height}",
381
+ "ELA analysis (Pass 1): Grayscale error map, quality 75.",
382
+ "ELA analysis (Pass 2): Grayscale error map, quality 75, enhanced contrast.",
383
+ "ELA analysis (Pass 3): Color error map, quality 75, enhanced contrast.",
384
+ "Gradient processing: Highlights edges and transitions.",
385
+ "MinMax processing: Deviations in local pixel values."
386
+ ]
387
+ # You could also add descriptions for Wavelet and Bit Plane if they were dynamic outputs
388
+ # For instance, if wavelet_blocking_noise_estimation had parameters that changed and you wanted to describe them.
389
+
390
+ # 8. Analyze forensic outputs for anomalies using ForensicAnomalyDetectionAgent
391
+ anomaly_detection_results = anomaly_agent.analyze_forensic_outputs(forensic_output_descriptions)
392
+ logger.info(f"Forensic anomaly detection: {anomaly_detection_results['summary']}")
393
+
394
+
395
+ # Prepare table rows for Dataframe (exclude model path)
396
+ table_rows = [[
397
+ r.get("Model", ""),
398
+ r.get("Contributor", ""),
399
+ r.get("AI Score", 0.0) if r.get("AI Score") is not None else 0.0,
400
+ r.get("Real Score", 0.0) if r.get("Real Score") is not None else 0.0,
401
+ r.get("Label", "Error")
402
+ ] for r in results]
403
+
404
+ logger.info(f"Type of table_rows: {type(table_rows)}")
405
+ for i, row in enumerate(table_rows):
406
+ logger.info(f"Row {i} types: {[type(item) for item in row]}")
407
+
408
+ # The get_consensus_label function is now replaced by final_prediction_label from weighted consensus
409
+ consensus_html = f"<b><span style='color:{'red' if final_prediction_label == 'AI' else ('green' if final_prediction_label == 'REAL' else 'orange')}'>{final_prediction_label}</span></b>"
410
+
411
+ # Prepare data for logging to Hugging Face dataset
412
+ inference_params = {
413
+ "confidence_threshold": confidence_threshold,
414
+ "augment_methods": augment_methods,
415
+ "rotate_degrees": rotate_degrees,
416
+ "noise_level": noise_level,
417
+ "sharpen_strength": sharpen_strength,
418
+ "detected_context_tags": detected_context_tags
419
+ }
420
+
421
+ ensemble_output_data = {
422
+ "final_prediction_label": final_prediction_label,
423
+ "weighted_predictions": weighted_predictions,
424
+ "adjusted_weights": adjusted_weights
425
+ }
426
+
427
+ # Collect agent monitoring data
428
+ agent_monitoring_data_log = {
429
+ "ensemble_monitor": {
430
+ "alerts": monitor_agent.alerts,
431
+ "performance_metrics": monitor_agent.performance_metrics
432
+ },
433
+ "weight_optimization": {
434
+ "prediction_history_length": len(optimization_agent.prediction_history),
435
+ # You might add a summary of recent accuracy here if _calculate_accuracy is exposed
436
+ },
437
+ "system_health": {
438
+ "memory_usage": health_agent.health_metrics["memory_usage"],
439
+ "gpu_utilization": health_agent.health_metrics["gpu_utilization"]
440
+ },
441
+ "context_intelligence": {
442
+ "detected_context_tags": detected_context_tags
443
+ },
444
+ "forensic_anomaly_detection": anomaly_detection_results
445
+ }
446
+
447
+ # Log the inference data
448
+ log_inference_data(
449
+ original_image=img, # Use the original uploaded image
450
+ inference_params=inference_params,
451
+ model_predictions=results, # This already contains detailed results for each model
452
+ ensemble_output=ensemble_output_data,
453
+ forensic_images=forensics_images, # This is the list of PIL images generated by forensic tools
454
+ agent_monitoring_data=agent_monitoring_data_log,
455
+ human_feedback=None # This can be populated later with human review data
456
+ )
457
+
458
+ # Final type safety check for forensic_images before returning
459
+ cleaned_forensics_images = []
460
+ for f_img in forensics_images:
461
+ if isinstance(f_img, Image.Image):
462
+ cleaned_forensics_images.append(f_img)
463
+ elif isinstance(f_img, np.ndarray):
464
+ try:
465
+ cleaned_forensics_images.append(Image.fromarray(f_img))
466
+ except Exception as e:
467
+ logger.warning(f"Could not convert numpy array to PIL Image for gallery: {e}")
468
+ # Optionally, append a placeholder or skip
469
+ else:
470
+ logger.warning(f"Unexpected type in forensic_images: {type(f_img)}. Skipping.")
471
+
472
+ logger.info(f"Cleaned forensic images types: {[type(img) for img in cleaned_forensics_images]}")
473
+
474
+ # Ensure numerical values in results are standard Python floats before JSON serialization
475
+ for i, res_dict in enumerate(results):
476
+ for key in ["AI Score", "Real Score"]:
477
+ value = res_dict.get(key)
478
+ if isinstance(value, np.float32):
479
+ res_dict[key] = float(value)
480
+ logger.info(f"Converted {key} for result {i} from numpy.float32 to float.")
481
+
482
+ # Return raw model results as JSON string for debug_json component
483
+ json_results = json.dumps(results, cls=NumpyEncoder)
484
+
485
+ return img_pil, cleaned_forensics_images, table_rows, json_results, consensus_html
486
+
487
+ with gr.Blocks(css="#post-gallery { overflow: hidden !important;} .grid-wrap{ overflow-y: hidden !important;} .ms-gr-ant-welcome-icon{ height:unset !important;} .tabs{margin-top:10px;}") as demo:
488
+ with ms.Application() as app:
489
+ with antd.ConfigProvider():
490
+ antdx.Welcome(
491
+ icon="https://cdn-avatars.huggingface.co/v1/production/uploads/639daf827270667011153fbc/WpeSFhuB81DY-1TjNUmV_.png",
492
+ title="Welcome to Project OpenSight",
493
+ description="The OpenSight aims to be an open-source SOTA generated image detection model. This HF Space is not only an introduction but a educational playground for the public to evaluate and challenge current open source models. **Space will be upgraded shortly; inference on all 6 models should take about 1.2~ seconds.** "
494
+ )
495
+ with gr.Tab("👀 Rethinking Detection Models: Multi-Model, Multi-Strategy Ensemble Team and Agentic Pipelines"):
496
+ gr.Markdown("# Open Source Detection Models Found on the Hub\n\n - **IMPORTANT UPDATE REGARDING YOUR DATA AND PRIVACY: [PLEASE REFER TO THE MCP SERVER HACKATHON SUBMISSION FOR CRUCIAL DETAILS](https://huggingface.co/spaces/Agents-MCP-Hackathon/mcp-deepfake-forensics).** ")
497
+
498
+ with gr.Row():
499
+ with gr.Column(scale=1):
500
+ image_input = gr.Image(label="Upload Image to Analyze", sources=['upload', 'webcam'], type='pil')
501
+ with gr.Accordion("Settings (Optional)", open=False, elem_id="settings_accordion"):
502
+ augment_checkboxgroup = gr.CheckboxGroup(["rotate", "add_noise", "sharpen"], label="Augmentation Methods")
503
+ rotate_slider = gr.Slider(0, 45, value=2, step=1, label="Rotate Degrees", visible=False)
504
+ noise_slider = gr.Slider(0, 50, value=4, step=1, label="Noise Level", visible=False)
505
+ sharpen_slider = gr.Slider(0, 50, value=11, step=1, label="Sharpen Strength", visible=False)
506
+ confidence_slider = gr.Slider(0.0, 1.0, value=0.75, step=0.05, label="Confidence Threshold")
507
+ inputs = [image_input, confidence_slider, augment_checkboxgroup, rotate_slider, noise_slider, sharpen_slider]
508
+ predict_button = gr.Button("Predict")
509
+ augment_button = gr.Button("Augment & Predict")
510
+ image_output = gr.Image(label="Processed Image", visible=False)
511
+
512
+
513
+ with gr.Column(scale=2):
514
+ # Use Gradio-native Dataframe to display results with headers
515
+ results_table = gr.Dataframe(
516
+ label="Model Predictions",
517
+ headers=["Model", "Contributor", "AI Score", "Real Score", "Label"],
518
+ datatype=["str", "str", "number", "number", "str"]
519
+ )
520
+ forensics_gallery = gr.Gallery(label="Post Processed Images", visible=True, columns=[4], rows=[2], container=False, height="auto", object_fit="contain", elem_id="post-gallery")
521
+ with gr.Accordion("Debug Output (Raw JSON)", open=False):
522
+ debug_json = gr.JSON(label="Raw Model Results")
523
+ consensus_md = gr.Markdown(label="Consensus", value="")
524
+
525
+ outputs = [image_output, forensics_gallery, results_table, debug_json, consensus_md]
526
+
527
+ # Show/hide rotate slider based on selected augmentation method
528
+ augment_checkboxgroup.change(lambda methods: gr.update(visible="rotate" in methods), inputs=[augment_checkboxgroup], outputs=[rotate_slider])
529
+ augment_checkboxgroup.change(lambda methods: gr.update(visible="add_noise" in methods), inputs=[augment_checkboxgroup], outputs=[noise_slider])
530
+ augment_checkboxgroup.change(lambda methods: gr.update(visible="sharpen" in methods), inputs=[augment_checkboxgroup], outputs=[sharpen_slider])
531
+
532
+ predict_button.click(
533
+ fn=predict_image_with_json,
534
+ inputs=inputs,
535
+ outputs=outputs,
536
+ api_name="/predict"
537
+ )
538
+ augment_button.click( # Connect Augment button to the function
539
+ fn=predict_image_with_json,
540
+ inputs=[
541
+ image_input,
542
+ confidence_slider,
543
+ gr.CheckboxGroup(["rotate", "add_noise", "sharpen"], value=["rotate", "add_noise", "sharpen"], visible=False), # Default values
544
+ rotate_slider,
545
+ noise_slider,
546
+ sharpen_slider
547
+ ],
548
+ outputs=outputs,
549
+ api_name="/augment"
550
+ )
551
+ with gr.Tab("🙈 Project Introduction"):
552
+ gr.Markdown(QUICK_INTRO)
553
+
554
+ with gr.Tab("👑 Community Forensics Preview"):
555
+ # temp_space = gr.load("aiwithoutborders-xyz/OpenSight-Community-Forensics-Preview", src="spaces")
556
+ gr.Markdown("Community Forensics Preview coming soon!") # Placeholder for now
557
+ with gr.Tab("🥇 Leaderboard"):
558
+ gr.Markdown("# AI Generated / Deepfake Detection Models Leaderboard: Soon™")
559
+
560
+ with gr.Tab("Wavelet Blocking Noise Estimation", visible=False):
561
+ gr.Interface(
562
+ fn=wavelet_blocking_noise_estimation,
563
+ inputs=[gr.Image(type="pil"), gr.Slider(1, 32, value=8, step=1, label="Block Size")],
564
+ outputs=gr.Image(type="pil"),
565
+ title="Wavelet-Based Noise Analysis",
566
+ description="Analyzes image noise patterns using wavelet decomposition. This tool helps detect compression artifacts and artificial noise patterns that may indicate image manipulation. Higher noise levels in specific regions can reveal areas of potential tampering.",
567
+ api_name="/tool_waveletnoise"
568
+ )
569
+
570
+
571
+ with gr.Tab("Bit Plane Values", visible=False):
572
+ """Forensics Tool: Bit Plane Extractor
573
+
574
+ Args:
575
+ image: PIL Image to analyze
576
+ channel: Color channel to extract bit plane from ("Luminance", "Red", "Green", "Blue", "RGB Norm")
577
+ bit_plane: Bit plane index to extract (0-7)
578
+ filter_type: Filter to apply ("Disabled", "Median", "Gaussian")
579
+ """
580
+ gr.Interface(
581
+
582
+ fn=bit_plane_extractor,
583
+ inputs=[
584
+ gr.Image(type="pil"),
585
+ gr.Dropdown(["Luminance", "Red", "Green", "Blue", "RGB Norm"], label="Channel", value="Luminance"),
586
+ gr.Slider(0, 7, value=0, step=1, label="Bit Plane"),
587
+ gr.Dropdown(["Disabled", "Median", "Gaussian"], label="Filter", value="Disabled")
588
+ ],
589
+ outputs=gr.Image(type="pil"),
590
+ title="Bit Plane Analysis",
591
+ description="Extracts and visualizes individual bit planes from different color channels. This forensic tool helps identify hidden patterns and artifacts in image data that may indicate manipulation. Different bit planes can reveal inconsistencies in image processing or editing.",
592
+ api_name="/tool_bitplane"
593
+ )
594
+ # with gr.Tab("EXIF Full Dump"):
595
+ # gr.Interface(
596
+ # fn=exif_full_dump,
597
+ # inputs=gr.Image(type="pil"),
598
+ # outputs=gr.JSON(),
599
+ # description="Extract all EXIF metadata from the uploaded image."
600
+ # )
601
+
602
+ # --- MCP-Ready Launch ---
603
+ if __name__ == "__main__":
604
+ # Initialize CommitScheduler
605
+ # The scheduler will monitor LOCAL_LOG_DIR and push changes to HF_DATASET_NAME
606
+ with CommitScheduler(
607
+ repo_id=HF_DATASET_NAME, # Your Hugging Face dataset repository ID
608
+ repo_type="dataset",
609
+ folder_path=LOCAL_LOG_DIR,
610
+ every=5, # Commit every 5 minutes
611
+ private=False, # Keep your dataset private
612
+ token=os.getenv("HF_TOKEN") # Uncomment and set if token is not saved globally
613
+ ) as scheduler:
614
+ demo.launch(mcp_server=True)
requirements.txt ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ gradio[mcp]
2
+ gradio_leaderboard
3
+ transformers
4
+ huggingface_hub[hf_xet]
5
+ torchvision
6
+ torch
7
+ spaces
8
+ # pillow
9
+ opencv-python
10
+ modelscope_studio
11
+ pydantic==2.10.6
12
+ tf-keras
13
+ PyWavelets
14
+ pyexiftool
15
+ psutil
16
+ datasets
17
+ Pillow
18
+ python-dotenv