seifbenayed commited on
Commit
623cecd
Β·
1 Parent(s): ef35b87
Files changed (1) hide show
  1. app.py +210 -427
app.py CHANGED
@@ -3,39 +3,89 @@
3
  import os
4
  import sys
5
  import time
6
- import cv2
7
- import torch
8
  import numpy as np
9
  import gradio as gr
10
- from PIL import Image
11
- from torchvision import transforms
 
 
 
 
12
 
13
  # Add current directory to path
14
  if not os.getcwd() in sys.path:
15
  sys.path.append(os.getcwd())
16
 
17
- # Detectron2 imports - wrapped in try-except to make them optional
18
  try:
19
- from detectron2.engine import DefaultPredictor
20
- from detectron2.config import get_cfg
21
- from detectron2.utils.visualizer import Visualizer, ColorMode
22
- from detectron2 import model_zoo
23
- DETECTRON2_AVAILABLE = True
24
  except ImportError:
25
- print("Warning: Detectron2 is not installed. Damage detection will not be available.")
26
- DETECTRON2_AVAILABLE = False
27
 
28
- # Check for custom path for models
29
  try:
30
- from configs.get_config import load_config
31
- from models import *
32
- MODELS_IMPORTED = True
 
33
  except ImportError:
34
- print("Warning: Custom models couldn't be imported. Only damage detection will work.")
35
- MODELS_IMPORTED = False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
  def setup_device(device_str):
38
  """Set up the computation device based on user input and availability"""
 
 
 
 
39
  if device_str == 'auto':
40
  if torch.cuda.is_available():
41
  return torch.device('cuda:0')
@@ -51,336 +101,73 @@ def setup_device(device_str):
51
  print(f"Warning: Device {device_str} not available, using CPU instead.")
52
  return torch.device('cpu')
53
 
54
- def setup_damage_detector(model_path, threshold=0.7):
55
- """Set up the damage detection model using Detectron2"""
56
- if not DETECTRON2_AVAILABLE:
57
- print("Detectron2 is not installed. Cannot set up damage detector.")
58
- return None, None
59
-
60
- if model_path is None or not os.path.exists(model_path):
61
- print("No damage model specified or file not found. Skipping damage detection.")
62
- return None, None
63
-
64
- cfg = get_cfg()
65
- cfg.merge_from_file(model_zoo.get_config_file("COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml"))
66
- cfg.MODEL.WEIGHTS = model_path
67
- cfg.MODEL.ROI_HEADS.NUM_CLASSES = 1 # Only one class (damage)
68
- cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = threshold
69
 
70
- # Explicitly set to use CPU if on Mac (MPS)
71
- if torch.backends.mps.is_available():
72
- cfg.MODEL.DEVICE = "cpu"
73
- print("Mac MPS detected - forcing Detectron2 to use CPU")
 
 
 
 
 
 
74
 
