|
''' |
|
Grad-CAM visualization utilities |
|
|
|
- Based on https://keras.io/examples/vision/grad_cam/ |
|
|
|
--- |
|
- 2021-12-18 jkang first created |
|
- 2022-01-16 |
|
- copied from https://huggingface.co/spaces/jkang/demo-gradcam-imagenet/blob/main/utils.py |
|
- updated for artis/trend classifier |
|
''' |
|
import matplotlib.cm as cm |
|
|
|
import os |
|
import re |
|
from glob import glob |
|
import numpy as np |
|
import tensorflow as tf |
|
tfk = tf.keras |
|
K = tfk.backend |
|
|
|
|
|
|
|
|
|
|
|
def get_imagenet_classes(): |
|
'''Retrieve all 1000 imagenet classes/labels as dictionaries''' |
|
classes = tfk.applications.imagenet_utils.decode_predictions( |
|
np.expand_dims(np.arange(1000), 0), top=1000 |
|
) |
|
idx2lab = {cla[2]: cla[1] for cla in classes[0]} |
|
lab2idx = {idx2lab[idx]: idx for idx in idx2lab} |
|
return idx2lab, lab2idx |
|
|
|
|
|
def search_by_name(str_part): |
|
'''Search imagenet class by partial matching string''' |
|
results = [key for key in list(lab2idx.keys()) if re.search(str_part, key)] |
|
if len(results) != 0: |
|
return [(key, lab2idx[key]) for key in results] |
|
else: |
|
return [] |
|
|
|
|
|
def get_xception_model(): |
|
'''Get model to use''' |
|
base_model = tfk.applications.xception.Xception |
|
preprocessor = tfk.applications.xception.preprocess_input |
|
decode_predictions = tfk.applications.xception.decode_predictions |
|
last_conv_layer_name = "block14_sepconv2_act" |
|
|
|
model = base_model(weights='imagenet') |
|
grad_model = tfk.models.Model( |
|
inputs=[model.inputs], |
|
outputs=[model.get_layer(last_conv_layer_name).output, |
|
model.output] |
|
) |
|
return model, grad_model, preprocessor, decode_predictions |
|
|
|
|
|
def get_img_4d_array(image_file, image_size=(299, 299)): |
|
'''Load image as 4d array''' |
|
img = tfk.preprocessing.image.load_img( |
|
image_file, target_size=image_size) |
|
img_array = tfk.preprocessing.image.img_to_array( |
|
img) |
|
img_array = np.expand_dims(img_array, axis=0) |
|
return img_array |
|
|
|
|
|
def make_gradcam_heatmap(grad_model, img_array, pred_idx=None): |
|
'''Generate heatmap to overlay with |
|
- img_array: 4d numpy array |
|
- pred_idx: eg. index out of 1000 imagenet classes |
|
if None, argmax is chosen from prediction |
|
''' |
|
|
|
with tf.GradientTape() as tape: |
|
last_conv_act, predictions = grad_model(img_array) |
|
if pred_idx == None: |
|
pred_idx = tf.argmax(predictions[0]) |
|
class_channel = predictions[:, pred_idx] |
|
|
|
|
|
grads = tape.gradient(class_channel, last_conv_act) |
|
pooled_grads = tf.reduce_mean(grads, axis=( |
|
0, 1, 2)) |
|
|
|
|
|
heatmap = last_conv_act[0] @ pooled_grads[..., tf.newaxis] |
|
heatmap = tf.squeeze(heatmap) |
|
|
|
|
|
heatmap = tf.maximum(heatmap, 0) / tf.math.reduce_max(heatmap) |
|
return heatmap, pred_idx.numpy(), predictions.numpy().squeeze() |
|
|
|
|
|
def align_image_with_heatmap(img_array, heatmap, alpha=0.3, cmap='jet'): |
|
'''Align the image with gradcam heatmap |
|
- img_array: 4d numpy array |
|
- heatmap: output of `def make_gradcam_heatmap()` as 2d numpy array |
|
''' |
|
img_array = img_array.squeeze() |
|
|
|
|
|
heatmap_scaled = np.uint8(255 * heatmap) |
|
img_array_scaled = np.uint8(255 * img_array) |
|
|
|
colormap = cm.get_cmap(cmap) |
|
colors = colormap(np.arange(256))[:, :3] |
|
heatmap_colored = colors[heatmap_scaled] |
|
|
|
|
|
heatmap_colored = (tfk.preprocessing.image.array_to_img(heatmap_colored) |
|
.resize((img_array.shape[1], img_array.shape[0]))) |
|
heatmap_colored = tfk.preprocessing.image.img_to_array( |
|
heatmap_colored) |
|
|
|
|
|
overlaid_img = heatmap_colored * alpha + img_array_scaled |
|
overlaid_img = tfk.preprocessing.image.array_to_img(overlaid_img) |
|
return overlaid_img |
|
|
|
|
|
if __name__ == '__main__': |
|
|
|
examples = sorted(glob(os.path.join('examples', '*.jpg'))) |
|
idx2lab, lab2idx = get_imagenet_classes() |
|
|
|
model, grad_model, preprocessor, decode_predictions = get_xception_model() |
|
|
|
img_4d_array = get_img_4d_array(examples[0]) |
|
img_4d_array = preprocessor(img_4d_array) |
|
|
|
heatmap = make_gradcam_heatmap(grad_model, img_4d_array, pred_idx=None) |
|
|
|
img_pil = align_image_with_heatmap( |
|
img_4d_array, heatmap, alpha=0.3, cmap='jet') |
|
|
|
img_pil.save('test.jpg') |
|
print('done') |