Spaces:
Paused
Paused
feat: generate seamless RGBA overlay with transparent authentic background
Browse files- api/model_server.py +19 -1
api/model_server.py
CHANGED
|
@@ -273,7 +273,10 @@ class TamperingModelServer:
|
|
| 273 |
# Directly evaluated from multi-frequency feature map, without artificial color or class overrides.
|
| 274 |
mask_np = torch.sigmoid(mask)[0, 0].cpu().numpy()
|
| 275 |
|
| 276 |
-
|
|
|
|
|
|
|
|
|
|
| 277 |
# 0.0 (Deep Navy/Indigo): Pristine Authentic | 0.50 (Lime Green): Transition | 1.0 (Crimson Red): High Tampering
|
| 278 |
lut_idx = np.clip(mask_np * 255.0, 0, 255).astype(np.int32)
|
| 279 |
heatmap_rgb = TURBO_LUT[lut_idx]
|
|
@@ -282,6 +285,18 @@ class TamperingModelServer:
|
|
| 282 |
mask_pil.save(buf, format="PNG")
|
| 283 |
mask_b64 = base64.b64encode(buf.getvalue()).decode("utf-8")
|
| 284 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 285 |
# Frequency bands
|
| 286 |
bands = self.model.decomposer(tensor)
|
| 287 |
band_mags = [float(b.abs().mean().item()) for b in bands]
|
|
@@ -310,6 +325,9 @@ class TamperingModelServer:
|
|
| 310 |
"tampering_detected": pred_idx in [1, 2],
|
| 311 |
"class_probabilities": class_probs,
|
| 312 |
"tampering_heatmap_b64": mask_b64,
|
|
|
|
|
|
|
|
|
|
| 313 |
"frequency_band_contributions": band_dict,
|
| 314 |
"processing_time_ms": round(elapsed_ms, 2),
|
| 315 |
"model_variant": self.variant,
|
|
|
|
| 273 |
# Directly evaluated from multi-frequency feature map, without artificial color or class overrides.
|
| 274 |
mask_np = torch.sigmoid(mask)[0, 0].cpu().numpy()
|
| 275 |
|
| 276 |
+
peak_score = round(float(mask_np.max()), 4)
|
| 277 |
+
coverage_ratio = round(float(np.mean(mask_np > 0.50)), 4)
|
| 278 |
+
|
| 279 |
+
# 1. Standalone publication-style Turbo false-color RGB heatmap (Matches Figure 2 Column d in paper)
|
| 280 |
# 0.0 (Deep Navy/Indigo): Pristine Authentic | 0.50 (Lime Green): Transition | 1.0 (Crimson Red): High Tampering
|
| 281 |
lut_idx = np.clip(mask_np * 255.0, 0, 255).astype(np.int32)
|
| 282 |
heatmap_rgb = TURBO_LUT[lut_idx]
|
|
|
|
| 285 |
mask_pil.save(buf, format="PNG")
|
| 286 |
mask_b64 = base64.b64encode(buf.getvalue()).decode("utf-8")
|
| 287 |
|
| 288 |
+
# 2. Seamless RGBA Overlay: Authentic background is 100% transparent (alpha=0),
|
| 289 |
+
# only tampering hotspots glow in vivid Turbo colors without discoloring the original photograph
|
| 290 |
+
overlay_rgba = np.zeros((self.img_size, self.img_size, 4), dtype=np.uint8)
|
| 291 |
+
overlay_rgba[:, :, :3] = heatmap_rgb
|
| 292 |
+
# Threshold: authentic pixels (p < 0.12) are completely transparent; ramps smoothly up to 210 alpha for hotspots
|
| 293 |
+
alpha_map = np.clip((mask_np - 0.12) / 0.25 * 210.0, 0, 210).astype(np.uint8)
|
| 294 |
+
overlay_rgba[:, :, 3] = alpha_map
|
| 295 |
+
overlay_pil = Image.fromarray(overlay_rgba, mode="RGBA")
|
| 296 |
+
buf_overlay = io.BytesIO()
|
| 297 |
+
overlay_pil.save(buf_overlay, format="PNG")
|
| 298 |
+
overlay_b64 = base64.b64encode(buf_overlay.getvalue()).decode("utf-8")
|
| 299 |
+
|
| 300 |
# Frequency bands
|
| 301 |
bands = self.model.decomposer(tensor)
|
| 302 |
band_mags = [float(b.abs().mean().item()) for b in bands]
|
|
|
|
| 325 |
"tampering_detected": pred_idx in [1, 2],
|
| 326 |
"class_probabilities": class_probs,
|
| 327 |
"tampering_heatmap_b64": mask_b64,
|
| 328 |
+
"tampering_overlay_b64": overlay_b64,
|
| 329 |
+
"tampering_peak_score": peak_score,
|
| 330 |
+
"tampering_coverage_ratio": coverage_ratio,
|
| 331 |
"frequency_band_contributions": band_dict,
|
| 332 |
"processing_time_ms": round(elapsed_ms, 2),
|
| 333 |
"model_variant": self.variant,
|