Spaces:
Running
Running
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)")
|