KoreaPeter commited on
Commit
1b4e597
·
verified ·
1 Parent(s): 9b0f6e7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -100
app.py CHANGED
@@ -6,15 +6,10 @@ import os
6
  from PIL import Image
7
 
8
  import deepguard # noqa: F401 # registers ms_eff_gcvit_* into timm
9
- import numpy as np
10
  from explainability import (
11
  HiResCAMExplainer, GradCAMElementWiseExplainer, LayerCAMExplainer,
12
  EigenGradCAMExplainer, GradCAMPlusPlusExplainer, XGradCAMExplainer,
13
  )
14
- from explainability.metrics.road import (
15
- ROADMostRelevantFirstAverage, ROADLeastRelevantFirstAverage,
16
- )
17
- from explainability.utils.model_targets import BinaryClassifierOutputSigmoidTarget
18
 
19
  # ── Registries ────────────────────────────────────────────────────────────────
20
  MODEL_NAME = {"fast": "ms_eff_gcvit_b0", "pro": "ms_eff_gcvit_b5"}
@@ -61,62 +56,6 @@ def _get_explainer(variant: str, dataset: str, method: str, branch: str):
61
  return _cache[key]
62
 
63
 
64
- # ── ROAD metric table (video-style dark table) ────────────────────────────────
65
- def _metric_bar(val: float, color: str) -> str:
66
- w = int(max(0.0, min(1.0, val)) * 100)
67
- return (
68
- '<div style="background:#0b0f19;border-radius:4px;height:14px;'
69
- 'width:160px;overflow:hidden;">'
70
- f'<div style="background:{color};width:{w}%;height:100%;border-radius:4px;"></div></div>'
71
- )
72
-
73
-
74
- def _build_metric_table(morf: float, lerf: float) -> str:
75
- rows = [
76
- ("MORF", "Most Relevant First", morf, "#f87171", "↓ lower = more faithful"),
77
- ("LERF", "Least Relevant First", lerf, "#4ade80", "↑ higher = more faithful"),
78
- ]
79
- body = ""
80
- for short, full, val, color, interp in rows:
81
- body += f"""
82
- <tr style="background:#12161f; border-bottom:1px solid #2d3748;">
83
- <td style="padding:10px 14px; color:#FFD21E; font-weight:700;">{short}</td>
84
- <td style="padding:10px 14px; color:#9ca3af;">{full}</td>
85
- <td style="padding:10px 14px;">{_metric_bar(val, color)}</td>
86
- <td style="padding:10px 14px; color:{color}; font-family:monospace; font-weight:700;">{val:.4f}</td>
87
- <td style="padding:10px 14px; color:#9ca3af; font-size:13px;">{interp}</td>
88
- </tr>"""
89
-
90
- return f"""
91
- <div style="overflow-x:auto; margin-top:8px;">
92
- <table style="width:100%; border-collapse:collapse; font-size:14px;
93
- background:#0b0f19; border-radius:8px; overflow:hidden;">
94
- <thead>
95
- <tr style="background:#FFD21E; color:#1a1a1a;">
96
- <th style="padding:10px 14px; text-align:left;">Metric</th>
97
- <th style="padding:10px 14px; text-align:left;">Perturbation</th>
98
- <th style="padding:10px 14px; text-align:left;">Faithfulness</th>
99
- <th style="padding:10px 14px; text-align:left;">Score</th>
100
- <th style="padding:10px 14px; text-align:left;">Interpretation</th>
101
- </tr>
102
- </thead>
103
- <tbody>{body}</tbody>
104
- </table>
105
- </div>"""
106
-
107
-
108
- def _evaluate_road(explainer, img_path: str) -> str:
109
- torch.manual_seed(2026)
110
- np.random.seed(2026)
111
- pct = [20, 40, 60, 80] # reduced from 9 → 4 for speed
112
- grayscale_cam, tensor, _ = explainer._build_grayscale_cam(img_path)
113
- cams = grayscale_cam[np.newaxis, ...] # (1, H, W)
114
- targets = [BinaryClassifierOutputSigmoidTarget()]
115
- morf = float(ROADMostRelevantFirstAverage(pct)(tensor, cams, targets, explainer.model)[0])
116
- lerf = float(ROADLeastRelevantFirstAverage(pct)(tensor, cams, targets, explainer.model)[0])
117
- return _build_metric_table(morf, lerf)
118
-
119
-
120
  # ── Inference ─────────────────────────────────────────────────────────────────
121
  @spaces.GPU
