import cv2
import numpy as np
from PIL import Image
import tensorflow as tf
import matplotlib.pyplot as plt
from skimage.transform import resize
from tensorflow.keras.models import Model
from tensorflow.keras.preprocessing.image import load_img, img_to_array
from tensorflow.keras.applications import EfficientNetB4
from tensorflow.keras.applications import MobileNetV2
import json
import random
import string

from google.cloud import storage

bucket_name = "postmanhack"


def random_char(y):
    return "".join(random.choice(string.ascii_letters) for x in range(y))


def upload_blob(bucket_name, filename, dest_filename):
    """Uploads a file to the bucket."""
    client = storage.Client()
    bucket = client.get_bucket(bucket_name)
    blob = bucket.blob(dest_filename)
    try:
        blob.delete()
    except:
        pass
    blob.upload_from_filename(filename)
    blob.make_public()
    return blob.public_url


def viz_grad_cam(model, image, interpolant=0.5):
    """VizGradCAM - Displays GradCAM based on Keras / TensorFlow models
    using the gradients from the last convolutional layer. This function
    should work with all Keras Application listed here:
    https://keras.io/api/applications/
    Parameters:
    model (keras.model): Compiled Model with Weights Loaded
    image: Image to Perform Inference On
    plot_results (boolean): True - Function Plots using PLT
                            False - Returns Heatmap Array
    Returns:
    Heatmap Array?
    """

    # Sanity Check
    assert 0 < interpolant < 1, "Heatmap Interpolation Must Be Between 0 - 1"

    last_conv_layer = next(
        x for x in model.layers[::-1] if isinstance(x, tf.keras.layers.Conv2D)
    )
    target_layer = model.get_layer(last_conv_layer.name)

    original_img = image
    img = np.expand_dims(original_img, axis=0)
    prediction = model.predict(img)

    # Obtain Prediction Index
    prediction_idx = np.argmax(prediction)

    # Compute Gradient of Top Predicted Class
    with tf.GradientTape() as tape:
        gradient_model = Model([model.inputs], [target_layer.output, model.output])
        conv2d_out, prediction = gradient_model(img)
        # Obtain the Prediction Loss
        loss = prediction[:, prediction_idx]

    # Gradient() computes the gradient using operations recorded
    # in context of this tape
    gradients = tape.gradient(loss, conv2d_out)

    # Obtain the Output from Shape [1 x H x W x CHANNEL] -> [H x W x CHANNEL]
    output = conv2d_out[0]

    # Obtain Depthwise Mean
    weights = tf.reduce_mean(gradients[0], axis=(0, 1))

    # Create a 7x7 Map for Aggregation
    activation_map = np.zeros(output.shape[0:2], dtype=np.float32)

    # Multiply Weights with Every Layer
    for idx, weight in enumerate(weights):
        activation_map += weight * output[:, :, idx]

    # Resize to Size of Image
    activation_map = cv2.resize(
        activation_map.numpy(), (original_img.shape[1], original_img.shape[0])
    )

    # Ensure No Negative Numbers
    activation_map = np.maximum(activation_map, 0)

    # Convert Class Activation Map to 0 - 255
    activation_map = (activation_map - activation_map.min()) / (
        activation_map.max() - activation_map.min()
    )
    activation_map = np.uint8(255 * activation_map)

    # Convert to Heatmap
    heatmap = cv2.applyColorMap(activation_map, cv2.COLORMAP_JET)

    # Superimpose Heatmap on Image Data
    original_img = np.uint8(
        (original_img - original_img.min())
        / (original_img.max() - original_img.min())
        * 255
    )

    cvt_heatmap = cv2.cvtColor(heatmap, cv2.COLOR_BGR2RGB)

    # Enlarge Plot
    plt.rcParams["figure.dpi"] = 100

    final_image = np.uint8(original_img * interpolant + cvt_heatmap * (1 - interpolant))
    plt.imsave("/tmp/finalimage.png", final_image)

    return True


def conv_vis(request):
    global bucket_name

    request_json = request.get_json(silent=True)
    request_args = request.args

    if request_json and "image" in request_json:
        url = request_json["image"]
    elif request_args and "image" in request_args:
        url = request_args["image"]
    else:
        url = "https://i.imgur.com/taUKyu1.jpg"

    print(url)

    if request_json and "destination" in request_json:
        dest = request_json["destination"]
    elif request_args and "destination" in request_args:
        dest = request_args["destination"]
    else:
        dest = random_char(7) + ".png"

    image_path = tf.keras.utils.get_file("/tmp/image.png", url)
    test_img = img_to_array(load_img(image_path, target_size=(224, 224)))

    viz_grad_cam(MobileNetV2(weights="imagenet"), test_img)  # load model

    public_url = upload_blob(bucket_name, "/tmp/finalimage.png", dest)

    response = {}
    response["output_image"] = public_url
    response = json.dumps(response)

    return response

import cv2
import numpy as np
from PIL import Image
import tensorflow as tf
import matplotlib.pyplot as plt
from skimage.transform import resize
from tensorflow.keras.models import Model
from tensorflow.keras.preprocessing.image import load_img, img_to_array
from tensorflow.keras.applications import EfficientNetB4
from tensorflow.keras.applications import MobileNetV2
import json
import random
import string

from google.cloud import storage

bucket_name = "postmanhack"


def random_char(y):
    return "".join(random.choice(string.ascii_letters) for x in range(y))


