seifbenayed commited on
Commit
a028174
Β·
1 Parent(s): 79a26c5
Files changed (1) hide show
  1. app.py +482 -451
app.py CHANGED
@@ -2,14 +2,7 @@
2
  import importlib.util
3
  import os
4
  import sys
5
- import cv2
6
- import time
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 traceback
13
 
14
  # Check if detectron2 is installed
15
  if importlib.util.find_spec("detectron2") is None:
@@ -18,11 +11,23 @@ if importlib.util.find_spec("detectron2") is None:
18
  os.system("pip install git+https://github.com/facebookresearch/detectron2.git")
19
  print("Installation complete!")
20
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  # Add current directory to path
22
  if not os.getcwd() in sys.path:
23
  sys.path.append(os.getcwd())
24
 
25
- # Detectron2 imports - wrapped in try-except to make them optional
26
  try:
27
  from detectron2.engine import DefaultPredictor
28
  from detectron2.config import get_cfg
@@ -33,7 +38,7 @@ except ImportError:
33
  print("Warning: Detectron2 is not installed. Damage detection will not be available.")
34
  DETECTRON2_AVAILABLE = False
35
 
36
- # Check for custom path for models
37
  try:
38
  from configs.get_config import load_config
39
  from models import *
@@ -42,16 +47,20 @@ except ImportError:
42
  print("Warning: Custom models couldn't be imported. Only damage detection will work.")
43
  MODELS_IMPORTED = False
44
 
45
- # Define the model paths at the beginning of your script
46
- DEFAULT_DAMAGE_MODEL_PATH = "./model_final.pth" # Replace with your actual path
47
- DEFAULT_DEEPFAKE_MODEL_PATH = "./PoseEfficientNet_custom_laanet_model_final.pth" # Replace with your actual path
48
- DEFAULT_DEEPFAKE_CFG_PATH = "./configs/detector2.yaml" # Replace with your actual path
 
 
 
 
 
 
 
49
 
50
  def verify_detectron2_installation():
51
- """Verify that Detectron2 is properly installed and can access model zoo"""
52
- import sys
53
- import importlib.util
54
-
55
  results = {
56
  "detectron2_installed": False,
57
  "model_zoo_accessible": False,
@@ -59,88 +68,36 @@ def verify_detectron2_installation():
59
  "error_messages": []
60
  }
61
 
62
- # Check if detectron2 is installed
63
  try:
 
64
  if importlib.util.find_spec("detectron2") is not None:
65
  results["detectron2_installed"] = True
66
- print("βœ… Detectron2 is installed")
67
 
68
- # Try importing detectron2
69
  try:
70
  import detectron2
71
- print(f"βœ… Detectron2 version: {detectron2.__version__}")
72
-
73
- # Try accessing model zoo
74
- try:
75
- from detectron2 import model_zoo
76
- # Try accessing a config file
77
- config_file = "COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml"
78
- config_path = model_zoo.get_config_file(config_file)
79
- if os.path.exists(config_path):
80
- results["model_zoo_accessible"] = True
81
- print(f"βœ… Model zoo is accessible, found config: {config_path}")
82
- else:
83
- results["error_messages"].append(f"Config file exists but cannot be accessed: {config_path}")
84
- print(f"❌ Config file exists but cannot be accessed: {config_path}")
85
- except Exception as e:
86
- results["error_messages"].append(f"Error accessing model zoo: {str(e)}")
87
- print(f"❌ Error accessing model zoo: {str(e)}")
88
- traceback.print_exc()
89
-
90
- # Try creating a config
91
- try:
92
- from detectron2.config import get_cfg
93
- cfg = get_cfg()
94
- results["can_create_cfg"] = True
95
- print("βœ… Can create Detectron2 config")
96
-
97
- # Try setting up a model configuration
98
- try:
99
- cfg.merge_from_file(model_zoo.get_config_file(config_file))
100
- print("βœ… Can load model configuration from model zoo")
101
-
102
- # Check if we can set up a default predictor (without actually creating it)
103
- try:
104
- from detectron2.engine import DefaultPredictor
105
- print("βœ… DefaultPredictor class is available")
106
- except Exception as e:
107
- results["error_messages"].append(f"Error importing DefaultPredictor: {str(e)}")
108
- print(f"❌ Error importing DefaultPredictor: {str(e)}")
109
- except Exception as e:
110
- results["error_messages"].append(f"Error setting up model configuration: {str(e)}")
111
- print(f"❌ Error setting up model configuration: {str(e)}")
112
- except Exception as e:
113
- results["error_messages"].append(f"Error creating Detectron2 config: {str(e)}")
114
- print(f"❌ Error creating Detectron2 config: {str(e)}")
115
  except Exception as e:
116
- results["error_messages"].append(f"Error importing detectron2: {str(e)}")
117
- print(f"❌ Error importing detectron2: {str(e)}")
118
  else:
119
  results["error_messages"].append("Detectron2 is not installed")
120
- print("❌ Detectron2 is not installed")
121
  except Exception as e:
122
  results["error_messages"].append(f"Error checking Detectron2 installation: {str(e)}")
123
- print(f"❌ Error checking Detectron2 installation: {str(e)}")
124
-
125
- # Print Python version and platform info
126
- print(f"Python version: {sys.version}")
127
- print(f"Platform: {sys.platform}")
128
-
129
- # Print PyTorch version if available
130
- try:
131
- import torch
132
- print(f"PyTorch version: {torch.__version__}")
133
- print(f"CUDA available: {torch.cuda.is_available()}")
134
- if torch.cuda.is_available():
135
- print(f"CUDA version: {torch.version.cuda}")
136
- print(f"GPU: {torch.cuda.get_device_name(0)}")
137
- except ImportError:
138
- print("PyTorch is not installed")
139
 
140
  return results
141
 
142
  def setup_device(device_str):
143
- """Set up the computation device based on user input and availability"""
144
  if device_str == 'auto':
145
  if torch.cuda.is_available():
146
  return torch.device('cuda:0')
@@ -157,60 +114,30 @@ def setup_device(device_str):
157
  return torch.device('cpu')
158
 
159
  def setup_damage_detector(model_path, threshold=0.7):
160
- """Set up the damage detection model using Detectron2 with enhanced error logging"""
161
  if not DETECTRON2_AVAILABLE:
162
  print("Detectron2 is not installed. Cannot set up damage detector.")
163
  return None, None
164
 
165
- try:
166
- print(f"Checking if damage model exists at path: {model_path}")
167
- if model_path is None:
168
- print("Error: No damage model path specified")
169
- return None, None
170
-
171
- if not os.path.exists(model_path):
172
  print(f"Error: Damage model file not found at {model_path}")
173
  return None, None
174
 
175
- print("Model file exists, setting up configuration...")
176
  cfg = get_cfg()
177
- print("Loading base config from model zoo...")
178
- try:
179
- cfg.merge_from_file(model_zoo.get_config_file("COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml"))
180
- print("Base config loaded successfully")
181
- except Exception as e:
182
- print(f"Error loading base config: {e}")
183
- traceback.print_exc()
184
- return None, None
185
-
186
- print(f"Setting model weights to: {model_path}")
187
  cfg.MODEL.WEIGHTS = model_path
188
  cfg.MODEL.ROI_HEADS.NUM_CLASSES = 1 # Only one class (damage)
189
  cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = threshold
190
 
191
- # Explicitly set to use CPU if on Mac (MPS)
192
  if hasattr(torch, 'backends') and hasattr(torch.backends, 'mps') and torch.backends.mps.is_available():
193
  cfg.MODEL.DEVICE = "cpu"
194
- print("Mac MPS detected - forcing Detectron2 to use CPU")
195
-
196
- # Print configuration for debugging
197
- print("Detectron2 configuration:")
198
- print(f"- Model weights: {cfg.MODEL.WEIGHTS}")
199
- print(f"- Number of classes: {cfg.MODEL.ROI_HEADS.NUM_CLASSES}")
200
- print(f"- Score threshold: {cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST}")
201
- print(f"- Device: {cfg.MODEL.DEVICE}")
202
-
203
- print("Creating predictor...")
204
- try:
205
- predictor = DefaultPredictor(cfg)
206
- print("Predictor created successfully!")
207
- return predictor, cfg
208
- except Exception as e:
209
- print(f"Error creating predictor: {e}")
210
- traceback.print_exc()
211
- return None, cfg
212
  except Exception as e:
213
- print(f"Unexpected error in damage detector setup: {e}")
214
  traceback.print_exc()
215
  return None, None
216
 
@@ -221,11 +148,11 @@ def load_deepfake_model(model_path, cfg_path, device):
221
  return None, None
222
 
223
  if model_path is None or not os.path.exists(model_path):
224
- print("No deepfake model specified or file not found. Skipping deepfake detection.")
225
  return None, None
226
 
227
  if cfg_path is None or not os.path.exists(cfg_path):
228
- print("No deepfake config specified or file not found. Skipping deepfake detection.")
229
  return None, None
230
 
231
  try:
