Commit ·
e303dd7
1
Parent(s): 53e84fb
Change attention crop to mask outside bounding boxes, fix overlapping text in zone reference
Browse files
backend/app/services/explainability_service.py
CHANGED
|
@@ -222,17 +222,16 @@ def _generate_zone_reference_image(figsize=(5, 5)):
|
|
| 222 |
ha='center', va='center', fontsize=11.5,
|
| 223 |
color='#85C1E9', fontweight='bold', zorder=4)
|
| 224 |
|
| 225 |
-
ax.text(0.50, 0.
|
| 226 |
ha='center', va='center', fontsize=11.5,
|
| 227 |
color='#82E0AA', fontweight='bold', zorder=4)
|
| 228 |
-
ax.text(0.50, 0.
|
| 229 |
ha='center', va='center', fontsize=10.0,
|
| 230 |
color='#4A8C5C', zorder=4)
|
| 231 |
|
| 232 |
# --- Title ---
|
| 233 |
-
ax.
|
| 234 |
-
|
| 235 |
-
color='#A0A0C0', fontweight='bold', zorder=4)
|
| 236 |
|
| 237 |
fig.canvas.draw()
|
| 238 |
buf = fig.canvas.buffer_rgba()
|
|
@@ -431,28 +430,41 @@ class HeatmapFeatureExtractor:
|
|
| 431 |
|
| 432 |
return {'overlay_with_bbox': overlay}
|
| 433 |
|
| 434 |
-
def get_highest_attention_crop(self,
|
| 435 |
heatmap_norm = (self.heatmap - self.heatmap.min()) / (self.heatmap.max() - self.heatmap.min() + 1e-6)
|
|
|
|
| 436 |
|
| 437 |
-
|
| 438 |
-
|
| 439 |
-
|
| 440 |
-
|
| 441 |
-
|
| 442 |
-
|
| 443 |
-
|
| 444 |
-
|
| 445 |
-
|
| 446 |
-
|
| 447 |
-
|
| 448 |
-
|
| 449 |
-
|
| 450 |
-
|
| 451 |
-
|
| 452 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 453 |
|
| 454 |
-
|
| 455 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 456 |
|
| 457 |
# -------------------------------------------
|
| 458 |
# Explainability Service
|
|
|
|
| 222 |
ha='center', va='center', fontsize=11.5,
|
| 223 |
color='#85C1E9', fontweight='bold', zorder=4)
|
| 224 |
|
| 225 |
+
ax.text(0.50, 0.90, 'Periphery',
|
| 226 |
ha='center', va='center', fontsize=11.5,
|
| 227 |
color='#82E0AA', fontweight='bold', zorder=4)
|
| 228 |
+
ax.text(0.50, 0.84, '(image frame)',
|
| 229 |
ha='center', va='center', fontsize=10.0,
|
| 230 |
color='#4A8C5C', zorder=4)
|
| 231 |
|
| 232 |
# --- Title ---
|
| 233 |
+
ax.set_title('Spatial attention zone reference',
|
| 234 |
+
fontsize=12, color='#A0A0C0', fontweight='bold', pad=12)
|
|
|
|
| 235 |
|
| 236 |
fig.canvas.draw()
|
| 237 |
buf = fig.canvas.buffer_rgba()
|
|
|
|
| 430 |
|
| 431 |
return {'overlay_with_bbox': overlay}
|
| 432 |
|
| 433 |
+
def get_highest_attention_crop(self, threshold=0.6):
|
| 434 |
heatmap_norm = (self.heatmap - self.heatmap.min()) / (self.heatmap.max() - self.heatmap.min() + 1e-6)
|
| 435 |
+
mask = (heatmap_norm >= threshold).astype(np.uint8) * 255
|
| 436 |
|
| 437 |
+
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
| 438 |
+
|
| 439 |
+
# Create a completely black mask for the whole image
|
| 440 |
+
visible_mask = np.zeros_like(self.original_image[:, :, 0])
|
| 441 |
+
|
| 442 |
+
if contours:
|
| 443 |
+
for cnt in contours:
|
| 444 |
+
if cv2.contourArea(cnt) < 50: continue
|
| 445 |
+
x, y, w, h = cv2.boundingRect(cnt)
|
| 446 |
+
|
| 447 |
+
# Add padding
|
| 448 |
+
pad = 25
|
| 449 |
+
x = max(0, x - pad)
|
| 450 |
+
y = max(0, y - pad)
|
| 451 |
+
w = min(self.original_image.shape[1] - x, w + 2*pad)
|
| 452 |
+
h = min(self.original_image.shape[0] - y, h + 2*pad)
|
| 453 |
+
|
| 454 |
+
# Fill the bounding box area with white (visible)
|
| 455 |
+
cv2.rectangle(visible_mask, (x, y), (x + w, y + h), 255, -1)
|
| 456 |
+
else:
|
| 457 |
+
# If no contours, show whole image
|
| 458 |
+
visible_mask.fill(255)
|
| 459 |
|
| 460 |
+
# Apply mask: where mask is 0, make image dark (20% brightness)
|
| 461 |
+
darkened_image = (self.original_image * 0.2).astype(np.uint8)
|
| 462 |
+
|
| 463 |
+
# Where visible_mask is 255, use original image, else use darkened_image
|
| 464 |
+
visible_mask_3d = np.stack([visible_mask]*3, axis=2)
|
| 465 |
+
composite = np.where(visible_mask_3d == 255, self.original_image, darkened_image)
|
| 466 |
+
|
| 467 |
+
return {'composite_image': composite}
|
| 468 |
|
| 469 |
# -------------------------------------------
|
| 470 |
# Explainability Service
|