Mood over an Hour
A memoryless model applied many times: you record your emotional course over a longer period and plot it — and see how mood moves across the day.
In a nutshell
What: You turn the expression mirror from 9.1 into a quiet recorder. It doesn't measure continuously on screen, but every few seconds saves one line: a timestamp and the recognised emotion probabilities. Afterwards you plot a curve of your mood from it.
The core idea: the time is not in the model — that only knows the moment — but in your analysis. This is often the cheapest route to a temporal analysis: a lightweight model, applied many times.
You need: the setup from 9.1 (webcam, hsemotion, OpenCV) plus pandas
and matplotlib for the analysis.
What it's about
On this book's map of AI there is a subtle but important distinction. A snapshot model decides for each input on its own, with no memory: one face — one mood. But you can run such a model once a second and look at the course only afterwards. Then the time lives in your analysis, not in the model. That is exactly what you do here: many snapshots become a mood curve.
This is not a gimmick but the basic idea behind virtual mirroring from Chapter 5. When staff at a Hamburg bank received daily feedback about their mood, they became measurably happier and more active — simply because their state was made visible. Whoever sees themselves honestly can change. Your hour-long curve is this mirror in miniature.
A little background
Why average? A memoryless model jumps from image to image. A single frame in which you happen to be yawning or looking away would distort the curve. So we smooth: we collect the predictions over a time window (about ten seconds) and take the average. That turns jittery noise into a readable trend — the same idea as the moving average in plant activity 13.1.
One number for "mood". Instead of tracking seven emotions at once, it is often clearer to form a single wellbeing axis: positive shares (happiness, pleasant surprise) minus negative ones (anger, sadness, fear, disgust). That is a rough simplification — but an honest one, as long as you know you are simplifying.
Your data stays yours
The recorder saves no images, only numbers — timestamps and probabilities. Even so, an hour of your face is very personal. Keep the file on your own computer, don't share it unasked, and if the class discusses results, do it anonymously and voluntarily. That is the golden rule of this book: what is found out about you belongs to you.
Part A — Recording
- Add the packages.
pip install pandas matplotlib(on top ofhsemotion opencv-pythonfrom 9.1). - Start the recorder. Run the script below. It opens no large preview window but
quietly writes one line every few seconds into
mood.csv. - Do something. Work normally for a while — read, solve problems, watch a short funny and a serious video. The more varied, the more interesting the curve. For a lesson 15–20 minutes is enough; "an hour" is the nicer target for home.
- Quit. With Ctrl+C in the console. The CSV file stays put.
import cv2, time, csv
from datetime import datetime
from hsemotion.facial_emotions import HSEmotionRecognizer
fer = HSEmotionRecognizer(model_name="enet_b0_8_best_afew")
detector = cv2.CascadeClassifier(
cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
cam = cv2.VideoCapture(0)
INTERVAL = 3 # one measurement every 3 seconds
labels = ["Anger","Contempt","Disgust","Fear","Happiness","Neutral","Sadness","Surprise"]
with open("mood.csv", "w", newline="") as f:
writer = csv.writer(f)
writer.writerow(["time"] + labels) # header row
print("Recorder running - Ctrl+C to quit.")
try:
while True:
ok, frame = cam.read()
if ok:
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = detector.detectMultiScale(gray, 1.3, 5)
if len(faces):
x, y, w, h = faces[0]
_, scores = fer.predict_emotions(frame[y:y+h, x:x+w], logits=False)
row = [datetime.now().isoformat(timespec="seconds")] + list(scores)
writer.writerow(row)
f.flush()
time.sleep(INTERVAL)
except KeyboardInterrupt:
pass
cam.release()
print("Done - data in mood.csv")
Part B — Analysing and plotting
The second part reads the CSV, smooths it and draws your mood curve. Runs in the browser too (Pyodide); full code on GitHub.
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_csv("mood.csv", parse_dates=["time"]).set_index("time")
# a single "wellbeing" axis: positive minus negative
positive = df["Happiness"] + df["Surprise"]
negative = df["Anger"] + df["Sadness"] + df["Fear"] + df["Disgust"]
df["wellbeing"] = positive - negative
# smooth: moving average over ~30 seconds (at a 3s interval = 10 measurements)
df["wellbeing_smooth"] = df["wellbeing"].rolling(window=10, center=True, min_periods=3).mean()
plt.figure(figsize=(10, 4))
plt.plot(df.index, df["wellbeing"], color="#cfc6b4", label="raw")
plt.plot(df.index, df["wellbeing_smooth"], color="#2F5D3A", lw=2, label="smoothed")
plt.axhline(0, color="#999", lw=0.8)
plt.ylabel("Wellbeing axis (positive - negative)")
plt.title("My mood over time")
plt.legend(); plt.tight_layout(); plt.show()
What you should see
A jittery raw line and, above it, a calm green trend curve. It rises in moments when you laughed or saw something nice, and falls with annoyance or boredom. Often you recognise events again: "that's when the funny video played." If your curve stays almost flat, the hour was either calm — or the model mostly read you as "Neutral", which is normal during focused work.
Worksheet
Moments become a course
- Mark two places in your curve and describe what you were doing at that moment. Does the deflection match your memory?
- What changes if you set the smoothing window from 10 to 3 (or to 30) measurements? What do you gain, what do you lose with heavy smoothing?
- Where exactly is the "time" in this experiment — in the model or in your analysis? Justify it.
- Give one reason why the "wellbeing axis" (positive minus negative) is a rough simplification. When might it mislead?
- This experiment is "virtual mirroring" in miniature. Why can merely making visible your own mood change something — even without any advice?
Show solution
1. Individual. The aim is for learners to link deflections to real events and, in doing so, notice that the curve fits roughly but doesn't capture every inner state.
2. A small window (3) follows the raw line closely — sensitive, but noisy. A large window (30) shows only the rough trend — calm, but short, real deflections disappear. Smoothing is always a trade: calm for resolution.
3. In the analysis. The model is a snapshot model with no memory; it decides each image on its own. Only our collecting many timestamped predictions and stringing them together produces a course.
4. It throws very different states into one pot: surprise can be positive (joy) or negative (fright), but here is always counted as positive. And "neutral" — often the most common state — disappears entirely. The axis condenses, but it loses meaning.
5. Because self-perception is imprecise: you often don't notice how your own mood moves across the day. A visible course gives an honest reference point at which you spot patterns ("it always dips after lunch") — and recognising is the first step to changing.
When it sticks
| Problem | Likely cause & fix |
|---|---|
| CSV stays empty | No face found (light, position) or camera occupied. Get 9.1 running first, then continue here. |
| Curve is almost all "Neutral" | Normal during focused work. Deliberately build in varied stimuli (funny/serious video), or record for longer. |
rolling curve full of gaps | min_periods too high for short recordings. Lower it to min_periods=1 or measure longer. |
| Time axis squashed and unreadable | Very many points. In matplotlib rotate the axis with fig.autofmt_xdate() or downsample to minutes (df.resample("1min").mean()). |
| Recorder eats a lot of CPU | Choose a larger interval (e.g. INTERVAL = 5) — for an hour-long course that is plenty. |
Food for thought
- You have turned a simple tool (a snapshot model) into a tool for time courses by mere repetition and collecting — without needing a more complicated model. That is a pattern you meet throughout the book: many simple measurements often beat a single complicated model.
- An hour-long curve of your mood is astonishingly intimate — it shows things you barely notice yourself. Precisely for that reason the question of who may see such data is no side issue. Aggregated for the team, personal only for you: that is the line that turns surveillance into self-knowledge.
- Keep the boundary: the curve shows which expressions your face displayed over time — not what you really felt or thought. Expression is not feeling, and a course of expressions stays a course of expressions.
Extension
- Log events. Keep a small log alongside ("14:03 funny video") and draw those marks into the plot. That way you check whether the curve really responds to events.
- Show all emotions. Instead of a wellbeing axis, plot all eight curves stacked (a stacked area plot). You see nicely how "Neutral" forms the base and the feelings flash up above it.
- Bridge to the Happimeter. Your curve estimates mood from the face; the Happimeter does the same from pulse and movement of a smartwatch (happimeter.socialcompass.ai). Compare: where do the two mirrors agree, where not?