@@ -236,7 +163,6 @@ def load_deepfake_model(model_path, cfg_path, device):
236
  model = build_model(cfg.MODEL, MODELS)
237
 
238
  # Load weights
239
- print(f"Loading deepfake model from: {model_path}")
240
  checkpoint = torch.load(model_path, map_location='cpu')
241
 
242
  if isinstance(checkpoint, dict) and 'state_dict' in checkpoint:
@@ -259,7 +185,7 @@ def load_deepfake_model(model_path, cfg_path, device):
259
  def preprocess_for_deepfake(image, cfg, device):
260
  """Preprocess an image for deepfake detection"""
261
  try:
262
- # Convert to RGB if needed
263
  if len(image.shape) == 3 and image.shape[2] == 3:
264
  if image.dtype != np.uint8:
265
  image = (image * 255).astype(np.uint8)
@@ -267,10 +193,10 @@ def preprocess_for_deepfake(image, cfg, device):
267
  else:
268
  rgb_img = image
269
 
270
- # Resize
271
  img_resized = cv2.resize(rgb_img, (cfg.DATASET.IMAGE_SIZE[0], cfg.DATASET.IMAGE_SIZE[1]))
272
 
273
- # Convert to PIL and apply transforms
274
  transform = transforms.Compose([
275
  transforms.ToTensor(),
276
  transforms.Normalize(
@@ -279,16 +205,16 @@ def preprocess_for_deepfake(image, cfg, device):
279
  )
280
  ])
281
 
282
- img_tensor = transform(Image.fromarray(img_resized)).unsqueeze(0) # Add batch dimension
283
  img_tensor = img_tensor.to(device)
284
 
285
- # Convert to correct precision
286
  if hasattr(cfg.MODEL, 'precision') and cfg.MODEL.precision == 'fp64':
287
  img_tensor = img_tensor.to(torch.float64)
288
 
289
  return img_tensor
290
  except Exception as e:
291
- print(f"Error preprocessing image for deepfake detection: {e}")
292
  traceback.print_exc()
293
  return None
294
 
@@ -298,9 +224,8 @@ def detect_damage(img, damage_detector):
298
  if img is None:
299
  raise ValueError("Invalid image")
300
 
301
- # If no damage detector available, return the whole image as region
302
  if damage_detector is None:
303
- print("No damage detector available. Using whole image as region.")
304
  h, w = img.shape[:2]
305
  damage_regions = [{
306
  "box": (0, 0, w, h),
@@ -312,7 +237,7 @@ def detect_damage(img, damage_detector):
312
  # Run inference
313
  outputs = damage_detector(img)
314
 
315
- # Get damage regions
316
  instances = outputs["instances"].to("cpu")
317
  boxes = instances.pred_boxes.tensor.numpy() if instances.has("pred_boxes") else []
318
  scores = instances.scores.numpy() if instances.has("scores") else []
@@ -327,8 +252,8 @@ def detect_damage(img, damage_detector):
327
  "mask": masks[i] if len(masks) > i else None
328
  })
329
 
 
330
  if not damage_regions:
331
- print("No damage detected. Using whole image.")
332
  h, w = img.shape[:2]
