seifbenayed commited on
Commit
79a26c5
Β·
1 Parent(s): 1b403e5
Files changed (1) hide show
  1. app.py +439 -131
app.py CHANGED
@@ -3,32 +3,20 @@ import importlib.util
3
  import os
4
  import sys
5
  import cv2
6
-
7
- # Check if detectron2 is installed
8
- if importlib.util.find_spec("detectron2") is None:
9
- print("Installing PyTorch and Detectron2...")
10
- os.system("pip install torch torchvision --extra-index-url https://download.pytorch.org/whl/cpu")
11
- os.system("pip install git+https://github.com/facebookresearch/detectron2.git")
12
-
13
- print("Installation complete!")
14
-
15
- # -*- coding: utf-8 -*-
16
-
17
-
18
- #!/usr/bin/env python3
19
- # -*- coding: utf-8 -*-
20
- import os
21
- import sys
22
  import time
23
  import torch
24
  import numpy as np
25
  import gradio as gr
26
  from PIL import Image
27
  from torchvision import transforms
 
28
 
29
- DEFAULT_DAMAGE_MODEL_PATH = "./model_final.pth" # Replace with your actual path
30
- DEFAULT_DEEPFAKE_MODEL_PATH = "./PoseEfficientNet_custom_laanet_model_final.pth" # Replace with your actual path
31
- DEFAULT_DEEPFAKE_CFG_PATH = "./configs/detector2.yaml" # Replace with your actual path
 
 
 
32
 
33
  # Add current directory to path
34
  if not os.getcwd() in sys.path:
@@ -54,6 +42,103 @@ except ImportError:
54
  print("Warning: Custom models couldn't be imported. Only damage detection will work.")
55
  MODELS_IMPORTED = False
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  def setup_device(device_str):
58
  """Set up the computation device based on user input and availability"""
59
  if device_str == 'auto':
@@ -72,32 +157,62 @@ def setup_device(device_str):
72
  return torch.device('cpu')
73
 
74
  def setup_damage_detector(model_path, threshold=0.7):
75
- """Set up the damage detection model using Detectron2"""
76
  if not DETECTRON2_AVAILABLE:
77
  print("Detectron2 is not installed. Cannot set up damage detector.")
78
  return None, None
 
 
 
 
 
 
 
 
 
 
79
 
80
- if model_path is None or not os.path.exists(model_path):
81
- print("No damage model specified or file not found. Skipping damage detection.")
82
- return None, None
 
 
 
 
 
 
 
83
 
84
- cfg = get_cfg()
85
- cfg.merge_from_file(model_zoo.get_config_file("COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml"))
86
- cfg.MODEL.WEIGHTS = model_path
87
- cfg.MODEL.ROI_HEADS.NUM_CLASSES = 1 # Only one class (damage)
88
- cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = threshold
89
-
90
- # Explicitly set to use CPU if on Mac (MPS)
91
- if torch.backends.mps.is_available():
92
- cfg.MODEL.DEVICE = "cpu"
93
- print("Mac MPS detected - forcing Detectron2 to use CPU")
94
-
95
- try:
96
- predictor = DefaultPredictor(cfg)
97
- return predictor, cfg
 
 
 
 
 
 
 
 
 
 
 
 
98
  except Exception as e:
99
- print(f"Error setting up damage detector: {e}")
100
- return None, cfg
 
101
 
102
  def load_deepfake_model(model_path, cfg_path, device):
103
  """Load the deepfake detection model"""
@@ -138,7 +253,6 @@ def load_deepfake_model(model_path, cfg_path, device):
138
  return model, cfg
139
  except Exception as e:
140
  print(f"Error loading deepfake model: {e}")
141
- import traceback
142
  traceback.print_exc()
143
  return None, None
144
 
@@ -175,7 +289,6 @@ def preprocess_for_deepfake(image, cfg, device):
175
  return img_tensor
176
  except Exception as e:
177
  print(f"Error preprocessing image for deepfake detection: {e}")
178
- import traceback
179
  traceback.print_exc()
180
  return None
181
 
@@ -226,6 +339,7 @@ def detect_damage(img, damage_detector):
226
  return img, outputs, damage_regions
227
  except Exception as e:
228
  print(f"Error detecting damage: {e}")
 
229
  # If error occurs, return the whole image as region
230
  if 'img' in locals() and img is not None:
231
  h, w = img.shape[:2]
