astrosbd commited on
Commit
e358e41
·
verified ·
1 Parent(s): 8592df7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +666 -401
app.py CHANGED
@@ -13,8 +13,6 @@ 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
- import uuid
17
- import json
18
 
19
  # Email functionality imports
20
  import smtplib
@@ -27,8 +25,6 @@ import base64
27
  import io
28
  from datetime import datetime
29
 
30
- print("🚀 Démarrage de l'application Car Damage Detector...")
31
-
32
  # Add current directory to path
33
  if not os.getcwd() in sys.path:
34
  sys.path.append(os.getcwd())
@@ -36,12 +32,9 @@ if not os.getcwd() in sys.path:
36
  # Check if detectron2 is installed
37
  if importlib.util.find_spec("detectron2") is None:
38
  print("Installing PyTorch and Detectron2...")
39
- try:
40
- os.system("pip install torch torchvision --extra-index-url https://download.pytorch.org/whl/cpu")
41
- os.system("pip install git+https://github.com/facebookresearch/detectron2.git")
42
- print("Installation complete!")
43
- except Exception as e:
44
- print(f"Warning: Could not install detectron2: {e}")
45
 
46
  # Check for detectron2
47
  try:
@@ -50,9 +43,8 @@ try:
50
  from detectron2.utils.visualizer import Visualizer, ColorMode
51
  from detectron2 import model_zoo
52
  DETECTRON2_AVAILABLE = True
53
- print("✅ Detectron2 disponible")
54
  except ImportError:
55
- print("⚠️ Warning: Detectron2 is not installed. Damage detection will not be available.")
56
  DETECTRON2_AVAILABLE = False
57
 
58
  # Define model paths