333
  damage_regions = [{
334
  "box": (0, 0, w, h),
@@ -340,7 +265,8 @@ def detect_damage(img, damage_detector):
340
  except Exception as e:
341
  print(f"Error detecting damage: {e}")
342
  traceback.print_exc()
343
- # If error occurs, return the whole image as region
 
344
  if 'img' in locals() and img is not None:
345
  h, w = img.shape[:2]
346
  damage_regions = [{
@@ -356,11 +282,10 @@ def check_deepfake(image, damage_regions, deepfake_model, deepfake_cfg, device,
356
  results = []
357
 
358
  if deepfake_model is None:
359
- print("No deepfake model available. Skipping deepfake detection.")
360
  return []
361
 
362
  try:
363
- # If no damage regions, check the entire image
364
  if not damage_regions:
365
  img_tensor = preprocess_for_deepfake(image, deepfake_cfg, device)
366
  if img_tensor is None:
@@ -378,7 +303,6 @@ def check_deepfake(image, damage_regions, deepfake_model, deepfake_cfg, device,
378
  cls_outputs = outputs['cls']
379
  cls_prob = cls_outputs.sigmoid().cpu().numpy()
380
  else:
381
- # Assuming the output is directly the classification probability
382
  cls_prob = outputs.sigmoid().cpu().numpy() if hasattr(outputs, 'sigmoid') else outputs.cpu().numpy()
383
 
384
  if cls_prob.size > 0:
@@ -396,13 +320,12 @@ def check_deepfake(image, damage_regions, deepfake_model, deepfake_cfg, device,
396
  # Process each damage region
397
  for i, region in enumerate(damage_regions):
398
  x1, y1, x2, y2 = region["box"]
399
- # Ensure coordinates are within image bounds
400
  x1, y1 = max(0, x1), max(0, y1)
401
  x2, y2 = min(image.shape[1], x2), min(image.shape[0], y2)
402
 
403
- # Extract region and check if it's a deepfake
404
  if x2 > x1 and y2 > y1:
405
- # Get ROI
406
  roi = image[y1:y2, x1:x2]
407
 
408
  # Preprocess
@@ -410,11 +333,10 @@ def check_deepfake(image, damage_regions, deepfake_model, deepfake_cfg, device,
410
  if img_tensor is None:
411
  continue
412
 
413
- # Run inference
414
  with torch.no_grad():
415
  outputs = deepfake_model(img_tensor)
416
 
417
- # Extract outputs
418
  if isinstance(outputs, list):
419
  outputs = outputs[0]
420
 
@@ -422,7 +344,6 @@ def check_deepfake(image, damage_regions, deepfake_model, deepfake_cfg, device,
422
  cls_outputs = outputs['cls']
423
  cls_prob = cls_outputs.sigmoid().cpu().numpy()
424
  else:
425
- # Assuming the output is directly the classification probability
426
  cls_prob = outputs.sigmoid().cpu().numpy() if hasattr(outputs, 'sigmoid') else outputs.cpu().numpy()
427
 
428
  if cls_prob.size > 0:
@@ -443,42 +364,39 @@ def check_deepfake(image, damage_regions, deepfake_model, deepfake_cfg, device,
443
  return []
444
 
445
  def visualize_results(image, damage_outputs, deepfake_results, damage_threshold):
446
- """Create visualization of damage detection and deepfake verification"""
447
  try:
448
- # Create a copy for visualization
449
  img_copy = image.copy()
450
 
451
- # Draw damage detection results
452
  if damage_outputs is not None and DETECTRON2_AVAILABLE:
453
  try:
454
  v = Visualizer(img_copy[:, :, ::-1], scale=1.0, instance_mode=ColorMode.IMAGE_BW)
455
  v = v.draw_instance_predictions(damage_outputs["instances"].to("cpu"))
456
  result_img = v.get_image()[:, :, ::-1]
457
-
458
- # Convert to a standard numpy array to ensure compatibility with OpenCV
459
  result_img = np.array(result_img, dtype=np.uint8)
460
  except Exception as e:
461
- print(f"Error visualizing damage detection: {e}")
462
  result_img = img_copy
463
  else:
464
  result_img = img_copy
465
 
466
- # Add deepfake detection results
467
  for result in deepfake_results:
468
  try:
469
  if "box" in result:
470
  x1, y1, x2, y2 = result["box"]
471
- fake_prob = result["deepfake_prob"]
472
  is_fake = result["is_fake"]
473
  region_id = result.get("region_id", 0)
474
 
475
- # Text for the region
476
  text = f"R{region_id}: {'FAKE' if is_fake else 'REAL'} ({fake_prob*100:.1f}%)"
477
 
478
- # Different colors for fake/real
479
- color = (0, 0, 255) if is_fake else (0, 255, 0) # Red for fake, green for real
480
 
481
- # Ensure we have a standard numpy array
482
  if not isinstance(result_img, np.ndarray):
483
  result_img = np.array(result_img, dtype=np.uint8)
484
 
@@ -489,71 +407,53 @@ def visualize_results(image, damage_outputs, deepfake_results, damage_threshold)
489
  fake_prob = result["deepfake_prob"]
490
  is_fake = result["is_fake"]
491
 
492
- # Text for the whole image
493
  text = f"Image: {'FAKE' if is_fake else 'REAL'} ({fake_prob*100:.1f}%)"
 
494
 
495
- # Different colors for fake/real
496
- color = (0, 0, 255) if is_fake else (0, 255, 0) # Red for fake, green for real
497
-
498
- # Ensure we have a standard numpy array
499
  if not isinstance(result_img, np.ndarray):
500
  result_img = np.array(result_img, dtype=np.uint8)
501
 
502
- # Draw text
503
  cv2.putText(result_img, text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, color, 2)
504
  except Exception as e:
505
- print(f"Error drawing result {result}: {e}")
506
 
507
  return result_img
508
  except Exception as e:
509
- print(f"Error visualizing results: {e}")
510
  traceback.print_exc()
511
- return np.array(image, dtype=np.uint8) # Return the original image as a numpy array
512
 
513
  def process_image(input_image, damage_model_path, deepfake_model_path, deepfake_cfg_path,
514
  damage_threshold, deepfake_threshold, skip_damage, device_str):
515
- """Process an image through the car damage and deepfake detection pipeline with enhanced error handling"""
516
  progress_info = []
517
 
518
- # Print all input paths for debugging
519
- progress_info.append(f"Input parameters:")
520
- progress_info.append(f"- Damage model path: {damage_model_path}")
521
- progress_info.append(f"- Deepfake model path: {deepfake_model_path}")
522
- progress_info.append(f"- Deepfake config path: {deepfake_cfg_path}")
523
- progress_info.append(f"- Damage threshold: {damage_threshold}")
524
- progress_info.append(f"- Deepfake threshold: {deepfake_threshold}")
525
- progress_info.append(f"- Skip damage detection: {skip_damage}")
526
- progress_info.append(f"- Device: {device_str}")
527
 
528
- # Check if model files exist
529
  if not skip_damage and damage_model_path:
530
- if os.path.exists(damage_model_path):
531
- progress_info.append(f"Damage model file exists at: {damage_model_path}")
532
- else:
533
- progress_info.append(f"ERROR: Damage model file NOT FOUND at: {damage_model_path}")
534
 
535
- if deepfake_model_path:
536
- if os.path.exists(deepfake_model_path):
537
- progress_info.append(f"Deepfake model file exists at: {deepfake_model_path}")
538
- else:
539
- progress_info.append(f"ERROR: Deepfake model file NOT FOUND at: {deepfake_model_path}")
540
 
541
- if deepfake_cfg_path:
542
- if os.path.exists(deepfake_cfg_path):
543
- progress_info.append(f"Deepfake config file exists at: {deepfake_cfg_path}")
544
- else:
545
- progress_info.append(f"ERROR: Deepfake config file NOT FOUND at: {deepfake_cfg_path}")
546
 
547
- # Convert Gradio image to numpy array
548
  try:
549
  if isinstance(input_image, dict) and "path" in input_image:
550
  img = cv2.imread(input_image["path"])
551
  elif isinstance(input_image, str):
552
  img = cv2.imread(input_image)
553
  elif isinstance(input_image, np.ndarray):
554
- # Make a copy to avoid modifying the original
555
  img = input_image.copy()
556
- # Convert from RGB to BGR (OpenCV format)
557
  if len(img.shape) == 3 and img.shape[2] == 3:
558
  img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
559
  else:
@@ -562,324 +462,422 @@ def process_image(input_image, damage_model_path, deepfake_model_path, deepfake_
562
  if img is None:
563
  return None, "Error: Could not read the image"
564
  except Exception as e:
565
- error_trace = traceback.format_exc()
566
- return None, f"Error loading image: {str(e)}\n{error_trace}"
567
 
568
- # Progress update
569
  progress_info.append("Image loaded successfully")
570
 
571
  # Setup device
572
- try:
573
- device = setup_device(device_str)
574
- progress_info.append(f"Using device: {device}")
575
- except Exception as e:
576
- error_trace = traceback.format_exc()
577
- progress_info.append(f"Error setting up device: {str(e)}\n{error_trace}")
578
- device = torch.device('cpu')
579
- progress_info.append(f"Falling back to CPU")
580
 
581
  # Initialize models
582
  damage_detector = None
583
  deepfake_model = None
584
  deepfake_cfg = None
585
 
586
- # Check Detectron2 availability
587
- if not skip_damage:
588
- progress_info.append(f"Detectron2 available: {DETECTRON2_AVAILABLE}")
589
-
590
- if not DETECTRON2_AVAILABLE:
591
- progress_info.append("Warning: Detectron2 is not available. Install with:")
592
- progress_info.append("pip install torch torchvision --extra-index-url https://download.pytorch.org/whl/cpu")
593
- progress_info.append("pip install git+https://github.com/facebookresearch/detectron2.git")
594
-
595
- # Setup damage detector if not skipped
596
  if not skip_damage and damage_model_path:
597
  progress_info.append("Setting up damage detector...")
598
- try:
599
- damage_detector, detector_cfg = setup_damage_detector(damage_model_path, float(damage_threshold))
600
- if damage_detector is None and DETECTRON2_AVAILABLE:
601
- progress_info.append("Failed to initialize damage detector")
602
- else:
603
- progress_info.append("Damage detector initialized successfully")
604
- except Exception as e:
605
- error_trace = traceback.format_exc()
606
- progress_info.append(f"Error in damage detector setup: {str(e)}\n{error_trace}")
607
 
608
  # Setup deepfake detector
609
  if deepfake_model_path and deepfake_cfg_path:
610
  progress_info.append("Setting up deepfake detector...")
611
- try:
612
- deepfake_model, deepfake_cfg = load_deepfake_model(deepfake_model_path, deepfake_cfg_path, device)
613
- if deepfake_model is None:
614
- progress_info.append("Failed to initialize deepfake detector")
615
- else:
616
- progress_info.append("Deepfake detector initialized successfully")
617
- except Exception as e:
618
- error_trace = traceback.format_exc()
619
- progress_info.append(f"Error in deepfake detector setup: {str(e)}\n{error_trace}")
620
 
621
- # Ensure at least one detector is working
622
  if damage_detector is None and deepfake_model is None:
623
- return None, "\n".join(progress_info) + "\nError: Neither damage nor deepfake detector is available"
624
 
625
- # Step 1: Detect damage or use whole image
626
  progress_info.append("Detecting damage regions...")
627
  start_time = time.time()
628
- try:
629
- img, damage_outputs, damage_regions = detect_damage(img, damage_detector)
630
- damage_time = time.time() - start_time
631
- except Exception as e:
632
- error_trace = traceback.format_exc()
633
- progress_info.append(f"Error in damage detection: {str(e)}\n{error_trace}")
634
- h, w = img.shape[:2]
635
- damage_regions = [{
636
- "box": (0, 0, w, h),
637
- "score": 1.0,
638
- "mask": None
639
- }]
640
- damage_outputs = None
641
- damage_time = time.time() - start_time
642
-
643
- if img is None:
644
- return None, "\n".join(progress_info) + "\nError: Failed to process image"
645
 
646
- # Print damage detection results
647
- if damage_detector is not None and damage_regions:
648
- progress_info.append(f"Detected {len(damage_regions)} damage regions in {damage_time:.3f} seconds")
649
  else:
650
- progress_info.append("Using the whole image for analysis")
651
 
652
- # Step 2: Check if damage is deepfake
653
  deepfake_results = []
654
  if deepfake_model is not None:
655
- progress_info.append("Performing deepfake detection...")
656
  start_time = time.time()
657
- try:
658
- deepfake_results = check_deepfake(
659
- img, damage_regions, deepfake_model, deepfake_cfg, device, float(deepfake_threshold)
660
- )
661
- deepfake_time = time.time() - start_time
 
 
662
 
663
- if deepfake_results:
664
- progress_info.append(f"Deepfake detection completed in {deepfake_time:.3f} seconds")
665
-
666
- # Generate report
667
- for result in deepfake_results:
668
- if "region_id" in result:
669
- region_id = result["region_id"]
670
- fake_prob = result["deepfake_prob"]
671
- is_fake = result["is_fake"]
672
- progress_info.append(f"Region {region_id}: {'FAKE' if is_fake else 'REAL'} (Probability: {fake_prob*100:.2f}%)")
673
- elif "region" in result and result["region"] == "full_image":
674
- fake_prob = result["deepfake_prob"]
675
- is_fake = result["is_fake"]
676
- progress_info.append(f"Whole image: {'FAKE' if is_fake else 'REAL'} (Probability: {fake_prob*100:.2f}%)")
677
- else:
678
- progress_info.append("No deepfake detection results")
679
- except Exception as e:
680
- error_trace = traceback.format_exc()
681
- progress_info.append(f"Error in deepfake detection: {str(e)}\n{error_trace}")
682
 
683
- # Step 3: Visualize final results
684
  progress_info.append("Generating visualization...")
685
- try:
686
- result_img = visualize_results(img, damage_outputs, deepfake_results, float(damage_threshold))
687
-
688
- # Convert back to RGB for Gradio
689
- if len(result_img.shape) == 3 and result_img.shape[2] == 3:
690
- result_img = cv2.cvtColor(result_img, cv2.COLOR_BGR2RGB)
691
- except Exception as e:
692
- error_trace = traceback.format_exc()
693
- progress_info.append(f"Error in visualization: {str(e)}\n{error_trace}")
694
- # Return original image if visualization fails
695
- result_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) if len(img.shape) == 3 and img.shape[2] == 3 else img
696
 
697
- progress_info.append("Processing complete!")
 
 
 
 
698
 
699
  return result_img, "\n".join(progress_info)
700
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
701
  def create_gradio_interface():
702
- with gr.Blocks(title="Car Damage & Deepfake Detection") as app:
703
- gr.Markdown("# Car Damage Detection & Deepfake Verification")
704
- gr.Markdown("Upload an image to detect car damage and check if it's a deepfake")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
705
 
706
- # Add a diagnostic tab
707
- with gr.Tab("Diagnostics"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
708
  with gr.Row():
709
- run_diagnostic_btn = gr.Button("Run Detectron2 Diagnostics", variant="primary")
710
- diagnostic_output = gr.Textbox(label="Diagnostic Results", lines=20)
 
 
711
 
712
- # Function to run diagnostics
713
  def run_diagnostics():
714
- results = verify_detectron2_installation()
715
 
716
- # Check model paths
717
- output_lines = ["# System Diagnostics", ""]
718
 
719
- # Check damage model
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
720
  if os.path.exists(DEFAULT_DAMAGE_MODEL_PATH):
721
  file_size = os.path.getsize(DEFAULT_DAMAGE_MODEL_PATH) / (1024 * 1024) # Size in MB
722
- output_lines.append(f"βœ… Damage model exists: {DEFAULT_DAMAGE_MODEL_PATH} ({file_size:.2f} MB)")
723
  else:
724
- output_lines.append(f"❌ Damage model NOT found: {DEFAULT_DAMAGE_MODEL_PATH}")
725
 
726
- # Check deepfake model
727
  if os.path.exists(DEFAULT_DEEPFAKE_MODEL_PATH):
728
  file_size = os.path.getsize(DEFAULT_DEEPFAKE_MODEL_PATH) / (1024 * 1024) # Size in MB
729
- output_lines.append(f"βœ… Deepfake model exists: {DEFAULT_DEEPFAKE_MODEL_PATH} ({file_size:.2f} MB)")
730
  else:
731
- output_lines.append(f"❌ Deepfake model NOT found: {DEFAULT_DEEPFAKE_MODEL_PATH}")
732
 
733
- # Check deepfake config
734
  if os.path.exists(DEFAULT_DEEPFAKE_CFG_PATH):
735
- output_lines.append(f"βœ… Deepfake config exists: {DEFAULT_DEEPFAKE_CFG_PATH}")
736
  else:
737
- output_lines.append(f"❌ Deepfake config NOT found: {DEFAULT_DEEPFAKE_CFG_PATH}")
738
-
739
- # Add Detectron2 status
740
- output_lines.append("")
741
- output_lines.append("# Detectron2 Status")
742
- output_lines.append(f"Detectron2 installed: {'βœ…' if results['detectron2_installed'] else '❌'}")
743
- output_lines.append(f"Model zoo accessible: {'βœ…' if results['model_zoo_accessible'] else '❌'}")
744
- output_lines.append(f"Can create config: {'βœ…' if results['can_create_cfg'] else '❌'}")
745
-
746
- if results['error_messages']:
747
- output_lines.append("")
748
- output_lines.append("# Error Messages")
749
- for error in results['error_messages']:
750
- output_lines.append(f"- {error}")
751
-
752
- # Try to load a small sample model from model zoo
753
- output_lines.append("")
754
- output_lines.append("# Test Loading Detectron2 Model")
755
- try:
756
- from detectron2 import model_zoo
757
- from detectron2.config import get_cfg
758
- from detectron2.engine import DefaultPredictor
759
-
760
- cfg = get_cfg()
761
- cfg.merge_from_file(model_zoo.get_config_file("COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml"))
762
-
763
- # Try to access a pre-trained model (without downloading it)
764
- model_url = model_zoo.get_checkpoint_url("COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml")
765
- output_lines.append(f"βœ… Can access pre-trained model URL: {model_url}")
766
-
767
- # Check if we can see the damage model contents
768
- if os.path.exists(DEFAULT_DAMAGE_MODEL_PATH):
769
- try:
770
- import torch
771
- # Try to load just the metadata without loading the whole model
772
- checkpoint = torch.load(DEFAULT_DAMAGE_MODEL_PATH, map_location='cpu')
773
- if isinstance(checkpoint, dict):
774
- output_lines.append("βœ… Successfully loaded damage model metadata")
775
- # Check some basic keys in the checkpoint
776
- keys = list(checkpoint.keys())
777
- output_lines.append(f"Model keys: {keys[:5]}...")
778
- else:
779
- output_lines.append("⚠️ Damage model loaded but has unexpected format")
780
- except Exception as e:
781
- output_lines.append(f"❌ Failed to load damage model: {str(e)}")
782
- except Exception as e:
783
- output_lines.append(f"❌ Error in test loading: {str(e)}")
784
 
785
- # Recommendation section
786
- output_lines.append("")
787
- output_lines.append("# Recommendations")
 
788
 
789
- if not results['detectron2_installed']:
790
- output_lines.append("- Install Detectron2: `pip install torch torchvision --extra-index-url https://download.pytorch.org/whl/cpu`")
791
- output_lines.append("- Then: `pip install git+https://github.com/facebookresearch/detectron2.git`")
 
 
 
792
 
793
  if not os.path.exists(DEFAULT_DAMAGE_MODEL_PATH):
794
- output_lines.append(f"- Place the damage model file at: {DEFAULT_DAMAGE_MODEL_PATH}")
 
 
 
 
 
 
795
 
796
- if results['detectron2_installed'] and not results['model_zoo_accessible']:
797
- output_lines.append("- Detectron2 is installed but model zoo is not accessible. Check your internet connection.")
798
- output_lines.append("- Try reinstalling Detectron2")
 
 
799
 
800
- return "\n".join(output_lines)
801
 
802
- # Connect the diagnostic button
803
- run_diagnostic_btn.click(fn=run_diagnostics, inputs=[], outputs=diagnostic_output)
804
-
805
- with gr.Tab("Basic Interface"):
806
- with gr.Row():
807
- with gr.Column(scale=1):
808
- input_image = gr.Image(type="numpy", label="Input Image")
809
-
810
- # Simple controls
811
- skip_damage = gr.Checkbox(label="Skip Damage Detection", value=False)
812
- damage_threshold = gr.Slider(minimum=0.1, maximum=1.0, value=0.7, step=0.05,
813
- label="Damage Detection Threshold")
814
- deepfake_threshold = gr.Slider(minimum=0.1, maximum=1.0, value=0.5, step=0.05,
815
- label="Deepfake Detection Threshold")
816
- device = gr.Dropdown(choices=["auto", "cuda", "cpu", "mps"], value="auto",
817
- label="Computation Device")
818
-
819
- process_btn = gr.Button("Process Image", variant="primary")
820
-
821
- with gr.Column(scale=1):
822
- output_image = gr.Image(type="numpy", label="Result")
823
- output_text = gr.Textbox(label="Detection Results", lines=10)
 
 
 
 
 
 
 
 
 
 
 
 
824
 
825
- with gr.Tab("Advanced Settings"):
826
  with gr.Row():
827
  with gr.Column():
828
- damage_model_path = gr.Textbox(label="Damage Model Path",
829
- value=DEFAULT_DAMAGE_MODEL_PATH,
830
- placeholder="Path to damage detection model (.pth)")
831
- deepfake_model_path = gr.Textbox(label="Deepfake Model Path",
832
- value=DEFAULT_DEEPFAKE_MODEL_PATH,
833
- placeholder="Path to deepfake detection model (.pth)")
834
- deepfake_cfg_path = gr.Textbox(label="Deepfake Config Path",
835
- value=DEFAULT_DEEPFAKE_CFG_PATH,
836
- placeholder="Path to deepfake model config (.yaml)")
837
-
838
- # Add a check model paths button
839
- check_paths_btn = gr.Button("Check Model Paths")
840
- paths_result = gr.Textbox(label="Path Check Results", lines=5)
841
 
842
- # Function to check if model paths exist
843
- def check_model_paths(damage_path, deepfake_path, cfg_path):
844
- results = []
845
-
846
- # Check damage model
847
- if os.path.exists(damage_path):
848
- file_size = os.path.getsize(damage_path) / (1024 * 1024) # Size in MB
849
- results.append(f"βœ… Damage model exists: {damage_path} ({file_size:.2f} MB)")
850
- else:
851
- results.append(f"❌ Damage model NOT found: {damage_path}")
852
-
853
- # Check deepfake model
854
- if os.path.exists(deepfake_path):
855
- file_size = os.path.getsize(deepfake_path) / (1024 * 1024) # Size in MB
856
- results.append(f"βœ… Deepfake model exists: {deepfake_path} ({file_size:.2f} MB)")
857
- else:
858
- results.append(f"❌ Deepfake model NOT found: {deepfake_path}")
859
-
860
- # Check deepfake config
861
- if os.path.exists(cfg_path):
862
- results.append(f"βœ… Deepfake config exists: {cfg_path}")
863
- else:
864
- results.append(f"❌ Deepfake config NOT found: {cfg_path}")
865
-
866
- return "\n".join(results)
867
 
868
- # Connect the check paths button
869
- check_paths_btn.click(
870
- fn=check_model_paths,
871
- inputs=[damage_model_path, deepfake_model_path, deepfake_cfg_path],
872
- outputs=paths_result
873
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
874
 
875
- # Connect the process function
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
876
  process_btn.click(
877
  fn=process_image,
878
  inputs=[
879
  input_image,
880
- damage_model_path,
881
- deepfake_model_path,
882
- deepfake_cfg_path,
883
  damage_threshold,
884
  deepfake_threshold,
885
  skip_damage,
@@ -888,13 +886,46 @@ def create_gradio_interface():
888
  outputs=[output_image, output_text]
889
  )
890
 
891
- # Examples
892
- gr.Markdown("## Examples")
893
- gr.Markdown("Note: Examples will only work if you have the appropriate models installed.")
894
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
895
  return app
896
 
897
- # For local testing and Hugging Face Spaces, with debugging enabled
898
  if __name__ == "__main__":
 
 
 
 
899
  app = create_gradio_interface()
900
- app.launch(debug=True) # Enable debug mode to see detailed error messages
 
2
  import importlib.util
3
  import os
4
  import sys
5
+
 
 
 
 
 
 
 
6
 
7
  # Check if detectron2 is installed
8
  if importlib.util.find_spec("detectron2") is None:
 
11
  os.system("pip install git+https://github.com/facebookresearch/detectron2.git")
12
  print("Installation complete!")
13
 
14
+ #!/usr/bin/env python3
15
+ import os
16
+ import sys
17
+ import time
18
+ import cv2
19
+ import torch
20
+ import numpy as np
21
+ import gradio as gr
22
+ from PIL import Image
23
+ from torchvision import transforms
24
+ import traceback
25
+
26
  # Add current directory to path
27
  if not os.getcwd() in sys.path:
28
  sys.path.append(os.getcwd())
29
 
30
+ # Check for detectron2
31
  try:
32
  from detectron2.engine import DefaultPredictor
33
  from detectron2.config import get_cfg
 
38
  print("Warning: Detectron2 is not installed. Damage detection will not be available.")
39
  DETECTRON2_AVAILABLE = False
40
 
41
+ # Check for custom models
42
  try:
43
  from configs.get_config import load_config
44
  from models import *
 
47
  print("Warning: Custom models couldn't be imported. Only damage detection will work.")
48
  MODELS_IMPORTED = False
49
 
50
+ # Define model paths
51
+ DEFAULT_DAMAGE_MODEL_PATH = "./model_final.pth"
52
+ DEFAULT_DEEPFAKE_MODEL_PATH = "./PoseEfficientNet_custom_laanet_model_final.pth"
53
+ DEFAULT_DEEPFAKE_CFG_PATH = "./configs/detector2.yaml"
54
+
55
+ # Sample images for demo (add your own paths)
56
+ SAMPLE_IMAGES = [
57
+ "./test3.png",
58
+ "./test5.png",
59
+
60
+ ]
61
 
62
  def verify_detectron2_installation():
63
+ """Verify that Detectron2 is properly installed"""
 
 
 
64
  results = {
65
  "detectron2_installed": False,
66
  "model_zoo_accessible": False,
 
68
  "error_messages": []
69
  }
70
 
 
71
  try:
72
+ import importlib.util
73
  if importlib.util.find_spec("detectron2") is not None:
74
  results["detectron2_installed"] = True
 
75
 
 
76
  try:
77
  import detectron2
78
+ from detectron2 import model_zoo
79
+ config_file = "COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml"
80
+ config_path = model_zoo.get_config_file(config_file)
81
+ if os.path.exists(config_path):
82
+ results["model_zoo_accessible"] = True
83
+ except Exception as e:
84
+ results["error_messages"].append(f"Error accessing model zoo: {str(e)}")
85
+
86
+ try:
87
+ from detectron2.config import get_cfg
88
+ cfg = get_cfg()
89
+ results["can_create_cfg"] = True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  except Exception as e:
91
+ results["error_messages"].append(f"Error creating Detectron2 config: {str(e)}")
 
92
  else:
93
  results["error_messages"].append("Detectron2 is not installed")
 
94
  except Exception as e:
95
  results["error_messages"].append(f"Error checking Detectron2 installation: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
 
97
  return results
98
 
99
  def setup_device(device_str):
100
+ """Set up the computation device"""
101
  if device_str == 'auto':
102
  if torch.cuda.is_available():
103
  return torch.device('cuda:0')
 
114
  return torch.device('cpu')
115
 
116
  def setup_damage_detector(model_path, threshold=0.7):
117
+ """Set up the damage detection model"""
118
  if not DETECTRON2_AVAILABLE:
119
  print("Detectron2 is not installed. Cannot set up damage detector.")
120
  return None, None
121
 
122
+ try:
123
+ if model_path is None or not os.path.exists(model_path):
 
 
 
 
 
124
  print(f"Error: Damage model file not found at {model_path}")
125
  return None, None
126
 
 
127
  cfg = get_cfg()
128
+ cfg.merge_from_file(model_zoo.get_config_file("COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml"))
 
 
 
 
 
 
 
 
 
129
  cfg.MODEL.WEIGHTS = model_path
130
  cfg.MODEL.ROI_HEADS.NUM_CLASSES = 1 # Only one class (damage)
131
  cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = threshold
132
 
133
+ # Use CPU if on Mac (MPS)
134
  if hasattr(torch, 'backends') and hasattr(torch.backends, 'mps') and torch.backends.mps.is_available():
135
  cfg.MODEL.DEVICE = "cpu"
136
+
137
+ predictor = DefaultPredictor(cfg)
138
+ return predictor, cfg
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  except Exception as e:
140
+ print(f"Error setting up damage detector: {e}")
141
  traceback.print_exc()
142
  return None, None
143
 
 
148
  return None, None
149
 
150
  if model_path is None or not os.path.exists(model_path):
151
+ print(f"Error: Deepfake model file not found at {model_path}")
152
  return None, None
153
 
154
  if cfg_path is None or not os.path.exists(cfg_path):
155
+ print(f"Error: Deepfake config file not found at {cfg_path}")
156
  return None, None
157
 
158
  try:
 
163
  model = build_model(cfg.MODEL, MODELS)
164
 
165
  # Load weights
 
166
  checkpoint = torch.load(model_path, map_location='cpu')
167
 
168
  if isinstance(checkpoint, dict) and 'state_dict' in checkpoint:
 
185
  def preprocess_for_deepfake(image, cfg, device):
186
  """Preprocess an image for deepfake detection"""
187
  try:
188
+ # Ensure image is RGB
189
  if len(image.shape) == 3 and image.shape[2] == 3:
190
  if image.dtype != np.uint8:
191
  image = (image * 255).astype(np.uint8)
 
193
  else:
194
  rgb_img = image
195
 
196
+ # Resize to match model input size
197
  img_resized = cv2.resize(rgb_img, (cfg.DATASET.IMAGE_SIZE[0], cfg.DATASET.IMAGE_SIZE[1]))
198
 
199
+ # Apply transforms
200
  transform = transforms.Compose([
201
  transforms.ToTensor(),
202
  transforms.Normalize(
 
205
  )
206
  ])
207
 
208
+ img_tensor = transform(Image.fromarray(img_resized)).unsqueeze(0)
209
  img_tensor = img_tensor.to(device)
210
 
211
+ # Convert precision if needed
212
  if hasattr(cfg.MODEL, 'precision') and cfg.MODEL.precision == 'fp64':
213
  img_tensor = img_tensor.to(torch.float64)
214
 
215
  return img_tensor
216
  except Exception as e:
217
+ print(f"Error preprocessing image: {e}")
218
  traceback.print_exc()
219
  return None
220
 
 
224
  if img is None:
225
  raise ValueError("Invalid image")
226
 
227
+ # If no detector, use whole image
228
  if damage_detector is None:
 
229
  h, w = img.shape[:2]
230
  damage_regions = [{
231
  "box": (0, 0, w, h),
 
237
  # Run inference
238
  outputs = damage_detector(img)
239
 
240
+ # Get regions
241
  instances = outputs["instances"].to("cpu")
242
  boxes = instances.pred_boxes.tensor.numpy() if instances.has("pred_boxes") else []
243
  scores = instances.scores.numpy() if instances.has("scores") else []
 
252
  "mask": masks[i] if len(masks) > i else None
253
  })
254
 
255
+ # If no regions found, use whole image
256
  if not damage_regions:
 
257
  h, w = img.shape[:2]
258
  damage_regions = [{
259
  "box": (0, 0, w, h),
 
265
  except Exception as e:
266
  print(f"Error detecting damage: {e}")
267
  traceback.print_exc()
268
+
269
+ # Return whole image if error
270
  if 'img' in locals() and img is not None:
271
  h, w = img.shape[:2]
272
  damage_regions = [{
 
282
  results = []
283
 
284
  if deepfake_model is None:
 
285
  return []
286
 
287
  try:
288
+ # If no damage regions, check entire image
289
  if not damage_regions:
290
  img_tensor = preprocess_for_deepfake(image, deepfake_cfg, device)
291
  if img_tensor is None:
 
303
  cls_outputs = outputs['cls']
304
  cls_prob = cls_outputs.sigmoid().cpu().numpy()
305
  else:
 
306
  cls_prob = outputs.sigmoid().cpu().numpy() if hasattr(outputs, 'sigmoid') else outputs.cpu().numpy()
307
 
308
  if cls_prob.size > 0:
 
320
  # Process each damage region
321
  for i, region in enumerate(damage_regions):
322
  x1, y1, x2, y2 = region["box"]
 
323
  x1, y1 = max(0, x1), max(0, y1)
324
  x2, y2 = min(image.shape[1], x2), min(image.shape[0], y2)
325
 
326
+ # Only process valid regions
327
  if x2 > x1 and y2 > y1:
328
+ # Extract region
329
  roi = image[y1:y2, x1:x2]
330
 
331
  # Preprocess
 
333
  if img_tensor is None:
334
  continue
335
 
336
+ # Inference
337
  with torch.no_grad():
338
  outputs = deepfake_model(img_tensor)
339
 
 
340
  if isinstance(outputs, list):
341
  outputs = outputs[0]
342
 
 
344
  cls_outputs = outputs['cls']
345
  cls_prob = cls_outputs.sigmoid().cpu().numpy()
346
  else:
 
347
  cls_prob = outputs.sigmoid().cpu().numpy() if hasattr(outputs, 'sigmoid') else outputs.cpu().numpy()
348
 
349
  if cls_prob.size > 0:
 
364
  return []
365
 
366
  def visualize_results(image, damage_outputs, deepfake_results, damage_threshold):
367
+ """Create visualization of results"""
368
  try:
 
369
  img_copy = image.copy()
370
 
371
+ # Draw damage detection
372
  if damage_outputs is not None and DETECTRON2_AVAILABLE:
373
  try:
374
  v = Visualizer(img_copy[:, :, ::-1], scale=1.0, instance_mode=ColorMode.IMAGE_BW)
375
  v = v.draw_instance_predictions(damage_outputs["instances"].to("cpu"))
376
  result_img = v.get_image()[:, :, ::-1]
 
 
377
  result_img = np.array(result_img, dtype=np.uint8)
378
  except Exception as e:
379
+ print(f"Error visualizing damage: {e}")
380
  result_img = img_copy
381
  else:
382
  result_img = img_copy
383
 
384
+ # Add deepfake results
385
  for result in deepfake_results:
386
  try:
387
  if "box" in result:
388
  x1, y1, x2, y2 = result["box"]
389
+ fake_prob = result["deepfake_prob"]
390
  is_fake = result["is_fake"]
391
  region_id = result.get("region_id", 0)
392
 
393
+ # Status text
394
  text = f"R{region_id}: {'FAKE' if is_fake else 'REAL'} ({fake_prob*100:.1f}%)"
395
 
396
+ # Red for fake, green for real
397
+ color = (0, 0, 255) if is_fake else (0, 255, 0)
398
 
399
+ # Ensure standard numpy array
400
  if not isinstance(result_img, np.ndarray):
401
  result_img = np.array(result_img, dtype=np.uint8)
402
 
 
407
  fake_prob = result["deepfake_prob"]
408
  is_fake = result["is_fake"]
409
 
 
410
  text = f"Image: {'FAKE' if is_fake else 'REAL'} ({fake_prob*100:.1f}%)"
411
+ color = (0, 0, 255) if is_fake else (0, 255, 0)
412
 
 
 
 
 
413
  if not isinstance(result_img, np.ndarray):
414
  result_img = np.array(result_img, dtype=np.uint8)
415
 
 
416
  cv2.putText(result_img, text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, color, 2)
417
  except Exception as e:
418
+ print(f"Error drawing result: {e}")
419
 
420
  return result_img
421
  except Exception as e:
422
+ print(f"Error in visualization: {e}")
423
  traceback.print_exc()
424
+ return np.array(image, dtype=np.uint8)
425
 
426
  def process_image(input_image, damage_model_path, deepfake_model_path, deepfake_cfg_path,
427
  damage_threshold, deepfake_threshold, skip_damage, device_str):
428
+ """Process an image through the detection pipeline"""
429
  progress_info = []
430
 
431
+ # Debug: log input parameters
432
+ progress_info.append(f"Processing with:")
433
+ progress_info.append(f"- Damage model: {damage_model_path}")
434
+ progress_info.append(f"- Deepfake model: {deepfake_model_path}")
435
+ progress_info.append(f"- Config: {deepfake_cfg_path}")
436
+ progress_info.append(f"- Thresholds: Damage={damage_threshold}, Deepfake={deepfake_threshold}")
 
 
 
437
 
438
+ # Check model files
439
  if not skip_damage and damage_model_path:
440
+ if not os.path.exists(damage_model_path):
441
+ progress_info.append(f"ERROR: Damage model not found at {damage_model_path}")
 
 
442
 
443
+ if deepfake_model_path and not os.path.exists(deepfake_model_path):
444
+ progress_info.append(f"ERROR: Deepfake model not found at {deepfake_model_path}")
 
 
 
445
 
446
+ if deepfake_cfg_path and not os.path.exists(deepfake_cfg_path):
447
+ progress_info.append(f"ERROR: Config not found at {deepfake_cfg_path}")
 
 
 
448
 
449
+ # Convert image to proper format
450
  try:
451
  if isinstance(input_image, dict) and "path" in input_image:
452
  img = cv2.imread(input_image["path"])
453
  elif isinstance(input_image, str):
454
  img = cv2.imread(input_image)
455
  elif isinstance(input_image, np.ndarray):
 
456
  img = input_image.copy()
 
457
  if len(img.shape) == 3 and img.shape[2] == 3:
458
  img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
459
  else:
 
462
  if img is None:
463
  return None, "Error: Could not read the image"
464
  except Exception as e:
465
+ return None, f"Error loading image: {str(e)}"
 
466
 
 
467
  progress_info.append("Image loaded successfully")
468
 
469
  # Setup device
470
+ device = setup_device(device_str)
471
+ progress_info.append(f"Using device: {device}")
 
 
 
 
 
 
472
 
473
  # Initialize models
474
  damage_detector = None
475
  deepfake_model = None
476
  deepfake_cfg = None
477
 
478
+ # Setup damage detector
 
 
 
 
 
 
 
 
 
479
  if not skip_damage and damage_model_path:
480
  progress_info.append("Setting up damage detector...")
481
+ damage_detector, _ = setup_damage_detector(damage_model_path, float(damage_threshold))
482
+ if damage_detector is None:
483
+ progress_info.append("Warning: Failed to initialize damage detector")
484
+ else:
485
+ progress_info.append("Damage detector ready")
 
 
 
 
486
 
487
  # Setup deepfake detector
488
  if deepfake_model_path and deepfake_cfg_path:
489
  progress_info.append("Setting up deepfake detector...")
490
+ deepfake_model, deepfake_cfg = load_deepfake_model(deepfake_model_path, deepfake_cfg_path, device)
491
+ if deepfake_model is None:
492
+ progress_info.append("Warning: Failed to initialize deepfake detector")
493
+ else:
494
+ progress_info.append("Deepfake detector ready")
 
 
 
 
495
 
496
+ # Check if we have at least one working detector
497
  if damage_detector is None and deepfake_model is None:
498
+ return None, "\n".join(progress_info) + "\nError: No working detectors available"
499
 
500
+ # Step 1: Detect damage
501
  progress_info.append("Detecting damage regions...")
502
  start_time = time.time()
503
+ img, damage_outputs, damage_regions = detect_damage(img, damage_detector)
504
+ damage_time = time.time() - start_time
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
505
 
506
+ if damage_regions:
507
+ progress_info.append(f"Found {len(damage_regions)} damage regions in {damage_time:.2f}s")
 
508
  else:
509
+ progress_info.append("No damage regions found, analyzing whole image")
510
 
511
+ # Step 2: Check for deepfakes
512
  deepfake_results = []
513
  if deepfake_model is not None:
514
+ progress_info.append("Checking for deepfakes...")
515
  start_time = time.time()
516
+ deepfake_results = check_deepfake(
517
+ img, damage_regions, deepfake_model, deepfake_cfg, device, float(deepfake_threshold)
518
+ )
519
+ deepfake_time = time.time() - start_time
520
+
521
+ if deepfake_results:
522
+ progress_info.append(f"Deepfake analysis completed in {deepfake_time:.2f}s")
523
 
524
+ # Generate report
525
+ for result in deepfake_results:
526
+ if "region_id" in result:
527
+ region_id = result["region_id"]
528
+ fake_prob = result["deepfake_prob"]
529
+ is_fake = result["is_fake"]
530
+ progress_info.append(f"Region {region_id}: {'⚠️ FAKE' if is_fake else 'βœ… REAL'} ({fake_prob*100:.2f}%)")
531
+ elif "region" in result and result["region"] == "full_image":
532
+ fake_prob = result["deepfake_prob"]
533
+ is_fake = result["is_fake"]
534
+ progress_info.append(f"Whole image: {'⚠️ FAKE' if is_fake else 'βœ… REAL'} ({fake_prob*100:.2f}%)")
535
+ else:
536
+ progress_info.append("No deepfake detection results")
 
 
 
 
 
 
537
 
538
+ # Step 3: Visualize results
539
  progress_info.append("Generating visualization...")
540
+ result_img = visualize_results(img, damage_outputs, deepfake_results, float(damage_threshold))
 
 
 
 
 
 
 
 
 
 
541
 
542
+ # Convert back to RGB for Gradio
543
+ if len(result_img.shape) == 3 and result_img.shape[2] == 3:
544
+ result_img = cv2.cvtColor(result_img, cv2.COLOR_BGR2RGB)
545
+
546
+ progress_info.append("βœ… Analysis complete!")
547
 
548
  return result_img, "\n".join(progress_info)
549
 
550
+ def auto_install_dependencies():
551
+ """Attempt to install dependencies if needed"""
552
+ try:
553
+ import importlib.util
554
+
555
+ # Check for PyTorch
556
+ if importlib.util.find_spec("torch") is None:
557
+ print("Installing PyTorch...")
558
+ os.system("pip install torch torchvision --extra-index-url https://download.pytorch.org/whl/cpu")
559
+
560
+ # Check for Detectron2
561
+ if importlib.util.find_spec("detectron2") is None:
562
+ print("Installing Detectron2...")
563
+ os.system("pip install git+https://github.com/facebookresearch/detectron2.git")
564
+
565
+ # Check for Gradio
566
+ if importlib.util.find_spec("gradio") is None:
567
+ print("Installing Gradio...")
568
+ os.system("pip install gradio")
569
+
570
+ print("Dependencies installation complete!")
571
+ return True
572
+ except Exception as e:
573
+ print(f"Error installing dependencies: {e}")
574
+ return False
575
+
576
  def create_gradio_interface():
577
+ # Define a theme
578
+ theme = gr.themes.Soft(
579
+ primary_hue="blue",
580
+ secondary_hue="orange",
581
+ )
582
+
583
+ with gr.Blocks(title="Car Damage & Deepfake Detector", theme=theme) as app:
584
+ gr.Markdown("""
585
+ # πŸš— Car Damage & Deepfake Detector
586
+
587
+ Upload a car image to:
588
+ 1. Detect damaged areas
589
+ 2. Verify if the damage is real or artificially generated (deepfake)
590
+
591
+ *This app requires both damage detection and deepfake models to be installed.*
592
+ """)
593
+
594
+ # System status indicator
595
+ with gr.Row():
596
+ damage_model_status = gr.Label(label="Damage Model", value="Checking...", elem_id="damage-status")
597
+ deepfake_model_status = gr.Label(label="Deepfake Model", value="Checking...", elem_id="deepfake-status")
598
+ detectron2_status = gr.Label(label="Detectron2", value="Checking...", elem_id="detectron2-status")
599
 
600
+ # Update system status
601
+ def update_system_status():
602
+ status = {
603
+ "damage_model": "Not Found",
604
+ "deepfake_model": "Not Found",
605
+ "deepfake_cfg": "Not Found",
606
+ "detectron2": "Not Installed"
607
+ }
608
+
609
+ # Check model files
610
+ if os.path.exists(DEFAULT_DAMAGE_MODEL_PATH):
611
+ status["damage_model"] = "Available βœ…"
612
+ else:
613
+ status["damage_model"] = "Not Found ❌"
614
+
615
+ if os.path.exists(DEFAULT_DEEPFAKE_MODEL_PATH):
616
+ status["deepfake_model"] = "Available βœ…"
617
+ else:
618
+ status["deepfake_model"] = "Not Found ❌"
619
+
620
+ if os.path.exists(DEFAULT_DEEPFAKE_CFG_PATH):
621
+ status["deepfake_cfg"] = "Available βœ…"
622
+ else:
623
+ status["deepfake_cfg"] = "Not Found ❌"
624
+
625
+ # Check Detectron2
626
+ if DETECTRON2_AVAILABLE:
627
+ status["detectron2"] = "Installed βœ…"
628
+ else:
629
+ status["detectron2"] = "Not Installed ❌"
630
+
631
+ return status["damage_model"], status["deepfake_model"], status["detectron2"]
632
+
633
+ # Main Interface Tab
634
+ with gr.Tab("Analyze Image"):
635
+ with gr.Row():
636
+ with gr.Column(scale=1):
637
+ input_image = gr.Image(type="numpy", label="Upload Car Image")
638
+
639
+ with gr.Row():
640
+ process_btn = gr.Button("Analyze Image", variant="primary")
641
+ clear_btn = gr.Button("Clear", variant="secondary")
642
+
643
+ with gr.Accordion("Advanced Settings", open=False):
644
+ skip_damage = gr.Checkbox(label="Skip Damage Detection", value=False)
645
+ damage_threshold = gr.Slider(minimum=0.1, maximum=1.0, value=0.7, step=0.05,
646
+ label="Damage Detection Threshold")
647
+ deepfake_threshold = gr.Slider(minimum=0.1, maximum=1.0, value=0.5, step=0.05,
648
+ label="Deepfake Detection Threshold")
649
+ device = gr.Dropdown(choices=["auto", "cuda", "cpu", "mps"], value="auto",
650
+ label="Computation Device")
651
+
652
+ with gr.Column(scale=1):
653
+ output_image = gr.Image(type="numpy", label="Analysis Result")
654
+
655
+ # Analysis info with nice formatting
656
+ with gr.Accordion("Analysis Details", open=True):
657
+ output_text = gr.Markdown(label="Detection Results")
658
+
659
+ # Diagnostics Tab
660
+ with gr.Tab("System Diagnostics"):
661
  with gr.Row():
662
+ run_diagnostic_btn = gr.Button("Run Diagnostics", variant="primary")
663
+ install_deps_btn = gr.Button("Install Dependencies", variant="secondary")
664
+
665
+ diagnostic_output = gr.Markdown(label="Diagnostic Results")
666
 
667
+ # Function to run system diagnostics
668
  def run_diagnostics():
669
+ detectron2_results = verify_detectron2_installation()
670
 
671
+ output = ["## System Diagnostics\n"]
 
672
 
673
+ # Python & PyTorch versions
674
+ import sys
675
+ output.append(f"**Python:** {sys.version.split()[0]}")
676
+
677
+ try:
678
+ import torch
679
+ output.append(f"**PyTorch:** {torch.__version__}")
680
+ output.append(f"**CUDA Available:** {'Yes βœ…' if torch.cuda.is_available() else 'No ❌'}")
681
+ if torch.cuda.is_available():
682
+ output.append(f"**GPU:** {torch.cuda.get_device_name(0)}")
683
+ except ImportError:
684
+ output.append("**PyTorch:** Not installed ❌")
685
+
686
+ output.append("\n## Model Files")
687
+
688
+ # Check model files
689
  if os.path.exists(DEFAULT_DAMAGE_MODEL_PATH):
690
  file_size = os.path.getsize(DEFAULT_DAMAGE_MODEL_PATH) / (1024 * 1024) # Size in MB
691
+ output.append(f"**Damage Model:** Available βœ… ({file_size:.2f} MB)")
692
  else:
693
+ output.append(f"**Damage Model:** Not found ❌ at {DEFAULT_DAMAGE_MODEL_PATH}")
694
 
 
695
  if os.path.exists(DEFAULT_DEEPFAKE_MODEL_PATH):
696
  file_size = os.path.getsize(DEFAULT_DEEPFAKE_MODEL_PATH) / (1024 * 1024) # Size in MB
697
+ output.append(f"**Deepfake Model:** Available βœ… ({file_size:.2f} MB)")
698
  else:
699
+ output.append(f"**Deepfake Model:** Not found ❌ at {DEFAULT_DEEPFAKE_MODEL_PATH}")
700
 
 
701
  if os.path.exists(DEFAULT_DEEPFAKE_CFG_PATH):
702
+ output.append(f"**Deepfake Config:** Available βœ…")
703
  else:
704
+ output.append(f"**Deepfake Config:** Not found ❌ at {DEFAULT_DEEPFAKE_CFG_PATH}")
705
+
706
+ output.append("\n## Detectron2 Status")
707
+ output.append(f"**Installed:** {'Yes βœ…' if detectron2_results['detectron2_installed'] else 'No ❌'}")
708
+ output.append(f"**Model Zoo Access:** {'Yes βœ…' if detectron2_results['model_zoo_accessible'] else 'No ❌'}")
709
+ output.append(f"**Config Creation:** {'Yes βœ…' if detectron2_results['can_create_cfg'] else 'No ❌'}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
710
 
711
+ if detectron2_results['error_messages']:
712
+ output.append("\n## Error Messages")
713
+ for error in detectron2_results['error_messages']:
714
+ output.append(f"- {error}")
715
 
716
+ output.append("\n## Recommendations")
717
+ recommendations = []
718
+
719
+ if not detectron2_results['detectron2_installed']:
720
+ recommendations.append("Install Detectron2: `pip install torch torchvision --extra-index-url https://download.pytorch.org/whl/cpu`")
721
+ recommendations.append("Then: `pip install git+https://github.com/facebookresearch/detectron2.git`")
722
 
723
  if not os.path.exists(DEFAULT_DAMAGE_MODEL_PATH):
724
+ recommendations.append(f"Place the damage model file at: {DEFAULT_DAMAGE_MODEL_PATH}")
725
+
726
+ if not os.path.exists(DEFAULT_DEEPFAKE_MODEL_PATH):
727
+ recommendations.append(f"Place the deepfake model file at: {DEFAULT_DEEPFAKE_MODEL_PATH}")
728
+
729
+ if not os.path.exists(DEFAULT_DEEPFAKE_CFG_PATH):
730
+ recommendations.append(f"Place the deepfake config file at: {DEFAULT_DEEPFAKE_CFG_PATH}")
731
 
732
+ if recommendations:
733
+ for rec in recommendations:
734
+ output.append(f"- {rec}")
735
+ else:
736
+ output.append("- All systems are ready! πŸŽ‰")
737
 
738
+ return "\n".join(output)
739
 
740
+ # Function to install dependencies
741
+ def install_dependencies():
742
+ output = ["## Installing Dependencies\n"]
743
+
744
+ # Try to install PyTorch
745
+ output.append("Installing PyTorch...")
746
+ try:
747
+ import importlib.util
748
+ if importlib.util.find_spec("torch") is None:
749
+ os.system("pip install torch torchvision --extra-index-url https://download.pytorch.org/whl/cpu")
750
+ output.append("βœ… PyTorch installed")
751
+ else:
752
+ output.append("βœ… PyTorch already installed")
753
+ except Exception as e:
754
+ output.append(f"❌ Error installing PyTorch: {str(e)}")
755
+
756
+ # Try to install Detectron2
757
+ output.append("\nInstalling Detectron2...")
758
+ try:
759
+ if importlib.util.find_spec("detectron2") is None:
760
+ os.system("pip install git+https://github.com/facebookresearch/detectron2.git")
761
+ output.append("βœ… Detectron2 installed")
762
+ else:
763
+ output.append("βœ… Detectron2 already installed")
764
+ except Exception as e:
765
+ output.append(f"❌ Error installing Detectron2: {str(e)}")
766
+
767
+ output.append("\n**Note:** You may need to restart the application for changes to take effect.")
768
+
769
+ return "\n".join(output)
770
+
771
+ # Settings Tab
772
+ with gr.Tab("Settings"):
773
+ gr.Markdown("## Model Settings")
774
 
 
775
  with gr.Row():
776
  with gr.Column():
777
+ custom_damage_model = gr.Textbox(
778
+ label="Custom Damage Model Path",
779
+ value=DEFAULT_DAMAGE_MODEL_PATH,
780
+ placeholder="Path to damage detection model (.pth)"
781
+ )
 
 
 
 
 
 
 
 
782
 
783
+ custom_deepfake_model = gr.Textbox(
784
+ label="Custom Deepfake Model Path",
785
+ value=DEFAULT_DEEPFAKE_MODEL_PATH,
786
+ placeholder="Path to deepfake detection model (.pth)"
787
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
788
 
789
+ custom_deepfake_cfg = gr.Textbox(
790
+ label="Custom Deepfake Config Path",
791
+ value=DEFAULT_DEEPFAKE_CFG_PATH,
792
+ placeholder="Path to deepfake model config (.yaml)"
 
793
  )
794
+
795
+ check_paths_btn = gr.Button("Verify Paths", variant="primary")
796
+
797
+ with gr.Column():
798
+ paths_result = gr.Markdown(label="Path Verification Results")
799
+
800
+ # Function to check model paths
801
+ def check_model_paths(damage_path, deepfake_path, cfg_path):
802
+ output = ["## Path Verification Results\n"]
803
+
804
+ # Check damage model
805
+ if os.path.exists(damage_path):
806
+ file_size = os.path.getsize(damage_path) / (1024 * 1024) # Size in MB
807
+ output.append(f"βœ… **Damage model:** Found at {damage_path} ({file_size:.2f} MB)")
808
+ else:
809
+ output.append(f"❌ **Damage model:** NOT found at {damage_path}")
810
+
811
+ # Check deepfake model
812
+ if os.path.exists(deepfake_path):
813
+ file_size = os.path.getsize(deepfake_path) / (1024 * 1024) # Size in MB
814
+ output.append(f"βœ… **Deepfake model:** Found at {deepfake_path} ({file_size:.2f} MB)")
815
+ else:
816
+ output.append(f"❌ **Deepfake model:** NOT found at {deepfake_path}")
817
+
818
+ # Check deepfake config
819
+ if os.path.exists(cfg_path):
820
+ output.append(f"βœ… **Deepfake config:** Found at {cfg_path}")
821
+ else:
822
+ output.append(f"❌ **Deepfake config:** NOT found at {cfg_path}")
823
+
824
+ return "\n".join(output)
825
 
826
+ # Help Tab
827
+ with gr.Tab("Help"):
828
+ gr.Markdown("""
829
+ ## πŸ“‹ How to Use This Tool
830
+
831
+ ### Basic Usage
832
+ 1. Upload a car image in the "Analyze Image" tab
833
+ 2. Click "Analyze Image" to process it
834
+ 3. View the results - damaged areas will be highlighted and classified as real or fake
835
+
836
+ ### Understanding Results
837
+ - **Green boxes/text:** Real damage
838
+ - **Red boxes/text:** Potential deepfake damage
839
+ - Percentage values show the confidence score
840
+
841
+ ### Requirements
842
+ - Damage detection model file (Detectron2 Mask R-CNN)
843
+ - Deepfake detection model file
844
+ - Deepfake model configuration file
845
+
846
+ ### Troubleshooting
847
+ - If models aren't loading, check the "System Diagnostics" tab
848
+ - Ensure all model files are in the expected locations
849
+ - Try installing dependencies from the Diagnostics tab
850
+
851
+ ### Advanced Settings
852
+ - **Damage Threshold:** Higher values mean only high-confidence damage regions are shown
853
+ - **Deepfake Threshold:** Higher values make the system more selective in flagging fakes
854
+ - **Skip Damage Detection:** Analyze the entire image for deepfakes without damage detection
855
+ """)
856
+
857
+ # Examples
858
+ if any(os.path.exists(img) for img in SAMPLE_IMAGES):
859
+ gr.Markdown("## Example Images")
860
+ with gr.Row():
861
+ example_inputs = [img for img in SAMPLE_IMAGES if os.path.exists(img)]
862
+ gr.Examples(
863
+ examples=example_inputs,
864
+ inputs=input_image,
865
+ outputs=[output_image, output_text],
866
+ fn=lambda x: process_image(
867
+ x, DEFAULT_DAMAGE_MODEL_PATH, DEFAULT_DEEPFAKE_MODEL_PATH,
868
+ DEFAULT_DEEPFAKE_CFG_PATH, 0.7, 0.5, False, "auto"
869
+ ),
870
+ cache_examples=True
871
+ )
872
+
873
+ # Connect functions to the UI
874
  process_btn.click(
875
  fn=process_image,
876
  inputs=[
877
  input_image,
878
+ custom_damage_model,
879
+ custom_deepfake_model,
880
+ custom_deepfake_cfg,
881
  damage_threshold,
882
  deepfake_threshold,
883
  skip_damage,
 
886
  outputs=[output_image, output_text]
887
  )
888
 
889
+ # Clear button functionality
890
+ clear_btn.click(
891
+ fn=lambda: [None, ""],
892
+ inputs=[],
893
+ outputs=[output_image, output_text]
894
+ )
895
+
896
+ # System diagnostic buttons
897
+ run_diagnostic_btn.click(
898
+ fn=run_diagnostics,
899
+ inputs=[],
900
+ outputs=diagnostic_output
901
+ )
902
+
903
+ install_deps_btn.click(
904
+ fn=install_dependencies,
905
+ inputs=[],
906
+ outputs=diagnostic_output
907
+ )
908
+
909
+ # Settings tab
910
+ check_paths_btn.click(
911
+ fn=check_model_paths,
912
+ inputs=[custom_damage_model, custom_deepfake_model, custom_deepfake_cfg],
913
+ outputs=paths_result
914
+ )
915
+
916
+ # Update system status on load
917
+ app.load(
918
+ fn=update_system_status,
919
+ inputs=[],
920
+ outputs=[damage_model_status, deepfake_model_status, detectron2_status]
921
+ )
922
+
923
  return app
924
 
 
925
  if __name__ == "__main__":
926
+ # Try to auto-install dependencies on startup
927
+ auto_install_dependencies()
928
+
929
+ # Create and launch the Gradio app
930
  app = create_gradio_interface()
931
+ app.launch(share=False) # Set share=True to create a public link