added occlusion sensitivity endpoint
Browse files
app.py
CHANGED
|
@@ -66,4 +66,73 @@ async def predict(file: UploadFile = File(...)):
|
|
| 66 |
|
| 67 |
# Return Stream
|
| 68 |
_, buffer = cv2.imencode('.jpg', processed_img)
|
| 69 |
-
return StreamingResponse(io.BytesIO(buffer), media_type="image/jpeg")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
|
| 67 |
# Return Stream
|
| 68 |
_, buffer = cv2.imencode('.jpg', processed_img)
|
| 69 |
+
return StreamingResponse(io.BytesIO(buffer), media_type="image/jpeg")
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
@app.post("/explain/occlusion")
|
| 73 |
+
async def explain_occlusion(file: UploadFile = File(...)):
|
| 74 |
+
# 1. Prepare Image
|
| 75 |
+
contents = await file.read()
|
| 76 |
+
image_pil = Image.open(io.BytesIO(contents)).convert("RGB")
|
| 77 |
+
image_np = np.array(image_pil)
|
| 78 |
+
h, w, _ = image_np.shape
|
| 79 |
+
|
| 80 |
+
# 2. Get Baseline Score
|
| 81 |
+
# We use the max score of the primary detection as our baseline
|
| 82 |
+
with graph.as_default():
|
| 83 |
+
baseline_results = model_eval.detect([image_np], verbose=0)[0]
|
| 84 |
+
|
| 85 |
+
if len(baseline_results['scores']) == 0:
|
| 86 |
+
return {"error": "No petrol station detected in the baseline image."}
|
| 87 |
+
|
| 88 |
+
baseline_score = baseline_results['scores'][0]
|
| 89 |
+
|
| 90 |
+
# 3. Occlusion Parameters
|
| 91 |
+
# Larger patches/strides prevent the API from hanging
|
| 92 |
+
patch_size = 64
|
| 93 |
+
stride = 32
|
| 94 |
+
|
| 95 |
+
# Initialize heatmap
|
| 96 |
+
sensitivity_map = np.zeros((h, w), dtype=np.float32)
|
| 97 |
+
|
| 98 |
+
# 4. Sliding Window Occlusion
|
| 99 |
+
# We mask out areas and see how much the confidence drops
|
| 100 |
+
with graph.as_default():
|
| 101 |
+
for y in range(0, h - patch_size, stride):
|
| 102 |
+
for x in range(0, w - patch_size, stride):
|
| 103 |
+
# Create occluded copy
|
| 104 |
+
img_occ = image_np.copy()
|
| 105 |
+
# Apply a "gray" occluder
|
| 106 |
+
img_occ[y:y+patch_size, x:x+patch_size, :] = 128
|
| 107 |
+
|
| 108 |
+
# Run inference
|
| 109 |
+
res = model_eval.detect([img_occ], verbose=0)[0]
|
| 110 |
+
|
| 111 |
+
# Calculate confidence drop
|
| 112 |
+
# If the target is lost or confidence drops, this area was important
|
| 113 |
+
current_score = res['scores'][0] if len(res['scores']) > 0 else 0
|
| 114 |
+
drop = max(0, baseline_score - current_score)
|
| 115 |
+
|
| 116 |
+
# Fill the patch area in our map
|
| 117 |
+
sensitivity_map[y:y+patch_size, x:x+patch_size] += drop
|
| 118 |
+
|
| 119 |
+
# 5. Normalize and Colorize
|
| 120 |
+
# Scale 0-1
|
| 121 |
+
if np.max(sensitivity_map) > 0:
|
| 122 |
+
sensitivity_map = (sensitivity_map - np.min(sensitivity_map)) / (np.max(sensitivity_map) - np.min(sensitivity_map) + 1e-8)
|
| 123 |
+
|
| 124 |
+
# Create JET Heatmap (Red = Highly sensitive/Important)
|
| 125 |
+
heatmap = cv2.applyColorMap(np.uint8(255 * sensitivity_map), cv2.COLORMAP_JET)
|
| 126 |
+
|
| 127 |
+
# 6. Final Overlay
|
| 128 |
+
# Convert original to BGR for OpenCV
|
| 129 |
+
original_bgr = cv2.cvtColor(image_np, cv2.COLOR_RGB2BGR)
|
| 130 |
+
overlay = cv2.addWeighted(original_bgr, 0.6, heatmap, 0.4, 0)
|
| 131 |
+
|
| 132 |
+
# Add Label with your signature Teal color
|
| 133 |
+
cv2.putText(overlay, "Occlusion Sensitivity Analysis", (20, 40),
|
| 134 |
+
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 0), 2)
|
| 135 |
+
|
| 136 |
+
# 7. Stream result
|
| 137 |
+
_, buffer = cv2.imencode('.jpg', overlay)
|
| 138 |
+
return StreamingResponse(io.BytesIO(buffer.tobytes()), media_type="image/jpeg")
|