WitneyWW commited on
Commit
c71f3e6
Β·
1 Parent(s): 5ff6bdb

audible audio, sentence-style moves/places, clearer sound and gaze questions, how-to panel

Browse files
Files changed (1) hide show
  1. app.py +95 -27
app.py CHANGED
@@ -8,6 +8,7 @@ import argparse
8
  import csv
9
  import json
10
  import os
 
11
  from datetime import datetime
12
  from pathlib import Path
13
 
@@ -68,6 +69,48 @@ if RESP_CSV.exists():
68
  RESP_CSV.rename(RESP_DIR / f"responses_old_{datetime.now():%Y%m%d_%H%M%S}.csv")
69
 
70
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  def _join(xs):
72
  return "; ".join(f"({i + 1}) {x}" for i, x in enumerate(xs))
73
 
@@ -83,29 +126,41 @@ def dim_questions(it):
83
  f"β€œ{a.get('looks_like')}”. Is that what the object is and looks like?")
84
  q["localization"] = (f"2. Localization β€” do the red box (video + keyframes) and the green mask (keyframes) "
85
  f"cover the {name}, and not a neighbouring object, the hand, or background?")
86
- hist = a.get("history") or []
87
- q["state_history"] = (f"3. Location history of the {name} β€” the memory says: {_join(hist)}. "
88
- f"At the end it is β€œ{now.get('relation')} {now.get('at')}”. Are the places and times correct?")
89
- moves = a.get("moves") or []
90
- q["moves"] = (f"4. Moves β€” the memory records these moves of the {name}: {_join(moves)}. "
91
- "Are the from β†’ to and the times correct, and is no move missing?"
 
92
  if moves else
93
- f"4. Moves β€” the memory records NO move for the {name}. Do you agree that this object was "
94
- "not moved from one place to another during the clip?")
95
- snd = [f"{e['label']} [{e['t0']:.1f}–{e['t1']:.1f}s]" for e in ev.get("sound") or []]
96
- sp = [f"β€œ{e['label']}” [{e['t0']:.1f}–{e['t1']:.1f}s]" for e in ev.get("speech") or []]
97
- q["sound_speech"] = ((f"5. Sounds β€” the memory links these sounds to the {name}: {_join(snd)}. "
98
- "Was each of them made by (or with) this object?"
 
 
 
99
  if snd else
100
- f"5. Sounds β€” the memory links NO sound to the {name}. Do you agree that this object "
101
- "made no audible sound in the clip?")
102
  + (f" Speech heard in the clip: {_join(sp)} β€” is it transcribed correctly?" if sp else ""))
103
- dwell = [f"{e['t0']:.1f}–{e['t1']:.1f}s" for e in ev.get("dwell") or []]
104
- q["gaze"] = (f"6. Gaze β€” the memory says the wearer looked at the {name} (first at {att.get('first_gaze_sec')}s; "
105
- f"dwell {', '.join(dwell)}). During those times, is the red gaze dot on or right next to this object?"
 
 
 
 
 
 
 
106
  if a.get("looked_at") else
107
- f"6. Gaze β€” the memory says the wearer did NOT look at the {name} before handling it. "
108
- "Do you agree β€” does the red gaze dot stay off this object before it is touched?")
 
109
  return [gr.update(label=q[k], value=None) for k, _ in DIMENSIONS]
110
 
111
 
@@ -188,19 +243,19 @@ def annotation_md(it):
188
  | **category** | {a.get('category')} |
189
  | **looks like** | {a.get('looks_like')} |
190
  | **color / material** | {a.get('color', '?')} / {a.get('material', '?')} |
191
- | **now** | {now.get('relation')} **{now.get('at')}** since {now.get('since_sec')}s{(' Β· contains: ' + str(now['contains'])) if now.get('contains') else ''} |
192
- | **looked at** | {a.get('looked_at')} β€” first gaze {att.get('first_gaze_sec')}s, {att.get('prime_gap_sec')}s before contact, total dwell {att.get('total_dwell_sec')}s |
193
  | **lifecycle** | first {lc.get('first_observed_sec')}s β†’ last {lc.get('last_observed_sec')}s, {lc.get('final_status')} |
194
  | **confidence** | {it.get('confidence')} |
195
 
196
- ### History (where it was)
197
- {fmt_list(a.get('history'))}
198
 
199
- ### Moves
200
- {fmt_list(a.get('moves'))}
201
 
202
- ### Gaze dwell intervals (yellow lane; red dot = gaze point)
203
- {fmt_iv(ev.get('dwell'))}
204
 
205
  ### Narrations linked to this object (blue lane) β€” annotator text, NOT audio
206
  {fmt_iv(ev.get('narration')) if ev.get('narration') else fmt_list(a.get('said'))}
@@ -287,6 +342,19 @@ with gr.Blocks(title="Object-centric annotation eval") as demo:
287
  ann_md = gr.Markdown()
