smartvision-models / src /streamlit_app.py
malaychand's picture
Fix: absolute paths and libGL dependencies
60e56c1
import streamlit as st
import os
"""
# Welcome to Streamlit!
Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
forums](https://discuss.streamlit.io).
In the meantime, below is an example of what you can do with just a few lines of code:
"""
# โœ… MUST be the very first Streamlit call
st.set_page_config(
page_title="SmartVision",
layout="wide",
page_icon="๐Ÿ‘๏ธ"
)
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# HERO WITH IMAGE
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
st.markdown(
"""
<div style="
background: linear-gradient(135deg, #0f172a, #111827);
border-radius: 18px;
padding: 30px 30px 40px 30px;
margin-bottom: 28px;
text-align: center;
">
""",
unsafe_allow_html=True,
)
# โœ… FIX 1: Use forward slash (works on Linux/Mac/Windows)
# image_path = "models/smartvision.png"
import os
APP_ROOT = "/app"
image_path = os.path.join(APP_ROOT, "models", "smartvision.png")
# โœ… FIX 2: Check if file exists before loading to give a clear error
if os.path.exists(image_path):
st.image(image_path, use_container_width=True)
else:
st.warning(f"โš ๏ธ Image not found at `{image_path}`. Please ensure the file is committed to your repository.")
st.markdown(
"""
<div style="margin-top:20px;">
<p style="font-size:16px; color:#9ca3af; margin:0;">
Unified Image Classification & Object Detection Platform
</p>
<p style="font-size:13px; color:#6b7280; margin-top:10px;">
25-Class COCO ยท YOLOv8 ยท VGG16 ยท ResNet50 ยท MobileNetV2 ยท EfficientNetB0
</p>
</div>
</div>
""",
unsafe_allow_html=True,
)
st.info("๐Ÿ“Œ Use the **sidebar** to navigate between pages.")
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# SECTION 1 โ€” TOP STATS
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
st.markdown("### ๐Ÿ“Š Platform Overview")
c1, c2, c3, c4, c5 = st.columns(5)
stats = [
("๐ŸŽฏ Classes", "25", "#2ecc71"),
("๐Ÿง  CNN Models", "4", "#3498db"),
("๐Ÿ” Detection", "YOLOv8s", "#f39c12"),
("๐Ÿ“ฆ Dataset", "COCO", "#9b59b6"),
("โ˜๏ธ Deployment", "HF Spaces", "#1abc9c"),
]
for col, (label, val, color) in zip([c1, c2, c3, c4, c5], stats):
with col:
st.markdown(
f"""<div style="
border:2px solid {color}55; border-radius:12px;
padding:16px 10px; text-align:center; background:{color}0d;
">
<p style="font-size:11px;color:#888;margin:0;font-weight:600;
letter-spacing:1px;text-transform:uppercase;">{label}</p>
<p style="font-size:20px;font-weight:800;color:{color};margin:6px 0 0;">{val}</p>
</div>""",
unsafe_allow_html=True,
)
st.divider()
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# SECTION 2 โ€” KEY FEATURES
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
st.markdown("### ๐Ÿš€ Key Features")
feat_col1, feat_col2 = st.columns(2)
features_left = [
("๐Ÿ–ผ๏ธ", "Single-object image classification", "Upload any image โ€” all 4 CNNs classify it simultaneously with Top-5 predictions."),
("๐Ÿ“ฆ", "Multi-object detection with bounding boxes","YOLOv8 detects and localises multiple objects in a single frame."),
("๐ŸŽจ", "Color-coded per-class visualisation", "Each of 25 classes has a unique bounding box color for instant recognition."),
]
features_right = [
("๐Ÿ“Š", "Interactive performance dashboard", "Live charts, confusion matrices, training curves, and per-class mAP breakdown."),
("๐Ÿ“ท", "Live webcam detection", "Real-time YOLOv8 inference on webcam feed with FPS overlay (local only)."),
]
for col, feats in zip([feat_col1, feat_col2], [features_left, features_right]):
with col:
for icon, title, desc in feats:
st.markdown(
f"""<div style="
display:flex; gap:12px; padding:12px 14px;
border:1.5px solid #dee2e6; border-radius:10px;
margin-bottom:10px; background:#fafbfc;
">
<span style="font-size:22px;flex-shrink:0;">{icon}</span>
<div>
<p style="font-size:13px;font-weight:700;margin:0 0 2px;">{title}</p>
<p style="font-size:12px;color:#666;margin:0;">{desc}</p>
</div>
</div>""",
unsafe_allow_html=True,
)
st.divider()
# =====================================================
# QUICK NAVIGATION BUTTONS
# =====================================================
st.header("๐Ÿš€ Explore Features")
col1, col2 = st.columns(2)
with col1:
if st.button("๐Ÿ–ผ๏ธ Image Classification", use_container_width=True):
st.switch_page("pages/image_classification.py")
if st.button("๐Ÿ“ˆ CNN Performance", use_container_width=True):
st.switch_page("pages/model_performance.py")
with col2:
if st.button("๐Ÿ” Object Detection", use_container_width=True):
st.switch_page("pages/yolo_object_detection.py")
if st.button("๐Ÿ“Š YOLO Performance", use_container_width=True):
st.switch_page("pages/yolo_performance.py")
st.divider()
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# SECTION 3 โ€” MODELS USED
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
st.markdown("### ๐Ÿค– Models Used")
# โ”€โ”€ YOLOv8 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
st.markdown("#### ๐Ÿ” Object Detection")
yolo_col, yolo_info = st.columns([1, 3])
with yolo_col:
st.markdown(
"""<div style="
background:linear-gradient(135deg,#f39c1222,#f39c1211);
border:2px solid #f39c1266; border-radius:14px;
padding:20px; text-align:center;
">
<p style="font-size:36px;margin:0;">โšก</p>
<p style="font-size:16px;font-weight:800;color:#f39c12;margin:8px 0 2px;">YOLOv8s</p>
<p style="font-size:11px;color:#888;margin:0;">Ultralytics</p>
</div>""",
unsafe_allow_html=True,
)
with yolo_info:
chips = [
("ARCHITECTURE", "YOLOv8 Small (YOLOv8s)"),
("PRETRAINED ON", "COCO (80 classes)"),
("FINE-TUNED ON", "25-Class COCO Subset"),
("EPOCHS", "50"),
("IMAGE SIZE", "640 ร— 640"),
("mAP@0.5", "0.893"),
("PRECISION", "95.1%"),
("RECALL", "87.7%"),
("INFERENCE", "~6.7 ms ยท ~120 FPS"),
]
chips_html = "".join(
f'<span style="display:inline-block;background:#f8f9fa;border:1px solid #dee2e6;'
f'border-radius:8px;padding:5px 10px;font-size:12px;margin:3px 4px 3px 0;">'
f'<span style="color:#888;font-size:10px;display:block;font-weight:600;'
f'letter-spacing:.5px;text-transform:uppercase;">{k}</span>'
f'<span style="font-weight:700;color:#f39c12;">{v}</span></span>'
for k, v in chips
)
st.markdown(
f"""<div style="border:1.5px solid #f39c1233;border-radius:12px;
padding:14px 16px;background:#f39c1208;">
<div style="margin-bottom:10px;">{chips_html}</div>
<p style="font-size:12px;color:#666;margin:0;line-height:1.6;">
YOLOv8s is Ultralytics' small variant โ€” optimised for the best balance between
accuracy and inference speed. Fine-tuned with mosaic augmentation,
auto-optimizer (AdamW), and AMP mixed precision.
</p>
</div>""",
unsafe_allow_html=True,
)
st.markdown("<br>", unsafe_allow_html=True)
# โ”€โ”€ CNN Models โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
st.markdown("#### ๐Ÿง  Image Classification โ€” CNN Models")
CNN_MODELS = [
{"name": "EfficientNetB0", "icon": "๐ŸŸจ", "color": "#f39c12", "highlight": "Best Accuracy"},
{"name": "ResNet50", "icon": "๐ŸŸฅ", "color": "#e74c3c", "highlight": "Best All-Rounder"},
{"name": "MobileNetV2", "icon": "๐ŸŸฉ", "color": "#2ecc71", "highlight": "Fastest Inference"},
{"name": "VGG16", "icon": "๐ŸŸฆ", "color": "#3498db", "highlight": "Reliable Baseline"},
]
for model in CNN_MODELS:
st.markdown(
f"""
<div style="
padding:10px 14px;
margin-bottom:8px;
border-radius:10px;
background:#f8f9fa;
border-left:5px solid {model['color']};
">
<span style="font-size:18px;">{model['icon']}</span>
<span style="font-weight:700;font-size:15px;color:#000000;">
{model['name']}
</span>
<span style="font-size:11px;margin-left:8px;color:{model['color']};font-weight:600;">
{model['highlight']}
</span>
</div>
""",
unsafe_allow_html=True
)
st.divider()
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# SECTION 4 โ€” DATASET + CLASSES
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
st.markdown("### ๐Ÿ“ฆ Dataset")
d1, d2 = st.columns([1, 2])
with d1:
dataset_rows = [
("Total Images", "2,500"),
("Images / Class", "100"),
("Train Split", "70% ยท 1,750 images"),
("Val Split", "15% ยท 375 images"),
("Test Split", "15% ยท 375 images"),
("Image Size", "224 ร— 224 px"),
("Annotation", "COCO Bounding Boxes"),
("Source", "Hugging Face (streaming)"),
]
rows_html = "".join(
f'<div style="display:flex;justify-content:space-between;align-items:center;'
f'padding:8px 14px;border-bottom:1px solid #f0f0f0;font-size:12px;">'
f'<span style="color:#666;font-weight:500;">{k}</span>'
f'<span style="font-weight:700;color:#2c3e50;">{v}</span></div>'
for k, v in dataset_rows
)
st.markdown(
f'<div style="border:1.5px solid #dee2e6;border-radius:12px;overflow:hidden;">'
f'<div style="background:#2c3e50;padding:10px 14px;">'
f'<p style="color:#fff;font-weight:700;font-size:14px;margin:0;">COCO-25 Subset</p></div>'
f'{rows_html}</div>',
unsafe_allow_html=True,
)
with d2:
st.markdown("**25 Supported Classes:**")
CLASS_NAMES = [
'person','bicycle','car','motorcycle','airplane','bus','train','truck',
'traffic light','stop sign','bench','bird','cat','dog','horse','cow',
'elephant','bottle','cup','bowl','pizza','cake','chair','couch','potted plant',
]
ICONS = {
'person':'๐Ÿง','bicycle':'๐Ÿšฒ','car':'๐Ÿš—','motorcycle':'๐Ÿ๏ธ','airplane':'โœˆ๏ธ',
'bus':'๐ŸšŒ','train':'๐Ÿš†','truck':'๐Ÿš›','traffic light':'๐Ÿšฆ','stop sign':'๐Ÿ›‘',
'bench':'๐Ÿช‘','bird':'๐Ÿฆ','cat':'๐Ÿฑ','dog':'๐Ÿถ','horse':'๐Ÿด','cow':'๐Ÿฎ',
'elephant':'๐Ÿ˜','bottle':'๐Ÿถ','cup':'โ˜•','bowl':'๐Ÿฅฃ','pizza':'๐Ÿ•',
'cake':'๐ŸŽ‚','chair':'๐Ÿช‘','couch':'๐Ÿ›‹๏ธ','potted plant':'๐Ÿชด',
}
badges = " ".join(
f'<span style="display:inline-block;background:#f0f4ff;border:1px solid #c8d8f8;'
f'border-radius:20px;padding:6px 14px;font-size:16px;margin:6px 4px;'
f'color:#000000;font-weight:600;">'
f'{ICONS.get(c,"โ€ข")} {c}</span>'
for c in CLASS_NAMES
)
st.markdown(f'<div style="line-height:2.6;">{badges}</div>', unsafe_allow_html=True)
st.divider()
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# SECTION 5 โ€” BUSINESS APPLICATIONS
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
st.markdown("### ๐ŸŒ Business Applications")
APPS = [
("๐Ÿ™๏ธ", "Smart Cities & Traffic Management",
"Automated vehicle detection ยท Pedestrian safety monitoring ยท Parking detection ยท Traffic violation detection"),
("๐Ÿ›’", "Retail & E-Commerce",
"Product recognition ยท Scan-free checkout ยท Customer behaviour analytics ยท Visual search"),
("๐Ÿ”’", "Security & Surveillance",
"Intrusion detection ยท Unattended object alerts ยท Perimeter monitoring ยท Crowd density analysis"),
("๐Ÿฆ", "Wildlife Conservation",
"Species identification ยท Habitat monitoring ยท Poaching prevention ยท Population studies"),
("๐Ÿฅ", "Healthcare",
"PPE compliance verification ยท Equipment tracking ยท Patient fall detection ยท Hygiene monitoring"),
("๐Ÿ ", "Smart Home & IoT",
"Home automation ยท Security alerts ยท Pet activity tracking ยท Energy usage detection"),
("๐ŸŒพ", "Agriculture",
"Livestock monitoring ยท Pest detection ยท Equipment tracking ยท Harvest readiness detection"),
("๐Ÿ“ฆ", "Logistics & Warehousing",
"Package sorting ยท Inventory tracking ยท Quality control ยท Loading bay monitoring"),
]
for icon, title, description in APPS:
st.markdown(
f"""
<div style="border:1.5px solid #dee2e6;border-radius:12px;padding:14px 16px;
margin-bottom:12px;background:#fafbfc;">
<span style="font-size:20px;">{icon}</span>
<span style="font-size:15px;font-weight:700;color:#000;">{title}: </span>
<span style="font-size:13px;color:#555;">{description}</span>
</div>
""",
unsafe_allow_html=True,
)
st.divider()
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# SECTION 6 โ€” TECH STACK + RESULTS
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
st.markdown("### ๐Ÿ› ๏ธ Tech Stack & Project Info")
tech_col, dev_col = st.columns([1, 1])
with tech_col:
TECH = [
("๐Ÿ", "Python 3.10", "Core programming language"),
("๐Ÿค–", "TensorFlow / Keras", "CNN training & inference"),
("โšก", "Ultralytics YOLOv8", "Object detection & fine-tuning"),
("๐Ÿ‘๏ธ", "OpenCV", "Image processing & annotation"),
("๐ŸŒ", "Streamlit", "Interactive web app framework"),
("๐Ÿค—", "Hugging Face Spaces", "Cloud deployment platform"),
("๐Ÿ“Š", "Matplotlib / Seaborn", "Visualisations"),
("๐Ÿ”ข", "NumPy / Pandas", "Data processing"),
("๐Ÿงช", "Scikit-learn", "Evaluation metrics"),
]
rows_html = "".join(
f'<div style="display:flex;align-items:center;gap:10px;padding:10px 14px;'
f'border-bottom:1px solid rgba(255,255,255,0.05);">'
f'<span style="font-size:18px;width:26px;text-align:center;">{icon}</span>'
f'<span style="font-size:13px;font-weight:600;min-width:170px;color:#e5e7eb;">{name}</span>'
f'<span style="font-size:12px;color:#9ca3af;">{desc}</span></div>'
for icon, name, desc in TECH
)
st.markdown(
f'''<div style="border:1px solid rgba(255,255,255,0.08);border-radius:14px;
overflow:hidden;background:linear-gradient(145deg,#0f172a,#111827);
box-shadow:0 8px 24px rgba(0,0,0,0.4);">
<div style="background:linear-gradient(90deg,#2563eb,#1d4ed8);padding:12px 16px;">
<p style="color:#fff;font-weight:700;font-size:14px;margin:0;">๐Ÿ› ๏ธ Tech Stack</p>
</div>
{rows_html}
</div>''',
unsafe_allow_html=True,
)
with dev_col:
st.markdown("""
<div style="border:1px solid rgba(255,255,255,0.08);border-radius:14px;padding:18px 20px;
background:linear-gradient(145deg,#0f172a,#111827);box-shadow:0 8px 24px rgba(0,0,0,0.4);
color:#e5e7eb;line-height:1.9;font-size:14px;">
<p style="font-weight:700;font-size:16px;margin-bottom:12px;">๐Ÿ“Š Model Scope & Results</p>
Image Classification (Transfer Learning)<br>
Object Detection (YOLOv8 Fine-tuning)<br>
Evaluation โ€” Accuracy ยท mAP@0.5 ยท Precision ยท Recall ยท F1<br><br>
<span style="color:#22c55e;font-weight:600;">CNN Result โ€” 92% (EfficientNetB0) โœ…</span><br>
<span style="color:#22c55e;font-weight:600;">YOLO Result โ€” mAP@0.5 = 0.893 โœ…</span>
</div>
""", unsafe_allow_html=True)