@@ -68,67 +60,32 @@ SAMPLE_IMAGES = [
68
  # Maximum number of tries allowed
69
  MAX_TRIES = 5
70
 
71
- # Email configuration using environment variables (plus sécurisé)
72
  EMAIL_CONFIG = {
73
  'SMTP_SERVER': 'smtp.mail.ovh.net',
74
  'EMAIL': os.getenv('login_email', 'sales@askhedi.fr'),
75
- 'PASSWORD': os.getenv('password_email', '@Esperance92'), # À déplacer vers les secrets HF
76
- 'SMTP_PORT': int('465')
77
  }
78
 
79
- print(f"📧 Configuration email: {EMAIL_CONFIG['EMAIL']} sur {EMAIL_CONFIG['SMTP_SERVER']}")
80
-
81
- # Simple in-memory storage for user tries (in production, use a proper database)
82
- user_tries = {}
83
-
84
  # Télécharger le modèle deepfake depuis Hugging Face
85
- huggingface_model_path = None
86
  try:
87
  huggingface_model_path = hf_hub_download(
88
  repo_id="Askhedi/Car_damage_fraud_detector",
89
  filename="vit_deepfake_final.pth",
90
  token=os.getenv('key')
91
  )
92
- print(f"Modèle téléchargé depuis Hugging Face: {huggingface_model_path}")
93
  except Exception as e:
94
- print(f"⚠️ Erreur lors du téléchargement du modèle depuis Hugging Face: {e}")
95
  huggingface_model_path = None
96
 
97
- def get_or_create_user_id():
98
- """Generate a unique user ID for session tracking"""
99
- user_id = f"user_{uuid.uuid4().hex[:8]}_{int(time.time())}"
100
- print(f"🆔 Nouvel User ID créé: {user_id}")
101
- return user_id
102
-
103
- def get_user_tries(user_id):
104
- """Get the number of tries for a user"""
105
- if not user_id:
106
- return 0
107
- tries = user_tries.get(user_id, 0)
108
- print(f"📊 User {user_id}: {tries}/{MAX_TRIES} essais")
109
- return tries
110
-
111
- def increment_user_tries(user_id):
112
- """Increment the number of tries for a user"""
113
- if not user_id:
114
- user_id = get_or_create_user_id()
115
-
116
- current_tries = user_tries.get(user_id, 0)
117
- user_tries[user_id] = current_tries + 1
118
- new_count = user_tries[user_id]
119
- print(f"📈 User {user_id}: {new_count}/{MAX_TRIES} essais (incrémenté)")
120
- return new_count
121
-
122
- def send_results_by_email(recipient_email, analysis_text, result_image=None, original_filename="car_image"):
123
  """Send analysis results by email"""
124
- print(f"📧 Tentative d'envoi email vers: {recipient_email}")
125
-
126
  if not EMAIL_CONFIG['PASSWORD']:
127
- print("❌ Configuration email manquante")
128
  return False, "❌ Email configuration not available. Please contact support."
129
 
130
  if not recipient_email or "@" not in recipient_email:
131
- print("❌ Email invalide")
132
  return False, "❌ Please provide a valid email address"
133
 
134
  try:
@@ -148,8 +105,6 @@ def send_results_by_email(recipient_email, analysis_text, result_image=None, ori
148
  .header {{ background-color: #f0f0f0; padding: 15px; border-radius: 5px; }}
149
  .results {{ margin: 20px 0; white-space: pre-wrap; }}
150
  .footer {{ color: #666; font-size: 12px; margin-top: 30px; }}
151
- .verdict-real {{ color: #28a745; font-weight: bold; }}
152
- .verdict-fake {{ color: #dc3545; font-weight: bold; }}
153
  </style>
154
  </head>
155
  <body>
@@ -166,7 +121,6 @@ def send_results_by_email(recipient_email, analysis_text, result_image=None, ori
166
  <div class="footer">
167
  <p><em>This analysis was generated by the Car Damage Fraud Detector AI system.</em></p>
168
  <p>Powered by Askhedi - Advanced AI Detection Services</p>
169
- <p><strong>Important:</strong> This AI analysis should be verified by professional inspection.</p>
170
  </div>
171
  </body>
172
  </html>
@@ -176,79 +130,88 @@ def send_results_by_email(recipient_email, analysis_text, result_image=None, ori
176
 
177
  # Attach result image if available
178
  if result_image is not None:
179
- try:
180
- # Convert numpy array to image bytes
181
- if isinstance(result_image, np.ndarray):
182
- # Convert from RGB to PIL Image
183
- pil_image = Image.fromarray(result_image.astype('uint8'))
184
- img_buffer = io.BytesIO()
185
- pil_image.save(img_buffer, format='PNG')
186
- img_data = img_buffer.getvalue()
187
-
188
- # Attach image
189
- img_part = MIMEBase('application', 'octet-stream')
190
- img_part.set_payload(img_data)
191
- encoders.encode_base64(img_part)
192
- img_part.add_header(
193
- 'Content-Disposition',
194
- f'attachment; filename="analysis_result_{original_filename}.png"'
195
- )
196
- msg.attach(img_part)
197
- print("✅ Image attachée à l'email")
198
- except Exception as img_error:
199
- print(f"⚠️ Erreur attachement image: {img_error}")
200
 
201
  # Send email
202
- print("📤 Connexion au serveur SMTP...")
203
  server = smtplib.SMTP_SSL(EMAIL_CONFIG['SMTP_SERVER'], EMAIL_CONFIG['SMTP_PORT'])
204
  server.login(EMAIL_CONFIG['EMAIL'], EMAIL_CONFIG['PASSWORD'])
205
  server.send_message(msg)
206
  server.quit()
207
 
208
- print(f"✅ Email envoyé avec succès à {recipient_email}")
209
  return True, f"✅ Results sent successfully to {recipient_email}"
210
 
211
  except Exception as e:
212
- error_msg = f"❌ Error sending email: {str(e)}"
213
- print(error_msg)
214
- print(f"📋 Détails erreur: {traceback.format_exc()}")
215
- return False, error_msg
216
 
217
  def setup_device(device_str):
218
  """Set up the computation device"""
219
- print(f"⚙️ Configuration device: {device_str}")
220
-
221
  if device_str == 'auto':
222
  if torch.cuda.is_available():
223
- device = torch.device('cuda:0')
224
- print("🚀 CUDA détecté et utilisé")
225
  elif hasattr(torch, 'backends') and hasattr(torch.backends, 'mps') and torch.backends.mps.is_available():
226
- device = torch.device('mps')
227
- print("🍎 MPS (Mac) détecté et utilisé")
228
  else:
229
- device = torch.device('cpu')
230
- print("💻 CPU utilisé")
231
- return device
232
  elif device_str == 'cuda' and torch.cuda.is_available():
233
- print("🚀 CUDA forcé")
234
  return torch.device('cuda:0')
235
  elif device_str == 'mps' and hasattr(torch, 'backends') and hasattr(torch.backends, 'mps') and torch.backends.mps.is_available():
236
- print("🍎 MPS forcé")
237
  return torch.device('mps')
238
  else:
239
- print(f"⚠️ Warning: Device {device_str} not available, using CPU instead.")
240
  return torch.device('cpu')
241
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
  def load_vit_deepfake_model(model_path, device):
243
  """Load the Vision Transformer (ViT) model for deepfake detection"""
244
- print(f"🤖 Chargement modèle ViT depuis: {model_path}")
245
-
246
  if model_path is None:
247
- print("⚠️ No deepfake model specified. Skipping deepfake detection.")
248
- return None
249
-
250
- if not os.path.exists(model_path):
251
- print(f"❌ Modèle non trouvé: {model_path}")
252
  return None
253
 
254
  try:
@@ -260,7 +223,7 @@ def load_vit_deepfake_model(model_path, device):
260
  model.heads.head = nn.Linear(in_features, 2)
261
 
262
  # Load weights
263
- print(f"📥 Loading ViT deepfake model from: {model_path}")
264
  checkpoint = torch.load(model_path, map_location='cpu')
265
 
266
  if isinstance(checkpoint, dict) and 'model_state_dict' in checkpoint:
@@ -274,109 +237,339 @@ def load_vit_deepfake_model(model_path, device):
274
  model = model.to(device)
275
  model.eval()
276
 
277
- print("✅ Modèle ViT chargé avec succès")
278
  return model
279
  except Exception as e:
280
- print(f"Error loading ViT deepfake model: {e}")
281
- print(f"📋 Détails: {traceback.format_exc()}")
 
282
  return None
283
 
284
- def create_simple_analysis(image, recipient_email, user_id):
285
- """Version simplifiée pour tester le pipeline de base"""
286
- print("🔄 Démarrage analyse simplifiée...")
287
-
288
  try:
289
- # Analyse basique de l'image
290
- if image is None:
291
- return "❌ Aucune image fournie"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
292
 
293
- height, width = image.shape[:2]
 
 
 
294
 
295
- analysis_text = f"""
296
- 🚗 ANALYSE SIMPLIFIÉE DE DOMMAGES AUTOMOBILE
297
- ==========================================
298
-
299
- 📧 Email: {recipient_email}
300
- 👤 User ID: {user_id}
301
- 🖼️ Image: {width}x{height} pixels
302
- ⏰ Analyse: {datetime.now().strftime('%d/%m/%Y à %H:%M:%S')}
303
-
304
- 🔍 DÉTECTION DE DOMMAGES:
305
- • Analyse automatique de l'image
306
- • Recherche de zones endommagées
307
- • Vérification de l'authenticité
308
-
309
- 📊 RÉSULTATS:
310
- • Status: ANALYSE BASIQUE RÉUSSIE ✅
311
- • Recommandation: Vérification manuelle recommandée
312
- • Confiance: 75% (mode test)
313
 
314
- ⚠️ NOTE: Ceci est une version de test simplifiée.
315
- L'analyse complète avec IA sera disponible prochainement.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
316
 
317
- 💡 PROCHAINES ÉTAPES:
318
- 1. Vérification par expert recommandée
319
- 2. Documentation des dommages
320
- 3. Contact avec votre assurance si nécessaire
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
321
 
322
- ---
323
- Généré par Askhedi Car Damage Detector v1.0
324
- """
 
325
 
326
- # Créer une image de test annotée
327
- test_image = image.copy()
328
- cv2.putText(test_image, "ANALYSE TEST", (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
329
- cv2.putText(test_image, f"{width}x{height}", (50, 100), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 0, 0), 2)
 
 
 
 
 
 
 
 
330
 
331
- # Envoyer l'email
332
- success, email_msg = send_results_by_email(recipient_email, analysis_text, test_image, "test_analysis")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
333
 
334
- if success:
335
- return f"✅ ANALYSE RÉUSSIE!\n\n{analysis_text}\n\n{email_msg}"
336
- else:
337
- return f"⚠️ ANALYSE TERMINÉE mais erreur email:\n\n{analysis_text}\n\n{email_msg}"
338
-
339
  except Exception as e:
340
- error_msg = f" Erreur lors de l'analyse: {str(e)}"
341
- print(error_msg)
342
- print(f"📋 Stack trace: {traceback.format_exc()}")
343
- return error_msg
344
 
345
- def process_image_main(input_image, recipient_email, damage_threshold, deepfake_threshold, skip_damage, device_str, user_id):
346
- """Fonction principale de traitement - version robuste"""
 
 
 
347
 
348
- print("=" * 60)
349
- print("🚀 DÉMARRAGE DU TRAITEMENT PRINCIPAL")
350
- print(f"📧 Email: {recipient_email}")
351
- print(f"🖼️ Image: {type(input_image)} - Present: {input_image is not None}")
352
- print(f"👤 User ID: {user_id}")
353
- print(f"⚙️ Device: {device_str}")
354
- print("=" * 60)
 
355
 
356
- # Validation de base
357
- if not recipient_email or "@" not in recipient_email:
358
- print("❌ Validation email échouée")
359
- return "❌ Veuillez fournir une adresse email valide", user_id, get_user_tries(user_id)
360
 
361
- if input_image is None:
362
- print("❌ Aucune image fournie")
363
- return " Veuillez télécharger une image à analyser", user_id, get_user_tries(user_id)
364
 
365
- # Vérifier les limites d'usage
366
- if not user_id:
367
- user_id = get_or_create_user_id()
368
 
369
- current_tries = get_user_tries(user_id)
370
- if current_tries >= MAX_TRIES:
371
- print(f"⚠️ Limite d'usage atteinte pour {user_id}")
372
- return f"⚠️ Vous avez atteint la limite de {MAX_TRIES} analyses. Veuillez réessayer plus tard.", user_id, current_tries
373
 
374
- # Incrémenter le compteur
375
- new_tries = increment_user_tries(user_id)
 
 
 
 
 
 
376
 
 
377
  try:
378
- # Traitement de l'image
379
- print("🔄 Conversion de l'image...")
 
380
  if isinstance(input_image, dict) and "path" in input_image:
381
  img = cv2.imread(input_image["path"])
382
  elif isinstance(input_image, str):
@@ -386,305 +579,377 @@ def process_image_main(input_image, recipient_email, damage_threshold, deepfake_
386
  if len(img.shape) == 3 and img.shape[2] == 3:
387
  img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
388
  else:
389
- print(f" Format d'image non supporté: {type(input_image)}")
390
- return f"❌ Format d'image non supporté: {type(input_image)}", user_id, new_tries
391
 
392
  if img is None:
393
- print(" Impossible de lire l'image")
394
- return "❌ Impossible de lire l'image", user_id, new_tries
395
-
396
- print(f"✅ Image chargée: {img.shape}")
397
-
398
- # Pour le moment, utiliser l'analyse simplifiée
399
- print("🔄 Exécution de l'analyse simplifiée...")
400
- result = create_simple_analysis(img, recipient_email, user_id)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
401
 
402
- print("✅ Analyse terminée avec succès")
403
- return result, user_id, new_tries
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
404
 
405
- except Exception as e:
406
- error_msg = f"❌ Erreur durant le traitement: {str(e)}"
407
- print(error_msg)
408
- print(f"📋 Stack trace complet:")
409
- print(traceback.format_exc())
410
- return error_msg, user_id, new_tries
411
-
412
- def create_gradio_interface():
413
- """Créer l'interface Gradio avec gestion d'erreur robuste"""
414
 
415
- print("🎨 Création de l'interface Gradio...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
416
 
 
 
 
 
417
  # Define a theme
418
  theme = gr.themes.Soft(
419
  primary_hue="blue",
420
  secondary_hue="orange",
421
  )
422
 
423
- # JavaScript simplifié pour le debugging
424
- simple_js = """
425
- console.log("🚀 JavaScript Gradio chargé");
426
-
427
- // Fonction pour générer un ID utilisateur simple
428
- function getSimpleUserId() {
429
- const timestamp = Date.now();
430
- const random = Math.random().toString(36).substr(2, 5);
431
- const userId = `user_${random}_${timestamp}`;
432
- console.log("🆔 User ID généré:", userId);
433
- return userId;
434
- }
435
-
436
- // Initialiser l'ID utilisateur
437
- window.currentUserId = getSimpleUserId();
438
-
439
- // Fonctions de test
440
- window.testFunction = function() {
441
- console.log("✅ Test fonction JavaScript OK");
442
- return true;
443
- };
444
-
445
- console.log("✅ JavaScript initialisé avec User ID:", window.currentUserId);
446
- """
447
-
448
- with gr.Blocks(title="Car Damage & Deepfake Detector", theme=theme, js=simple_js) as app:
449
-
450
- # Header
451
  gr.Markdown("""
452
  # 🚗 Car Damage Fraud Detector
453
 
454
- **Version de test simplifiée** - Upload a car image and provide your email to:
455
  1. **Detect damaged areas** using AI
456
- 2. **Verify if damage is real** or artificially generated
457
- 3. **Receive detailed results by email** 📧
458
 
459
- ⚠️ **Note: Maximum 5 analyses per user.**
460
  """)
461
 
462
- # État pour l'ID utilisateur - initialisé directement
463
- user_id_state = gr.State(value=get_or_create_user_id())
464
-
465
- # Interface principale
466
  with gr.Tab("🔍 Analyze Image"):
467
  with gr.Row():
468
  with gr.Column(scale=1):
469
- # Email input (required)
470
- recipient_email = gr.Textbox(
471
- label="📧 Your Email Address (Required)",
472
- placeholder="votre.email@exemple.com",
473
- info="Results will be sent to this email address"
474
- )
475
-
476
- input_image = gr.Image(
477
- type="numpy",
478
- label="Upload Car Image",
479
- height=300
480
- )
481
 
482
  with gr.Row():
483
- process_btn = gr.Button(
484
- "🚀 Analyze & Send Results",
485
- variant="primary",
486
- size="lg"
487
- )
488
  clear_btn = gr.Button("🗑️ Clear", variant="secondary")
489
 
490
- # Usage display
491
  usage_display = gr.Markdown("**Usage: 0/5**")
492
 
493
- # Advanced settings (simplifié)
 
 
 
 
 
 
 
 
 
494
  with gr.Accordion("⚙️ Advanced Settings", open=False):
495
  skip_damage = gr.Checkbox(
496
  label="Skip Damage Detection",
497
  value=False,
498
- info="Analyze entire image for deepfakes"
499
  )
500
  damage_threshold = gr.Slider(
501
  minimum=0.1, maximum=1.0, value=0.7, step=0.05,
502
- label="Damage Detection Threshold"
 
503
  )
504
  deepfake_threshold = gr.Slider(
505
  minimum=0.1, maximum=1.0, value=0.5, step=0.05,
506
- label="Deepfake Detection Threshold"
 
507
  )
508
  device = gr.Dropdown(
509
  choices=["auto", "cuda", "cpu", "mps"],
510
  value="auto",
511
- label="Computation Device"
 
512
  )
513
 
514
  with gr.Column(scale=1):
515
- gr.Markdown("### 📋 Analysis Status")
516
-
517
- output_text = gr.Markdown("""
518
- **Ready to analyze!**
519
 
520
- 1. Enter your email address
521
- 2. Upload a car image
522
- 3. Click "Analyze & Send Results"
523
- 4. Check your email for detailed results
524
-
525
- 🔧 **Current Status**: Test mode - simplified analysis
526
- """)
527
-
528
- # Status détaillé
529
- with gr.Accordion("🔍 Processing Details", open=False):
530
- processing_details = gr.Markdown("Waiting for analysis...")
531
 
532
- # Tab d'aide (raccourci)
533
  with gr.Tab("❓ Help"):
534
  gr.Markdown("""
535
  ## 📋 How to Use This Tool
536
 
537
- ### 🚀 Quick Start (Test Mode)
538
- 1. **Enter your email** - Required to receive results
539
- 2. **Upload** a car image
540
- 3. **Click "Analyze & Send Results"**
541
- 4. **Check your email** for analysis report
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
542
 
543
- ### 🔧 Current Status
544
- - **Test mode active**: Simplified analysis pipeline
545
- - **Email delivery**: Fully functional
546
- - **AI models**: Loading in progress
547
- - **Usage limit**: 5 analyses per user
548
 
549
- ### 📧 What You'll Receive
550
- - Basic image analysis report
551
- - Test annotations on your image
552
- - Professional email format
553
- - Technical details about the process
554
 
555
- ### 🚨 Note
556
- This is a test version. Full AI analysis with damage detection and deepfake verification will be available soon.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
557
 
558
  ---
559
- **🚀 Ready to test? Enter your email and upload an image!**
 
 
 
560
  """)
561
-
562
- # Exemples d'images (si disponibles)
563
- existing_samples = [img for img in SAMPLE_IMAGES if os.path.exists(img)]
564
- if existing_samples:
565
  gr.Markdown("## 📸 Example Images")
566
  with gr.Row():
 
567
  gr.Examples(
568
- examples=existing_samples,
569
  inputs=input_image,
570
- label="Click on an example image to load it"
 
 
571
  )
572
 
573
- # Fonction de traitement simplifiée pour l'interface
574
- def process_for_interface(input_image, recipient_email, damage_threshold, deepfake_threshold, skip_damage, device, user_id):
575
- """Wrapper pour l'interface Gradio"""
576
- print("\n" + "="*50)
577
- print("🎯 TRAITEMENT INTERFACE GRADIO")
578
- print("="*50)
579
-
580
- try:
581
- # Validation immédiate
582
- if not recipient_email:
583
- return "❌ Veuillez entrer une adresse email", user_id, get_user_tries(user_id), "❌ Email manquant"
584
-
585
- if "@" not in recipient_email:
586
- return "❌ Adresse email invalide (manque @)", user_id, get_user_tries(user_id), "❌ Email invalide"
587
-
588
- if input_image is None:
589
- return "❌ Veuillez télécharger une image", user_id, get_user_tries(user_id), "❌ Image manquante"
590
-
591
- # Appeler la fonction principale
592
- result, updated_user_id, usage_count = process_image_main(
593
- input_image, recipient_email, damage_threshold,
594
- deepfake_threshold, skip_damage, device, user_id
595
- )
596
-
597
- # Mettre à jour les détails de traitement
598
- details = f"✅ Analyse terminée pour {recipient_email}\n📊 Usage: {usage_count}/{MAX_TRIES}\n⏰ {datetime.now().strftime('%H:%M:%S')}"
599
-
600
- return result, updated_user_id, usage_count, details
601
-
602
- except Exception as e:
603
- error_msg = f"💥 Erreur interface: {str(e)}"
604
- print(error_msg)
605
- print(traceback.format_exc())
606
- return error_msg, user_id, get_user_tries(user_id), f"💥 Exception: {str(e)}"
607
-
608
- # Connecter le bouton principal
609
  process_btn.click(
610
- fn=process_for_interface,
611
  inputs=[
612
  input_image,
613
- recipient_email,
614
  damage_threshold,
615
  deepfake_threshold,
616
  skip_damage,
617
  device,
618
- user_id_state
619
  ],
620
- outputs=[output_text, user_id_state, usage_display, processing_details]
621
  )
622
 
623
- # Fonction de nettoyage
624
- def clear_interface():
625
- """Nettoyer l'interface"""
626
- print("🧹 Nettoyage de l'interface")
627
- return [
628
- None, # input_image
629
- "", # recipient_email
630
- """**Ready to analyze!**
631
-
632
- 1. Enter your email address
633
- 2. Upload a car image
634
- 3. Click "Analyze & Send Results"
635
- 4. Check your email for detailed results
636
-
637
- 🔧 **Current Status**: Test mode - simplified analysis""", # output_text
638
- "Interface nettoyée - Ready for new analysis" # processing_details
639
- ]
640
 
 
641
  clear_btn.click(
642
- fn=clear_interface,
643
  inputs=[],
644
- outputs=[input_image, recipient_email, output_text, processing_details]
645
  )
646
 
647
- # Mettre à jour l'affichage d'usage
648
- def update_usage_display(user_id):
649
- """Mettre à jour l'affichage d'usage"""
650
- if user_id:
651
- tries = get_user_tries(user_id)
652
- return f"**Usage: {tries}/{MAX_TRIES}**"
653
- return "**Usage: 0/5**"
654
-
655
- # Mettre à jour l'usage quand l'user_id change
656
- user_id_state.change(
657
- fn=update_usage_display,
658
- inputs=[user_id_state],
659
  outputs=[usage_display]
660
  )
661
 
662
- print("✅ Interface Gradio créée avec succès")
663
  return app
664
 
665
  if __name__ == "__main__":
666
- print("\n" + "="*60)
667
- print("🚀 LANCEMENT DE L'APPLICATION")
668
- print("="*60)
669
-
670
- # Vérifier la configuration
671
- print("🔍 Vérification de la configuration:")
672
- print(f"📧 Email configuré: {EMAIL_CONFIG['EMAIL']}")
673
- print(f"🤖 Detectron2 disponible: {DETECTRON2_AVAILABLE}")
674
- print(f"📱 Modèle HF téléchargé: {huggingface_model_path is not None}")
675
- print(f"💾 Modèle local damage: {os.path.exists(DEFAULT_DAMAGE_MODEL_PATH)}")
676
- print(f"🧠 Modèle local deepfake: {os.path.exists(DEFAULT_DEEPFAKE_MODEL_PATH)}")
677
-
678
- try:
679
- # Create and launch the Gradio app
680
- app = create_gradio_interface()
681
- print("🚀 Lancement de l'interface Gradio...")
682
- app.launch(
683
- share=False,
684
- server_name="0.0.0.0",
685
- server_port=7860,
686
- show_error=True
687
- )
688
- except Exception as e:
689
- print(f"💥 ERREUR CRITIQUE: {e}")
690
- print(traceback.format_exc())
 
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
 
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())
 
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:
 
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
 
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': '@Esperance92',
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:
 
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>
 
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>
 
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:
 
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:
 
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):
529
+ """Process an image through the detection pipeline"""
530
+ # Handle empty or None usage_count
531
+ if usage_count is None:
532
+ usage_count = 0
533
 
534
+ # Ensure usage_count is an integer
535
+ try:
536
+ usage_count = int(usage_count)
537
+ except (TypeError, ValueError):
538
+ usage_count = 0
539
+
540
+ # Increment usage count and check if limit reached
541
+ usage_count = usage_count + 1
542
 
543
+ progress_info = []
544
+ progress_info.append(f"Usage: {usage_count}/{MAX_TRIES}")
 
 
545
 
546
+ # Check if usage limit reached
547
+ if usage_count > MAX_TRIES:
548
+ return None, f"⚠️ You have reached the maximum number of tries allowed ({MAX_TRIES}).", usage_count
549
 
550
+ # Use default model paths
551
+ damage_model_path = DEFAULT_DAMAGE_MODEL_PATH
552
+ deepfake_model_path = DEFAULT_DEEPFAKE_MODEL_PATH
553
 
554
+ # Check model files and use downloaded model if needed
555
+ if not skip_damage and damage_model_path:
556
+ if not os.path.exists(damage_model_path):
557
+ progress_info.append(f"INFO: Damage model not found, will analyze whole image")
558
 
559
+ # Vérifier si le modèle deepfake est disponible localement ou téléchargé
560
+ actual_deepfake_model_path = deepfake_model_path
561
+ if deepfake_model_path and not os.path.exists(deepfake_model_path):
562
+ if huggingface_model_path and os.path.exists(huggingface_model_path):
563
+ progress_info.append(f"INFO: Using Hugging Face model")
564
+ actual_deepfake_model_path = huggingface_model_path
565
+ else:
566
+ progress_info.append(f"WARNING: Deepfake model not found")
567
 
568
+ # Convert image to proper format
569
  try:
570
+ if input_image is None:
571
+ return None, "Please upload an image to analyze.", usage_count
572
+
573
  if isinstance(input_image, dict) and "path" in input_image:
574
  img = cv2.imread(input_image["path"])
575
  elif isinstance(input_image, str):
 
579
  if len(img.shape) == 3 and img.shape[2] == 3:
580
  img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
581
  else:
582
+ return None, "Error: Unsupported image format", usage_count
 
583
 
584
  if img is None:
585
+ return None, "Error: Could not read the image", usage_count
586
+ except Exception as e:
587
+ return None, f"Error loading image: {str(e)}", usage_count
588
+
589
+ # Setup device
590
+ device = setup_device(device_str)
591
+ progress_info.append(f"Using device: {device}")
592
+
593
+ # Initialize models
594
+ damage_detector = None
595
+ deepfake_model = None
596
+
597
+ # Setup damage detector
598
+ if not skip_damage and damage_model_path:
599
+ progress_info.append("Setting up damage detector...")
600
+ damage_detector, _ = setup_damage_detector(damage_model_path, float(damage_threshold))
601
+ if damage_detector:
602
+ progress_info.append("✅ Damage detector initialized")
603
+ else:
604
+ progress_info.append("⚠️ Damage detector not available, analyzing whole image")
605
+
606
+ # Setup deepfake detector with the appropriate path
607
+ if actual_deepfake_model_path:
608
+ progress_info.append(f"Setting up deepfake detector...")
609
+ deepfake_model = load_vit_deepfake_model(actual_deepfake_model_path, device)
610
+ if deepfake_model:
611
+ progress_info.append("✅ Deepfake detector initialized")
612
+ else:
613
+ progress_info.append("❌ Failed to initialize deepfake detector")
614
+
615
+ # Step 1: Detect damage
616
+ progress_info.append("Detecting damaged regions...")
617
+ start_time = time.time()
618
+ img, damage_outputs, damage_regions = detect_damage(img, damage_detector)
619
+ damage_time = time.time() - start_time
620
+
621
+ if damage_regions:
622
+ progress_info.append(f"Found {len(damage_regions)} damage regions in {damage_time:.2f} seconds")
623
+ # Add details about each damage region
624
+ for i, region in enumerate(damage_regions):
625
+ x1, y1, x2, y2 = region["box"]
626
+ score = region["score"]
627
+ progress_info.append(f" - Region {i}: Score: {score:.2f}, Size: {x2-x1}x{y2-y1}px")
628
+ else:
629
+ progress_info.append("No damage regions detected")
630
+
631
+ # Step 2: Check for deepfakes
632
+ deepfake_results = []
633
+ if deepfake_model is not None:
634
+ progress_info.append("Analyzing regions for deepfakes...")
635
+ start_time = time.time()
636
+ deepfake_results = check_deepfake_vit(
637
+ img, damage_regions, deepfake_model, device, float(deepfake_threshold)
638
+ )
639
+ deepfake_time = time.time() - start_time
640
 
641
+ if deepfake_results:
642
+ progress_info.append(f"Deepfake analysis completed in {deepfake_time:.2f} seconds")
643
+
644
+ # Generate detailed report
645
+ progress_info.append("\n🔍 DETAILED DEEPFAKE ANALYSIS:")
646
+ for result in deepfake_results:
647
+ if "region_id" in result:
648
+ region_id = result["region_id"]
649
+ fake_prob = result["deepfake_prob"]
650
+ real_prob = result.get("real_prob", 1.0 - fake_prob)
651
+ is_fake = result["is_fake"]
652
+ progress_info.append(f"Region {region_id}: {'🚨 FAKE' if is_fake else '✅ REAL'}")
653
+ progress_info.append(f" - Fake probability: {fake_prob*100:.2f}%")
654
+ progress_info.append(f" - Real probability: {real_prob*100:.2f}%")
655
+
656
+ elif "region" in result and result["region"] == "full_image":
657
+ fake_prob = result["deepfake_prob"]
658
+ real_prob = result.get("real_prob", 1.0 - fake_prob)
659
+ is_fake = result["is_fake"]
660
+ progress_info.append(f"Whole image: {'🚨 FAKE' if is_fake else '✅ REAL'}")
661
+ progress_info.append(f" - Fake probability: {fake_prob*100:.2f}%")
662
+ progress_info.append(f" - Real probability: {real_prob*100:.2f}%")
663
+ else:
664
+ progress_info.append("No deepfake detection results.")
665
+
666
+ # Final verdict with more details
667
+ fake_regions = [r for r in deepfake_results if r.get("is_fake", False)]
668
+ if fake_regions:
669
+ fake_count = len(fake_regions)
670
+ total_count = len(deepfake_results)
671
+ progress_info.append(f"\n🚨 VERDICT: This image contains FAKE damage ({fake_count}/{total_count} regions) 🚨")
672
+
673
+ # Calculate average fake probability
674
+ avg_fake_prob = sum(r["deepfake_prob"] for r in fake_regions) / len(fake_regions)
675
+ progress_info.append(f"Average fake probability: {avg_fake_prob*100:.2f}%")
676
+
677
+ # Identify the most suspicious region
678
+ most_fake_region = max(deepfake_results, key=lambda r: r.get("deepfake_prob", 0))
679
+ if "region_id" in most_fake_region:
680
+ most_fake_id = most_fake_region["region_id"]
681
+ most_fake_prob = most_fake_region["deepfake_prob"]
682
+ progress_info.append(f"Most suspicious region: Region {most_fake_id} ({most_fake_prob*100:.2f}% fake)")
683
+ elif "region" in most_fake_region and most_fake_region["region"] == "full_image":
684
+ most_fake_prob = most_fake_region["deepfake_prob"]
685
+ progress_info.append(f"Whole image is suspicious: {most_fake_prob*100:.2f}% fake")
686
+ else:
687
+ if deepfake_results:
688
+ # Calculate average real probability
689
+ avg_real_prob = sum(r.get("real_prob", 1.0 - r["deepfake_prob"]) for r in deepfake_results) / len(deepfake_results)
690
+ progress_info.append(f"\n✅ VERDICT: All damage appears REAL (Average confidence: {avg_real_prob*100:.2f}%)")
691
+ else:
692
+ progress_info.append("\n⚠️ VERDICT: Could not determine if damage is real or fake")
693
 
694
+ # Step 3: Visualize results
695
+ result_img = visualize_results(img, damage_outputs, deepfake_results, float(damage_threshold))
696
+
697
+ # Convert back to RGB for Gradio
698
+ if len(result_img.shape) == 3 and result_img.shape[2] == 3:
699
+ result_img = cv2.cvtColor(result_img, cv2.COLOR_BGR2RGB)
 
 
 
700
 
701
+ # Add usage information at the bottom of the image
702
+ if usage_count >= MAX_TRIES:
703
+ # Add a "Usage limit reached" message to the bottom of the image in red
704
+ cv2.putText(result_img, f"USAGE LIMIT REACHED: {usage_count}/{MAX_TRIES}",
705
+ (10, result_img.shape[0] - 20), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 0, 0), 2)
706
+ else:
707
+ # Add a usage counter to the bottom of the image
708
+ cv2.putText(result_img, f"Usage: {usage_count}/{MAX_TRIES}",
709
+ (10, result_img.shape[0] - 20), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
710
+
711
+ # Add a timestamp to the image
712
+ timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
713
+ cv2.putText(result_img, f"Analysis time: {timestamp}",
714
+ (10, result_img.shape[0] - 50), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
715
+
716
+ # Add usage info to the progress text
717
+ if usage_count >= MAX_TRIES:
718
+ progress_info.append("\n⚠️ You have reached the maximum number of tries allowed ⚠️")
719
+ else:
720
+ progress_info.append(f"\nRemaining tries: {MAX_TRIES - usage_count}")
721
+
722
+ return result_img, "\n".join(progress_info), usage_count
723
+
724
+ def send_email_wrapper(recipient_email, result_image, analysis_text):
725
+ """Wrapper function for sending email results"""
726
+ if not recipient_email:
727
+ return "⚠️ Please enter an email address"
728
 
729
+ success, message = send_results_by_email(recipient_email, analysis_text, result_image)
730
+ return message
731
+
732
+ def create_gradio_interface():
733
  # Define a theme
734
  theme = gr.themes.Soft(
735
  primary_hue="blue",
736
  secondary_hue="orange",
737
  )
738
 
739
+ with gr.Blocks(title="Car Damage & Deepfake Detector", theme=theme) as app:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
740
  gr.Markdown("""
741
  # 🚗 Car Damage Fraud Detector
742
 
743
+ Upload a car image to:
744
  1. **Detect damaged areas** using AI
745
+ 2. **Verify if damage is real** or artificially generated (deepfake)
746
+ 3. **Send results by email** 📧
747
 
748
+ ⚠️ **Note: You have a maximum of 5 tries to analyze images.**
749
  """)
750
 
751
+ usage_counter = gr.State(0)
752
+
753
+ # Main Interface Tab
 
754
  with gr.Tab("🔍 Analyze Image"):
755
  with gr.Row():
756
  with gr.Column(scale=1):
757
+ input_image = gr.Image(type="numpy", label="Upload Car Image")
 
 
 
 
 
 
 
 
 
 
 
758
 
759
  with gr.Row():
760
+ process_btn = gr.Button("🚀 Analyze Image", variant="primary", size="lg")
 
 
 
 
761
  clear_btn = gr.Button("🗑️ Clear", variant="secondary")
762
 
763
+ # Usage limit display
764
  usage_display = gr.Markdown("**Usage: 0/5**")
765
 
766
+ # Email section
767
+ with gr.Accordion("📧 Send Results by Email", open=False):
768
+ recipient_email = gr.Textbox(
769
+ label="Email Address",
770
+ placeholder="Enter email to receive detailed results...",
771
+ info="Professional report with annotated images will be sent"
772
+ )
773
+ send_email_btn = gr.Button("📤 Send Results by Email", variant="secondary", size="sm")
774
+ email_status = gr.Markdown("")
775
+
776
  with gr.Accordion("⚙️ Advanced Settings", open=False):
777
  skip_damage = gr.Checkbox(
778
  label="Skip Damage Detection",
779
  value=False,
780
+ info="Analyze entire image for deepfakes without damage detection"
781
  )
782
  damage_threshold = gr.Slider(
783
  minimum=0.1, maximum=1.0, value=0.7, step=0.05,
784
+ label="Damage Detection Threshold",
785
+ info="Higher = more selective damage detection"
786
  )
787
  deepfake_threshold = gr.Slider(
788
  minimum=0.1, maximum=1.0, value=0.5, step=0.05,
789
+ label="Deepfake Detection Threshold",
790
+ info="Higher = more selective fake detection"
791
  )
792
  device = gr.Dropdown(
793
  choices=["auto", "cuda", "cpu", "mps"],
794
  value="auto",
795
+ label="Computation Device",
796
+ info="Auto selects best available device"
797
  )
798
 
799
  with gr.Column(scale=1):
800
+ output_image = gr.Image(type="numpy", label="🎯 Analysis Result")
 
 
 
801
 
802
+ # Analysis info with nice formatting
803
+ with gr.Accordion("📋 Analysis Details", open=True):
804
+ output_text = gr.Markdown("Upload an image and click 'Analyze Image' to start...")
 
 
 
 
 
 
 
 
805
 
 
806
  with gr.Tab("❓ Help"):
807
  gr.Markdown("""
808
  ## 📋 How to Use This Tool
809
 
810
+ ### 🚀 Quick Start
811
+ 1. **Upload** a car image showing damage
812
+ 2. **Click "Analyze Image"** and wait for processing
813
+ 3. **View results** - damaged areas highlighted in green (real) or red (fake)
814
+ 4. **Optional**: Send detailed results to your email
815
+
816
+ ### 📧 Email Feature
817
+ - Enter your email address in the "Send Results by Email" section
818
+ - Click "Send Results by Email" after analysis is complete
819
+ - Receive a professional HTML report with annotated images
820
+ - Results include confidence scores and recommendations
821
+
822
+ ### 🎯 Understanding Results
823
+
824
+ #### Visual Indicators
825
+ - **🟢 Green boxes**: Real damage detected
826
+ - **🔴 Red boxes**: Potential deepfake damage detected
827
+ - **Percentages**: Confidence scores (higher = more confident)
828
+
829
+ #### Verdict Types
830
+ - **✅ REAL**: Damage appears authentic
831
+ - **🚨 FAKE**: Damage shows signs of AI generation
832
+ - **⚠️ UNCERTAIN**: Analysis inconclusive
833
+
834
+ ### ⚙️ Advanced Settings
835
+
836
+ - **Damage Threshold**: Controls sensitivity of damage detection
837
+ - Higher values = only high-confidence damage regions shown
838
+ - Lower values = more damage regions detected
839
+
840
+ - **Deepfake Threshold**: Controls sensitivity of fake detection
841
+ - Higher values = more selective in flagging fakes
842
+ - Lower values = more aggressive fake detection
843
+
844
+ - **Skip Damage Detection**: Analyze entire image for deepfakes
845
+ - Useful when damage areas are already known
846
+ - Speeds up processing for whole-image analysis
847
+
848
+ ### 🏁 Use Cases
849
 
850
+ #### 🏢 Insurance Companies
851
+ - Verify claim authenticity before processing
852
+ - Detect fraudulent damage submissions
853
+ - Streamline claim review process
 
854
 
855
+ #### 🚗 Car Dealerships
856
+ - Document vehicle condition accurately
857
+ - Verify repair estimates and warranties
858
+ - Assess trade-in vehicle damage
 
859
 
860
+ #### 👤 Individual Users
861
+ - Verify repair shop estimates
862
+ - Document accident damage for insurance
863
+ - Check if damage photos are authentic
864
+
865
+ ### ⚠️ Important Notes
866
+
867
+ - **Not a replacement** for professional inspection
868
+ - **AI predictions** should be verified by experts
869
+ - **Image quality** affects detection accuracy
870
+ - **Lighting conditions** can impact results
871
+ - **5 tries maximum** per session to prevent abuse
872
+
873
+ ### 🔒 Privacy & Security
874
+
875
+ - **No data storage**: Images processed in real-time only
876
+ - **Secure email**: Encrypted SMTP connections
877
+ - **No tracking**: Privacy-first approach
878
+ - **Email only**: For sending results, not stored
879
+
880
+ ### 💡 Tips for Best Results
881
+
882
+ 1. **Good lighting**: Clear, well-lit images work best
883
+ 2. **Close-up shots**: Focus on damaged areas
884
+ 3. **High resolution**: Better quality = better detection
885
+ 4. **Multiple angles**: Try different perspectives if results unclear
886
+ 5. **Clean lens**: Ensure camera lens is clean
887
+
888
+ ### 🔬 Technology Behind the Scenes
889
+
890
+ - **Detectron2**: Facebook's object detection framework
891
+ - **Vision Transformer (ViT)**: Advanced deepfake detection
892
+ - **Pre-trained models**: Fine-tuned on automotive datasets
893
+ - **Multi-stage pipeline**: Damage detection → Deepfake analysis
894
 
895
  ---
896
+
897
+ **🚨 Disclaimer**: This tool assists in damage assessment but should not replace professional inspection. Always consult qualified experts for final decisions on insurance claims or vehicle repairs.
898
+
899
+ **Powered by Askhedi** - Advanced AI Detection Services 🚀
900
  """)
901
+
902
+ # Examples
903
+ if any(os.path.exists(img) for img in SAMPLE_IMAGES):
 
904
  gr.Markdown("## 📸 Example Images")
905
  with gr.Row():
906
+ example_inputs = [img for img in SAMPLE_IMAGES if os.path.exists(img)]
907
  gr.Examples(
908
+ examples=example_inputs,
909
  inputs=input_image,
910
+ outputs=[output_image, output_text, usage_counter],
911
+ fn=lambda x: process_image(x, 0.7, 0.5, False, "auto", 0),
912
+ cache_examples=True
913
  )
914
 
915
+ # Connect functions to the UI
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
916
  process_btn.click(
917
+ fn=process_image,
918
  inputs=[
919
  input_image,
 
920
  damage_threshold,
921
  deepfake_threshold,
922
  skip_damage,
923
  device,
924
+ usage_counter
925
  ],
926
+ outputs=[output_image, output_text, usage_counter]
927
  )
928
 
929
+ # Email sending functionality
930
+ send_email_btn.click(
931
+ fn=send_email_wrapper,
932
+ inputs=[recipient_email, output_image, output_text],
933
+ outputs=email_status
934
+ )
 
 
 
 
 
 
 
 
 
 
 
935
 
936
+ # Clear button functionality
937
  clear_btn.click(
938
+ fn=lambda: [None, "Upload an image and click 'Analyze Image' to start...", 0, ""],
939
  inputs=[],
940
+ outputs=[output_image, output_text, usage_counter, email_status]
941
  )
942
 
943
+ # Update usage display when counter changes
944
+ usage_counter.change(
945
+ fn=lambda count: f"**Usage: {count}/{MAX_TRIES}**",
946
+ inputs=[usage_counter],
 
 
 
 
 
 
 
 
947
  outputs=[usage_display]
948
  )
949
 
 
950
  return app
951
 
952
  if __name__ == "__main__":
953
+ # Create and launch the Gradio app
954
+ app = create_gradio_interface()
955
+ app.launch(share=False)