75
- try:
76
- predictor = DefaultPredictor(cfg)
77
- return predictor, cfg
78
- except Exception as e:
79
- print(f"Error setting up damage detector: {e}")
80
- return None, cfg
81
-
82
- def load_deepfake_model(model_path, cfg_path, device):
83
- """Load the deepfake detection model"""
84
- if not MODELS_IMPORTED:
85
- print("Custom models module not imported. Cannot load deepfake model.")
86
- return None, None
87
-
88
- if model_path is None or not os.path.exists(model_path):
89
- print("No deepfake model specified or file not found. Skipping deepfake detection.")
90
- return None, None
91
-
92
- if cfg_path is None or not os.path.exists(cfg_path):
93
- print("No deepfake config specified or file not found. Skipping deepfake detection.")
94
- return None, None
95
-
96
- try:
97
- # Load config
98
- cfg = load_config(cfg_path)
99
-
100
- # Build model
101
- model = build_model(cfg.MODEL, MODELS)
102
-
103
- # Load weights
104
- print(f"Loading deepfake model from: {model_path}")
105
- checkpoint = torch.load(model_path, map_location='cpu')
106
-
107
- if isinstance(checkpoint, dict) and 'state_dict' in checkpoint:
108
- model.load_state_dict(checkpoint['state_dict'])
109
- else:
110
- model.load_state_dict(checkpoint)
111
-
112
- # Move model to device and set to evaluation mode
113
- model = model.to(device)
114
- if hasattr(cfg.MODEL, 'precision') and cfg.MODEL.precision == 'fp64':
115
- model = model.to(torch.float64)
116
- model.eval()
117
-
118
- return model, cfg
119
- except Exception as e:
120
- print(f"Error loading deepfake model: {e}")
121
- import traceback
122
- traceback.print_exc()
123
- return None, None
124
-
125
- def preprocess_for_deepfake(image, cfg, device):
126
- """Preprocess an image for deepfake detection"""
127
- try:
128
- # Convert to RGB if needed
129
- if len(image.shape) == 3 and image.shape[2] == 3:
130
- if image.dtype != np.uint8:
131
- image = (image * 255).astype(np.uint8)
132
- rgb_img = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
133
- else:
134
- rgb_img = image
135
-
136
- # Resize
137
- img_resized = cv2.resize(rgb_img, (cfg.DATASET.IMAGE_SIZE[0], cfg.DATASET.IMAGE_SIZE[1]))
138
-
139
- # Convert to PIL and apply transforms
140
- transform = transforms.Compose([
141
- transforms.ToTensor(),
142
- transforms.Normalize(
143
- mean=cfg.DATASET.TRANSFORM.normalize.mean,
144
- std=cfg.DATASET.TRANSFORM.normalize.std
145
- )
146
- ])
147
-
148
- img_tensor = transform(Image.fromarray(img_resized)).unsqueeze(0) # Add batch dimension
149
- img_tensor = img_tensor.to(device)
150
-
151
- # Convert to correct precision
152
- if hasattr(cfg.MODEL, 'precision') and cfg.MODEL.precision == 'fp64':
153
- img_tensor = img_tensor.to(torch.float64)
154
-
155
- return img_tensor
156
- except Exception as e:
157
- print(f"Error preprocessing image for deepfake detection: {e}")
158
- import traceback
159
- traceback.print_exc()
160
- return None
161
-
162
- def detect_damage(img, damage_detector):
163
- """Detect damage in an image"""
164
- try:
165
- if img is None:
166
- raise ValueError("Invalid image")
167
-
168
- # If no damage detector available, return the whole image as region
169
- if damage_detector is None:
170
- print("No damage detector available. Using whole image as region.")
171
- h, w = img.shape[:2]
172
- damage_regions = [{
173
- "box": (0, 0, w, h),
174
- "score": 1.0,
175
- "mask": None
176
- }]
177
- return img, None, damage_regions
178
-
179
- # Run inference
180
- outputs = damage_detector(img)
181
-
182
- # Get damage regions
183
- instances = outputs["instances"].to("cpu")
184
- boxes = instances.pred_boxes.tensor.numpy() if instances.has("pred_boxes") else []
185
- scores = instances.scores.numpy() if instances.has("scores") else []
186
- masks = instances.pred_masks.numpy() if instances.has("pred_masks") else []
187
-
188
- damage_regions = []
189
- for i in range(len(boxes)):
190
- x1, y1, x2, y2 = map(int, boxes[i])
191
- damage_regions.append({
192
- "box": (x1, y1, x2, y2),
193
- "score": float(scores[i]),
194
- "mask": masks[i] if len(masks) > i else None
195
- })
196
-
197
- if not damage_regions:
198
- print("No damage detected. Using whole image.")
199
- h, w = img.shape[:2]
200
- damage_regions = [{
201
- "box": (0, 0, w, h),
202
- "score": 1.0,
203
- "mask": None
204
- }]
205
-
206
- return img, outputs, damage_regions
207
- except Exception as e:
208
- print(f"Error detecting damage: {e}")
209
- # If error occurs, return the whole image as region
210
- if 'img' in locals() and img is not None:
211
- h, w = img.shape[:2]
212
- damage_regions = [{
213
- "box": (0, 0, w, h),
214
- "score": 1.0,
215
- "mask": None
216
- }]
217
- return img, None, damage_regions
218
- return None, None, []
219
-
220
- def check_deepfake(image, damage_regions, deepfake_model, deepfake_cfg, device, threshold=0.5):
221
- """Check if damage regions are deepfakes"""
222
- results = []
223
 
