import gradio as gr import tensorflow as tf from tensorflow.keras.models import load_model from tensorflow.keras.preprocessing import image import numpy as np import cv2 import PIL.Image import traceback from io import BytesIO # Function to generate a simple explanation based on the saliency map and prediction def generate_explanation(model_prediction, confidence): explanation = ( f"The model predicts the tumor type is '{model_prediction}' with a confidence of {confidence * 100:.2f}%. " "This prediction is based on the highlighted regions of the MRI scan which contributed most to the decision." ) return explanation def generate_saliency_map(model, img_array, class_index, img_size): with tf.GradientTape() as tape: img_tensor = tf.convert_to_tensor(img_array) tape.watch(img_tensor) predictions = model(img_tensor) target_class = predictions[:, class_index] gradients = tape.gradient(target_class, img_tensor) gradients = tf.math.abs(gradients) gradients = tf.reduce_max(gradients, axis=-1) gradients = gradients.numpy().squeeze() gradients = cv2.resize(gradients, img_size) # Create a circular mask to focus on the brain region center = (gradients.shape[0] // 2, gradients.shape[1] // 2) radius = min(center[0], center[1]) - 10 y, x = np.ogrid[:gradients.shape[0], :gradients.shape[1]] mask = (x - center[0])**2 + (y - center[1])**2 <= radius**2 gradients = gradients * mask # Normalize the gradients within the brain region brain_gradients = gradients[mask] if brain_gradients.max() > brain_gradients.min(): brain_gradients = (brain_gradients - brain_gradients.min()) / (brain_gradients.max() - brain_gradients.min()) gradients[mask] = brain_gradients # Apply thresholding to highlight important regions threshold = np.percentile(gradients[mask], 80) gradients[gradients < threshold] = 0 # Apply Gaussian blur to smooth the saliency map gradients = cv2.GaussianBlur(gradients, (11, 11), 0) # Create a heatmap from the gradients heatmap = cv2.applyColorMap(np.uint8(255 * gradients), cv2.COLORMAP_JET) heatmap = cv2.cvtColor(heatmap, cv2.COLOR_BGR2RGB) heatmap = cv2.resize(heatmap, img_size) # Superimpose the heatmap on the original image original_img = image.img_to_array(PIL.Image.fromarray((img_array[0] * 255).astype(np.uint8))) superimposed_img = heatmap * 0.7 + original_img * 0.3 superimposed_img = superimposed_img.astype(np.uint8) return superimposed_img def load_xception_model(model_path): img_shape = (299, 299, 3) base_model = tf.keras.applications.Xception(include_top=False, weights="imagenet", input_shape=img_shape, pooling='max') model = tf.keras.Sequential([ base_model, tf.keras.layers.Flatten(), tf.keras.layers.Dropout(rate=0.3), tf.keras.layers.Dense(128, activation='relu'), tf.keras.layers.Dropout(rate=0.25), tf.keras.layers.Dense(4, activation='softmax') ]) model.compile(optimizer=tf.keras.optimizers.Adamax(learning_rate=0.001), loss='categorical_crossentropy', metrics=['accuracy', tf.keras.metrics.Precision(), tf.keras.metrics.Recall()]) model.load_weights(model_path) return model def classify_brain_tumor(image_file, model_choice): try: # Load the selected model if model_choice == "Transfer Learning - Xception": model = load_xception_model('xception_model.weights.h5') img_size = (299, 299) else: model = load_model('cnn_model.h5') img_size = (224, 224) labels = ['Glioma', 'Meningioma', 'No Tumor', 'Pituitary'] # Preprocess the input image img = image.load_img(image_file, target_size=img_size) img_array = image.img_to_array(img) img_array = np.expand_dims(img_array, axis=0) img_array /= 255.0 # Make the prediction prediction = model.predict(img_array) class_index = np.argmax(prediction[0]) result = labels[class_index] confidence = prediction[0][class_index] # Generate the saliency map saliency_map = generate_saliency_map(model, img_array, class_index, img_size) # Generate the explanation explanation = generate_explanation(result, confidence) # Prepare probabilities for all classes probabilities = prediction[0] prob_dict = dict(zip(labels, probabilities)) # Return the outputs in the expected order return [ result, confidence, saliency_map, explanation, "", # Empty string for Logs prob_dict # For displaying probabilities ] except Exception as e: # Return error information return [ "Error", 0.0, None, "", f"Error: {str(e)}\nTraceback:\n{traceback.format_exc()}", {} # Empty probabilities ] def main(): # Define the interface interface = gr.Interface( fn=classify_brain_tumor, inputs=[ gr.Image(type="filepath"), gr.Radio(choices=["Transfer Learning - Xception", "Custom CNN"], label="Select Model") ], outputs=[ gr.Textbox(label="Prediction"), gr.Number(label="Confidence", precision=2), gr.Image(type="numpy", label="Saliency Map"), gr.Textbox(label="Explanation"), gr.Textbox(label="Logs"), gr.Label(num_top_classes=4, label="Class Probabilities") ], title="Brain Tumor Classification", description="Upload an MRI scan image to classify the tumor and view saliency maps with model explanations.", ) interface.launch() if __name__ == "__main__": main()