288
  with gr.Accordion("Cross-check vs original HD-EPIC annotation (auto-generated, for reference)", open=False):
289
  xc_md = gr.Markdown()
 
 
 
 
 
 
 
 
 
 
 
 
 
290
  gr.Markdown("## Your judgement")
291
  radios = [gr.Radio(CHOICES, label=q) for _, q in DIMENSIONS]
292
  gr.Markdown("### Narrations β€” judge each one separately\n"
 
8
  import csv
9
  import json
10
  import os
11
+ import re
12
  from datetime import datetime
13
  from pathlib import Path
14
 
 
69
  RESP_CSV.rename(RESP_DIR / f"responses_old_{datetime.now():%Y%m%d_%H%M%S}.csv")
70
 
71
 
72
+ def place(anchor):
73
+ """`P02_counter.002` -> `the counter #2`; `person_01` -> `the wearer's hand`."""
74
+ if not anchor or anchor == "unknown":
75
+ return "an unknown place"
76
+ if anchor.startswith("person"):
77
+ return "the wearer's hand"
78
+ kind, _, num = re.sub(r"^P\d\d_", "", anchor).partition(".")
79
+ return f"the {kind.replace('_', ' ')}" + (f" #{int(num)}" if num.isdigit() else "")
80
+
81
+
82
+ def where(rel, anchor):
83
+ if rel == "held_by":
84
+ return "held in the wearer's hand"
85
+ return {"inside": "inside", "on": "on", "hanging_on": "hanging on"}.get(rel, "at") + " " + place(anchor)
86
+
87
+
88
+ def state_sentence(st):
89
+ a, b = st["interval_sec"]
90
+ w = where(st["relation"], st["anchor"])
91
+ if b is None:
92
+ return f"from {a:.1f}s until the end of the clip: {w}"
93
+ if abs(b - a) < 0.05:
94
+ return f"at {a:.1f}s: {w}"
95
+ return f"from {a:.1f}s to {b:.1f}s: {w}"
96
+
97
+
98
+ def move_sentence(tr):
99
+ a, b = tr["interval_sec"]
100
+ when = f"at {a:.1f}s" if abs(b - a) < 0.05 else f"between {a:.1f}s and {b:.1f}s"
101
+ return (f"{when} it goes from β€œ{where(tr['from']['relation'], tr['from']['anchor'])}” to "
102
+ f"β€œ{where(tr['to']['relation'], tr['to']['anchor'])}” (action: {tr['event'].replace('_', ' ')})")
103
+
104
+
105
+ def gaze_summary(it):
106
+ a, att = it["annotation"], it.get("attention") or {}
107
+ if a.get("looked_at"):
108
+ gap = att.get("prime_gap_sec")
109
+ return (f"Yes β€” first look at {att.get('first_gaze_sec')}s"
110
+ + (f", about {gap:.1f}s before the hand touches it" if isinstance(gap, (int, float)) else ""))
111
+ return "No β€” the memory found no look at this object before the hand touches it"
112
+
113
+
114
  def _join(xs):
115
  return "; ".join(f"({i + 1}) {x}" for i, x in enumerate(xs))
116
 
 
126
  f"β€œ{a.get('looks_like')}”. Is that what the object is and looks like?")
127
  q["localization"] = (f"2. Localization β€” do the red box (video + keyframes) and the green mask (keyframes) "
128
  f"cover the {name}, and not a neighbouring object, the hand, or background?")
129
+ hist = [state_sentence(x) for x in it.get("states") or []] or (a.get("history") or [])
130
+ q["state_history"] = (f"3. Where it is β€” the memory says the {name} is: {_join(hist)}. "
131
+ "Are these places and times correct?")
132
+ moves = [move_sentence(x) for x in it.get("transitions") or []]
133
+ MOVE_DEF = "A move = the object changes where it is (picked up, put down, put into or taken out of something). "
134
+ q["moves"] = (f"4. Moves β€” {MOVE_DEF}The memory records for the {name}: {_join(moves)}. "
135
+ "Is each move real, with the right start place, end place and time β€” and is no move missing?"
136
  if moves else
137
+ f"4. Moves β€” {MOVE_DEF}The memory records NO move for the {name}. Do you agree that this "
138
+ "object stays where it is for the whole clip?")
139
+ snd = [f"β€œ{e['label']}” at {e['t0']:.1f}–{e['t1']:.1f}s" for e in ev.get("sound") or []]
140
+ sp = [f"β€œ{e['label']}” at {e['t0']:.1f}–{e['t1']:.1f}s" for e in ev.get("speech") or []]
141
+ LISTEN = "πŸ”Š Turn the sound ON for this one (the audio is amplified; click the speaker icon if the player is muted). "
142
+ q["sound_speech"] = ((f"5. Sounds β€” {LISTEN}The memory links these sounds to the {name}: {_join(snd)}. "
143
+ "Sound names are dataset categories (e.g. β€œstir / mix / whisk” = a stirring sound, "
144
+ "β€œmetal / plastic collision” = metal hitting plastic). For each: can you hear it at that "
145
+ "time, and is it made by or with THIS object?"
146
  if snd else
147
+ f"5. Sounds β€” {LISTEN}The memory links NO sound to the {name}. Do you agree that this "
148
+ "object makes no audible sound in the clip?")
149
  + (f" Speech heard in the clip: {_join(sp)} β€” is it transcribed correctly?" if sp else ""))
