astrosbd commited on
Commit
d09366d
·
verified ·
1 Parent(s): cd8cefd
Files changed (1) hide show
  1. app.py +533 -2
app.py CHANGED
@@ -1,3 +1,530 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  def process_image(input_image, damage_threshold, deepfake_threshold, skip_damage, device_str, usage_count, recipient_email):
2
  """Process an image through the detection pipeline and send results by email"""
3
  # Handle empty or None usage_count
@@ -212,7 +739,6 @@ def process_image(input_image, damage_threshold, deepfake_threshold, skip_damage
212
 
213
  return analysis_text + f"\n\n📧 {email_message}", usage_count, email_message
214
 
215
-
216
  def create_gradio_interface():
217
  # Define a theme
218
  theme = gr.themes.Soft(
@@ -428,4 +954,9 @@ def create_gradio_interface():
428
  outputs=[usage_display]
429
  )
430
 
431
- return app
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ import importlib.util
3
+ import os
4
+ import sys
5
+ import time
6
+ import cv2
7
+ import torch
8
+ import numpy as np
9
+ import gradio as gr
10
+ from PIL import Image
11
+ from torchvision import transforms
12
+ import torch.nn as nn
13
+ import traceback
14
+ from huggingface_hub import hf_hub_download
15
+ from torchvision.models import vit_b_16
16
+
17
+ # Email functionality imports
18
+ import smtplib
19
+ from email.mime.multipart import MIMEMultipart
20
+ from email.mime.text import MIMEText
21
+ from email.mime.image import MIMEImage
22
+ from email.mime.base import MIMEBase
23
+ from email import encoders
24
+ import base64
25
+ import io
26
+ from datetime import datetime
27
+
28
+ # Add current directory to path
29
+ if not os.getcwd() in sys.path:
30
+ sys.path.append(os.getcwd())
31
+
32
+ # Check if detectron2 is installed
33
+ if importlib.util.find_spec("detectron2") is None:
34
+ print("Installing PyTorch and Detectron2...")
35
+ os.system("pip install torch torchvision --extra-index-url https://download.pytorch.org/whl/cpu")
36
+ os.system("pip install git+https://github.com/facebookresearch/detectron2.git")
37
+ print("Installation complete!")
38
+
39
+ # Check for detectron2
40
+ try:
41
+ from detectron2.engine import DefaultPredictor
42
+ from detectron2.config import get_cfg
43
+ from detectron2.utils.visualizer import Visualizer, ColorMode
44
+ from detectron2 import model_zoo
45
+ DETECTRON2_AVAILABLE = True
46
+ except ImportError:
47
+ print("Warning: Detectron2 is not installed. Damage detection will not be available.")
48
+ DETECTRON2_AVAILABLE = False
49
+
50
+ # Define model paths
51
+ DEFAULT_DAMAGE_MODEL_PATH = "./output/model_final.pth"
52
+ DEFAULT_DEEPFAKE_MODEL_PATH = "./output/vit_deepfake_final.pth"
53
+
54
+ # Sample images for demo (add your own paths)
55
+ SAMPLE_IMAGES = [
56
+ "./test3.png",
57
+ "./test5.png",
58
+ ]
59
+
60
+ # Maximum number of tries allowed
61
+ MAX_TRIES = 5
62
+
63
+ # Email configuration using environment variables
64
+ EMAIL_CONFIG = {
65
+ 'SMTP_SERVER': 'smtp.mail.ovh.net',
66
+ 'EMAIL': os.getenv('login_email', 'sales@askhedi.fr'),
67
+ 'PASSWORD': os.getenv('password_email'),
68
+ 'SMTP_PORT': 465
69
+ }
70
+
71
+ # Télécharger le modèle deepfake depuis Hugging Face
72
+ try:
73
+ huggingface_model_path = hf_hub_download(
74
+ repo_id="Askhedi/Car_damage_fraud_detector",
75
+ filename="vit_deepfake_final.pth",
76
+ token=os.getenv('key')
77
+ )
78
+ print(f"Modèle téléchargé depuis Hugging Face: {huggingface_model_path}")
79
+ except Exception as e:
80
+ print(f"Erreur lors du téléchargement du modèle depuis Hugging Face: {e}")
81
+ huggingface_model_path = None
82
+
83
+ def send_results_by_email(recipient_email, analysis_text, result_image, original_filename="car_image"):
84
+ """Send analysis results by email"""
85
+ if not EMAIL_CONFIG['PASSWORD']:
86
+ return False, "❌ Email configuration not available. Please contact support."
87
+
88
+ if not recipient_email or "@" not in recipient_email:
89
+ return False, "❌ Please provide a valid email address"
90
+
91
+ try:
92
+ # Create message
93
+ msg = MIMEMultipart('related')
94
+ msg['From'] = EMAIL_CONFIG['EMAIL']
95
+ msg['To'] = recipient_email
96
+ msg['Subject'] = f"🚗 Car Damage Analysis Results - {original_filename}"
97
+
98
+ # Create HTML body
99
+ html_body = f"""
100
+ <html>
101
+ <head>
102
+ <title>Car Damage Analysis Results</title>
103
+ <style>
104
+ body {{ font-family: Arial, sans-serif; margin: 20px; }}
105
+ .header {{ background-color: #f0f0f0; padding: 15px; border-radius: 5px; }}
106
+ .results {{ margin: 20px 0; white-space: pre-wrap; }}
107
+ .footer {{ color: #666; font-size: 12px; margin-top: 30px; }}
108
+ </style>
109
+ </head>
110
+ <body>
111
+ <div class="header">
112
+ <h1>🚗 Car Damage Fraud Detection Results</h1>
113
+ <p>Analysis performed on: {datetime.now().strftime('%d/%m/%Y at %H:%M:%S')}</p>
114
+ </div>
115
+
116
+ <div class="results">
117
+ <h3>📋 Analysis Details:</h3>
118
+ <pre>{analysis_text}</pre>
119
+ </div>
120
+
121
+ <div class="footer">
122
+ <p><em>This analysis was generated by the Car Damage Fraud Detector AI system.</em></p>
123
+ <p>Powered by Askhedi - Advanced AI Detection Services</p>
124
+ </div>
125
+ </body>
126
+ </html>
127
+ """
128
+
129
+ msg.attach(MIMEText(html_body, 'html'))
130
+
131
+ # Attach result image if available
132
+ if result_image is not None:
133
+ # Convert numpy array to image bytes
134
+ if isinstance(result_image, np.ndarray):
135
+ # Convert from RGB to PIL Image
136
+ pil_image = Image.fromarray(result_image.astype('uint8'))
137
+ img_buffer = io.BytesIO()
138
+ pil_image.save(img_buffer, format='PNG')
139
+ img_data = img_buffer.getvalue()
140
+
141
+ # Attach image
142
+ img_part = MIMEBase('application', 'octet-stream')
143
+ img_part.set_payload(img_data)
144
+ encoders.encode_base64(img_part)
145
+ img_part.add_header(
146
+ 'Content-Disposition',
147
+ f'attachment; filename="analysis_result_{original_filename}.png"'
148
+ )
149
+ msg.attach(img_part)
150
+
151
+ # Send email
152
+ server = smtplib.SMTP_SSL(EMAIL_CONFIG['SMTP_SERVER'], EMAIL_CONFIG['SMTP_PORT'])
153
+ server.login(EMAIL_CONFIG['EMAIL'], EMAIL_CONFIG['PASSWORD'])
154
+ server.send_message(msg)
155
+ server.quit()
156
+
157
+ return True, f"✅ Results sent successfully to {recipient_email}"
158
+
159
+ except Exception as e:
160
+ return False, f"❌ Error sending email: {str(e)}"
161
+
162
+ def setup_device(device_str):
163
+ """Set up the computation device"""
164
+ if device_str == 'auto':
165
+ if torch.cuda.is_available():
166
+ return torch.device('cuda:0')
167
+ elif hasattr(torch, 'backends') and hasattr(torch.backends, 'mps') and torch.backends.mps.is_available():
168
+ return torch.device('mps')
169
+ else:
170
+ return torch.device('cpu')
171
+ elif device_str == 'cuda' and torch.cuda.is_available():
172
+ return torch.device('cuda:0')
173
+ elif device_str == 'mps' and hasattr(torch, 'backends') and hasattr(torch.backends, 'mps') and torch.backends.mps.is_available():
174
+ return torch.device('mps')
175
+ else:
176
+ print(f"Warning: Device {device_str} not available, using CPU instead.")
177
+ return torch.device('cpu')
178
+
179
+ def setup_damage_detector(model_path, threshold=0.7):
180
+ """Set up the damage detection model"""
181
+ if not DETECTRON2_AVAILABLE:
182
+ print("Detectron2 is not installed. Cannot set up damage detector.")
183
+ return None, None
184
+
185
+ try:
186
+ print(f"Checking model path: {model_path}")
187
+ print(f"Model exists: {os.path.exists(model_path)}")
188
+
189
+ if model_path is None or not os.path.exists(model_path):
190
+ print(f"Error: Damage model file not found at {model_path}")
191
+ return None, None
192
+
193
+ cfg = get_cfg()
194
+ cfg.merge_from_file(model_zoo.get_config_file("COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml"))
195
+ cfg.MODEL.WEIGHTS = model_path
196
+ cfg.MODEL.ROI_HEADS.NUM_CLASSES = 1 # Only one class (damage)
197
+ cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = threshold
198
+
199
+ # Use CPU if on Mac (MPS)
200
+ cfg.MODEL.DEVICE = "cpu"
201
+ print("Forcing Detectron2 to use CPU")
202
+
203
+ predictor = DefaultPredictor(cfg)
204
+ return predictor, cfg
205
+ except Exception as e:
206
+ print(f"Detailed error: {str(e)}")
207
+ import traceback
208
+ traceback.print_exc()
209
+ return None, None
210
+
211
+ def load_vit_deepfake_model(model_path, device):
212
+ """Load the Vision Transformer (ViT) model for deepfake detection"""
213
+ if model_path is None:
214
+ print("No deepfake model specified. Skipping deepfake detection.")
215
+ return None
216
+
217
+ try:
218
+ # Create ViT model with binary classification head
219
+ model = vit_b_16(weights=None)
220
+
221
+ # Modify the classifier head for binary classification (real vs fake)
222
+ in_features = model.heads.head.in_features
223
+ model.heads.head = nn.Linear(in_features, 2)
224
+
225
+ # Load weights
226
+ print(f"Loading ViT deepfake model from: {model_path}")
227
+ checkpoint = torch.load(model_path, map_location='cpu')
228
+
229
+ if isinstance(checkpoint, dict) and 'model_state_dict' in checkpoint:
230
+ model.load_state_dict(checkpoint['model_state_dict'])
231
+ elif isinstance(checkpoint, dict) and 'state_dict' in checkpoint:
232
+ model.load_state_dict(checkpoint['state_dict'])
233
+ else:
234
+ model.load_state_dict(checkpoint)
235
+
236
+ # Move model to device and set to evaluation mode
237
+ model = model.to(device)
238
+ model.eval()
239
+
240
+ return model
241
+ except Exception as e:
242
+ print(f"Error loading ViT deepfake model: {e}")
243
+ import traceback
244
+ traceback.print_exc()
245
+ return None
246
+
247
+ def preprocess_for_vit(image, device):
248
+ """Prétraite une image pour le modèle ViT de torchvision"""
249
+ try:
250
+ print("Début du prétraitement de l'image...")
251
+ import torch
252
+ from torchvision import transforms
253
+ from PIL import Image
254
+ import numpy as np
255
+ import cv2
256
+
257
+ # Transformation standard pour ViT (similaire à celle utilisée lors de l'entraînement)
258
+ transform = transforms.Compose([
259
+ transforms.Resize((224, 224)),
260
+ transforms.ToTensor(),
261
+ transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
262
+ ])
263
+
264
+ # Convertir l'image en RGB si nécessaire
265
+ if len(image.shape) == 3 and image.shape[2] == 3:
266
+ if image.dtype != np.uint8:
267
+ image = (image * 255).astype(np.uint8)
268
+ rgb_img = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
269
+ else:
270
+ rgb_img = image
271
+ print("Image convertie en RGB")
272
+
273
+ # Convertir en PIL Image et appliquer les transformations
274
+ pil_img = Image.fromarray(rgb_img)
275
+ img_tensor = transform(pil_img).unsqueeze(0).to(device) # Ajouter la dimension batch et envoyer au device
276
+ print("Image prétraitée avec succès")
277
+
278
+ return img_tensor
279
+ except Exception as e:
280
+ print(f"ERREUR lors du prétraitement de l'image: {e}")
281
+ traceback.print_exc()
282
+ return None
283
+
284
+ def detect_damage(img, damage_detector):
285
+ """Detect damage in an image"""
286
+ try:
287
+ if img is None:
288
+ raise ValueError("Invalid image")
289
+
290
+ # If no detector, use whole image
291
+ if damage_detector is None:
292
+ h, w = img.shape[:2]
293
+ damage_regions = [{
294
+ "box": (0, 0, w, h),
295
+ "score": 1.0,
296
+ "mask": None
297
+ }]
298
+ return img, None, damage_regions
299
+
300
+ # Run inference
301
+ outputs = damage_detector(img)
302
+
303
+ # Get regions
304
+ instances = outputs["instances"].to("cpu")
305
+ boxes = instances.pred_boxes.tensor.numpy() if instances.has("pred_boxes") else []
306
+ scores = instances.scores.numpy() if instances.has("scores") else []
307
+ masks = instances.pred_masks.numpy() if instances.has("pred_masks") else []
308
+
309
+ damage_regions = []
310
+ for i in range(len(boxes)):
311
+ x1, y1, x2, y2 = map(int, boxes[i])
312
+ damage_regions.append({
313
+ "box": (x1, y1, x2, y2),
314
+ "score": float(scores[i]),
315
+ "mask": masks[i] if len(masks) > i else None
316
+ })
317
+
318
+ # If no regions found, use whole image
319
+ if not damage_regions:
320
+ h, w = img.shape[:2]
321
+ damage_regions = [{
322
+ "box": (0, 0, w, h),
323
+ "score": 1.0,
324
+ "mask": None
325
+ }]
326
+
327
+ return img, outputs, damage_regions
328
+ except Exception as e:
329
+ print(f"Error detecting damage: {e}")
330
+ traceback.print_exc()
331
+
332
+ # Return whole image if error
333
+ if 'img' in locals() and img is not None:
334
+ h, w = img.shape[:2]
335
+ damage_regions = [{
336
+ "box": (0, 0, w, h),
337
+ "score": 1.0,
338
+ "mask": None
339
+ }]
340
+ return img, None, damage_regions
341
+ return None, None, []
342
+
343
+ def check_deepfake_vit(image, damage_regions, deepfake_model, device, threshold=0.5):
344
+ """Vérifie si les régions endommagées sont des deepfakes en utilisant un modèle ViT de torchvision"""
345
+ results = []
346
+
347
+ if deepfake_model is None:
348
+ print("Le modèle deepfake est None, ignorant la détection")
349
+ return []
350
+
351
+ print(f"Début de la détection de deepfake avec {len(damage_regions)} régions")
352
+ detailed_info = []
353
+
354
+ try:
355
+ # Si pas de régions endommagées, vérifier l'image entière
356
+ if not damage_regions:
357
+ print("Pas de régions endommagées, vérification de l'image entière")
358
+ img_tensor = preprocess_for_vit(image, device)
359
+ if img_tensor is None:
360
+ print("Échec du prétraitement de l'image")
361
+ return []
362
+
363
+ # Exécuter l'inférence - Passer directement le tensor
364
+ print("Exécution de l'inférence sur l'image entière")
365
+ with torch.no_grad():
366
+ outputs = deepfake_model(img_tensor) # Modèle torchvision attend directement le tensor
367
+
368
+ # Obtenir les prédictions
369
+ logits = outputs
370
+ probabilities = torch.nn.functional.softmax(logits, dim=1)
371
+
372
+ # Obtenir les probabilités de classe
373
+ for i in range(probabilities.shape[1]):
374
+ prob = probabilities[0, i].item()
375
+ print(f"Probabilité classe {i}: {prob*100:.2f}%")
376
+
377
+ fake_prob = probabilities[0, 1].item() # Probabilité d'être faux (classe 1)
378
+ real_prob = probabilities[0, 0].item() # Probabilité d'être réel (classe 0)
379
+ is_fake = fake_prob > threshold
380
+
381
+ detailed_info.append(f"Image entière: RÉEL={real_prob*100:.2f}%, FAUX={fake_prob*100:.2f}%")
382
+
383
+ results.append({
384
+ "region": "full_image",
385
+ "deepfake_prob": float(fake_prob),
386
+ "real_prob": float(real_prob),
387
+ "is_fake": bool(is_fake),
388
+ "detailed_info": detailed_info[-1]
389
+ })
390
+
391
+ return results
392
+
393
+ # Traiter chaque région endommagée
394
+ for i, region in enumerate(damage_regions):
395
+ print(f"Traitement de la région {i}...")
396
+ x1, y1, x2, y2 = region["box"]
397
+ x1, y1 = max(0, x1), max(0, y1)
398
+ x2, y2 = min(image.shape[1], x2), min(image.shape[0], y2)
399
+
400
+ # Ne traiter que les régions valides
401
+ if x2 > x1 and y2 > y1:
402
+ # Extraire la région
403
+ roi = image[y1:y2, x1:x2]
404
+ print(f"Taille de la région {i}: {roi.shape}")
405
+
406
+ # Prétraiter
407
+ img_tensor = preprocess_for_vit(roi, device)
408
+ if img_tensor is None:
409
+ print(f"Échec du prétraitement de la région {i}")
410
+ continue
411
+
412
+ # Inférence - Passer directement le tensor
413
+ print(f"Exécution de l'inférence sur la région {i}")
414
+ with torch.no_grad():
415
+ outputs = deepfake_model(img_tensor) # Modèle torchvision attend directement le tensor
416
+
417
+ # Obtenir les prédictions
418
+ logits = outputs
419
+ probabilities = torch.nn.functional.softmax(logits, dim=1)
420
+
421
+ # Obtenir les probabilités de classe
422
+ for j in range(probabilities.shape[1]):
423
+ prob = probabilities[0, j].item()
424
+ print(f"Région {i} - Probabilité classe {j}: {prob*100:.2f}%")
425
+
426
+ fake_prob = probabilities[0, 1].item() # Probabilité d'être faux (classe 1)
427
+ real_prob = probabilities[0, 0].item() # Probabilité d'être réel (classe 0)
428
+ is_fake = fake_prob > threshold
429
+
430
+ region_info = f"Région {i}: RÉEL={real_prob*100:.2f}%, FAUX={fake_prob*100:.2f}%"
431
+ detailed_info.append(region_info)
432
+
433
+ results.append({
434
+ "region_id": i,
435
+ "box": (x1, y1, x2, y2),
436
+ "deepfake_prob": float(fake_prob),
437
+ "real_prob": float(real_prob),
438
+ "is_fake": bool(is_fake),
439
+ "detailed_info": region_info
440
+ })
441
+
442
+ # Afficher un résumé global
443
+ if results:
444
+ print("===== RÉSUMÉ DE LA DÉTECTION DE DEEPFAKE =====")
445
+ for info in detailed_info:
446
+ print(info)
447
+
448
+ fake_regions = sum(1 for r in results if r.get("is_fake", False))
449
+ print(f"Total des régions analysées: {len(results)}")
450
+ print(f"Régions fausses détectées: {fake_regions}")
451
+ print(f"Régions réelles détectées: {len(results) - fake_regions}")
452
+ else:
453
+ print("Aucune région n'a été analysée avec succès pour les deepfakes")
454
+
455
+ return results
456
+ except Exception as e:
457
+ print(f"Erreur dans la détection de deepfake: {e}")
458
+ traceback.print_exc()
459
+ return []
460
+
461
+ def visualize_results(image, damage_outputs, deepfake_results, damage_threshold):
462
+ """Create visualization of results"""
463
+ try:
464
+ img_copy = image.copy()
465
+
466
+ # Draw damage detection
467
+ if damage_outputs is not None and DETECTRON2_AVAILABLE:
468
+ try:
469
+ v = Visualizer(img_copy[:, :, ::-1], scale=1.0, instance_mode=ColorMode.IMAGE_BW)
470
+ v = v.draw_instance_predictions(damage_outputs["instances"].to("cpu"))
471
+ result_img = v.get_image()[:, :, ::-1]
472
+ result_img = np.array(result_img, dtype=np.uint8)
473
+ except Exception as e:
474
+ print(f"Error visualizing damage: {e}")
475
+ result_img = img_copy
476
+ else:
477
+ result_img = img_copy
478
+
479
+ # Add deepfake results with enhanced information
480
+ for result in deepfake_results:
481
+ try:
482
+ if "box" in result:
483
+ x1, y1, x2, y2 = result["box"]
484
+ fake_prob = result["deepfake_prob"]
485
+ real_prob = result.get("real_prob", 1.0 - fake_prob) # Calculate real_prob if not present
486
+ is_fake = result["is_fake"]
487
+ region_id = result.get("region_id", 0)
488
+
489
+ # Enhanced status text with both probabilities
490
+ text = f"R{region_id}: {'FAKE' if is_fake else 'REAL'}"
491
+ prob_text = f"F:{fake_prob*100:.1f}% R:{real_prob*100:.1f}%"
492
+
493
+ # Red for fake, green for real
494
+ color = (0, 0, 255) if is_fake else (0, 255, 0)
495
+
496
+ # Ensure standard numpy array
497
+ if not isinstance(result_img, np.ndarray):
498
+ result_img = np.array(result_img, dtype=np.uint8)
499
+
500
+ # Draw rectangle and text
501
+ cv2.rectangle(result_img, (x1, y1), (x2, y2), color, 2)
502
+ cv2.putText(result_img, text, (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, color, 2)
503
+ cv2.putText(result_img, prob_text, (x1, y1+20), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2)
504
+
505
+ elif "region" in result and result["region"] == "full_image":
506
+ fake_prob = result["deepfake_prob"]
507
+ real_prob = result.get("real_prob", 1.0 - fake_prob)
508
+ is_fake = result["is_fake"]
509
+
510
+ text = f"Image: {'FAKE' if is_fake else 'REAL'}"
511
+ prob_text = f"FAKE: {fake_prob*100:.1f}%, REAL: {real_prob*100:.1f}%"
512
+ color = (0, 0, 255) if is_fake else (0, 255, 0)
513
+
514
+ if not isinstance(result_img, np.ndarray):
515
+ result_img = np.array(result_img, dtype=np.uint8)
516
+
517
+ cv2.putText(result_img, text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, color, 2)
518
+ cv2.putText(result_img, prob_text, (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.7, color, 2)
519
+ except Exception as e:
520
+ print(f"Error drawing result: {e}")
521
+
522
+ return result_img
523
+ except Exception as e:
524
+ print(f"Error in visualization: {e}")
525
+ traceback.print_exc()
526
+ return np.array(image, dtype=np.uint8)
527
+
528
  def process_image(input_image, damage_threshold, deepfake_threshold, skip_damage, device_str, usage_count, recipient_email):
529
  """Process an image through the detection pipeline and send results by email"""
530
  # Handle empty or None usage_count
 
739
 
740
  return analysis_text + f"\n\n📧 {email_message}", usage_count, email_message
741
 
 
742
  def create_gradio_interface():
743
  # Define a theme
744
  theme = gr.themes.Soft(
 
954
  outputs=[usage_display]
955
  )
956
 
957
+ return app
958
+
959
+ if __name__ == "__main__":
960
+ # Create and launch the Gradio app
961
+ app = create_gradio_interface()
962
+ app.launch(share=False)