|
import streamlit as st |
|
import numpy as np |
|
import pandas as pd |
|
import joblib |
|
|
|
|
|
st.set_page_config(page_title="Stress Detection using One-Class SVM", layout="centered") |
|
|
|
|
|
st.markdown( |
|
""" |
|
<style> |
|
.stApp { |
|
background-image: url("https://i.postimg.cc/vZb3ymYT/360-F-1375669005-ebg3mldxps5-ZYr-QFl-Y6-EX3e-CINw-VDeo-F.jpg"); |
|
background-size: cover; |
|
background-repeat: no-repeat; |
|
background-attachment: fixed; |
|
} |
|
|
|
.block-container { |
|
background-color: rgba(0, 0, 0, 0.6); |
|
color: white; |
|
padding: 2rem; |
|
border-radius: 15px; |
|
max-width: 800px; |
|
margin: 2rem auto; |
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5); |
|
backdrop-filter: blur(8px); |
|
-webkit-backdrop-filter: blur(8px); |
|
border: 1px solid rgba(255, 255, 255, 0.1); |
|
} |
|
|
|
.sensor-row { |
|
display: flex; |
|
justify-content: space-around; |
|
font-size: 1.1rem; |
|
margin-top: 1.2rem; |
|
margin-bottom: 1rem; |
|
} |
|
|
|
.sensor-row > div { |
|
padding: 0.5rem 1rem; |
|
background-color: rgba(255, 255, 255, 0.1); |
|
border-radius: 8px; |
|
} |
|
|
|
.scroll-box { |
|
max-height: 400px; |
|
overflow-y: auto; |
|
border: 1px solid #ccc; |
|
padding: 1rem; |
|
background-color: rgba(255,255,255,0.05); |
|
border-radius: 10px; |
|
} |
|
|
|
h1, h2, h3, p, div { |
|
color: white !important; |
|
} |
|
|
|
section.main > div:first-child { |
|
padding-top: 0rem; |
|
} |
|
|
|
header[data-testid="stHeader"] { |
|
height: 0rem; |
|
visibility: hidden; |
|
} |
|
</style> |
|
""", |
|
unsafe_allow_html=True |
|
) |
|
|
|
st.title("Stress Detection") |
|
st.markdown("Select a mode to detect stress from sensor readings:") |
|
|
|
|
|
try: |
|
model = joblib.load("one_class_svm_stress_model.pkl") |
|
scaler = joblib.load("scaler.pkl") |
|
except Exception as e: |
|
st.error(f"Error loading model or scaler: {e}") |
|
st.stop() |
|
|
|
|
|
try: |
|
df = pd.read_csv("simulated_stress_data.csv") |
|
df = df[['HR', 'HRV', 'EDA']].head(100) |
|
except: |
|
df = pd.DataFrame({ |
|
"HR": np.random.randint(60, 120, 100), |
|
"HRV": np.random.uniform(20, 80, 100), |
|
"EDA": np.random.uniform(0.1, 5.0, 100) |
|
}) |
|
|
|
|
|
mode = st.radio("Choose input mode:", ["Manual Readings", "Generate Readings", "Test Dataset"], horizontal=True) |
|
|
|
|
|
if mode == "Manual Readings": |
|
hr = st.number_input("Heart Rate (HR)", min_value=60, max_value=120, value=80) |
|
hrv = st.number_input("Heart Rate Variability (HRV)", min_value=20.0, max_value=80.0, value=50.0) |
|
eda = st.number_input("Electrodermal Activity (EDA)", min_value=0.1, max_value=5.0, value=2.0) |
|
|
|
if st.button("Predict"): |
|
sample = np.array([[hr, hrv, eda]]) |
|
scaled = scaler.transform(sample) |
|
pred = model.predict(scaled) |
|
label = "Stress" if pred[0] == -1 else "No Stress" |
|
|
|
st.markdown( |
|
f""" |
|
<div class="sensor-row"> |
|
<div><strong>HR:</strong> {hr} bpm</div> |
|
<div><strong>HRV:</strong> {hrv:.2f} ms</div> |
|
<div><strong>EDA:</strong> {eda:.2f} µS</div> |
|
</div> |
|
""", unsafe_allow_html=True |
|
) |
|
|
|
st.subheader("Prediction") |
|
if label == "No Stress": |
|
st.success(label) |
|
else: |
|
st.error(label) |
|
|
|
|
|
elif mode == "Generate Readings": |
|
if st.button("Generate and Predict"): |
|
hr = np.random.randint(60, 120) |
|
hrv = np.random.uniform(20, 80) |
|
eda = np.random.uniform(0.1, 5.0) |
|
|
|
sample = np.array([[hr, hrv, eda]]) |
|
scaled = scaler.transform(sample) |
|
pred = model.predict(scaled) |
|
label = "Stress" if pred[0] == -1 else "No Stress" |
|
|
|
st.markdown( |
|
f""" |
|
<div class="sensor-row"> |
|
<div><strong>HR:</strong> {hr} bpm</div> |
|
<div><strong>HRV:</strong> {hrv:.2f} ms</div> |
|
<div><strong>EDA:</strong> {eda:.2f} µS</div> |
|
</div> |
|
""", unsafe_allow_html=True |
|
) |
|
|
|
st.subheader("Prediction") |
|
if label == "No Stress": |
|
st.success(label) |
|
else: |
|
st.error(label) |
|
|
|
|
|
|
|
|
|
elif mode == "Test Dataset": |
|
st.markdown("### Select a row from test dataset for prediction:") |
|
|
|
|
|
if "page" not in st.session_state: |
|
st.session_state.page = 0 |
|
if "last_prediction" not in st.session_state: |
|
st.session_state.last_prediction = None |
|
st.session_state.last_row = None |
|
|
|
rows_per_page = 5 |
|
df_filtered = df |
|
total_pages = max(1, (len(df_filtered) - 1) // rows_per_page + 1) |
|
|
|
|
|
st.markdown(""" |
|
<style> |
|
.scrollable-table { |
|
max-height: 350px; |
|
overflow-y: auto; |
|
padding: 10px; |
|
background-color: rgba(255,255,255,0.05); |
|
border-radius: 10px; |
|
border: 1px solid #ccc; |
|
} |
|
.row-box { |
|
border-radius: 8px; |
|
padding: 6px; |
|
margin-bottom: 4px; |
|
background-color: rgba(0, 128, 255, 0.15); |
|
height: 40px; |
|
display: flex; |
|
align-items: center; |
|
transition: background-color 0.3s ease; |
|
} |
|
.row-box:hover { |
|
background-color: rgba(0, 128, 255, 0.3); |
|
} |
|
</style> |
|
""", unsafe_allow_html=True) |
|
|
|
|
|
with st.container(): |
|
|
|
|
|
col1, col2, col3, col4 = st.columns([3, 3, 3, 2]) |
|
col1.markdown("**HR**") |
|
col2.markdown("**HRV**") |
|
col3.markdown("**EDA**") |
|
col4.markdown("**Predict**") |
|
|
|
page_data = df_filtered.iloc[ |
|
st.session_state.page * rows_per_page : (st.session_state.page + 1) * rows_per_page |
|
] |
|
|
|
for idx, row in page_data.iterrows(): |
|
row_style = "row-box" |
|
|
|
col1, col2, col3, col4 = st.columns([3, 3, 3, 2]) |
|
with col1: |
|
st.markdown(f'<div class="{row_style}">{row["HR"]}</div>', unsafe_allow_html=True) |
|
with col2: |
|
st.markdown(f'<div class="{row_style}">{row["HRV"]:.2f}</div>', unsafe_allow_html=True) |
|
with col3: |
|
st.markdown(f'<div class="{row_style}">{row["EDA"]:.2f}</div>', unsafe_allow_html=True) |
|
with col4: |
|
if st.button("Select", key=f"select_{idx}"): |
|
sample = np.array([[row['HR'], row['HRV'], row['EDA']]]) |
|
sample_scaled = scaler.transform(sample) |
|
pred = model.predict(sample_scaled) |
|
label = "Stress" if pred[0] == -1 else "No Stress" |
|
|
|
st.session_state.last_prediction = label |
|
st.session_state.last_row = row |
|
|
|
st.markdown('</div>', unsafe_allow_html=True) |
|
|
|
|
|
|
|
col1, col2, col3 = st.columns([1.2, 1.2, 3]) |
|
with col1: |
|
if st.button("⬅️ Previous"): |
|
if st.session_state.page > 0: |
|
st.session_state.page -= 1 |
|
|
|
with col2: |
|
if st.button("Next ➡️"): |
|
if st.session_state.page < total_pages - 1: |
|
st.session_state.page += 1 |
|
|
|
with col3: |
|
st.markdown( |
|
f"<div style='text-align:left; margin-top: 0.5rem;'>Page {st.session_state.page + 1} of {total_pages}</div>", |
|
unsafe_allow_html=True |
|
) |
|
|
|
|
|
|
|
if st.session_state.last_prediction and st.session_state.last_row is not None: |
|
row = st.session_state.last_row |
|
label = st.session_state.last_prediction |
|
|
|
st.markdown("---") |
|
st.markdown("### Prediction") |
|
st.markdown( |
|
f""" |
|
<div class="sensor-row"> |
|
<div><strong>HR:</strong> {row['HR']} bpm</div> |
|
<div><strong>HRV:</strong> {row['HRV']:.2f} ms</div> |
|
<div><strong>EDA:</strong> {row['EDA']:.2f} µS</div> |
|
</div> |
|
""", unsafe_allow_html=True |
|
) |
|
|
|
if label == "No Stress": |
|
st.success(label) |
|
else: |
|
st.error(label) |
|
|