import os import streamlit as st import numpy as np import pandas as pd # Paths anchored to the repo root regardless of working directory _SRC_DIR = os.path.dirname(os.path.abspath(__file__)) _REPO_ROOT = os.path.dirname(_SRC_DIR) _SAMPLES_PATH = os.path.join(_REPO_ROOT, "data", "samples.csv") _NORM_PATH = os.path.join(_REPO_ROOT, "data", "norm_params.json") 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.", } @st.cache_resource def load_model(filename: str): try: from huggingface_hub import hf_hub_download import tensorflow as tf from model_def import FeedForwardNetwork, Conv1DNetwork # noqa: F401 model_path = hf_hub_download( repo_id="Group3DActRecog/actRecog", filename=filename, repo_type="space", ) model = tf.keras.models.load_model( model_path, custom_objects={ "FeedForwardNetwork": FeedForwardNetwork, "Conv1DNetwork": Conv1DNetwork, }, ) return model, "ready" except Exception as e: return None, f"error: {e}" st.set_page_config( page_title="Human Activity Recognition", page_icon="🏃", layout="wide", ) 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." ) 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("**Models**") st.markdown(""" **FFN**: Feedforward Network Dense(512) → Dense(256) → Dense(128) BatchNorm + Dropout(0.3) per layer **CNN**: 1D Convolutional Network Conv1D(64) → Conv1D(128) → Conv1D(256) GlobalAvgPool → Dense(128) """) st.markdown("---") ffn_model, ffn_status = load_model("model.keras") cnn_model, cnn_status = load_model("har_cnn.keras") if ffn_status != "ready" or cnn_status != "ready": if ffn_status != "ready": st.warning(f"FFN not loaded: {ffn_status}") if cnn_status != "ready": st.warning(f"CNN not loaded: {cnn_status}") tab1, tab2 = st.tabs(["Select a Sample", "Upload Phyphox CSV"]) 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(_SAMPLES_PATH) 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 ffn_status != "ready" or cnn_status != "ready": st.error("One or both models not loaded: cannot predict yet.") else: arr = feature_vector.reshape(1, -1) ffn_probs = ffn_model.predict(arr, verbose=0)[0] cnn_probs = cnn_model.predict(arr, verbose=0)[0] ffn_idx = int(np.argmax(ffn_probs)) cnn_idx = int(np.argmax(cnn_probs)) ffn_label = LABEL_MAP[ffn_idx] cnn_label = LABEL_MAP[cnn_idx] ffn_conf = float(ffn_probs[ffn_idx]) * 100 cnn_conf = float(cnn_probs[cnn_idx]) * 100 st.markdown("---") st.subheader("Model comparison") left, right = st.columns(2) with left: st.markdown("#### Feedforward Network") if ffn_label == true_label: st.success(f"**{ffn_label}** · {ffn_conf:.1f}% confidence · ✓ Correct") else: st.error(f"**{ffn_label}** · {ffn_conf:.1f}% confidence · ✗ Incorrect (true: {true_label})") st.markdown(f"_{EXPLANATIONS[ffn_label]}_") st.markdown("**Confidence across all classes**") st.bar_chart(pd.DataFrame( {"Confidence (%)": [float(ffn_probs[i]) * 100 for i in range(6)]}, index=[LABEL_MAP[i] for i in range(6)] )) with right: st.markdown("#### 1D Convolutional Network") if cnn_label == true_label: st.success(f"**{cnn_label}** · {cnn_conf:.1f}% confidence · ✓ Correct") else: st.error(f"**{cnn_label}** · {cnn_conf:.1f}% confidence · ✗ Incorrect (true: {true_label})") st.markdown(f"_{EXPLANATIONS[cnn_label]}_") st.markdown("**Confidence across all classes**") st.bar_chart(pd.DataFrame( {"Confidence (%)": [float(cnn_probs[i]) * 100 for i in range(6)]}, index=[LABEL_MAP[i] for i in range(6)] )) except FileNotFoundError: st.error("Sample data file not found. Add `data/samples.csv` to the repo.") with tab2: from phyphox_app_block import render_phyphox_tab render_phyphox_tab(ffn_model, ffn_status, cnn_model, cnn_status, _NORM_PATH)