import numpy as np import cv2 import matplotlib.pyplot as plt import tensorflow as tf from tensorflow.keras.preprocessing import image from tensorflow.keras import Model from gcg.utils import logging def grad_cam(model, img, layer_name="block5_conv3", label_name=None, category_id=None): """Get a heatmap by Grad-CAM. Args: model: A model object, build from tf.keras 2.X. img: An image ndarray. layer_name: A string, layer name in model. label_name: A list or None, show the label name by assign this argument, it should be a list of all label names. category_id: An integer, index of the class. Default is the category with the highest score in the prediction. Return: A heatmap ndarray(without color). """ img_tensor = np.expand_dims(img, axis=0) conv_layer = model.get_layer(layer_name) heatmap_model = Model([model.inputs], [conv_layer.output, model.output]) with tf.GradientTape() as gtape: conv_output, predictions = heatmap_model(img_tensor) if category_id is None: category_id = np.argmax(predictions[0]) if label_name is not None: print(label_name[category_id]) output = predictions[:, category_id] grads = gtape.gradient(output, conv_output) pooled_grads = tf.reduce_mean(grads, axis=(0, 1, 2)) heatmap = tf.reduce_mean(tf.multiply(pooled_grads, conv_output), axis=-1) heatmap = np.maximum(heatmap, 0) max_heat = np.max(heatmap) if max_heat == 0: max_heat = 1e-10 heatmap /= max_heat return np.squeeze(heatmap) def grad_cam_plus(model, img, layer_name="block5_conv3", label_name=None, category_id=None): """Get a heatmap by Grad-CAM++. Args: model: A model object, build from tf.keras 2.X. img: An image ndarray. layer_name: A string, layer name in model. label_name: A list or None, show the label name by assign this argument, it should be a list of all label names. category_id: An integer, index of the class. Default is the category with the highest score in the prediction. Return: A heatmap ndarray(without color). """ img_tensor = np.expand_dims(img, axis=0) conv_layer = model.get_layer(layer_name) heatmap_model = Model([model.inputs], [conv_layer.output, model.output]) with tf.GradientTape() as gtape1: with tf.GradientTape() as gtape2: with tf.GradientTape() as gtape3: conv_output, predictions = heatmap_model(img_tensor) if category_id is None: category_id = np.argmax(predictions[0]) if label_name is not None: print(label_name[category_id]) output = predictions[:, category_id] conv_first_grad = gtape3.gradient(output, conv_output) conv_second_grad = gtape2.gradient(conv_first_grad, conv_output) conv_third_grad = gtape1.gradient(conv_second_grad, conv_output) global_sum = np.sum(conv_output, axis=(0, 1, 2)) alpha_num = conv_second_grad[0] alpha_denom = conv_second_grad[0]*2.0 + conv_third_grad[0]*global_sum alpha_denom = np.where(alpha_denom != 0.0, alpha_denom, 1e-10) alphas = alpha_num/alpha_denom alpha_normalization_constant = np.sum(alphas, axis=(0,1)) alphas /= alpha_normalization_constant weights = np.maximum(conv_first_grad[0], 0.0) deep_linearization_weights = np.sum(weights*alphas, axis=(0,1)) grad_cam_map = np.sum(deep_linearization_weights*conv_output[0], axis=2) heatmap = np.maximum(grad_cam_map, 0) max_heat = np.max(heatmap) if max_heat == 0: max_heat = 1e-10 heatmap /= max_heat return heatmap def preprocess_image(img_path, image_size=(512, 512, 3)): """Preprocess the image by reshape and normalization. Args: img_path: A string. target_size: A tuple, reshape to this size. Return: An image array. """ # Read the image from the specified path #img = cv2.imread(img_path) # Convert the image from BGR to RGB #img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) #img_array=cv2.resize(img, (image_size[0],image_size[1]), fx=1, fy=1,interpolation = cv2.INTER_CUBIC) img = image.load_img(img_path, target_size=image_size) img = image.img_to_array(img) return img def show_GradCAM(img, heatmap, alpha=0.4, save_path=None, return_array=False): """Show the image with heatmap. Args: img_path: string. heatmap: image array, get it by calling grad_cam(). alpha: float, transparency of heatmap. return_array: bool, return a superimposed image array or not. Return: None or image array. """ # Resize the heatmap to match the original image dimensions heatmap = cv2.resize(heatmap, (img.shape[1], img.shape[0])) # Apply color map to the heatmap heatmap = (heatmap * 255).astype("uint8") heatmap_colored = cv2.applyColorMap(heatmap, cv2.COLORMAP_JET) # Create superimposed image superimposed_img = heatmap_colored * alpha + img superimposed_img = np.clip(superimposed_img, 0, 255).astype("uint8") # Create the combined plot fig, axes = plt.subplots(1, 3, figsize=(12, 4)) # Remove space around subplots fig.subplots_adjust(wspace=0, hspace=0) # Original Image axes[0].imshow(img) axes[0].set_title('Original Image') axes[0].axis('off') # Heatmap axes[1].imshow(heatmap_colored) axes[1].set_title('Heatmap') axes[1].axis('off') # Superimposed Image axes[2].imshow(superimposed_img) axes[2].set_title('Superimposed Image') axes[2].axis('off') # Adjust layout plt.tight_layout() # plt.show() # Save the figure if save_path is provided if save_path: fig.savefig(save_path) logging.info(f"Saved combined visualization to {save_path}") # cv2.imwrite(save_path, superimposed_img) # Return superimposed image if return_array is True if return_array: return superimposed_img