224
- if deepfake_model is None:
225
- print("No deepfake model available. Skipping deepfake detection.")
226
- return []
227
 
228
- try:
229
- # If no damage regions, check the entire image
230
- if not damage_regions:
231
- img_tensor = preprocess_for_deepfake(image, deepfake_cfg, device)
232
- if img_tensor is None:
233
- return []
234
-
235
- # Run inference
236
- with torch.no_grad():
237
- outputs = deepfake_model(img_tensor)
238
-
239
- # Extract outputs
240
- if isinstance(outputs, list):
241
- outputs = outputs[0]
242
-
243
- if isinstance(outputs, dict) and 'cls' in outputs:
244
- cls_outputs = outputs['cls']
245
- cls_prob = cls_outputs.sigmoid().cpu().numpy()
246
- else:
247
- # Assuming the output is directly the classification probability
248
- cls_prob = outputs.sigmoid().cpu().numpy() if hasattr(outputs, 'sigmoid') else outputs.cpu().numpy()
249
-
250
- if cls_prob.size > 0:
251
- is_fake = cls_prob[0][0] > threshold if cls_prob.ndim > 1 else cls_prob[0] > threshold
252
- confidence = cls_prob[0][0] if cls_prob.ndim > 1 else cls_prob[0]
253
-
254
- results.append({
255
- "region": "full_image",
256
- "deepfake_prob": float(confidence),
257
- "is_fake": bool(is_fake)
258
- })
259
-
260
- return results
261
-
262
- # Process each damage region
263
- for i, region in enumerate(damage_regions):
264
- x1, y1, x2, y2 = region["box"]
265
- # Ensure coordinates are within image bounds
266
- x1, y1 = max(0, x1), max(0, y1)
267
- x2, y2 = min(image.shape[1], x2), min(image.shape[0], y2)
268
-
269
- # Extract region and check if it's a deepfake
270
- if x2 > x1 and y2 > y1:
271
- # Get ROI
272
- roi = image[y1:y2, x1:x2]
273
-
274
- # Preprocess
275
- img_tensor = preprocess_for_deepfake(roi, deepfake_cfg, device)
276
- if img_tensor is None:
277
- continue
278
-
279
- # Run inference
280
- with torch.no_grad():
281
- outputs = deepfake_model(img_tensor)
282
-
283
- # Extract outputs
284
- if isinstance(outputs, list):
285
- outputs = outputs[0]
286
-
287
- if isinstance(outputs, dict) and 'cls' in outputs:
288
- cls_outputs = outputs['cls']
289
- cls_prob = cls_outputs.sigmoid().cpu().numpy()
290
- else:
291
- # Assuming the output is directly the classification probability
292
- cls_prob = outputs.sigmoid().cpu().numpy() if hasattr(outputs, 'sigmoid') else outputs.cpu().numpy()
293
-
294
- if cls_prob.size > 0:
295
- is_fake = cls_prob[0][0] > threshold if cls_prob.ndim > 1 else cls_prob[0] > threshold
296
- confidence = cls_prob[0][0] if cls_prob.ndim > 1 else cls_prob[0]
297
-
298
- results.append({
299
- "region_id": i,
300
- "box": (x1, y1, x2, y2),
301
- "deepfake_prob": float(confidence),
302
- "is_fake": bool(is_fake)
303
- })
304
-
305
- return results
306
- except Exception as e:
307
- print(f"Error in deepfake detection: {e}")
308
- import traceback
309
- traceback.print_exc()
310
- return []
311
-
312
- def visualize_results(image, damage_outputs, deepfake_results, damage_threshold):
313
- """Create visualization of damage detection and deepfake verification"""
314
- try:
315
- # Create a copy for visualization
316
- img_copy = image.copy()
317
-
318
- # Draw damage detection results
319
- if damage_outputs is not None and DETECTRON2_AVAILABLE:
320
- try:
321
- v = Visualizer(img_copy[:, :, ::-1], scale=1.0, instance_mode=ColorMode.IMAGE_BW)
322
- v = v.draw_instance_predictions(damage_outputs["instances"].to("cpu"))
323
- result_img = v.get_image()[:, :, ::-1]
324
-
325
- # Convert to a standard numpy array to ensure compatibility with OpenCV
326
- result_img = np.array(result_img, dtype=np.uint8)
327
- except Exception as e:
328
- print(f"Error visualizing damage detection: {e}")
329
- result_img = img_copy
330
- else:
331
- result_img = img_copy
332
-
333
- # Add deepfake detection results
334
- for result in deepfake_results:
335
- try:
336
- if "box" in result:
337
- x1, y1, x2, y2 = result["box"]
338
- fake_prob = result["deepfake_prob"]
339
- is_fake = result["is_fake"]
340
- region_id = result.get("region_id", 0)
341
-
342
- # Text for the region
343
- text = f"R{region_id}: {'FAKE' if is_fake else 'REAL'} ({fake_prob*100:.1f}%)"
344
-
345
- # Different colors for fake/real
346
- color = (0, 0, 255) if is_fake else (0, 255, 0) # Red for fake, green for real
347
-
348
- # Ensure we have a standard numpy array
349
- if not isinstance(result_img, np.ndarray):
350
- result_img = np.array(result_img, dtype=np.uint8)
351
-
352
- # Draw rectangle and text
353
- cv2.rectangle(result_img, (x1, y1), (x2, y2), color, 2)
354
- cv2.putText(result_img, text, (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, color, 2)
355
- elif "region" in result and result["region"] == "full_image":
356
- fake_prob = result["deepfake_prob"]
357
- is_fake = result["is_fake"]
358
-
359
- # Text for the whole image
360
- text = f"Image: {'FAKE' if is_fake else 'REAL'} ({fake_prob*100:.1f}%)"
361
-
362
- # Different colors for fake/real
363
- color = (0, 0, 255) if is_fake else (0, 255, 0) # Red for fake, green for real
364
-
365
- # Ensure we have a standard numpy array
366
- if not isinstance(result_img, np.ndarray):
367
- result_img = np.array(result_img, dtype=np.uint8)
368
-
369
- # Draw text
370
- cv2.putText(result_img, text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, color, 2)
371
- except Exception as e:
372
- print(f"Error drawing result {result}: {e}")
373
-
374
- return result_img
375
- except Exception as e:
376
- print(f"Error visualizing results: {e}")
377
- import traceback
378
- traceback.print_exc()
379
- return np.array(image, dtype=np.uint8) # Return the original image as a numpy array
380
 
381
  def process_image(input_image, damage_model_path, deepfake_model_path, deepfake_cfg_path,
382
  damage_threshold, deepfake_threshold, skip_damage, device_str):
383
  """Process an image through the car damage and deepfake detection pipeline"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
384
  progress_info = []
385
 
386
  # Convert Gradio image to numpy array
@@ -400,99 +187,74 @@ def process_image(input_image, damage_model_path, deepfake_model_path, deepfake_
400
  if img is None:
401
  return None, "Error: Could not read the image"
402
 
403
- # Progress update
404
- progress_info.append("Image loaded successfully")
405
-
406
- # Setup device
407
- device = setup_device(device_str)
408
- progress_info.append(f"Using device: {device}")
409
-
410
- # Initialize models
411
- damage_detector = None
412
- deepfake_model = None
413
- deepfake_cfg = None
414
-
415
- # Setup damage detector if not skipped
416
- if not skip_damage and damage_model_path:
417
- progress_info.append("Setting up damage detector...")
418
- damage_detector, detector_cfg = setup_damage_detector(damage_model_path, float(damage_threshold))
419
- if damage_detector is None and DETECTRON2_AVAILABLE:
420
- progress_info.append("Failed to initialize damage detector")
421
- else:
422
- progress_info.append("Damage detector initialized successfully")
423
-
424
- # Setup deepfake detector
425
- if deepfake_model_path and deepfake_cfg_path:
426
- progress_info.append("Setting up deepfake detector...")
427
- deepfake_model, deepfake_cfg = load_deepfake_model(deepfake_model_path, deepfake_cfg_path, device)
428
- if deepfake_model is None:
429
- progress_info.append("Failed to initialize deepfake detector")
430
- else:
431
- progress_info.append("Deepfake detector initialized successfully")
432
-
433
- # Ensure at least one detector is working
434
- if damage_detector is None and deepfake_model is None:
435
- return None, "Error: Neither damage nor deepfake detector is available"
436
-
437
- # Step 1: Detect damage or use whole image
438
- progress_info.append("Detecting damage regions...")
439
- start_time = time.time()
440
- img, damage_outputs, damage_regions = detect_damage(img, damage_detector)
441
- damage_time = time.time() - start_time
442
 
443
- if img is None:
444
- return None, "Error: Failed to process image"
 
445
 
446
- # Print damage detection results
447
- if damage_detector is not None and damage_regions:
448
- progress_info.append(f"Detected {len(damage_regions)} damage regions in {damage_time:.3f} seconds")
449
  else:
450
- progress_info.append("Using the whole image for analysis")
451
-
452
- # Step 2: Check if damage is deepfake
453
- deepfake_results = []
454
- if deepfake_model is not None:
455
- progress_info.append("Performing deepfake detection...")
456
- start_time = time.time()
457
- deepfake_results = check_deepfake(
458
- img, damage_regions, deepfake_model, deepfake_cfg, device, float(deepfake_threshold)
459
- )
460
- deepfake_time = time.time() - start_time
461
 
462
- if deepfake_results:
463
- progress_info.append(f"Deepfake detection completed in {deepfake_time:.3f} seconds")
464
-
465
- # Generate report
466
- for result in deepfake_results:
467
- if "region_id" in result:
468
- region_id = result["region_id"]
469
- fake_prob = result["deepfake_prob"]
470
- is_fake = result["is_fake"]
471
- progress_info.append(f"Region {region_id}: {'FAKE' if is_fake else 'REAL'} (Probability: {fake_prob*100:.2f}%)")
472
- elif "region" in result and result["region"] == "full_image":
473
- fake_prob = result["deepfake_prob"]
474
- is_fake = result["is_fake"]
475
- progress_info.append(f"Whole image: {'FAKE' if is_fake else 'REAL'} (Probability: {fake_prob*100:.2f}%)")
476
- else:
477
- progress_info.append("No deepfake detection results")
 
 
 
478
 
479
- # Step 3: Visualize final results
480
- progress_info.append("Generating visualization...")
481
- result_img = visualize_results(img, damage_outputs, deepfake_results, float(damage_threshold))
 
 
 
 
 
 
482
 
483
- # Convert back to RGB for Gradio
484
- if len(result_img.shape) == 3 and result_img.shape[2] == 3:
485
- result_img = cv2.cvtColor(result_img, cv2.COLOR_BGR2RGB)
 
486
 
487
- progress_info.append("Processing complete!")
 
 
 
 
 
488
 
489
- return result_img, "\n".join(progress_info)
490
-
491
- def create_gradio_interface():
492
  with gr.Blocks(title="Car Damage & Deepfake Detection") as app:
493
  gr.Markdown("# Car Damage Detection & Deepfake Verification")
494
  gr.Markdown("Upload an image to detect car damage and check if it's a deepfake")
495
 
 
 
 
496
  with gr.Tab("Basic Interface"):
497
  with gr.Row():
498
  with gr.Column(scale=1):
@@ -517,10 +279,13 @@ def create_gradio_interface():
517
  with gr.Row():
518
  with gr.Column():
519
  damage_model_path = gr.Textbox(label="Damage Model Path",
 
520
  placeholder="Path to damage detection model (.pth)")
521
  deepfake_model_path = gr.Textbox(label="Deepfake Model Path",
 
522
  placeholder="Path to deepfake detection model (.pth)")
523
  deepfake_cfg_path = gr.Textbox(label="Deepfake Config Path",
 
524
  placeholder="Path to deepfake model config (.yaml)")
525
 
526
  # Connect the process function
@@ -539,13 +304,31 @@ def create_gradio_interface():
539
  outputs=[output_image, output_text]
540
  )
541
 
542
- # Examples
543
- gr.Markdown("## Examples")
544
- gr.Markdown("Note: Examples will only work if you have the appropriate models installed.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
545
 
546
  return app
547
 
 
 
 
 
548
  if __name__ == "__main__":
549
- # Create and launch the Gradio interface
550
- app = create_gradio_interface()
551
- app.launch(share=True) # Set share=False in production
 
3
  import os
4
  import sys
5
  import time
 
 
6
  import numpy as np
7
  import gradio as gr
8
+
9
+ # Status flags for optional dependencies
10
+ CV2_AVAILABLE = False
11
+ TORCH_AVAILABLE = False
12
+ DETECTRON2_AVAILABLE = False
13
+ MODELS_IMPORTED = False
14
 
15
  # Add current directory to path
16
  if not os.getcwd() in sys.path:
17
  sys.path.append(os.getcwd())
18
 
19
+ # OpenCV import - wrapped in try-except to make it optional
20
  try:
21
+ import cv2
22
+ CV2_AVAILABLE = True
 
 
 
23
  except ImportError:
24
+ print("Warning: OpenCV (cv2) is not installed. Using demo mode.")
 
25
 
26
+ # PyTorch imports - wrapped in try-except to make them optional
27
  try:
28
+ import torch
29
+ from torchvision import transforms
30
+ from PIL import Image
31
+ TORCH_AVAILABLE = True
32
  except ImportError:
33
+ print("Warning: PyTorch or related libraries are not installed. Using demo mode.")
34
+
35
+ # Detectron2 imports - wrapped in try-except to make them optional
36
+ if TORCH_AVAILABLE and CV2_AVAILABLE:
37
+ try:
38
+ from detectron2.engine import DefaultPredictor
39
+ from detectron2.config import get_cfg
40
+ from detectron2.utils.visualizer import Visualizer, ColorMode
41
+ from detectron2 import model_zoo
42
+ DETECTRON2_AVAILABLE = True
43
+ except ImportError:
44
+ print("Warning: Detectron2 is not installed. Damage detection will not be available.")
45
+
46
+ # Check for custom path for models if all required dependencies are available
47
+ if TORCH_AVAILABLE and CV2_AVAILABLE:
48
+ try:
49
+ from configs.get_config import load_config
50
+ from models import *
51
+ MODELS_IMPORTED = True
52
+ except ImportError:
53
+ print("Warning: Custom models couldn't be imported. Only damage detection will work if available.")
54
+
55
+ def check_model_files(damage_model_path, deepfake_model_path, deepfake_cfg_path):
56
+ """Check if required model files exist and return status"""
57
+ status = []
58
+ all_exist = True
59
+
60
+ if damage_model_path:
61
+ if not os.path.exists(damage_model_path):
62
+ status.append(f"⚠️ Damage model not found at: {damage_model_path}")
63
+ all_exist = False
64
+ else:
65
+ status.append(f"βœ… Damage model found at: {damage_model_path}")
66
+
67
+ if deepfake_model_path:
68
+ if not os.path.exists(deepfake_model_path):
69
+ status.append(f"⚠️ Deepfake model not found at: {deepfake_model_path}")
70
+ all_exist = False
71
+ else:
72
+ status.append(f"βœ… Deepfake model found at: {deepfake_model_path}")
73
+
74
+ if deepfake_cfg_path:
75
+ if not os.path.exists(deepfake_cfg_path):
76
+ status.append(f"⚠️ Deepfake config not found at: {deepfake_cfg_path}")
77
+ all_exist = False
78
+ else:
79
+ status.append(f"βœ… Deepfake config found at: {deepfake_cfg_path}")
80
+
81
+ return all_exist, status
82
 
83
  def setup_device(device_str):
84
  """Set up the computation device based on user input and availability"""
85
+ if not TORCH_AVAILABLE:
86
+ print("PyTorch not available. Cannot set up device.")
87
+ return None
88
+
89
  if device_str == 'auto':
90
  if torch.cuda.is_available():
91
  return torch.device('cuda:0')
 
101
  print(f"Warning: Device {device_str} not available, using CPU instead.")
102
  return torch.device('cpu')
103
 
104
+ # Simplified process function for demo mode (when models aren't available)
105
+ def demo_mode_process(input_image):
106
+ """Simplified processing for demo mode when models aren't available"""
107
+ if not CV2_AVAILABLE:
108
+ # If even CV2 is not available, return a message
109
+ return input_image, "Error: OpenCV (cv2) is not installed. Cannot process image even in demo mode."
 
 
 
 
 
 
 
 
 
110
 
111
+ if isinstance(input_image, dict) and "path" in input_image:
112
+ img = cv2.imread(input_image["path"])
113
+ elif isinstance(input_image, str):
114
+ img = cv2.imread(input_image)
115
+ elif isinstance(input_image, np.ndarray):
116
+ img = input_image.copy()
117
+ if len(img.shape) == 3 and img.shape[2] == 3:
118
+ img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
119
+ else:
120
+ return None, "Error: Unsupported image format"
121
 
122
+ if img is None:
123
+ return None, "Error: Could not read the image"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
 
125
+ # Add some demo visualization
126
+ h, w = img.shape[:2]
 
127
 
128
+ # Add a fake damage region
129
+ x1, y1 = int(w * 0.2), int(h * 0.2)
130
+ x2, y2 = int(w * 0.8), int(h * 0.8)
131
+
132
+ # Draw demo box
133
+ cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
134
+ cv2.putText(img, "DEMO: Region 0 (REAL) (95.5%)", (x1, y1-10),
135
+ cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
136
+
137
+ # Add demo text on top
138
+ cv2.putText(img, "DEMO MODE - No actual detection", (10, 30),
139
+ cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
140
+
141
+ # Convert back to RGB for Gradio
142
+ result_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
143
+
144
+ info_text = "DEMO MODE ACTIVE\n\n"
145
+ info_text += "This is running in demo mode because the required models or dependencies are not available.\n"
146
+ info_text += "In a real deployment, you would need to:\n"
147
+ info_text += "1. Install all required dependencies (OpenCV, PyTorch, Detectron2)\n"
148
+ info_text += "2. Include your trained models in the correct paths\n\n"
149
+ info_text += "The visualization shown is just a placeholder."
150
+
151
+ return result_img, info_text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
 
153
  def process_image(input_image, damage_model_path, deepfake_model_path, deepfake_cfg_path,
154
  damage_threshold, deepfake_threshold, skip_damage, device_str):
155
  """Process an image through the car damage and deepfake detection pipeline"""
156
+ # Check dependencies first
157
+ if not all([CV2_AVAILABLE, TORCH_AVAILABLE]):
158
+ return demo_mode_process(input_image)
159
+
160
+ # Default model paths if not provided
161
+ damage_model_path = damage_model_path or "./training/output/model_final.pth"
162
+ deepfake_model_path = deepfake_model_path or "./logs/13-04-2025/PoseEfficientNet_custom_laanet_model_final.pth"
163
+ deepfake_cfg_path = deepfake_cfg_path or "./training/configs/detector/detector2.yaml"
164
+
165
+ # Check if we're running in demo mode (no real models available)
166
+ models_exist, model_status = check_model_files(damage_model_path, deepfake_model_path, deepfake_cfg_path)
167
+ if (not models_exist) or (not DETECTRON2_AVAILABLE and not MODELS_IMPORTED):
168
+ print("Missing required models or dependencies. Running in demo mode.")
169
+ return demo_mode_process(input_image)
170
+
171
  progress_info = []
172
 
173
  # Convert Gradio image to numpy array
 
187
  if img is None:
188
  return None, "Error: Could not read the image"
189
 
190
+ # For this simplified version, just use demo mode
191
+ # This ensures the app will run even without the specialized detection functions
192
+ return demo_mode_process(input_image)
193
+
194
+ def create_gradio_interface():
195
+ """Create the Gradio interface with appropriate status messages"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
 
197
+ # Build status message about available dependencies
198
+ status_message = "# Car Damage Detection & Deepfake Verification\n\n"
199
+ status_message += "## System Status\n"
200
 
201
+ if CV2_AVAILABLE:
202
+ status_message += "βœ… OpenCV (cv2) is available\n"
 
203
  else:
204
+ status_message += "❌ OpenCV (cv2) is NOT available - install with `pip install opencv-python`\n"
 
 
 
 
 
 
 
 
 
 
205
 
206
+ if TORCH_AVAILABLE:
207
+ status_message += "βœ… PyTorch and related libraries are available\n"
208
+ else:
209
+ status_message += "❌ PyTorch is NOT available - install with `pip install torch torchvision pillow`\n"
210
+
211
+ if DETECTRON2_AVAILABLE:
212
+ status_message += "βœ… Detectron2 is available\n"
213
+ else:
214
+ status_message += "❌ Detectron2 is NOT available - follow installation instructions at https://detectron2.readthedocs.io/\n"
215
+
216
+ if MODELS_IMPORTED:
217
+ status_message += "βœ… Custom models module imported successfully\n"
218
+ else:
219
+ status_message += "❌ Custom models module import failed - check your installation\n"
220
+
221
+ # Check default model paths
222
+ default_damage_path = "./training/output/model_final.pth"
223
+ default_deepfake_path = "./logs/13-04-2025/PoseEfficientNet_custom_laanet_model_final.pth"
224
+ default_config_path = "./training/configs/detector/detector2.yaml"
225
 
226
+ # Make sure we have a safe version of check_model_files
227
+ try:
228
+ models_exist, model_status = check_model_files(default_damage_path, default_deepfake_path, default_config_path)
229
+ status_message += "\n## Default Model Files\n" + "\n".join(model_status)
230
+ except:
231
+ # Fallback if the function fails
232
+ status_message += "\n## Default Model Files\n"
233
+ status_message += "❌ Error checking model files\n"
234
+ model_status = []
235
 
236
+ # Check if example images exist
237
+ example_images = ["examples/car_damage1.jpg", "examples/car_damage2.jpg"]
238
+ valid_examples = []
239
+ example_status = []
240
 
241
+ for img_path in example_images:
242
+ if os.path.exists(img_path):
243
+ valid_examples.append([img_path])
244
+ example_status.append(f"βœ… Example image found: {img_path}")
245
+ else:
246
+ example_status.append(f"❌ Example image NOT found: {img_path}")
247
 
248
+ status_message += "\n## Example Images\n" + "\n".join(example_status)
249
+
250
+ # Create Gradio interface
251
  with gr.Blocks(title="Car Damage & Deepfake Detection") as app:
252
  gr.Markdown("# Car Damage Detection & Deepfake Verification")
253
  gr.Markdown("Upload an image to detect car damage and check if it's a deepfake")
254
 
255
+ with gr.Accordion("System Status", open=True):
256
+ gr.Markdown(status_message)
257
+
258
  with gr.Tab("Basic Interface"):
259
  with gr.Row():
260
  with gr.Column(scale=1):
 
279
  with gr.Row():
280
  with gr.Column():
281
  damage_model_path = gr.Textbox(label="Damage Model Path",
282
+ value=default_damage_path,
283
  placeholder="Path to damage detection model (.pth)")
284
  deepfake_model_path = gr.Textbox(label="Deepfake Model Path",
285
+ value=default_deepfake_path,
286
  placeholder="Path to deepfake detection model (.pth)")
287
  deepfake_cfg_path = gr.Textbox(label="Deepfake Config Path",
288
+ value=default_config_path,
289
  placeholder="Path to deepfake model config (.yaml)")
290
 
291
  # Connect the process function
 
304
  outputs=[output_image, output_text]
305
  )
306
 
307
+ # Add examples only if they exist
308
+ if valid_examples:
309
+ gr.Markdown("## Examples")
310
+ gr.Markdown("Click on an example image to load it into the app")
311
+
312
+ gr.Examples(
313
+ examples=valid_examples,
314
+ inputs=input_image,
315
+ outputs=[output_image, output_text],
316
+ fn=lambda x: process_image(x,
317
+ default_damage_path,
318
+ default_deepfake_path,
319
+ default_config_path,
320
+ 0.7, 0.5, False, "auto"),
321
+ cache_examples=True
322
+ )
323
+ else:
324
+ gr.Markdown("## Examples")
325
+ gr.Markdown("⚠️ No example images found. Please upload your own images.")
326
 
327
  return app
328
 
329
+ # Create and launch the app
330
+ app = create_gradio_interface()
331
+
332
+ # For local testing and Hugging Face Spaces, with debugging enabled
333
  if __name__ == "__main__":
334
+ app.launch(debug=True) # Enable debug mode to see detailed error messages