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>
291 lines
14 KiB
SQL
291 lines
14 KiB
SQL
-- 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);
|