150
+ GAZE_DEF = ("The red dot shows where the wearer is looking at every moment β€” it is always on screen. "
151
+ "This question is only about whether they looked at THIS object just before touching it. ")
152
+ gap = att.get("prime_gap_sec")
153
+ q["gaze"] = (f"6. Gaze before touching β€” {GAZE_DEF}The memory claims the wearer looked at the {name} at about "
154
+ f"{att.get('first_gaze_sec')}s"
155
+ + (f", {gap:.1f}s before the hand touches it" if isinstance(gap, (int, float)) else "")
156
+ + (" (yellow bar on the timeline). Around that time, is the red dot on or very close to this object?"
157
+ if it["window_sec"][0] <= (att.get("first_gaze_sec") or -1) <= it["window_sec"][1] else
158
+ f". That moment is OUTSIDE this clip ({it['window_sec'][0]:.0f}–{it['window_sec'][1]:.0f}s), so it "
159
+ "cannot be checked here β€” please answer β€œCan't tell”.")
160
  if a.get("looked_at") else
161
+ f"6. Gaze before touching β€” {GAZE_DEF}The memory claims the wearer did NOT look at the {name} before "
162
+ "touching it (grabbed or put it down without looking). In the few seconds before the hand first "
163
+ "touches this object, does the red dot stay away from it?")
164
  return [gr.update(label=q[k], value=None) for k, _ in DIMENSIONS]
165
 
166
 
 
243
  | **category** | {a.get('category')} |
244
  | **looks like** | {a.get('looks_like')} |
245
  | **color / material** | {a.get('color', '?')} / {a.get('material', '?')} |
246
+ | **at the end of the clip** | {where(now.get('relation'), now.get('at'))} (since {now.get('since_sec')}s){(' Β· contains: ' + str(now['contains'])) if now.get('contains') else ''} |
247
+ | **looked at before touching** | {gaze_summary(it)} |
248
  | **lifecycle** | first {lc.get('first_observed_sec')}s β†’ last {lc.get('last_observed_sec')}s, {lc.get('final_status')} |
249
  | **confidence** | {it.get('confidence')} |
250
 
251
+ ### Where it is (location history)
252
+ {fmt_list([state_sentence(x) for x in it.get('states') or []] or a.get('history'))}
253
 
254
+ ### Moves (changes of place)
255
+ {fmt_list([move_sentence(x) for x in it.get('transitions') or []])}
256
 
257
+ ### Claimed look at the object before touching it (yellow lane; the red dot itself is always shown)
258
+ {fmt_iv(ev.get('dwell')) if a.get('looked_at') else '- *(none claimed)*'}
259
 
260
  ### Narrations linked to this object (blue lane) β€” annotator text, NOT audio
261
  {fmt_iv(ev.get('narration')) if ev.get('narration') else fmt_list(a.get('said'))}
 
342
  ann_md = gr.Markdown()
343
  with gr.Accordion("Cross-check vs original HD-EPIC annotation (auto-generated, for reference)", open=False):
344
  xc_md = gr.Markdown()
345
+ with gr.Accordion("How to judge β€” read this once", open=True):
346
+ gr.Markdown(
347
+ "- You are checking an automatically generated **memory record of ONE object** against the video. "
348
+ "Each question states a claim from that record; answer whether the claim matches what you see and hear.\n"
349
+ "- **Correct** = fully right Β· **Partially correct** = right idea but a place, time or detail is off Β· "
350
+ "**Incorrect** = wrong Β· **Can't tell** = the video does not let you decide.\n"
351
+ "- **Times** are seconds of the full recording: read them from the boxed **VIDEO TIME** (top-right) "
352
+ "or the numbers on the bottom time axis. Small differences (under about 1 s) are fine.\n"
353
+ "- **Places** such as β€œthe counter #2” or β€œthe fridge #1” are the dataset's names for kitchen fixtures; "
354
+ "judge the kind of place (counter, fridge, drawer, hand …), you do not need to know which number it is.\n"
355
+ "- **Sound**: πŸ”Š turn the sound on. The audio has been amplified because the original recording is very quiet.\n"
356
+ "- **Gaze**: the red dot is where the wearer looks at each moment and is always visible. "
357
+ "The gaze question only asks whether they looked at this object just before touching it.")
358
  gr.Markdown("## Your judgement")
359
  radios = [gr.Radio(CHOICES, label=q) for _, q in DIMENSIONS]
360
  gr.Markdown("### Narrations β€” judge each one separately\n"