Spaces:
Sleeping
Sleeping
Kevin King
commited on
Commit
·
fa097da
1
Parent(s):
ca7a908
REFAC: Improve emotion vector creation and add ECI timeline calculation in Streamlit app
Browse files- src/streamlit_app.py +68 -38
src/streamlit_app.py
CHANGED
|
@@ -52,13 +52,16 @@ def load_models():
|
|
| 52 |
whisper_model, text_classifier, ser_model, ser_feature_extractor = load_models()
|
| 53 |
|
| 54 |
# --- Helper Functions for Analysis ---
|
| 55 |
-
def create_unified_vector(scores_dict):
|
| 56 |
vector = np.zeros(len(UNIFIED_EMOTIONS))
|
| 57 |
-
total_score =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
if total_score > 0:
|
| 59 |
-
|
| 60 |
-
if label in UNIFIED_EMOTIONS:
|
| 61 |
-
vector[UNIFIED_EMOTIONS.index(label)] = score / total_score
|
| 62 |
return vector
|
| 63 |
|
| 64 |
def get_consistency_level(cosine_sim):
|
|
@@ -138,25 +141,38 @@ if uploaded_file is not None:
|
|
| 138 |
|
| 139 |
st.header("Analysis Results")
|
| 140 |
|
| 141 |
-
def
|
| 142 |
-
if not timeline: return
|
| 143 |
df = pd.DataFrame.from_dict(timeline, orient='index')
|
| 144 |
-
|
| 145 |
-
for
|
| 146 |
-
|
| 147 |
-
if
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 160 |
|
| 161 |
similarities = [cosine_similarity([fer_vector], [text_vector])[0][0], cosine_similarity([fer_vector], [ser_vector])[0][0], cosine_similarity([ser_vector], [text_vector])[0][0]]
|
| 162 |
avg_similarity = np.nanmean([s for s in similarities if not np.isnan(s)])
|
|
@@ -179,23 +195,27 @@ if uploaded_file is not None:
|
|
| 179 |
with col2:
|
| 180 |
st.subheader("Unified Emotion Timeline")
|
| 181 |
|
| 182 |
-
def create_timeline_df(timeline, mapping):
|
| 183 |
-
if not timeline: return pd.DataFrame(columns=UNIFIED_EMOTIONS)
|
| 184 |
-
df = pd.DataFrame.from_dict(timeline, orient='index')
|
| 185 |
-
df_unified = pd.DataFrame(index=df.index, columns=UNIFIED_EMOTIONS).fillna(0.0)
|
| 186 |
-
for raw_col in df.columns:
|
| 187 |
-
unified_col = mapping.get(raw_col)
|
| 188 |
-
if unified_col:
|
| 189 |
-
df_unified[unified_col] += df[raw_col]
|
| 190 |
-
return df_unified
|
| 191 |
-
|
| 192 |
-
fer_df = create_timeline_df(fer_timeline, FACIAL_TO_UNIFIED)
|
| 193 |
-
ser_df = create_timeline_df(ser_timeline, SER_TO_UNIFIED)
|
| 194 |
-
ter_df = create_timeline_df(ter_timeline, TEXT_TO_UNIFIED)
|
| 195 |
-
|
| 196 |
full_index = np.arange(0, duration, 0.5)
|
| 197 |
combined_df = pd.DataFrame(index=full_index)
|
| 198 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 199 |
if not fer_df.empty:
|
| 200 |
fer_df_resampled = fer_df.reindex(fer_df.index.union(full_index)).interpolate(method='linear').reindex(full_index)
|
| 201 |
for e in UNIFIED_EMOTIONS: combined_df[f'Facial_{e}'] = fer_df_resampled.get(e, 0.0)
|
|
@@ -207,6 +227,11 @@ if uploaded_file is not None:
|
|
| 207 |
if not ter_df.empty:
|
| 208 |
ter_df_resampled = ter_df.reindex(ter_df.index.union(full_index)).interpolate(method='linear').reindex(full_index)
|
| 209 |
for e in UNIFIED_EMOTIONS: combined_df[f'Text_{e}'] = ter_df_resampled.get(e, 0.0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 210 |
|
| 211 |
combined_df.fillna(0, inplace=True)
|
| 212 |
|
|
@@ -216,9 +241,14 @@ if uploaded_file is not None:
|
|
| 216 |
styles = {'Facial': '-', 'Speech': '--', 'Text': ':'}
|
| 217 |
|
| 218 |
for col in combined_df.columns:
|
|
|
|
| 219 |
modality, emotion = col.split('_')
|
| 220 |
if emotion in colors:
|
| 221 |
-
ax.plot(combined_df.index, combined_df[col], label=f'{modality} {emotion.capitalize()}', color=colors[emotion], linestyle=styles[modality], alpha=0.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 222 |
|
| 223 |
ax.set_title("Emotion Confidence Over Time (Normalized)")
|
| 224 |
ax.set_xlabel("Time (seconds)")
|
|
|
|
| 52 |
whisper_model, text_classifier, ser_model, ser_feature_extractor = load_models()
|
| 53 |
|
| 54 |
# --- Helper Functions for Analysis ---
|
| 55 |
+
def create_unified_vector(scores_dict, mapping_dict):
|
| 56 |
vector = np.zeros(len(UNIFIED_EMOTIONS))
|
| 57 |
+
total_score = 0
|
| 58 |
+
for label, score in scores_dict.items():
|
| 59 |
+
unified_label = mapping_dict.get(label)
|
| 60 |
+
if unified_label in UNIFIED_EMOTIONS:
|
| 61 |
+
vector[UNIFIED_EMOTIONS.index(unified_label)] += score
|
| 62 |
+
total_score += score
|
| 63 |
if total_score > 0:
|
| 64 |
+
vector /= total_score
|
|
|
|
|
|
|
| 65 |
return vector
|
| 66 |
|
| 67 |
def get_consistency_level(cosine_sim):
|
|
|
|
| 141 |
|
| 142 |
st.header("Analysis Results")
|
| 143 |
|
| 144 |
+
def process_timeline_to_df(timeline, mapping):
|
| 145 |
+
if not timeline: return pd.DataFrame(columns=UNIFIED_EMOTIONS)
|
| 146 |
df = pd.DataFrame.from_dict(timeline, orient='index')
|
| 147 |
+
df_unified = pd.DataFrame(index=df.index, columns=UNIFIED_EMOTIONS).fillna(0.0)
|
| 148 |
+
for raw_col in df.columns:
|
| 149 |
+
unified_col = mapping.get(raw_col)
|
| 150 |
+
if unified_col:
|
| 151 |
+
df_unified[unified_col] += df[raw_col]
|
| 152 |
+
return df_unified
|
| 153 |
+
|
| 154 |
+
fer_df = process_timeline_to_df(fer_timeline, FACIAL_TO_UNIFIED)
|
| 155 |
+
ser_df = process_timeline_to_df(ser_timeline, SER_TO_UNIFIED)
|
| 156 |
+
ter_df = process_timeline_to_df(ter_timeline, TEXT_TO_UNIFIED)
|
| 157 |
+
|
| 158 |
+
def get_dominant_emotion_from_df(df):
|
| 159 |
+
if df.empty or df.sum().sum() == 0: return "N/A"
|
| 160 |
+
return df.sum().idxmax().capitalize()
|
| 161 |
+
|
| 162 |
+
dominant_fer = get_dominant_emotion_from_df(fer_df)
|
| 163 |
+
dominant_ser = get_dominant_emotion_from_df(ser_df)
|
| 164 |
+
dominant_text = get_dominant_emotion_from_df(ter_df)
|
| 165 |
+
|
| 166 |
+
def get_avg_unified_scores(df):
|
| 167 |
+
return df.mean().to_dict() if not df.empty else {}
|
| 168 |
+
|
| 169 |
+
fer_avg_scores = get_avg_unified_scores(fer_df)
|
| 170 |
+
ser_avg_scores = get_avg_unified_scores(ser_df)
|
| 171 |
+
ter_avg_scores = get_avg_unified_scores(ter_df)
|
| 172 |
+
|
| 173 |
+
fer_vector = create_unified_vector(fer_avg_scores, {e:e for e in UNIFIED_EMOTIONS})
|
| 174 |
+
ser_vector = create_unified_vector(ser_avg_scores, {e:e for e in UNIFIED_EMOTIONS})
|
| 175 |
+
text_vector = create_unified_vector(ter_avg_scores, {e:e for e in UNIFIED_EMOTIONS})
|
| 176 |
|
| 177 |
similarities = [cosine_similarity([fer_vector], [text_vector])[0][0], cosine_similarity([fer_vector], [ser_vector])[0][0], cosine_similarity([ser_vector], [text_vector])[0][0]]
|
| 178 |
avg_similarity = np.nanmean([s for s in similarities if not np.isnan(s)])
|
|
|
|
| 195 |
with col2:
|
| 196 |
st.subheader("Unified Emotion Timeline")
|
| 197 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 198 |
full_index = np.arange(0, duration, 0.5)
|
| 199 |
combined_df = pd.DataFrame(index=full_index)
|
| 200 |
|
| 201 |
+
# --- NEW: ECI Timeline Calculation ---
|
| 202 |
+
eci_timeline = {}
|
| 203 |
+
for t_stamp in full_index:
|
| 204 |
+
vectors = []
|
| 205 |
+
# Get interpolated facial vector if available
|
| 206 |
+
if not fer_df.empty and t_stamp in fer_df.index:
|
| 207 |
+
vectors.append(create_unified_vector(fer_df.loc[t_stamp].to_dict(), {e:e for e in UNIFIED_EMOTIONS}))
|
| 208 |
+
# Get speech vector if available
|
| 209 |
+
if not ser_df.empty and int(t_stamp) in ser_df.index:
|
| 210 |
+
vectors.append(create_unified_vector(ser_df.loc[int(t_stamp)].to_dict(), {e:e for e in UNIFIED_EMOTIONS}))
|
| 211 |
+
# Get text vector if available
|
| 212 |
+
if not ter_df.empty and int(t_stamp) in ter_df.index:
|
| 213 |
+
vectors.append(create_unified_vector(ter_df.loc[int(t_stamp)].to_dict(), {e:e for e in UNIFIED_EMOTIONS}))
|
| 214 |
+
|
| 215 |
+
if len(vectors) >= 2:
|
| 216 |
+
sims = [cosine_similarity([v1], [v2])[0][0] for i, v1 in enumerate(vectors) for v2 in vectors[i+1:]]
|
| 217 |
+
eci_timeline[t_stamp] = np.mean(sims)
|
| 218 |
+
|
| 219 |
if not fer_df.empty:
|
| 220 |
fer_df_resampled = fer_df.reindex(fer_df.index.union(full_index)).interpolate(method='linear').reindex(full_index)
|
| 221 |
for e in UNIFIED_EMOTIONS: combined_df[f'Facial_{e}'] = fer_df_resampled.get(e, 0.0)
|
|
|
|
| 227 |
if not ter_df.empty:
|
| 228 |
ter_df_resampled = ter_df.reindex(ter_df.index.union(full_index)).interpolate(method='linear').reindex(full_index)
|
| 229 |
for e in UNIFIED_EMOTIONS: combined_df[f'Text_{e}'] = ter_df_resampled.get(e, 0.0)
|
| 230 |
+
|
| 231 |
+
# Add ECI timeline to the DataFrame
|
| 232 |
+
if eci_timeline:
|
| 233 |
+
eci_df = pd.Series(eci_timeline, name="ECI")
|
| 234 |
+
combined_df['ECI'] = eci_df
|
| 235 |
|
| 236 |
combined_df.fillna(0, inplace=True)
|
| 237 |
|
|
|
|
| 241 |
styles = {'Facial': '-', 'Speech': '--', 'Text': ':'}
|
| 242 |
|
| 243 |
for col in combined_df.columns:
|
| 244 |
+
if col == 'ECI': continue
|
| 245 |
modality, emotion = col.split('_')
|
| 246 |
if emotion in colors:
|
| 247 |
+
ax.plot(combined_df.index, combined_df[col], label=f'{modality} {emotion.capitalize()}', color=colors[emotion], linestyle=styles[modality], alpha=0.7)
|
| 248 |
+
|
| 249 |
+
# Plot ECI on the same axis
|
| 250 |
+
if 'ECI' in combined_df.columns:
|
| 251 |
+
ax.plot(combined_df.index, combined_df['ECI'], label='Emotion Consistency', color='black', linewidth=2.5, alpha=0.9)
|
| 252 |
|
| 253 |
ax.set_title("Emotion Confidence Over Time (Normalized)")
|
| 254 |
ax.set_xlabel("Time (seconds)")
|