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
Files changed (1) hide show
  1. 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 = sum(scores_dict.values())
 
 
 
 
 
58
  if total_score > 0:
59
- for label, score in scores_dict.items():
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 process_and_get_dominant(timeline, mapping):
142
- if not timeline: return "N/A", {}
143
  df = pd.DataFrame.from_dict(timeline, orient='index')
144
- unified_scores = {e: 0.0 for e in UNIFIED_EMOTIONS}
145
- for raw_label, scores in df.items():
146
- unified_label = mapping.get(raw_label)
147
- if unified_label:
148
- unified_scores[unified_label] += scores.mean()
149
- if sum(unified_scores.values()) == 0: return "N/A", {}
150
- dominant_emotion = max(unified_scores, key=unified_scores.get)
151
- return dominant_emotion.capitalize(), unified_scores
152
-
153
- dominant_fer, fer_avg_scores = process_and_get_dominant(fer_timeline, FACIAL_TO_UNIFIED)
154
- dominant_ser, ser_avg_scores = process_and_get_dominant(ser_timeline, SER_TO_UNIFIED)
155
- dominant_text, ter_avg_scores = process_and_get_dominant(ter_timeline, TEXT_TO_UNIFIED)
156
-
157
- fer_vector = create_unified_vector(fer_avg_scores)
158
- ser_vector = create_unified_vector(ser_avg_scores)
159
- text_vector = create_unified_vector(ter_avg_scores)
 
 
 
 
 
 
 
 
 
 
 
 
 
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.8)
 
 
 
 
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)")