Spaces:
Runtime error
Runtime error
Commit
Β·
817736f
1
Parent(s):
8ecb82d
V0.1
Browse files- PoseEfficientNet_custom_laanet_model_final.pth +0 -3
- app.py +108 -179
PoseEfficientNet_custom_laanet_model_final.pth
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:2d7cc177dd24ae429b74a651d38754ba05eb6552cc02cfe84ed0be305dc297ac
|
| 3 |
-
size 217985170
|
|
|
|
|
|
|
|
|
|
|
|
app.py
CHANGED
|
@@ -2,18 +2,6 @@
|
|
| 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:
|
| 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 |
-
print("Installation complete!")
|
| 13 |
-
|
| 14 |
-
#!/usr/bin/env python3
|
| 15 |
-
import os
|
| 16 |
-
import sys
|
| 17 |
import time
|
| 18 |
import cv2
|
| 19 |
import torch
|
|
@@ -21,14 +9,21 @@ import numpy as np
|
|
| 21 |
import gradio as gr
|
| 22 |
from PIL import Image
|
| 23 |
from torchvision import transforms
|
|
|
|
|
|
|
| 24 |
import traceback
|
| 25 |
-
from configs.get_config import load_config
|
| 26 |
-
from models import *
|
| 27 |
|
| 28 |
# Add current directory to path
|
| 29 |
if not os.getcwd() in sys.path:
|
| 30 |
sys.path.append(os.getcwd())
|
| 31 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
# Check for detectron2
|
| 33 |
try:
|
| 34 |
from detectron2.engine import DefaultPredictor
|
|
@@ -40,24 +35,14 @@ except ImportError:
|
|
| 40 |
print("Warning: Detectron2 is not installed. Damage detection will not be available.")
|
| 41 |
DETECTRON2_AVAILABLE = False
|
| 42 |
|
| 43 |
-
# Check for custom models
|
| 44 |
-
try:
|
| 45 |
-
|
| 46 |
-
MODELS_IMPORTED = True
|
| 47 |
-
except ImportError:
|
| 48 |
-
print("Warning: Custom models couldn't be imported. Only damage detection will work.")
|
| 49 |
-
MODELS_IMPORTED = False
|
| 50 |
-
|
| 51 |
# Define model paths
|
| 52 |
DEFAULT_DAMAGE_MODEL_PATH = "./model_final.pth"
|
| 53 |
-
DEFAULT_DEEPFAKE_MODEL_PATH = "./
|
| 54 |
-
DEFAULT_DEEPFAKE_CFG_PATH = "./configs/detector2.yaml"
|
| 55 |
|
| 56 |
# Sample images for demo (add your own paths)
|
| 57 |
SAMPLE_IMAGES = [
|
| 58 |
"./test3.png",
|
| 59 |
"./test5.png",
|
| 60 |
-
|
| 61 |
]
|
| 62 |
|
| 63 |
def verify_detectron2_installation():
|
|
@@ -146,49 +131,43 @@ def setup_damage_detector(model_path, threshold=0.7):
|
|
| 146 |
traceback.print_exc()
|
| 147 |
return None, None
|
| 148 |
|
| 149 |
-
def
|
| 150 |
-
"""Load the deepfake detection
|
| 151 |
-
if not MODELS_IMPORTED:
|
| 152 |
-
print("Custom models module not imported. Cannot load deepfake model.")
|
| 153 |
-
return None, None
|
| 154 |
-
|
| 155 |
if model_path is None or not os.path.exists(model_path):
|
| 156 |
-
print(f"Error:
|
| 157 |
-
return None
|
| 158 |
-
|
| 159 |
-
if cfg_path is None or not os.path.exists(cfg_path):
|
| 160 |
-
print(f"Error: Deepfake config file not found at {cfg_path}")
|
| 161 |
-
return None, None
|
| 162 |
|
| 163 |
try:
|
| 164 |
-
#
|
| 165 |
-
|
| 166 |
|
| 167 |
-
#
|
| 168 |
-
|
|
|
|
| 169 |
|
| 170 |
# Load weights
|
|
|
|
| 171 |
checkpoint = torch.load(model_path, map_location='cpu')
|
| 172 |
|
| 173 |
-
if isinstance(checkpoint, dict) and '
|
|
|
|
|
|
|
| 174 |
model.load_state_dict(checkpoint['state_dict'])
|
| 175 |
else:
|
| 176 |
model.load_state_dict(checkpoint)
|
| 177 |
|
| 178 |
# Move model to device and set to evaluation mode
|
| 179 |
model = model.to(device)
|
| 180 |
-
if hasattr(cfg.MODEL, 'precision') and cfg.MODEL.precision == 'fp64':
|
| 181 |
-
model = model.to(torch.float64)
|
| 182 |
model.eval()
|
| 183 |
|
| 184 |
-
return model
|
| 185 |
except Exception as e:
|
| 186 |
-
print(f"Error loading deepfake model: {e}")
|
| 187 |
traceback.print_exc()
|
| 188 |
-
return None
|
| 189 |
|
| 190 |
-
def
|
| 191 |
-
"""Preprocess an image for deepfake detection"""
|
| 192 |
try:
|
| 193 |
# Ensure image is RGB
|
| 194 |
if len(image.shape) == 3 and image.shape[2] == 3:
|
|
@@ -198,28 +177,21 @@ def preprocess_for_deepfake(image, cfg, device):
|
|
| 198 |
else:
|
| 199 |
rgb_img = image
|
| 200 |
|
| 201 |
-
# Resize to
|
| 202 |
-
img_resized = cv2.resize(rgb_img, (
|
| 203 |
|
| 204 |
# Apply transforms
|
| 205 |
transform = transforms.Compose([
|
| 206 |
transforms.ToTensor(),
|
| 207 |
-
transforms.Normalize(
|
| 208 |
-
mean=cfg.DATASET.TRANSFORM.normalize.mean,
|
| 209 |
-
std=cfg.DATASET.TRANSFORM.normalize.std
|
| 210 |
-
)
|
| 211 |
])
|
| 212 |
|
| 213 |
img_tensor = transform(Image.fromarray(img_resized)).unsqueeze(0)
|
| 214 |
img_tensor = img_tensor.to(device)
|
| 215 |
|
| 216 |
-
# Convert precision if needed
|
| 217 |
-
if hasattr(cfg.MODEL, 'precision') and cfg.MODEL.precision == 'fp64':
|
| 218 |
-
img_tensor = img_tensor.to(torch.float64)
|
| 219 |
-
|
| 220 |
return img_tensor
|
| 221 |
except Exception as e:
|
| 222 |
-
print(f"Error preprocessing image: {e}")
|
| 223 |
traceback.print_exc()
|
| 224 |
return None
|
| 225 |
|
|
@@ -282,8 +254,8 @@ def detect_damage(img, damage_detector):
|
|
| 282 |
return img, None, damage_regions
|
| 283 |
return None, None, []
|
| 284 |
|
| 285 |
-
def
|
| 286 |
-
"""Check if damage regions are deepfakes"""
|
| 287 |
results = []
|
| 288 |
|
| 289 |
if deepfake_model is None:
|
|
@@ -292,7 +264,7 @@ def check_deepfake(image, damage_regions, deepfake_model, deepfake_cfg, device,
|
|
| 292 |
try:
|
| 293 |
# If no damage regions, check entire image
|
| 294 |
if not damage_regions:
|
| 295 |
-
img_tensor =
|
| 296 |
if img_tensor is None:
|
| 297 |
return []
|
| 298 |
|
|
@@ -300,25 +272,16 @@ def check_deepfake(image, damage_regions, deepfake_model, deepfake_cfg, device,
|
|
| 300 |
with torch.no_grad():
|
| 301 |
outputs = deepfake_model(img_tensor)
|
| 302 |
|
| 303 |
-
#
|
| 304 |
-
|
| 305 |
-
|
|
|
|
| 306 |
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
if cls_prob.size > 0:
|
| 314 |
-
is_fake = cls_prob[0][0] > threshold if cls_prob.ndim > 1 else cls_prob[0] > threshold
|
| 315 |
-
confidence = cls_prob[0][0] if cls_prob.ndim > 1 else cls_prob[0]
|
| 316 |
-
|
| 317 |
-
results.append({
|
| 318 |
-
"region": "full_image",
|
| 319 |
-
"deepfake_prob": float(confidence),
|
| 320 |
-
"is_fake": bool(is_fake)
|
| 321 |
-
})
|
| 322 |
|
| 323 |
return results
|
| 324 |
|
|
@@ -334,7 +297,7 @@ def check_deepfake(image, damage_regions, deepfake_model, deepfake_cfg, device,
|
|
| 334 |
roi = image[y1:y2, x1:x2]
|
| 335 |
|
| 336 |
# Preprocess
|
| 337 |
-
img_tensor =
|
| 338 |
if img_tensor is None:
|
| 339 |
continue
|
| 340 |
|
|
@@ -342,25 +305,17 @@ def check_deepfake(image, damage_regions, deepfake_model, deepfake_cfg, device,
|
|
| 342 |
with torch.no_grad():
|
| 343 |
outputs = deepfake_model(img_tensor)
|
| 344 |
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
|
| 348 |
-
|
| 349 |
-
cls_outputs = outputs['cls']
|
| 350 |
-
cls_prob = cls_outputs.sigmoid().cpu().numpy()
|
| 351 |
-
else:
|
| 352 |
-
cls_prob = outputs.sigmoid().cpu().numpy() if hasattr(outputs, 'sigmoid') else outputs.cpu().numpy()
|
| 353 |
|
| 354 |
-
|
| 355 |
-
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
"box": (x1, y1, x2, y2),
|
| 361 |
-
"deepfake_prob": float(confidence),
|
| 362 |
-
"is_fake": bool(is_fake)
|
| 363 |
-
})
|
| 364 |
|
| 365 |
return results
|
| 366 |
except Exception as e:
|
|
@@ -428,23 +383,18 @@ def visualize_results(image, damage_outputs, deepfake_results, damage_threshold)
|
|
| 428 |
traceback.print_exc()
|
| 429 |
return np.array(image, dtype=np.uint8)
|
| 430 |
|
| 431 |
-
def process_image(input_image, damage_model_path, deepfake_model_path,
|
| 432 |
-
|
| 433 |
"""Process an image through the detection pipeline"""
|
| 434 |
progress_info = []
|
| 435 |
|
| 436 |
-
# Debug: log input parameters
|
| 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:
|
| 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:
|
|
@@ -464,40 +414,55 @@ def process_image(input_image, damage_model_path, deepfake_model_path, deepfake_
|
|
| 464 |
except Exception as e:
|
| 465 |
return None, f"Error loading image: {str(e)}"
|
| 466 |
|
| 467 |
-
|
| 468 |
# Setup device
|
| 469 |
device = setup_device(device_str)
|
|
|
|
| 470 |
|
| 471 |
# Initialize models
|
| 472 |
damage_detector = None
|
| 473 |
deepfake_model = None
|
| 474 |
-
deepfake_cfg = None
|
| 475 |
|
| 476 |
# Setup damage detector
|
| 477 |
if not skip_damage and damage_model_path:
|
|
|
|
| 478 |
damage_detector, _ = setup_damage_detector(damage_model_path, float(damage_threshold))
|
| 479 |
-
|
| 480 |
-
|
| 481 |
-
|
| 482 |
-
|
| 483 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 484 |
|
| 485 |
# Step 1: Detect damage
|
|
|
|
| 486 |
start_time = time.time()
|
| 487 |
img, damage_outputs, damage_regions = detect_damage(img, damage_detector)
|
| 488 |
damage_time = time.time() - start_time
|
| 489 |
|
| 490 |
-
|
| 491 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 492 |
deepfake_results = []
|
| 493 |
if deepfake_model is not None:
|
|
|
|
| 494 |
start_time = time.time()
|
| 495 |
-
deepfake_results =
|
| 496 |
-
img, damage_regions, deepfake_model,
|
| 497 |
)
|
| 498 |
deepfake_time = time.time() - start_time
|
| 499 |
|
| 500 |
if deepfake_results:
|
|
|
|
| 501 |
|
| 502 |
# Generate report
|
| 503 |
for result in deepfake_results:
|
|
@@ -505,17 +470,21 @@ def process_image(input_image, damage_model_path, deepfake_model_path, deepfake_
|
|
| 505 |
region_id = result["region_id"]
|
| 506 |
fake_prob = result["deepfake_prob"]
|
| 507 |
is_fake = result["is_fake"]
|
|
|
|
| 508 |
elif "region" in result and result["region"] == "full_image":
|
| 509 |
fake_prob = result["deepfake_prob"]
|
| 510 |
is_fake = result["is_fake"]
|
|
|
|
| 511 |
else:
|
| 512 |
progress_info.append("No deepfake detection results")
|
| 513 |
|
|
|
|
| 514 |
fake_regions = [r for r in deepfake_results if r.get("is_fake", False)]
|
| 515 |
if fake_regions:
|
| 516 |
progress_info.append("\nπ¨ VERDICT: This image contains FAKE damage π¨")
|
| 517 |
else:
|
| 518 |
progress_info.append("\nβ
VERDICT: All damage appears REAL")
|
|
|
|
| 519 |
# Step 3: Visualize results
|
| 520 |
result_img = visualize_results(img, damage_outputs, deepfake_results, float(damage_threshold))
|
| 521 |
|
|
@@ -523,7 +492,6 @@ def process_image(input_image, damage_model_path, deepfake_model_path, deepfake_
|
|
| 523 |
if len(result_img.shape) == 3 and result_img.shape[2] == 3:
|
| 524 |
result_img = cv2.cvtColor(result_img, cv2.COLOR_BGR2RGB)
|
| 525 |
|
| 526 |
-
|
| 527 |
return result_img, "\n".join(progress_info)
|
| 528 |
|
| 529 |
def auto_install_dependencies():
|
|
@@ -561,13 +529,13 @@ def create_gradio_interface():
|
|
| 561 |
|
| 562 |
with gr.Blocks(title="Car Damage & Deepfake Detector", theme=theme) as app:
|
| 563 |
gr.Markdown("""
|
| 564 |
-
# π Car Damage
|
| 565 |
|
| 566 |
Upload a car image to:
|
| 567 |
1. Detect damaged areas
|
| 568 |
2. Verify if the damage is real or artificially generated (deepfake)
|
| 569 |
|
| 570 |
-
*This app
|
| 571 |
""")
|
| 572 |
|
| 573 |
# Main Interface Tab
|
|
@@ -620,6 +588,7 @@ def create_gradio_interface():
|
|
| 620 |
output.append(f"**CUDA Available:** {'Yes β
' if torch.cuda.is_available() else 'No β'}")
|
| 621 |
if torch.cuda.is_available():
|
| 622 |
output.append(f"**GPU:** {torch.cuda.get_device_name(0)}")
|
|
|
|
| 623 |
except ImportError:
|
| 624 |
output.append("**PyTorch:** Not installed β")
|
| 625 |
|
|
@@ -634,14 +603,9 @@ def create_gradio_interface():
|
|
| 634 |
|
| 635 |
if os.path.exists(DEFAULT_DEEPFAKE_MODEL_PATH):
|
| 636 |
file_size = os.path.getsize(DEFAULT_DEEPFAKE_MODEL_PATH) / (1024 * 1024) # Size in MB
|
| 637 |
-
output.append(f"**Deepfake Model:** Available β
({file_size:.2f} MB)")
|
| 638 |
else:
|
| 639 |
-
output.append(f"**Deepfake Model:** Not found β at {DEFAULT_DEEPFAKE_MODEL_PATH}")
|
| 640 |
-
|
| 641 |
-
if os.path.exists(DEFAULT_DEEPFAKE_CFG_PATH):
|
| 642 |
-
output.append(f"**Deepfake Config:** Available β
")
|
| 643 |
-
else:
|
| 644 |
-
output.append(f"**Deepfake Config:** Not found β at {DEFAULT_DEEPFAKE_CFG_PATH}")
|
| 645 |
|
| 646 |
output.append("\n## Detectron2 Status")
|
| 647 |
output.append(f"**Installed:** {'Yes β
' if detectron2_results['detectron2_installed'] else 'No β'}")
|
|
@@ -664,10 +628,7 @@ def create_gradio_interface():
|
|
| 664 |
recommendations.append(f"Place the damage model file at: {DEFAULT_DAMAGE_MODEL_PATH}")
|
| 665 |
|
| 666 |
if not os.path.exists(DEFAULT_DEEPFAKE_MODEL_PATH):
|
| 667 |
-
recommendations.append(f"Place the deepfake model file at: {DEFAULT_DEEPFAKE_MODEL_PATH}")
|
| 668 |
-
|
| 669 |
-
if not os.path.exists(DEFAULT_DEEPFAKE_CFG_PATH):
|
| 670 |
-
recommendations.append(f"Place the deepfake config file at: {DEFAULT_DEEPFAKE_CFG_PATH}")
|
| 671 |
|
| 672 |
if recommendations:
|
| 673 |
for rec in recommendations:
|
|
@@ -708,7 +669,7 @@ def create_gradio_interface():
|
|
| 708 |
|
| 709 |
return "\n".join(output)
|
| 710 |
|
| 711 |
-
|
| 712 |
with gr.Tab("Settings"):
|
| 713 |
gr.Markdown("## Model Settings")
|
| 714 |
|
|
@@ -721,49 +682,16 @@ def create_gradio_interface():
|
|
| 721 |
)
|
| 722 |
|
| 723 |
custom_deepfake_model = gr.Textbox(
|
| 724 |
-
label="Custom Deepfake Model Path",
|
| 725 |
value=DEFAULT_DEEPFAKE_MODEL_PATH,
|
| 726 |
-
placeholder="Path to deepfake detection model (.pth)"
|
| 727 |
-
)
|
| 728 |
-
|
| 729 |
-
custom_deepfake_cfg = gr.Textbox(
|
| 730 |
-
label="Custom Deepfake Config Path",
|
| 731 |
-
value=DEFAULT_DEEPFAKE_CFG_PATH,
|
| 732 |
-
placeholder="Path to deepfake model config (.yaml)"
|
| 733 |
)
|
| 734 |
|
| 735 |
check_paths_btn = gr.Button("Verify Paths", variant="primary")
|
| 736 |
|
| 737 |
with gr.Column():
|
| 738 |
paths_result = gr.Markdown(label="Path Verification Results")
|
| 739 |
-
|
| 740 |
-
# Function to check model paths
|
| 741 |
-
def check_model_paths(damage_path, deepfake_path, cfg_path):
|
| 742 |
-
output = ["## Path Verification Results\n"]
|
| 743 |
-
|
| 744 |
-
# Check damage model
|
| 745 |
-
if os.path.exists(damage_path):
|
| 746 |
-
file_size = os.path.getsize(damage_path) / (1024 * 1024) # Size in MB
|
| 747 |
-
output.append(f"β
**Damage model:** Found at {damage_path} ({file_size:.2f} MB)")
|
| 748 |
-
else:
|
| 749 |
-
output.append(f"β **Damage model:** NOT found at {damage_path}")
|
| 750 |
-
|
| 751 |
-
# Check deepfake model
|
| 752 |
-
if os.path.exists(deepfake_path):
|
| 753 |
-
file_size = os.path.getsize(deepfake_path) / (1024 * 1024) # Size in MB
|
| 754 |
-
output.append(f"β
**Deepfake model:** Found at {deepfake_path} ({file_size:.2f} MB)")
|
| 755 |
-
else:
|
| 756 |
-
output.append(f"β **Deepfake model:** NOT found at {deepfake_path}")
|
| 757 |
-
|
| 758 |
-
# Check deepfake config
|
| 759 |
-
if os.path.exists(cfg_path):
|
| 760 |
-
output.append(f"β
**Deepfake config:** Found at {cfg_path}")
|
| 761 |
-
else:
|
| 762 |
-
output.append(f"β **Deepfake config:** NOT found at {cfg_path}")
|
| 763 |
-
|
| 764 |
-
return "\n".join(output)
|
| 765 |
-
|
| 766 |
-
# Help Tab
|
| 767 |
with gr.Tab("Help"):
|
| 768 |
gr.Markdown("""
|
| 769 |
## π How to Use This Tool
|
|
@@ -780,8 +708,12 @@ def create_gradio_interface():
|
|
| 780 |
|
| 781 |
### Requirements
|
| 782 |
- Damage detection model file (Detectron2 Mask R-CNN)
|
| 783 |
-
-
|
| 784 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 785 |
|
| 786 |
### Troubleshooting
|
| 787 |
- If models aren't loading, check the "System Diagnostics" tab
|
|
@@ -793,7 +725,7 @@ def create_gradio_interface():
|
|
| 793 |
- **Deepfake Threshold:** Higher values make the system more selective in flagging fakes
|
| 794 |
- **Skip Damage Detection:** Analyze the entire image for deepfakes without damage detection
|
| 795 |
""")
|
| 796 |
-
|
| 797 |
# Examples
|
| 798 |
if any(os.path.exists(img) for img in SAMPLE_IMAGES):
|
| 799 |
gr.Markdown("## Example Images")
|
|
@@ -805,7 +737,7 @@ def create_gradio_interface():
|
|
| 805 |
outputs=[output_image, output_text],
|
| 806 |
fn=lambda x: process_image(
|
| 807 |
x, DEFAULT_DAMAGE_MODEL_PATH, DEFAULT_DEEPFAKE_MODEL_PATH,
|
| 808 |
-
|
| 809 |
),
|
| 810 |
cache_examples=True
|
| 811 |
)
|
|
@@ -817,7 +749,6 @@ def create_gradio_interface():
|
|
| 817 |
input_image,
|
| 818 |
custom_damage_model,
|
| 819 |
custom_deepfake_model,
|
| 820 |
-
custom_deepfake_cfg,
|
| 821 |
damage_threshold,
|
| 822 |
deepfake_threshold,
|
| 823 |
skip_damage,
|
|
@@ -849,16 +780,14 @@ def create_gradio_interface():
|
|
| 849 |
# Settings tab
|
| 850 |
check_paths_btn.click(
|
| 851 |
fn=check_model_paths,
|
| 852 |
-
inputs=[custom_damage_model, custom_deepfake_model
|
| 853 |
outputs=paths_result
|
| 854 |
)
|
| 855 |
|
| 856 |
return app
|
| 857 |
|
| 858 |
if __name__ == "__main__":
|
| 859 |
-
# Try to auto-install dependencies on startup
|
| 860 |
-
auto_install_dependencies()
|
| 861 |
|
| 862 |
# Create and launch the Gradio app
|
| 863 |
app = create_gradio_interface()
|
| 864 |
-
app.launch(share=False) # Set share=True to create a public link
|
|
|
|
| 2 |
import importlib.util
|
| 3 |
import os
|
| 4 |
import sys
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
import time
|
| 6 |
import cv2
|
| 7 |
import torch
|
|
|
|
| 9 |
import gradio as gr
|
| 10 |
from PIL import Image
|
| 11 |
from torchvision import transforms
|
| 12 |
+
from torchvision.models import vit_b_16
|
| 13 |
+
import torch.nn as nn
|
| 14 |
import traceback
|
|
|
|
|
|
|
| 15 |
|
| 16 |
# Add current directory to path
|
| 17 |
if not os.getcwd() in sys.path:
|
| 18 |
sys.path.append(os.getcwd())
|
| 19 |
|
| 20 |
+
# Check if detectron2 is installed
|
| 21 |
+
if importlib.util.find_spec("detectron2") is None:
|
| 22 |
+
print("Installing PyTorch and Detectron2...")
|
| 23 |
+
os.system("pip install torch torchvision --extra-index-url https://download.pytorch.org/whl/cpu")
|
| 24 |
+
os.system("pip install git+https://github.com/facebookresearch/detectron2.git")
|
| 25 |
+
print("Installation complete!")
|
| 26 |
+
|
| 27 |
# Check for detectron2
|
| 28 |
try:
|
| 29 |
from detectron2.engine import DefaultPredictor
|
|
|
|
| 35 |
print("Warning: Detectron2 is not installed. Damage detection will not be available.")
|
| 36 |
DETECTRON2_AVAILABLE = False
|
| 37 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
# Define model paths
|
| 39 |
DEFAULT_DAMAGE_MODEL_PATH = "./model_final.pth"
|
| 40 |
+
DEFAULT_DEEPFAKE_MODEL_PATH = "./vit_deepfake_best_20250422_222444.pth"
|
|
|
|
| 41 |
|
| 42 |
# Sample images for demo (add your own paths)
|
| 43 |
SAMPLE_IMAGES = [
|
| 44 |
"./test3.png",
|
| 45 |
"./test5.png",
|
|
|
|
| 46 |
]
|
| 47 |
|
| 48 |
def verify_detectron2_installation():
|
|
|
|
| 131 |
traceback.print_exc()
|
| 132 |
return None, None
|
| 133 |
|
| 134 |
+
def load_vit_deepfake_model(model_path, device):
|
| 135 |
+
"""Load the Vision Transformer (ViT) model for deepfake detection"""
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
if model_path is None or not os.path.exists(model_path):
|
| 137 |
+
print(f"Error: ViT deepfake model file not found at {model_path}")
|
| 138 |
+
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
| 139 |
|
| 140 |
try:
|
| 141 |
+
# Create ViT model with binary classification head
|
| 142 |
+
model = vit_b_16(weights=None)
|
| 143 |
|
| 144 |
+
# Modify the classifier head for binary classification (real vs fake)
|
| 145 |
+
in_features = model.heads.head.in_features
|
| 146 |
+
model.heads.head = nn.Linear(in_features, 2)
|
| 147 |
|
| 148 |
# Load weights
|
| 149 |
+
print(f"Loading ViT deepfake model from: {model_path}")
|
| 150 |
checkpoint = torch.load(model_path, map_location='cpu')
|
| 151 |
|
| 152 |
+
if isinstance(checkpoint, dict) and 'model_state_dict' in checkpoint:
|
| 153 |
+
model.load_state_dict(checkpoint['model_state_dict'])
|
| 154 |
+
elif isinstance(checkpoint, dict) and 'state_dict' in checkpoint:
|
| 155 |
model.load_state_dict(checkpoint['state_dict'])
|
| 156 |
else:
|
| 157 |
model.load_state_dict(checkpoint)
|
| 158 |
|
| 159 |
# Move model to device and set to evaluation mode
|
| 160 |
model = model.to(device)
|
|
|
|
|
|
|
| 161 |
model.eval()
|
| 162 |
|
| 163 |
+
return model
|
| 164 |
except Exception as e:
|
| 165 |
+
print(f"Error loading ViT deepfake model: {e}")
|
| 166 |
traceback.print_exc()
|
| 167 |
+
return None
|
| 168 |
|
| 169 |
+
def preprocess_for_vit(image, device):
|
| 170 |
+
"""Preprocess an image for Vision Transformer deepfake detection"""
|
| 171 |
try:
|
| 172 |
# Ensure image is RGB
|
| 173 |
if len(image.shape) == 3 and image.shape[2] == 3:
|
|
|
|
| 177 |
else:
|
| 178 |
rgb_img = image
|
| 179 |
|
| 180 |
+
# Resize to standard ViT input size (224x224)
|
| 181 |
+
img_resized = cv2.resize(rgb_img, (224, 224))
|
| 182 |
|
| 183 |
# Apply transforms
|
| 184 |
transform = transforms.Compose([
|
| 185 |
transforms.ToTensor(),
|
| 186 |
+
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
|
|
|
|
|
|
|
|
|
|
| 187 |
])
|
| 188 |
|
| 189 |
img_tensor = transform(Image.fromarray(img_resized)).unsqueeze(0)
|
| 190 |
img_tensor = img_tensor.to(device)
|
| 191 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 192 |
return img_tensor
|
| 193 |
except Exception as e:
|
| 194 |
+
print(f"Error preprocessing image for ViT: {e}")
|
| 195 |
traceback.print_exc()
|
| 196 |
return None
|
| 197 |
|
|
|
|
| 254 |
return img, None, damage_regions
|
| 255 |
return None, None, []
|
| 256 |
|
| 257 |
+
def check_deepfake_vit(image, damage_regions, deepfake_model, device, threshold=0.5):
|
| 258 |
+
"""Check if damage regions are deepfakes using ViT model"""
|
| 259 |
results = []
|
| 260 |
|
| 261 |
if deepfake_model is None:
|
|
|
|
| 264 |
try:
|
| 265 |
# If no damage regions, check entire image
|
| 266 |
if not damage_regions:
|
| 267 |
+
img_tensor = preprocess_for_vit(image, device)
|
| 268 |
if img_tensor is None:
|
| 269 |
return []
|
| 270 |
|
|
|
|
| 272 |
with torch.no_grad():
|
| 273 |
outputs = deepfake_model(img_tensor)
|
| 274 |
|
| 275 |
+
# Get predictions
|
| 276 |
+
probabilities = torch.nn.functional.softmax(outputs, dim=1)
|
| 277 |
+
fake_prob = probabilities[0, 1].item() # Probability of being fake (class 1)
|
| 278 |
+
is_fake = fake_prob > threshold
|
| 279 |
|
| 280 |
+
results.append({
|
| 281 |
+
"region": "full_image",
|
| 282 |
+
"deepfake_prob": float(fake_prob),
|
| 283 |
+
"is_fake": bool(is_fake)
|
| 284 |
+
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 285 |
|
| 286 |
return results
|
| 287 |
|
|
|
|
| 297 |
roi = image[y1:y2, x1:x2]
|
| 298 |
|
| 299 |
# Preprocess
|
| 300 |
+
img_tensor = preprocess_for_vit(roi, device)
|
| 301 |
if img_tensor is None:
|
| 302 |
continue
|
| 303 |
|
|
|
|
| 305 |
with torch.no_grad():
|
| 306 |
outputs = deepfake_model(img_tensor)
|
| 307 |
|
| 308 |
+
# Get predictions
|
| 309 |
+
probabilities = torch.nn.functional.softmax(outputs, dim=1)
|
| 310 |
+
fake_prob = probabilities[0, 1].item() # Probability of being fake (class 1)
|
| 311 |
+
is_fake = fake_prob > threshold
|
|
|
|
|
|
|
|
|
|
|
|
|
| 312 |
|
| 313 |
+
results.append({
|
| 314 |
+
"region_id": i,
|
| 315 |
+
"box": (x1, y1, x2, y2),
|
| 316 |
+
"deepfake_prob": float(fake_prob),
|
| 317 |
+
"is_fake": bool(is_fake)
|
| 318 |
+
})
|
|
|
|
|
|
|
|
|
|
|
|
|
| 319 |
|
| 320 |
return results
|
| 321 |
except Exception as e:
|
|
|
|
| 383 |
traceback.print_exc()
|
| 384 |
return np.array(image, dtype=np.uint8)
|
| 385 |
|
| 386 |
+
def process_image(input_image, damage_model_path, deepfake_model_path, damage_threshold,
|
| 387 |
+
deepfake_threshold, skip_damage, device_str):
|
| 388 |
"""Process an image through the detection pipeline"""
|
| 389 |
progress_info = []
|
| 390 |
|
|
|
|
|
|
|
| 391 |
# Check model files
|
| 392 |
if not skip_damage and damage_model_path:
|
| 393 |
if not os.path.exists(damage_model_path):
|
| 394 |
progress_info.append(f"ERROR: Damage model not found at {damage_model_path}")
|
| 395 |
|
| 396 |
if deepfake_model_path and not os.path.exists(deepfake_model_path):
|
| 397 |
+
progress_info.append(f"ERROR: ViT deepfake model not found at {deepfake_model_path}")
|
|
|
|
|
|
|
|
|
|
| 398 |
|
| 399 |
# Convert image to proper format
|
| 400 |
try:
|
|
|
|
| 414 |
except Exception as e:
|
| 415 |
return None, f"Error loading image: {str(e)}"
|
| 416 |
|
|
|
|
| 417 |
# Setup device
|
| 418 |
device = setup_device(device_str)
|
| 419 |
+
progress_info.append(f"Using device: {device}")
|
| 420 |
|
| 421 |
# Initialize models
|
| 422 |
damage_detector = None
|
| 423 |
deepfake_model = None
|
|
|
|
| 424 |
|
| 425 |
# Setup damage detector
|
| 426 |
if not skip_damage and damage_model_path:
|
| 427 |
+
progress_info.append("Setting up damage detector...")
|
| 428 |
damage_detector, _ = setup_damage_detector(damage_model_path, float(damage_threshold))
|
| 429 |
+
if damage_detector:
|
| 430 |
+
progress_info.append("β
Damage detector initialized")
|
| 431 |
+
else:
|
| 432 |
+
progress_info.append("β Failed to initialize damage detector")
|
| 433 |
+
|
| 434 |
+
# Setup ViT deepfake detector
|
| 435 |
+
if deepfake_model_path:
|
| 436 |
+
progress_info.append("Setting up ViT deepfake detector...")
|
| 437 |
+
deepfake_model = load_vit_deepfake_model(deepfake_model_path, device)
|
| 438 |
+
if deepfake_model:
|
| 439 |
+
progress_info.append("β
ViT deepfake detector initialized")
|
| 440 |
+
else:
|
| 441 |
+
progress_info.append("β Failed to initialize ViT deepfake detector")
|
| 442 |
|
| 443 |
# Step 1: Detect damage
|
| 444 |
+
progress_info.append("Detecting damaged regions...")
|
| 445 |
start_time = time.time()
|
| 446 |
img, damage_outputs, damage_regions = detect_damage(img, damage_detector)
|
| 447 |
damage_time = time.time() - start_time
|
| 448 |
|
| 449 |
+
if damage_regions:
|
| 450 |
+
progress_info.append(f"Found {len(damage_regions)} damage regions in {damage_time:.2f} seconds")
|
| 451 |
+
else:
|
| 452 |
+
progress_info.append("No damage regions detected")
|
| 453 |
+
|
| 454 |
+
# Step 2: Check for deepfakes using ViT
|
| 455 |
deepfake_results = []
|
| 456 |
if deepfake_model is not None:
|
| 457 |
+
progress_info.append("Analyzing regions for deepfakes using ViT model...")
|
| 458 |
start_time = time.time()
|
| 459 |
+
deepfake_results = check_deepfake_vit(
|
| 460 |
+
img, damage_regions, deepfake_model, device, float(deepfake_threshold)
|
| 461 |
)
|
| 462 |
deepfake_time = time.time() - start_time
|
| 463 |
|
| 464 |
if deepfake_results:
|
| 465 |
+
progress_info.append(f"Deepfake analysis completed in {deepfake_time:.2f} seconds")
|
| 466 |
|
| 467 |
# Generate report
|
| 468 |
for result in deepfake_results:
|
|
|
|
| 470 |
region_id = result["region_id"]
|
| 471 |
fake_prob = result["deepfake_prob"]
|
| 472 |
is_fake = result["is_fake"]
|
| 473 |
+
progress_info.append(f"Region {region_id}: {'FAKE' if is_fake else 'REAL'} (Probability: {fake_prob*100:.2f}%)")
|
| 474 |
elif "region" in result and result["region"] == "full_image":
|
| 475 |
fake_prob = result["deepfake_prob"]
|
| 476 |
is_fake = result["is_fake"]
|
| 477 |
+
progress_info.append(f"Whole image: {'FAKE' if is_fake else 'REAL'} (Probability: {fake_prob*100:.2f}%)")
|
| 478 |
else:
|
| 479 |
progress_info.append("No deepfake detection results")
|
| 480 |
|
| 481 |
+
# Final verdict
|
| 482 |
fake_regions = [r for r in deepfake_results if r.get("is_fake", False)]
|
| 483 |
if fake_regions:
|
| 484 |
progress_info.append("\nπ¨ VERDICT: This image contains FAKE damage π¨")
|
| 485 |
else:
|
| 486 |
progress_info.append("\nβ
VERDICT: All damage appears REAL")
|
| 487 |
+
|
| 488 |
# Step 3: Visualize results
|
| 489 |
result_img = visualize_results(img, damage_outputs, deepfake_results, float(damage_threshold))
|
| 490 |
|
|
|
|
| 492 |
if len(result_img.shape) == 3 and result_img.shape[2] == 3:
|
| 493 |
result_img = cv2.cvtColor(result_img, cv2.COLOR_BGR2RGB)
|
| 494 |
|
|
|
|
| 495 |
return result_img, "\n".join(progress_info)
|
| 496 |
|
| 497 |
def auto_install_dependencies():
|
|
|
|
| 529 |
|
| 530 |
with gr.Blocks(title="Car Damage & Deepfake Detector", theme=theme) as app:
|
| 531 |
gr.Markdown("""
|
| 532 |
+
# π Car Damage Fraud Detector with Vision Transformer
|
| 533 |
|
| 534 |
Upload a car image to:
|
| 535 |
1. Detect damaged areas
|
| 536 |
2. Verify if the damage is real or artificially generated (deepfake)
|
| 537 |
|
| 538 |
+
*This app uses a Vision Transformer (ViT) model for deepfake detection.*
|
| 539 |
""")
|
| 540 |
|
| 541 |
# Main Interface Tab
|
|
|
|
| 588 |
output.append(f"**CUDA Available:** {'Yes β
' if torch.cuda.is_available() else 'No β'}")
|
| 589 |
if torch.cuda.is_available():
|
| 590 |
output.append(f"**GPU:** {torch.cuda.get_device_name(0)}")
|
| 591 |
+
output.append(f"**MPS Available:** {'Yes β
' if hasattr(torch.backends, 'mps') and torch.backends.mps.is_available() else 'No β'}")
|
| 592 |
except ImportError:
|
| 593 |
output.append("**PyTorch:** Not installed β")
|
| 594 |
|
|
|
|
| 603 |
|
| 604 |
if os.path.exists(DEFAULT_DEEPFAKE_MODEL_PATH):
|
| 605 |
file_size = os.path.getsize(DEFAULT_DEEPFAKE_MODEL_PATH) / (1024 * 1024) # Size in MB
|
| 606 |
+
output.append(f"**ViT Deepfake Model:** Available β
({file_size:.2f} MB)")
|
| 607 |
else:
|
| 608 |
+
output.append(f"**ViT Deepfake Model:** Not found β at {DEFAULT_DEEPFAKE_MODEL_PATH}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 609 |
|
| 610 |
output.append("\n## Detectron2 Status")
|
| 611 |
output.append(f"**Installed:** {'Yes β
' if detectron2_results['detectron2_installed'] else 'No β'}")
|
|
|
|
| 628 |
recommendations.append(f"Place the damage model file at: {DEFAULT_DAMAGE_MODEL_PATH}")
|
| 629 |
|
| 630 |
if not os.path.exists(DEFAULT_DEEPFAKE_MODEL_PATH):
|
| 631 |
+
recommendations.append(f"Place the ViT deepfake model file at: {DEFAULT_DEEPFAKE_MODEL_PATH}")
|
|
|
|
|
|
|
|
|
|
| 632 |
|
| 633 |
if recommendations:
|
| 634 |
for rec in recommendations:
|
|
|
|
| 669 |
|
| 670 |
return "\n".join(output)
|
| 671 |
|
| 672 |
+
# Settings Tab
|
| 673 |
with gr.Tab("Settings"):
|
| 674 |
gr.Markdown("## Model Settings")
|
| 675 |
|
|
|
|
| 682 |
)
|
| 683 |
|
| 684 |
custom_deepfake_model = gr.Textbox(
|
| 685 |
+
label="Custom ViT Deepfake Model Path",
|
| 686 |
value=DEFAULT_DEEPFAKE_MODEL_PATH,
|
| 687 |
+
placeholder="Path to ViT deepfake detection model (.pth)"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 688 |
)
|
| 689 |
|
| 690 |
check_paths_btn = gr.Button("Verify Paths", variant="primary")
|
| 691 |
|
| 692 |
with gr.Column():
|
| 693 |
paths_result = gr.Markdown(label="Path Verification Results")
|
| 694 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 695 |
with gr.Tab("Help"):
|
| 696 |
gr.Markdown("""
|
| 697 |
## π How to Use This Tool
|
|
|
|
| 708 |
|
| 709 |
### Requirements
|
| 710 |
- Damage detection model file (Detectron2 Mask R-CNN)
|
| 711 |
+
- Vision Transformer (ViT) deepfake detection model file
|
| 712 |
+
|
| 713 |
+
### About the Vision Transformer (ViT)
|
| 714 |
+
- This version uses a state-of-the-art Vision Transformer model for deepfake detection
|
| 715 |
+
- The ViT model has been trained specifically to detect artificially generated car damage
|
| 716 |
+
- ViT models are better at capturing global features in images compared to traditional CNNs
|
| 717 |
|
| 718 |
### Troubleshooting
|
| 719 |
- If models aren't loading, check the "System Diagnostics" tab
|
|
|
|
| 725 |
- **Deepfake Threshold:** Higher values make the system more selective in flagging fakes
|
| 726 |
- **Skip Damage Detection:** Analyze the entire image for deepfakes without damage detection
|
| 727 |
""")
|
| 728 |
+
|
| 729 |
# Examples
|
| 730 |
if any(os.path.exists(img) for img in SAMPLE_IMAGES):
|
| 731 |
gr.Markdown("## Example Images")
|
|
|
|
| 737 |
outputs=[output_image, output_text],
|
| 738 |
fn=lambda x: process_image(
|
| 739 |
x, DEFAULT_DAMAGE_MODEL_PATH, DEFAULT_DEEPFAKE_MODEL_PATH,
|
| 740 |
+
0.7, 0.5, False, "auto"
|
| 741 |
),
|
| 742 |
cache_examples=True
|
| 743 |
)
|
|
|
|
| 749 |
input_image,
|
| 750 |
custom_damage_model,
|
| 751 |
custom_deepfake_model,
|
|
|
|
| 752 |
damage_threshold,
|
| 753 |
deepfake_threshold,
|
| 754 |
skip_damage,
|
|
|
|
| 780 |
# Settings tab
|
| 781 |
check_paths_btn.click(
|
| 782 |
fn=check_model_paths,
|
| 783 |
+
inputs=[custom_damage_model, custom_deepfake_model],
|
| 784 |
outputs=paths_result
|
| 785 |
)
|
| 786 |
|
| 787 |
return app
|
| 788 |
|
| 789 |
if __name__ == "__main__":
|
|
|
|
|
|
|
| 790 |
|
| 791 |
# Create and launch the Gradio app
|
| 792 |
app = create_gradio_interface()
|
| 793 |
+
app.launch(share=False) # Set share=True to create a public link#!/usr/bin/env python3
|