@@ -325,7 +439,6 @@ def check_deepfake(image, damage_regions, deepfake_model, deepfake_cfg, device,
325
  return results
326
  except Exception as e:
327
  print(f"Error in deepfake detection: {e}")
328
- import traceback
329
  traceback.print_exc()
330
  return []
331
 
@@ -394,74 +507,141 @@ def visualize_results(image, damage_outputs, deepfake_results, damage_threshold)
394
  return result_img
395
  except Exception as e:
396
  print(f"Error visualizing results: {e}")
397
- import traceback
398
  traceback.print_exc()
399
  return np.array(image, dtype=np.uint8) # Return the original image as a numpy array
400
 
401
  def process_image(input_image, damage_model_path, deepfake_model_path, deepfake_cfg_path,
402
  damage_threshold, deepfake_threshold, skip_damage, device_str):
403
- """Process an image through the car damage and deepfake detection pipeline"""
404
  progress_info = []
405
 
406
- # Convert Gradio image to numpy array
407
- if isinstance(input_image, dict) and "path" in input_image:
408
- img = cv2.imread(input_image["path"])
409
- elif isinstance(input_image, str):
410
- img = cv2.imread(input_image)
411
- elif isinstance(input_image, np.ndarray):
412
- # Make a copy to avoid modifying the original
413
- img = input_image.copy()
414
- # Convert from RGB to BGR (OpenCV format)
415
- if len(img.shape) == 3 and img.shape[2] == 3:
416
- img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
417
- else:
418
- return None, "Error: Unsupported image format"
419
 
420
- if img is None:
421
- return None, "Error: Could not read the image"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
422
 
423
  # Progress update
424
  progress_info.append("Image loaded successfully")
425
 
426
  # Setup device
427
- device = setup_device(device_str)
428
- progress_info.append(f"Using device: {device}")
 
 
 
 
 
 
429
 
430
  # Initialize models
431
  damage_detector = None
432
  deepfake_model = None
433
  deepfake_cfg = None
434
 
 
 
 
 
 
 
 
 
 
435
  # Setup damage detector if not skipped
436
  if not skip_damage and damage_model_path:
437
  progress_info.append("Setting up damage detector...")
438
- damage_detector, detector_cfg = setup_damage_detector(damage_model_path, float(damage_threshold))
439
- if damage_detector is None and DETECTRON2_AVAILABLE:
440
- progress_info.append("Failed to initialize damage detector")
441
- else:
442
- progress_info.append("Damage detector initialized successfully")
 
 
 
 
443
 
444
  # Setup deepfake detector
445
  if deepfake_model_path and deepfake_cfg_path:
446
  progress_info.append("Setting up deepfake detector...")
447
- deepfake_model, deepfake_cfg = load_deepfake_model(deepfake_model_path, deepfake_cfg_path, device)
448
- if deepfake_model is None:
449
- progress_info.append("Failed to initialize deepfake detector")
450
- else:
451
- progress_info.append("Deepfake detector initialized successfully")
 
 
 
 
452
 
453
  # Ensure at least one detector is working
454
  if damage_detector is None and deepfake_model is None:
455
- return None, "Error: Neither damage nor deepfake detector is available"
456
 
457
  # Step 1: Detect damage or use whole image
458
  progress_info.append("Detecting damage regions...")
459
  start_time = time.time()
460
- img, damage_outputs, damage_regions = detect_damage(img, damage_detector)
461
- damage_time = time.time() - start_time
 
 
 
 
 
 
 
 
 
 
 
 
462
 
463
  if img is None:
464
- return None, "Error: Failed to process image"
465
 
466
  # Print damage detection results
467
  if damage_detector is not None and damage_regions:
@@ -474,48 +654,154 @@ def process_image(input_image, damage_model_path, deepfake_model_path, deepfake_
474
  if deepfake_model is not None:
475
  progress_info.append("Performing deepfake detection...")
476
  start_time = time.time()
477
- deepfake_results = check_deepfake(
478
- img, damage_regions, deepfake_model, deepfake_cfg, device, float(deepfake_threshold)
479
- )
480
- deepfake_time = time.time() - start_time
481
-
482
- if deepfake_results:
483
- progress_info.append(f"Deepfake detection completed in {deepfake_time:.3f} seconds")
484
 
485
- # Generate report
486
- for result in deepfake_results:
487
- if "region_id" in result:
488
- region_id = result["region_id"]
489
- fake_prob = result["deepfake_prob"]
490
- is_fake = result["is_fake"]
491
- progress_info.append(f"Region {region_id}: {'FAKE' if is_fake else 'REAL'} (Probability: {fake_prob*100:.2f}%)")
492
- elif "region" in result and result["region"] == "full_image":
493
- fake_prob = result["deepfake_prob"]
494
- is_fake = result["is_fake"]
495
- progress_info.append(f"Whole image: {'FAKE' if is_fake else 'REAL'} (Probability: {fake_prob*100:.2f}%)")
496
- else:
497
- progress_info.append("No deepfake detection results")
 
 
 
 
 
 
498
 
499
  # Step 3: Visualize final results
500
  progress_info.append("Generating visualization...")
501
- result_img = visualize_results(img, damage_outputs, deepfake_results, float(damage_threshold))
502
-
503
- # Convert back to RGB for Gradio
504
- if len(result_img.shape) == 3 and result_img.shape[2] == 3:
505
- result_img = cv2.cvtColor(result_img, cv2.COLOR_BGR2RGB)
 
 
 
 
 
 
506
 
507
  progress_info.append("Processing complete!")
508
 
509
  return result_img, "\n".join(progress_info)
510
 
511
-
512
-
513
-
514
  def create_gradio_interface():
515
  with gr.Blocks(title="Car Damage & Deepfake Detection") as app:
516
  gr.Markdown("# Car Damage Detection & Deepfake Verification")
517
  gr.Markdown("Upload an image to detect car damage and check if it's a deepfake")
518
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
519
  with gr.Tab("Basic Interface"):
520
  with gr.Row():
521
  with gr.Column(scale=1):
@@ -539,31 +825,56 @@ def create_gradio_interface():
539
  with gr.Tab("Advanced Settings"):
540
  with gr.Row():
541
  with gr.Column():
542
- # Create empty textboxes - values will be provided by the wrapper function
543
- damage_model_path = gr.Textbox(label="Damage Model Path (optional)",
544
- placeholder="Leave blank to use default model")
545
- deepfake_model_path = gr.Textbox(label="Deepfake Model Path (optional)",
546
- placeholder="Leave blank to use default model")
547
- deepfake_cfg_path = gr.Textbox(label="Deepfake Config Path (optional)",
548
- placeholder="Leave blank to use default config")
549
-
550
- # Create a wrapper function that applies default values when fields are empty
551
- def process_with_defaults(input_image, damage_path, deepfake_path, cfg_path,
552
- damage_threshold, deepfake_threshold, skip_damage, device):
553
- # Use default paths if none provided
554
- actual_damage_path = damage_path if damage_path.strip() else DEFAULT_DAMAGE_MODEL_PATH
555
- actual_deepfake_path = deepfake_path if deepfake_path.strip() else DEFAULT_DEEPFAKE_MODEL_PATH
556
- actual_cfg_path = cfg_path if cfg_path.strip() else DEFAULT_DEEPFAKE_CFG_PATH
557
-
558
- # Call the original process function with the actual paths
559
- return process_image(
560
- input_image, actual_damage_path, actual_deepfake_path, actual_cfg_path,
561
- damage_threshold, deepfake_threshold, skip_damage, device
562
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
563
 
564
- # Connect the wrapper function
565
  process_btn.click(
566
- fn=process_with_defaults,
567
  inputs=[
568
  input_image,
569
  damage_model_path,
@@ -579,12 +890,9 @@ def create_gradio_interface():
579
 
580
  # Examples
581
  gr.Markdown("## Examples")
582
- gr.Markdown("Note: Examples will use the default models if you don't specify custom paths.")
583
 
584
- return app
585
-
586
-
587
- # Create and launch the app
588
 
589
  # For local testing and Hugging Face Spaces, with debugging enabled
590
  if __name__ == "__main__":
 
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:
16
+ print("Installing PyTorch and Detectron2...")
17
+ os.system("pip install torch torchvision --extra-index-url https://download.pytorch.org/whl/cpu")
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:
 
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,
58
+ "can_create_cfg": False,
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':
 
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
 
217
  def load_deepfake_model(model_path, cfg_path, device):
218
  """Load the deepfake detection model"""
 
253
  return model, cfg
254
  except Exception as e:
255
  print(f"Error loading deepfake model: {e}")
 
256
  traceback.print_exc()
257
  return None, None
258
 
 
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
 
 
339
  return img, outputs, damage_regions
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]
 
439
  return results
440
  except Exception as e:
441
  print(f"Error in deepfake detection: {e}")
 
442
  traceback.print_exc()
443
  return []
444
 
 
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:
560
+ return None, "Error: Unsupported image format"
561
+
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:
 
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):
 
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,
 
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__":