model : ajoute beat_atoms (V0.10 atomes — voir video_analysis/adr/0018)

Table pour les atomes de mouvement par-beat (1 enregistrement par beat × danseur).
Modèle hybride à 3 niveaux : continu (foot_height/speed/stability) + soft
distribution (state_dist_json sommant à 1 sur planted/touch/lifted/transitioning)
+ argmax (state + confidence).

Implémenté dans pipeline/atoms.py côté video_analysis (commit 654320a).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
CI Bot
2026-05-10 01:24:05 +02:00
parent 73b2ccb917
commit 5f0311ffa5

View File

@@ -0,0 +1,290 @@
-- Extension du schéma DanceVideos/ pour le projet video_analysis/
-- Voir adr/0004-db-extension-strategy.md
-- Convention héritée de DanceVideos : la PK de `videos` est `file_name VARCHAR(255)`,
-- les tables filles utilisent `video_file_name VARCHAR(255)` comme FK.
PRAGMA foreign_keys = ON;
-- Groupes de vidéos d'un même cours (V0.6, voir adr/0010)
CREATE TABLE IF NOT EXISTS video_groups (
id INTEGER PRIMARY KEY AUTOINCREMENT,
label TEXT,
time_start DATETIME,
time_end DATETIME,
n_videos INTEGER,
style TEXT,
pipeline_version TEXT NOT NULL,
notes TEXT
);
CREATE TABLE IF NOT EXISTS video_group_members (
video_file_name VARCHAR(255) PRIMARY KEY,
group_id INTEGER NOT NULL,
role TEXT CHECK (role IN ('explicative', 'demonstration', 'mixte', 'unknown')),
seq_idx INTEGER,
FOREIGN KEY (video_file_name) REFERENCES videos(file_name) ON DELETE CASCADE,
FOREIGN KEY (group_id) REFERENCES video_groups(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_vgm_group ON video_group_members(group_id);
-- Une analyse par version de pipeline et par vidéo
CREATE TABLE IF NOT EXISTS analyses (
id INTEGER PRIMARY KEY AUTOINCREMENT,
video_file_name VARCHAR(255) NOT NULL,
niveau TEXT NOT NULL, -- "audio_demix" | "beats" | "asr" | "pose" | "segment" | "label"
pipeline_version TEXT NOT NULL,
started_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
finished_at DATETIME,
summary_json TEXT,
FOREIGN KEY (video_file_name) REFERENCES videos(file_name) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_analyses_video ON analyses(video_file_name, niveau);
-- Segmentation audio (parole / musique / silence / mixed)
CREATE TABLE IF NOT EXISTS audio_segments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
video_file_name VARCHAR(255) NOT NULL,
start_s REAL NOT NULL,
end_s REAL NOT NULL,
kind TEXT NOT NULL CHECK (kind IN ('parole', 'musique', 'silence', 'mixed')),
speaker_id TEXT,
text TEXT,
confidence REAL,
FOREIGN KEY (video_file_name) REFERENCES videos(file_name) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_audio_segments_video ON audio_segments(video_file_name, start_s);
-- Beats / downbeats / tempo
CREATE TABLE IF NOT EXISTS beats (
id INTEGER PRIMARY KEY AUTOINCREMENT,
video_file_name VARCHAR(255) NOT NULL,
t_s REAL NOT NULL,
beat_in_bar INTEGER NOT NULL, -- 1..4 typiquement
is_downbeat BOOLEAN NOT NULL,
bpm REAL,
FOREIGN KEY (video_file_name) REFERENCES videos(file_name) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_beats_video ON beats(video_file_name, t_s);
-- Régimes intra-vidéo (avec_compte / en_musique / mixte / explication / silence) — voir adr/0009
CREATE TABLE IF NOT EXISTS regime_segments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
video_file_name VARCHAR(255) NOT NULL,
start_s REAL NOT NULL,
end_s REAL NOT NULL,
regime TEXT NOT NULL CHECK (regime IN ('avec_compte', 'en_musique', 'mixte', 'explication', 'compte_avec_explication', 'silence')),
confidence REAL,
pipeline_version TEXT NOT NULL,
FOREIGN KEY (video_file_name) REFERENCES videos(file_name) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_regime_video_t ON regime_segments(video_file_name, start_s);
-- Transcription word-level (WhisperX) — voir adr/0008
CREATE TABLE IF NOT EXISTS transcription_words (
id INTEGER PRIMARY KEY AUTOINCREMENT,
video_file_name VARCHAR(255) NOT NULL,
audio_segment_id INTEGER,
t_start_s REAL NOT NULL,
t_end_s REAL NOT NULL,
word TEXT NOT NULL,
speaker_id TEXT,
confidence REAL,
pipeline_version TEXT NOT NULL,
FOREIGN KEY (video_file_name) REFERENCES videos(file_name) ON DELETE CASCADE,
FOREIGN KEY (audio_segment_id) REFERENCES audio_segments(id) ON DELETE SET NULL
);
CREATE INDEX IF NOT EXISTS idx_words_video_t ON transcription_words(video_file_name, t_start_s);
CREATE INDEX IF NOT EXISTS idx_words_text ON transcription_words(word);
-- Labels sémantiques attachés aux dance_segments depuis l'ASR (V0.5.4)
CREATE TABLE IF NOT EXISTS dance_segment_labels (
id INTEGER PRIMARY KEY AUTOINCREMENT,
segment_id TEXT NOT NULL,
label TEXT NOT NULL,
category TEXT, -- "body_part" | "movement" | "direction" | "figure" | "rhythm"
source TEXT NOT NULL CHECK (source IN ('asr-keyword', 'asr-figure', 'user', 'manual')),
t_offset_s REAL, -- décalage du keyword par rapport au début du segment (négatif = avant)
confidence REAL,
pipeline_version TEXT NOT NULL,
FOREIGN KEY (segment_id) REFERENCES dance_segments(segment_id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_dsl_segment ON dance_segment_labels(segment_id);
CREATE INDEX IF NOT EXISTS idx_dsl_label ON dance_segment_labels(label);
-- Personnes détectées dans la vidéo (prof, partenaire, audience)
CREATE TABLE IF NOT EXISTS persons (
id INTEGER PRIMARY KEY AUTOINCREMENT,
video_file_name VARCHAR(255) NOT NULL,
person_id INTEGER NOT NULL, -- ID stable inter-frames (ByteTrack)
role TEXT NOT NULL CHECK (role IN ('prof', 'partner', 'audience', 'unknown')),
notes TEXT,
FOREIGN KEY (video_file_name) REFERENCES videos(file_name) ON DELETE CASCADE,
UNIQUE (video_file_name, person_id)
);
-- Pose keypoints (frame-decimated, 1/N frame typiquement)
CREATE TABLE IF NOT EXISTS pose_keypoints (
id INTEGER PRIMARY KEY AUTOINCREMENT,
video_file_name VARCHAR(255) NOT NULL,
frame INTEGER NOT NULL,
person_id INTEGER NOT NULL,
kp_json TEXT NOT NULL, -- JSON compressé : 133 keypoints {x, y, score}
FOREIGN KEY (video_file_name) REFERENCES videos(file_name) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_pose_kp_video_frame ON pose_keypoints(video_file_name, frame);
-- Segments hiérarchiques de chorégraphie (cœur de l'UX cumulation/réduction)
CREATE TABLE IF NOT EXISTS dance_segments (
segment_id TEXT PRIMARY KEY, -- ex. "v17:meso:0008"
video_file_name VARCHAR(255) NOT NULL,
start_s REAL NOT NULL,
end_s REAL NOT NULL,
level TEXT NOT NULL CHECK (level IN ('macro', 'meso', 'micro', 'nano')),
parent_segment_id TEXT,
label TEXT,
source TEXT NOT NULL CHECK (source IN ('auto-rules', 'auto-tcn', 'teacher-said', 'user')),
confidence REAL,
FOREIGN KEY (video_file_name) REFERENCES videos(file_name) ON DELETE CASCADE,
FOREIGN KEY (parent_segment_id) REFERENCES dance_segments(segment_id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_dance_segments_video ON dance_segments(video_file_name, level, start_s);
CREATE INDEX IF NOT EXISTS idx_dance_segments_parent ON dance_segments(parent_segment_id);
-- Patterns nommés (mise en espagnol, dile que no, pas de bourré...)
CREATE TABLE IF NOT EXISTS patterns (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT UNIQUE NOT NULL,
description TEXT,
style TEXT, -- "salsa cubaine" | "bachata" | "common" | ...
templates_json TEXT, -- liste d'occurrences (video_file_name + segment_id) servant de templates
occurrences_count INTEGER DEFAULT 0
);
-- Benchmarks — résultats d'évaluation par pipeline_version × méthode × tolérance × vidéo
-- (voir doc/observabilite-backoffice.md § 5, notebooks/02-segmentation-eval)
CREATE TABLE IF NOT EXISTS benchmarks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
pipeline_version TEXT NOT NULL, -- ex. "v0.5.0", "v0.5.0-beat_this-raw-audio"
run_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
notebook TEXT, -- nom/chemin du notebook qui a produit la mesure
golden_set TEXT, -- ex. "decoupe/diegoRiviera.json" ou "decoupe/all"
video_file_name VARCHAR(255), -- vidéo ; nullable si métrique agrégée
method TEXT NOT NULL, -- "V0_meso" | "4bar_grid" | "uniform_5s" | ...
tol_s REAL NOT NULL, -- tolérance en secondes
precision_score REAL NOT NULL,
recall_score REAL NOT NULL,
f1_score REAL NOT NULL,
n_pred INTEGER NOT NULL,
n_gold INTEGER NOT NULL,
notes TEXT,
FOREIGN KEY (video_file_name) REFERENCES videos(file_name) ON DELETE SET NULL
);
CREATE INDEX IF NOT EXISTS idx_benchmarks_version ON benchmarks(pipeline_version, method, tol_s);
CREATE INDEX IF NOT EXISTS idx_benchmarks_run ON benchmarks(run_at);
-- Journal d'éditions (audit trail + undo/redo unifiés) — voir adr/0013
CREATE TABLE IF NOT EXISTS edits_journal (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id TEXT NOT NULL DEFAULT 'default',
video_file_name VARCHAR(255),
transaction_id TEXT NOT NULL,
op_type TEXT NOT NULL,
target_table TEXT NOT NULL,
target_id TEXT NOT NULL,
before_json TEXT,
after_json TEXT,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
undone_at DATETIME,
redone_at DATETIME,
pipeline_version TEXT,
FOREIGN KEY (video_file_name) REFERENCES videos(file_name) ON DELETE SET NULL
);
CREATE INDEX IF NOT EXISTS idx_edits_video_t ON edits_journal(video_file_name, created_at);
CREATE INDEX IF NOT EXISTS idx_edits_txn ON edits_journal(transaction_id);
CREATE INDEX IF NOT EXISTS idx_edits_active ON edits_journal(undone_at, created_at);
-- Handles d'ancrage cross-vidéo (paires _C_/_M_) — voir adr/0014
CREATE TABLE IF NOT EXISTS video_group_handles (
id INTEGER PRIMARY KEY AUTOINCREMENT,
group_id INTEGER NOT NULL,
source_video VARCHAR(255) NOT NULL,
source_t_s REAL NOT NULL,
target_video VARCHAR(255) NOT NULL,
target_t_s REAL NOT NULL,
label TEXT,
created_by TEXT NOT NULL DEFAULT 'default',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
notes TEXT,
FOREIGN KEY (group_id) REFERENCES video_groups(id) ON DELETE CASCADE,
FOREIGN KEY (source_video) REFERENCES videos(file_name) ON DELETE CASCADE,
FOREIGN KEY (target_video) REFERENCES videos(file_name) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_handle_group ON video_group_handles(group_id);
CREATE INDEX IF NOT EXISTS idx_handle_pair ON video_group_handles(source_video, target_video);
-- Prédictions de style par classifieur RF (V0.6.1, voir adr/0011)
CREATE TABLE IF NOT EXISTS style_predictions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
file_name VARCHAR(255) NOT NULL,
style TEXT NOT NULL,
confidence REAL,
model_version TEXT NOT NULL,
predicted_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (file_name) REFERENCES videos(file_name) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_stylepred_file ON style_predictions(file_name);
CREATE INDEX IF NOT EXISTS idx_stylepred_style ON style_predictions(style);
-- État de navigation persistant (UX cumulation/réduction)
CREATE TABLE IF NOT EXISTS nav_state (
id INTEGER PRIMARY KEY AUTOINCREMENT,
video_file_name VARCHAR(255) NOT NULL,
user_id TEXT NOT NULL DEFAULT 'default',
current_segment_id TEXT,
view_level TEXT CHECK (view_level IN ('macro', 'meso', 'micro', 'nano')),
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (video_file_name) REFERENCES videos(file_name) ON DELETE CASCADE,
FOREIGN KEY (current_segment_id) REFERENCES dance_segments(segment_id) ON DELETE SET NULL,
UNIQUE (video_file_name, user_id)
);
-- Atomes de mouvement (V0.10, voir adr/0018) — granularité beat / demi-beat.
-- Modèle hybride : continu + soft distribution + argmax + confidence.
CREATE TABLE IF NOT EXISTS beat_atoms (
id INTEGER PRIMARY KEY AUTOINCREMENT,
video_file_name VARCHAR(255) NOT NULL,
person_id INTEGER NOT NULL DEFAULT 0,
t_s REAL NOT NULL,
beat_position REAL NOT NULL,
is_downbeat BOOLEAN NOT NULL,
-- Pied gauche
left_foot_height REAL,
left_foot_speed REAL,
left_foot_stability REAL,
left_foot_state VARCHAR(20),
left_foot_state_confidence REAL,
left_foot_state_dist_json TEXT,
-- Pied droit
right_foot_height REAL,
right_foot_speed REAL,
right_foot_stability REAL,
right_foot_state VARCHAR(20),
right_foot_state_confidence REAL,
right_foot_state_dist_json TEXT,
-- Transfert de poids
weight_ratio REAL,
weight_transfer_velocity REAL,
weight_on VARCHAR(10),
-- Cinématique bassin (en hip-widths)
pelvis_x REAL,
pelvis_y REAL,
pelvis_vel_x REAL,
pelvis_vel_y REAL,
-- Qualité globale
pose_confidence REAL NOT NULL,
pipeline_version TEXT NOT NULL,
computed_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (video_file_name) REFERENCES videos(file_name) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_atom_video_t ON beat_atoms(video_file_name, t_s);
CREATE INDEX IF NOT EXISTS idx_atom_video_beat ON beat_atoms(video_file_name, beat_position);