122
  def explain(
@@ -170,31 +109,6 @@ def explain(
170
  return result
171
 
172
 
173
- # ── ROAD evaluation (separate button) ─────────────────────────────────────────
174
- @spaces.GPU
175
- def evaluate_metrics(image, variant, dataset, branch, method):
176
- if image is None:
177
- raise gr.Error("Please upload an image first.")
178
-
179
- with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as f:
180
- image.save(f.name)
181
- tmp_path = f.name
182
-
183
- try:
184
- explainer = _get_explainer(variant, dataset, method, branch)
185
- if torch.cuda.is_available():
186
- explainer.model.to("cuda:0")
187
- explainer.device = "cuda:0"
188
- return _evaluate_road(explainer, tmp_path)
189
- except Exception as e:
190
- msg = str(e)
191
- if "face" in msg.lower() or "NoneType" in msg or "index" in msg.lower():
192
- raise gr.Error("😶 No face detected. Please use an image with a clearly visible face.")
193
- raise gr.Error(f"ROAD evaluation error: {e}")
194
- finally:
195
- os.unlink(tmp_path)
196
-
197
-
198
  # ── Assets (HF CDN absolute URL — keeps GIF animation) ─────────────────────────
199
  _HF_BASE = "https://huggingface.co/spaces/KoreaPeter/DeepFake-Detection-XAI/resolve/main"
200
  DUAL_BRANCH_SRC = f"{_HF_BASE}/dual_branch.gif"
@@ -410,15 +324,6 @@ with gr.Blocks(title="DeepGuard XAI") as demo:
410
  info="PCA noise reduction → keeps dominant forgery pattern only.",
411
  )
412
 
413
- with gr.Accordion("📊 Explainability Metrics — ROAD", open=True):
414
- gr.Markdown(
415
- "CAM **faithfulness** via ROAD (perturbation-based). "
416
- "Pixels are removed by relevance and the drop in prediction confidence is measured. "
417
- "⚠️ Heavier than visualization — run only when needed."
418
- )
419
- eval_btn = gr.Button("📊 Evaluate ROAD", variant="secondary")
420
- metric_output = gr.HTML("")
421
-
422
  # branch → method choices sync (recommended method becomes default)
423
  def _sync_methods(branch):
424
  choices = METHODS_BY_BRANCH[branch]
@@ -446,11 +351,6 @@ with gr.Blocks(title="DeepGuard XAI") as demo:
446
  image_weight_slider, threshold_slider,
447
  ]
448
  run_btn.click(fn=explain, inputs=inputs, outputs=result_output)
449
- eval_btn.click(
450
- fn=evaluate_metrics,
451
- inputs=[image_input, variant_radio, dataset_radio, branch_radio, method_radio],
452
- outputs=metric_output,
453
- )
454
 
455
  if __name__ == "__main__":
456
  demo.launch(theme=THEME, css=CSS)
 
6
  from PIL import Image
7
 
8
  import deepguard # noqa: F401 # registers ms_eff_gcvit_* into timm
 
9
  from explainability import (
10
  HiResCAMExplainer, GradCAMElementWiseExplainer, LayerCAMExplainer,
11
  EigenGradCAMExplainer, GradCAMPlusPlusExplainer, XGradCAMExplainer,
12
  )
 
 
 
 
13
 
14
  # ── Registries ────────────────────────────────────────────────────────────────
15
  MODEL_NAME = {"fast": "ms_eff_gcvit_b0", "pro": "ms_eff_gcvit_b5"}
 
56
  return _cache[key]
57
 
58
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  # ── Inference ─────────────────────────────────────────────────────────────────
60
  @spaces.GPU
61
  def explain(
 
109
  return result
110
 
111
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  # ── Assets (HF CDN absolute URL — keeps GIF animation) ─────────────────────────
113
  _HF_BASE = "https://huggingface.co/spaces/KoreaPeter/DeepFake-Detection-XAI/resolve/main"
114
  DUAL_BRANCH_SRC = f"{_HF_BASE}/dual_branch.gif"
 
324
  info="PCA noise reduction → keeps dominant forgery pattern only.",
325
  )
326
 
 
 
 
 
 
 
 
 
 
327
  # branch → method choices sync (recommended method becomes default)
328
  def _sync_methods(branch):
329
  choices = METHODS_BY_BRANCH[branch]
 
351
  image_weight_slider, threshold_slider,
352
  ]
353
  run_btn.click(fn=explain, inputs=inputs, outputs=result_output)
 
 
 
 
 
354
 
355
  if __name__ == "__main__":
356
  demo.launch(theme=THEME, css=CSS)