import streamlit as st import tensorflow as tf import numpy as np from PIL import Image import os # === Fix font/matplotlib warnings for Hugging Face === os.environ["MPLCONFIGDIR"] = "/tmp/matplotlib" os.environ["XDG_CACHE_HOME"] = "/tmp" # === Custom loss and metrics === def weighted_dice_loss(y_true, y_pred): smooth = 1e-6 y_true_f = tf.reshape(y_true, [-1]) y_pred_f = tf.reshape(y_pred, [-1]) intersection = tf.reduce_sum(y_true_f * y_pred_f) return 1 - ((2. * intersection + smooth) / (tf.reduce_sum(y_true_f) + tf.reduce_sum(y_pred_f) + smooth)) def iou_metric(y_true, y_pred): y_true = tf.cast(y_true > 0.5, tf.float32) y_pred = tf.cast(y_pred > 0.5, tf.float32) intersection = tf.reduce_sum(y_true * y_pred) union = tf.reduce_sum(y_true) + tf.reduce_sum(y_pred) - intersection return intersection / (union + 1e-6) def bce_loss(y_true, y_pred): return tf.keras.losses.binary_crossentropy(y_true, y_pred) # === Load model === model_path = "final_model_after_third_iteration_WDL0.07_0.5155/" @st.cache_resource def load_model(): return tf.keras.models.load_model( model_path, custom_objects={ "weighted_dice_loss": weighted_dice_loss, "iou_metric": iou_metric, "bce_loss": bce_loss } ) model = load_model() # === Streamlit UI === st.title("🕳️ Sinkhole Segmentation with EffV2-UNet") uploaded_image = st.file_uploader("Upload an image", type=["png", "jpg", "jpeg"]) if uploaded_image: image = Image.open(uploaded_image).convert("RGB") st.image(image, caption="Original Image", use_column_width=True) # Preprocess and predict resized = image.resize((512, 512)) x = np.expand_dims(np.array(resized) / 255.0, axis=0) y = model.predict(x)[0, :, :, 0] y_norm = (y - y.min()) / (y.max() - y.min() + 1e-6) mask = (y_norm * 255).astype(np.uint8) result = Image.fromarray(mask) st.image(result, caption="Predicted Segmentation", use_column_width=True)