Spaces:
Paused
Paused
Download scripts/gradcam.py from juddy17/DamageLensAI: direct link, hf CLI and curl.
- Browser
- Download file 3.34 kB
-
https://huggingface.co/spaces/juddy17/DamageLensAI/resolve/main/scripts/gradcam.py
- Command line
-
hf download hf://spaces/juddy17/DamageLensAI/scripts/gradcam.py
-
curl -L -o gradcam.py https://huggingface.co/spaces/juddy17/DamageLensAI/resolve/main/scripts/gradcam.py
3.34 kB
| import cv2 | |
| import numpy as np | |
| from PIL import Image | |
| import torch | |
| import torch.nn.functional as F | |
| import logging | |
| logger = logging.getLogger(__name__) | |
| class _GradCAMHook: | |
| __slots__ = ("activation", "gradient", "fwd_handle", "bwd_handle") | |
| def __init__(self, target_layer): | |
| self.activation = None | |
| self.gradient = None | |
| self.fwd_handle = target_layer.register_forward_hook(self._fwd_hook) | |
| self.bwd_handle = None | |
| def _fwd_hook(self, module, inp, out): | |
| self.activation = out | |
| self.bwd_handle = out.register_hook(self._bwd_hook) | |
| def _bwd_hook(self, grad): | |
| self.gradient = grad | |
| def remove(self): | |
| self.fwd_handle.remove() | |
| if self.bwd_handle is not None: | |
| self.bwd_handle.remove() | |
| def _postprocess_cam(cam_tensor, original_img, output_path, alpha=0.6, beta=0.4): | |
| h, w = original_img.height, original_img.width | |
| cam_min = cam_tensor.min() | |
| cam_max = cam_tensor.max() | |
| if cam_max > cam_min: | |
| cam_tensor = (cam_tensor - cam_min) / (cam_max - cam_min) | |
| else: | |
| cam_tensor = torch.zeros_like(cam_tensor) | |
| cam_np = cam_tensor.numpy() | |
| cam_np = cv2.resize(cam_np, (w, h), interpolation=cv2.INTER_LINEAR) | |
| cam_np = np.uint8(255 * cam_np) | |
| heatmap = cv2.applyColorMap(cam_np, cv2.COLORMAP_JET) | |
| original_bgr = cv2.cvtColor(np.array(original_img), cv2.COLOR_RGB2BGR) | |
| overlay = cv2.addWeighted(original_bgr, alpha, heatmap, beta, 0) | |
| cv2.imwrite(output_path, overlay) | |
| def get_resnet_gradcam(image_path, predictor, output_path): | |
| """Generates Grad-CAM visual heatmap using the PyTorch ResNet classifier.""" | |
| logger.info("Starting ResNet Grad-CAM generation...") | |
| model = predictor.model | |
| model.eval() | |
| target_layer = model.model.layer4[-1] | |
| hook = _GradCAMHook(target_layer) | |
| try: | |
| original_img = Image.open(image_path).convert("RGB") | |
| input_tensor = predictor.test_transforms(original_img).unsqueeze(0).to(predictor.device) | |
| output = model(input_tensor) | |
| score, pred_class_idx = output[0].max(dim=0) | |
| logger.info(f"Predicted class index for Grad-CAM: {pred_class_idx.item()}") | |
| score.backward() | |
| if hook.activation is None or hook.gradient is None: | |
| raise RuntimeError("Failed to capture activations or gradients.") | |
| acts = hook.activation[0].detach().cpu().float() | |
| grads = hook.gradient[0].detach().cpu().float() | |
| weights = grads.mean(dim=(1, 2), keepdim=True) | |
| cam = (weights * acts).sum(dim=0) | |
| cam = F.relu(cam) | |
| _postprocess_cam(cam, original_img, output_path) | |
| logger.info(f"ResNet Grad-CAM heatmap saved to: {output_path}") | |
| return True | |
| except Exception as e: | |
| logger.exception("ResNet Grad-CAM generation failed.") | |
| raise RuntimeError(f"ResNet Grad-CAM failed: {e}") from e | |
| finally: | |
| hook.remove() | |
| def get_fusion_gradcam(image_path, predictor, output_path): | |
| """Fallback handler mapping to ResNet Grad-CAM (since single Grad-CAM architecture is used).""" | |
| logger.info("Fusion Grad-CAM requested. Directing to ResNet Grad-CAM engine...") | |
| return get_resnet_gradcam(image_path, predictor, output_path) |