Pipeline & Tools
Open source annotation pipeline for egocentric household activity data
View on GitHubHow It Works
Clips scoring below 60 on the quality gate are moved to
assets/videos/rejected/ — never deleted.
Tools
HomeHands-Pose
21-point hand skeleton detection per frame. Detects left and right hand with confidence scores and pixel coordinates.
pip install mediapipe
HomeHands-Arm
Wrist-to-elbow arm segment tracking with EMA smoothing and a temporal hold buffer to prevent flickering across frames.
pip install ultralytics
HomeHands-Seg
Box-prompted SAM2 segmentation per detected hand: a tight mask around the hand itself, plus a wider mask around whatever active object it's holding.
pip install sam2
HomeHands-Depth
Per-frame metric depth in meters, fine-tuned for indoor scenes — not just a relative depth ordering.
pip install transformers torch
HomeHands-Audio
Speech transcription from egocentric narration. Generates subtitles and timestamped narration JSON locally.
pip install openai-whisper
HomeHands-QC
Scores a clip 0-100 on resolution, hand detection rate, blur, brightness, and narration presence.
pip install mediapipe openai-whisper opencv-python
HomeHands-Gate
Runs the quality check and quarantines clips scoring below 60 —
moved to assets/videos/rejected/, never deleted, so
they naturally drop out of the rest of the pipeline.
python pipeline/quality_gate.py assets/videos/
HomeHands-Pipeline
Master script that runs all modules automatically on every video in a folder. Produces one combined annotation JSON per clip.
python pipeline/run_pipeline.py
Quick Start
# 1. Clone the repository git clone https://github.com/aneessaheba/Egocentric_Homes cd Egocentric_Homes # 2. Install dependencies pip install -r requirements.txt brew install ffmpeg # 3. Add your videos cp your_videos/*.mp4 assets/videos/ # 4. Run full pipeline python pipeline/run_pipeline.py # Output per video: # Hand pose JSON → assets/processed/hand_pose/ # Arm pose JSON → assets/processed/arm_pose/ # Segmentation masks → assets/processed/masks/ # Depth maps (16-bit) → assets/processed/depth/ # Narration JSON → assets/processed/narrations/ # Quality reports → assets/processed/quality/ # (rejected clips moved to assets/videos/rejected/) # Combined JSON → assets/processed/annotations/
Models Used
| Model | Task | Made by | Size | License |
|---|---|---|---|---|
| MediaPipe Hands | Hand tracking | 8 MB | Apache 2.0 | |
| SAM2.1 Hiera-Tiny | Segmentation | Meta AI | 156 MB | Apache 2.0 |
| YOLOv8n-pose | Arm tracking | Ultralytics | 6.5 MB | AGPL-3.0 |
| Depth-Anything-V2-Metric-Indoor-Small | Depth estimation | HKU / TikTok | 95 MB | Apache 2.0 |
| Whisper base | Transcription | OpenAI | 145 MB | MIT |
| ffmpeg | Audio extraction | OSS | — | LGPL |
| OpenCV | Video processing | OSS | — | Apache 2.0 |
Output Format
{
"clip_id": "HH_001",
"filename": "WashingCup.mp4",
"activity_category": "[from clip_metadata.csv, or null]",
"activity_label": "[from clip_metadata.csv, or null]",
"participant_id": "[from clip_metadata.csv, or null]",
"home_id": "[from clip_metadata.csv, or null]",
"room": "[from clip_metadata.csv, or null]",
"camera_model": "[from clip_metadata.csv, or null]",
"duration_sec": 76.109,
"total_frames": 2281,
"fps": 29.97,
"resolution": "3840x2160",
"quality_control": {
"score": 100,
"verdict": "✅ GOOD",
"issues": []
},
"hand_detection_rate": 0.9527,
"narrations": [
{
"id": 1,
"start": 4.2,
"end": 7.8,
"text": "[transcribed narration text]"
}
],
"frames": [
{
"frame_id": 600,
"timestamp_sec": 20.02,
"hands": {
"hands_found": 2,
"wrists": [
{
"label": "left",
"confidence": 0.9821,
"px": 1620, "py": 1435,
"keypoints": { "WRIST": { "px": 1620, "py": 1435, "x": 0.422, "y": 0.664, "z": 0.0 } /* + 20 more named landmarks */ }
}
]
},
"arm": {
"left_wrist": { "px": 1564, "py": 1451 },
"left_elbow": { "px": 1324, "py": 2158 },
"right_wrist": { "px": 2060, "py": 1294 },
"right_elbow": null
},
"segmentation": {
"method": "SAM2.1-Hiera-Tiny",
"prompted_by": "hand_bbox",
"masks": [
{ "category": "hand", "label": "left", "pixel_count": 107266, "coverage_pct": 1.2932, "mask_path": "assets/processed/masks/WashingCup/frame_000600_hand_left.png" },
{ "category": "active_object", "label": "near_left", "pixel_count": 162690, "coverage_pct": 1.9614, "mask_path": "assets/processed/masks/WashingCup/frame_000600_object_left.png" }
]
},
"depth": {
"method": "Depth-Anything-V2-Metric-Indoor-Small",
"depth_path": "assets/processed/depth/WashingCup/frame_000600.png",
"depth_format": "png16",
"depth_unit": "mm",
"depth_scale": 1000,
"min_depth_m": 0.7469, "max_depth_m": 2.6691, "avg_depth_m": 1.5871
},
"narration_id": null
}
]
}
Every frame always has the same key set — hands,
arm, segmentation, depth,
narration_id — defaulting to null/empty when a given
module wasn't run for that clip, never omitted.