def upload_blob(bucket_name, filename, dest_filename):
    """Uploads a file to the bucket."""
    client = storage.Client()
    bucket = client.get_bucket(bucket_name)
    blob = bucket.blob(dest_filename)
    try:
        blob.delete()
    except:
        pass
    blob.upload_from_filename(filename)
    blob.make_public()
    return blob.public_url


def viz_grad_cam(model, image, interpolant=0.5):
    """VizGradCAM - Displays GradCAM based on Keras / TensorFlow models
    using the gradients from the last convolutional layer. This function
    should work with all Keras Application listed here:
    https://keras.io/api/applications/
    Parameters:
    model (keras.model): Compiled Model with Weights Loaded
    image: Image to Perform Inference On
    plot_results (boolean): True - Function Plots using PLT
                            False - Returns Heatmap Array
    Returns:
    Heatmap Array?
    """

    # Sanity Check
    assert 0 < interpolant < 1, "Heatmap Interpolation Must Be Between 0 - 1"

    last_conv_layer = next(
        x for x in model.layers[::-1] if isinstance(x, tf.keras.layers.Conv2D)
    )
    target_layer = model.get_layer(last_conv_layer.name)

    original_img = image
    img = np.expand_dims(original_img, axis=0)
    prediction = model.predict(img)

    # Obtain Prediction Index
    prediction_idx = np.argmax(prediction)

    # Compute Gradient of Top Predicted Class
    with tf.GradientTape() as tape:
        gradient_model = Model([model.inputs], [target_layer.output, model.output])
        conv2d_out, prediction = gradient_model(img)
        # Obtain the Prediction Loss
        loss = prediction[:, prediction_idx]

    # Gradient() computes the gradient using operations recorded
    # in context of this tape
    gradients = tape.gradient(loss, conv2d_out)

    # Obtain the Output from Shape [1 x H x W x CHANNEL] -> [H x W x CHANNEL]
    output = conv2d_out[0]

    # Obtain Depthwise Mean
    weights = tf.reduce_mean(gradients[0], axis=(0, 1))

    # Create a 7x7 Map for Aggregation
    activation_map = np.zeros(output.shape[0:2], dtype=np.float32)

    # Multiply Weights with Every Layer
    for idx, weight in enumerate(weights):
        activation_map += weight * output[:, :, idx]

    # Resize to Size of Image
    activation_map = cv2.resize(
        activation_map.numpy(), (original_img.shape[1], original_img.shape[0])
    )

    # Ensure No Negative Numbers
    activation_map = np.maximum(activation_map, 0)

    # Convert Class Activation Map to 0 - 255
    activation_map = (activation_map - activation_map.min()) / (
        activation_map.max() - activation_map.min()
    )
    activation_map = np.uint8(255 * activation_map)

    # Convert to Heatmap
    heatmap = cv2.applyColorMap(activation_map, cv2.COLORMAP_JET)

    # Superimpose Heatmap on Image Data
    original_img = np.uint8(
        (original_img - original_img.min())
        / (original_img.max() - original_img.min())
        * 255
    )

    cvt_heatmap = cv2.cvtColor(heatmap, cv2.COLOR_BGR2RGB)

    # Enlarge Plot
    plt.rcParams["figure.dpi"] = 100

    final_image = np.uint8(original_img * interpolant + cvt_heatmap * (1 - interpolant))
    plt.imsave("/tmp/finalimage.png", final_image)

    return True


def conv_vis(request):
    global bucket_name

    request_json = request.get_json(silent=True)
    request_args = request.args

    if request_json and "image" in request_json:
        url = request_json["image"]
    elif request_args and "image" in request_args:
        url = request_args["image"]
    else:
        url = "https://i.imgur.com/taUKyu1.jpg"

    print(url)

    if request_json and "destination" in request_json:
        dest = request_json["destination"]
    elif request_args and "destination" in request_args:
        dest = request_args["destination"]
    else:
        dest = random_char(7) + ".png"

    image_path = tf.keras.utils.get_file("/tmp/image.png", url)
    test_img = img_to_array(load_img(image_path, target_size=(224, 224)))

    viz_grad_cam(MobileNetV2(weights="imagenet"), test_img)  # load model

    public_url = upload_blob(bucket_name, "/tmp/finalimage.png", dest)

    response = {}
    response["output_image"] = public_url
    response = json.dumps(response)

    return response

Uploads a file to the bucket. VizGradCAM - Displays GradCAM based on Keras / TensorFlow models     using the gradients from the last convolutional layer. This function     should work with all Keras Application listed here:     https://keras.io/api/applications/     Parameters:     model (keras.model): Compiled Model with Weights Loaded     image: Image to Perform Inference On     plot_results (boolean): True - Function Plots using PLT                             False - Returns Heatmap Array     Returns:     Heatmap Array? # Sanity Check # Obtain Prediction Index # Compute Gradient of Top Predicted Class # Obtain the Prediction Loss # Gradient() computes the gradient using operations recorded # in context of this tape # Obtain the Output from Shape [1 x H x W x CHANNEL] -> [H x W x CHANNEL] # Obtain Depthwise Mean # Create a 7x7 Map for Aggregation # Multiply Weights with Every Layer # Resize to Size of Image # Ensure No Negative Numbers # Convert Class Activation Map to 0 - 255 # Convert to Heatmap # Superimpose Heatmap on Image Data # Enlarge Plot # load model