| """ |
| dl_module.py - Deep Learning Module |
| Image classification using pretrained MobileNetV2/ResNet50 + OpenCV object detection |
| """ |
|
|
| import streamlit as st |
| import numpy as np |
| import cv2 |
| import io |
| import warnings |
| warnings.filterwarnings("ignore") |
|
|
| from PIL import Image |
|
|
| |
|
|
| def _load_tf_model(model_name): |
| """Load a Keras pretrained model.""" |
| import tensorflow as tf |
| from tensorflow.keras.applications import MobileNetV2, ResNet50, VGG16 |
| from tensorflow.keras.applications.mobilenet_v2 import preprocess_input as mn_pre, decode_predictions as mn_dec |
| from tensorflow.keras.applications.resnet50 import preprocess_input as rn_pre, decode_predictions as rn_dec |
| from tensorflow.keras.applications.vgg16 import preprocess_input as vg_pre, decode_predictions as vg_dec |
|
|
| models_map = { |
| "MobileNetV2": (MobileNetV2, mn_pre, mn_dec, (224, 224)), |
| "ResNet50": (ResNet50, rn_pre, rn_dec, (224, 224)), |
| "VGG16": (VGG16, vg_pre, vg_dec, (224, 224)), |
| } |
| ModelClass, preprocess, decode, size = models_map[model_name] |
| model = ModelClass(weights="imagenet") |
| return model, preprocess, decode, size |
|
|
|
|
| def _classify_image_tf(image_pil, model_name): |
| """Classify an image using TF/Keras pretrained model.""" |
| import numpy as np |
| from tensorflow.keras.preprocessing.image import img_to_array |
|
|
| model, preprocess, decode, (h, w) = _load_tf_model(model_name) |
| img = image_pil.convert("RGB").resize((w, h)) |
| arr = img_to_array(img) |
| arr = np.expand_dims(arr, axis=0) |
| arr = preprocess(arr) |
| preds = model.predict(arr, verbose=0) |
| top = decode(preds, top=5)[0] |
| results = [{"Rank": i+1, "Label": label.replace("_", " ").title(), |
| "Confidence": f"{prob*100:.2f}%", "Score": round(prob, 4)} |
| for i, (_, label, prob) in enumerate(top)] |
| return results |
|
|
|
|
| def _classify_image_torch(image_pil, model_name): |
| """Classify an image using PyTorch pretrained model.""" |
| import torch |
| import torchvision.transforms as T |
| import torchvision.models as models_tv |
| import json |
| import urllib.request |
|
|
| |
| LABELS_URL = "https://raw.githubusercontent.com/anishathalye/imagenet-simple-labels/master/imagenet-simple-labels.json" |
| try: |
| with urllib.request.urlopen(LABELS_URL, timeout=5) as r: |
| class_labels = json.load(r) |
| except Exception: |
| class_labels = [str(i) for i in range(1000)] |
|
|
| torch_models = { |
| "MobileNetV2": models_tv.mobilenet_v2, |
| "ResNet50": models_tv.resnet50, |
| } |
| model_fn = torch_models.get(model_name, models_tv.mobilenet_v2) |
| model = model_fn(pretrained=True) |
| model.eval() |
|
|
| transform = T.Compose([ |
| T.Resize(256), |
| T.CenterCrop(224), |
| T.ToTensor(), |
| T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]), |
| ]) |
|
|
| img = image_pil.convert("RGB") |
| tensor = transform(img).unsqueeze(0) |
| with torch.no_grad(): |
| output = model(tensor) |
| probs = torch.nn.functional.softmax(output[0], dim=0) |
|
|
| top_probs, top_idxs = torch.topk(probs, 5) |
| results = [] |
| for i, (prob, idx) in enumerate(zip(top_probs, top_idxs)): |
| label = class_labels[idx.item()] if idx.item() < len(class_labels) else str(idx.item()) |
| results.append({ |
| "Rank": i+1, |
| "Label": label.replace("_", " ").title(), |
| "Confidence": f"{prob.item()*100:.2f}%", |
| "Score": round(prob.item(), 4), |
| }) |
| return results |
|
|
|
|
| def detect_edges_opencv(image_pil): |
| """Apply Canny edge detection using OpenCV.""" |
| img_array = np.array(image_pil.convert("RGB")) |
| gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY) |
| blurred = cv2.GaussianBlur(gray, (5, 5), 0) |
| edges = cv2.Canny(blurred, threshold1=50, threshold2=150) |
| return edges |
|
|
|
|
| def detect_faces_opencv(image_pil): |
| """Detect faces using Haar Cascade classifier.""" |
| img_array = np.array(image_pil.convert("RGB")) |
| img_bgr = cv2.cvtColor(img_array, cv2.COLOR_RGB2BGR) |
| gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY) |
|
|
| cascade_path = cv2.data.haarcascades + "haarcascade_frontalface_default.xml" |
| face_cascade = cv2.CascadeClassifier(cascade_path) |
| faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30)) |
|
|
| result_img = img_array.copy() |
| for (x, y, w, h) in faces: |
| cv2.rectangle(result_img, (x, y), (x+w, y+h), (0, 200, 255), 2) |
| cv2.putText(result_img, "Face", (x, y-8), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 200, 255), 2) |
| return result_img, len(faces) |
|
|
|
|
| def apply_image_filters(image_pil): |
| """Apply various OpenCV image processing filters and return dict of results.""" |
| img = np.array(image_pil.convert("RGB")) |
| gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) |
| blurred = cv2.GaussianBlur(img, (15, 15), 0) |
| sharpened = cv2.addWeighted(img, 1.5, blurred, -0.5, 0) |
| thresh = cv2.adaptiveThreshold( |
| gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2 |
| ) |
| contours_img = img.copy() |
| contours, _ = cv2.findContours( |
| cv2.Canny(gray, 50, 150), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE |
| ) |
| cv2.drawContours(contours_img, contours, -1, (0, 255, 120), 1) |
|
|
| return { |
| "Grayscale": gray, |
| "Blurred": blurred, |
| "Sharpened": sharpened, |
| "Threshold": thresh, |
| "Contours": contours_img, |
| } |
|
|
|
|
| |
|
|
| def render_dl_module(): |
| st.header("π§ Deep Learning Module") |
| st.markdown("Upload an image to classify it with pretrained CNNs or run OpenCV computer vision pipelines.") |
|
|
| uploaded = st.file_uploader("Upload Image (JPG/PNG)", type=["jpg", "jpeg", "png"], key="dl_upload") |
|
|
| if uploaded is None: |
| st.info("π Upload an image (JPG or PNG) to begin. Try uploading a photo of an animal, vehicle, or everyday object.") |
| return |
|
|
| image_pil = Image.open(uploaded) |
| st.image(image_pil, caption="Uploaded Image", use_column_width=True) |
|
|
| tabs = st.tabs(["π·οΈ Image Classification", "ποΈ OpenCV Analysis", "π¨ Image Filters"]) |
|
|
| |
| with tabs[0]: |
| st.subheader("Image Classification (ImageNet)") |
|
|
| backend = st.radio("Choose Backend", ["TensorFlow/Keras", "PyTorch"], horizontal=True) |
| if backend == "TensorFlow/Keras": |
| model_choice = st.selectbox("Model", ["MobileNetV2", "ResNet50", "VGG16"]) |
| else: |
| model_choice = st.selectbox("Model", ["MobileNetV2", "ResNet50"]) |
|
|
| if st.button("π Classify Image", type="primary"): |
| with st.spinner(f"Running {model_choice} inference..."): |
| try: |
| if backend == "TensorFlow/Keras": |
| results = _classify_image_tf(image_pil, model_choice) |
| else: |
| results = _classify_image_torch(image_pil, model_choice) |
|
|
| import pandas as pd |
| import matplotlib.pyplot as plt |
|
|
| st.success(f"β
Top prediction: **{results[0]['Label']}** ({results[0]['Confidence']})") |
| st.subheader("Top 5 Predictions") |
| df_preds = pd.DataFrame(results) |
| st.dataframe(df_preds, use_container_width=True) |
|
|
| |
| fig, ax = plt.subplots(figsize=(8, 4)) |
| labels = [r["Label"][:30] for r in results] |
| scores = [r["Score"] for r in results] |
| colors = ["#0ea5e9" if i == 0 else "#334155" for i in range(len(scores))] |
| bars = ax.barh(labels[::-1], scores[::-1], color=colors[::-1]) |
| ax.set_xlabel("Confidence Score") |
| ax.set_title("Top 5 Predictions") |
| ax.set_xlim(0, max(scores) * 1.2) |
| for bar, score in zip(bars, scores[::-1]): |
| ax.text(bar.get_width() + 0.005, bar.get_y() + bar.get_height()/2, |
| f"{score*100:.1f}%", va="center", fontsize=9) |
| plt.tight_layout() |
| st.pyplot(fig) |
|
|
| except Exception as e: |
| st.error(f"Classification failed: {e}") |
| st.info("Make sure TensorFlow or PyTorch is installed. Run: `pip install tensorflow` or `pip install torch torchvision`") |
|
|
| |
| with tabs[1]: |
| st.subheader("OpenCV Computer Vision") |
|
|
| cv_task = st.selectbox("Select Analysis", ["Edge Detection", "Face Detection"]) |
|
|
| if st.button("βΆ Run OpenCV Analysis", type="primary"): |
| with st.spinner("Processing with OpenCV..."): |
| if cv_task == "Edge Detection": |
| edges = detect_edges_opencv(image_pil) |
| col1, col2 = st.columns(2) |
| with col1: |
| st.image(image_pil, caption="Original", use_column_width=True) |
| with col2: |
| st.image(edges, caption="Canny Edge Detection", use_column_width=True, clamp=True) |
| st.info(f"Detected approximately **{np.sum(edges > 0):,}** edge pixels.") |
|
|
| elif cv_task == "Face Detection": |
| result_img, face_count = detect_faces_opencv(image_pil) |
| col1, col2 = st.columns(2) |
| with col1: |
| st.image(image_pil, caption="Original", use_column_width=True) |
| with col2: |
| st.image(result_img, caption="Face Detection", use_column_width=True) |
| if face_count > 0: |
| st.success(f"β
Detected **{face_count}** face(s).") |
| else: |
| st.warning("No faces detected. Try a clear portrait photo.") |
|
|
| |
| with tabs[2]: |
| st.subheader("OpenCV Image Processing Filters") |
| if st.button("π¨ Apply All Filters", type="primary"): |
| with st.spinner("Applying filters..."): |
| filters = apply_image_filters(image_pil) |
| cols = st.columns(3) |
| for i, (name, img) in enumerate(filters.items()): |
| with cols[i % 3]: |
| if len(img.shape) == 2: |
| st.image(img, caption=name, use_column_width=True, clamp=True) |
| else: |
| st.image(img, caption=name, use_column_width=True) |