import streamlit as st import numpy as np import pandas as pd # ── Constants ────────────────────────────────────────────────────────────── LABEL_MAP = { 0: "WALKING", 1: "WALKING_UPSTAIRS", 2: "WALKING_DOWNSTAIRS", 3: "SITTING", 4: "STANDING", 5: "LAYING", } EXPLANATIONS = { "LAYING": "Minimal movement detected across all axes with low acceleration magnitude — consistent with a stationary horizontal posture.", "SITTING": "Low dynamic acceleration with a stable gravity component suggests a stationary upright posture with little body movement.", "STANDING": "Similar to sitting but with slight postural micro-movements. This class is often the hardest to distinguish from sitting.", "WALKING": "Rhythmic periodic acceleration with peaks on the vertical axis — consistent with level walking at normal cadence.", "WALKING_DOWNSTAIRS": "Downward gravitational shift with higher impact peaks characteristic of descending a staircase.", "WALKING_UPSTAIRS": "Elevated vertical acceleration effort with upward body displacement — consistent with climbing stairs.", } # ── Model loader ──────────────────────────────────────────────────────────── @st.cache_resource def load_model(): try: import tensorflow as tf from model_def import FeedForwardNetwork model = tf.keras.models.load_model( "model.keras", custom_objects={"FeedForwardNetwork": FeedForwardNetwork}, ) return model, "ready" except Exception as e: return None, f"error: {e}" # ── Page config ───────────────────────────────────────────────────────────── st.set_page_config( page_title="Human Activity Recognition", page_icon="🏃", layout="centered" ) st.title("Human Activity Recognition") st.markdown( "Deep learning classifier trained on 561 smartphone sensor features " "from the [UCI HAR dataset](https://www.kaggle.com/datasets/uciml/human-activity-recognition-with-smartphones). " "Classifies six daily activities from accelerometer and gyroscope readings." ) # ── Sidebar ────────────────────────────────────────────────────────────────── with st.sidebar: st.header("About") st.markdown(""" **Dataset:** UCI Human Activity Recognition **Subjects:** 30 volunteers aged 19–48 **Sensor:** Samsung Galaxy S II (waist-mounted) **Sampling rate:** 50Hz **Features:** 561 time + frequency domain features **Classes:** 6 activities of daily living """) st.markdown("---") st.markdown("**Model performance on test set**") st.metric("Architecture", "FFN 512→256→128") st.metric("Status", "FFN live · CNN pending") st.markdown("---") st.caption("DAT606 Group Assignment · Pan-Atlantic University") # ── Model status ───────────────────────────────────────────────────────────── model, model_status = load_model() if model_status != "ready": st.warning(f"Model not loaded — {model_status}") # ── Tabs ───────────────────────────────────────────────────────────────────── tab1, tab2 = st.tabs(["Select a Sample", "Upload Phyphox CSV"]) # ── Tab 1: Sample selector ─────────────────────────────────────────────────── with tab1: st.subheader("Select a pre-loaded test sample") st.caption( "Each sample is one 2.56-second window of sensor data " "from a test subject the model has never seen during training." ) try: samples_df = pd.read_csv("data/samples.csv") feature_cols = [ c for c in samples_df.columns if c not in ["Activity", "subject"] ] sample_labels = [ f"Sample {i+1} — {row['Activity']}" for i, (_, row) in enumerate(samples_df.iterrows()) ] selected = st.selectbox("Choose a sample:", sample_labels) selected_idx = sample_labels.index(selected) selected_row = samples_df.iloc[selected_idx] true_label = selected_row["Activity"] feature_vector = selected_row[feature_cols].values.astype(np.float32) col1, col2 = st.columns(2) with col1: st.metric("True Activity", true_label) with col2: st.metric("Feature count", len(feature_vector)) if st.button("Classify this sample", type="primary"): if model_status == "no_model": st.error("Model not loaded — cannot predict yet.") else: arr = feature_vector.reshape(1, -1) probs = model.predict(arr, verbose=0)[0] pred_idx = int(np.argmax(probs)) pred_label = LABEL_MAP[pred_idx] confidence = float(probs[pred_idx]) * 100 correct = pred_label == true_label st.markdown("---") st.subheader("Result") if correct: st.success( f"**{pred_label}** · {confidence:.1f}% confidence · ✓ Correct" ) else: st.error( f"**{pred_label}** · {confidence:.1f}% confidence · " f"✗ Incorrect (true: {true_label})" ) st.markdown(f"_{EXPLANATIONS[pred_label]}_") st.markdown("**Confidence across all classes**") chart_data = pd.DataFrame({ "Confidence (%)": [ float(probs[i]) * 100 for i in range(6) ] }, index=[LABEL_MAP[i] for i in range(6)]) st.bar_chart(chart_data) except FileNotFoundError: st.error("Sample data file not found. Add `data/samples.csv` to the repo.") # ── Tab 2: Phyphox upload (placeholder) ───────────────────────────────────── with tab2: st.subheader("Upload Phyphox sensor recording") st.markdown(""" **How to record your own data:** 1. Install [Phyphox](https://phyphox.org/) on your phone 2. Open the **Acceleration (without g)** and **Gyroscope** experiments 3. Record at least 3 seconds of a single activity 4. Export as CSV and upload below """) uploaded_file = st.file_uploader( "Upload Phyphox CSV export", type=["csv"], help="Export from Phyphox as CSV — must contain accelerometer and gyroscope columns" ) if uploaded_file is not None: st.info( "Phyphox pipeline coming soon. " "Feature extraction from raw sensor readings " "(filtering → jerk → FFT → 561 features) is under development." ) try: preview = pd.read_csv(uploaded_file) st.markdown("**File preview:**") st.dataframe(preview.head(10)) st.caption( f"{len(preview)} rows · {len(preview.columns)} columns detected" ) except Exception as e: st.error(f"Could not read file: {e}")