From 5f0311ffa582718d0626459304fbb4f096d9ddf7 Mon Sep 17 00:00:00 2001 From: CI Bot Date: Sun, 10 May 2026 01:24:05 +0200 Subject: [PATCH] =?UTF-8?q?model=20:=20ajoute=20beat=5Fatoms=20(V0.10=20at?= =?UTF-8?q?omes=20=E2=80=94=20voir=20video=5Fanalysis/adr/0018)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- model/video_analysis_extension.sql | 290 +++++++++++++++++++++++++++++ 1 file changed, 290 insertions(+) create mode 100644 model/video_analysis_extension.sql diff --git a/model/video_analysis_extension.sql b/model/video_analysis_extension.sql new file mode 100644 index 0000000..563823c --- /dev/null +++ b/model/video_analysis_extension.sql @@ -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);