# VideoSummarizer: RAG réduit pour le vidéo (Shots → Scenes MSC3 EvidenceMSC4

<!-- category -- AI,Video,ONNX,Patterns,Architecture,LLM,NER,CLIP -->
<datetime class="hidden">2026-01-15T19:00</datetime>

> **État d'avancement**: En cours de développement dans le cadre du [lucidRAG](https://www.lucidrag.com).
> **Source**: [github.comM SK1scottgal/lucidrag](https://github.com/scottgal/lucidrag)

**Lorsque cela convient**: VideoSummarizer est le **orchestrateur** de la [lucidRAG](https://www.lucidrag.com) famille, combining five pipelines into a unified video analysis engine:

- **[DocSummarizer](/blog/building-a-document-summarizer-with-rag)** - Documents
- **[ImageSummarizer](/blog/constrained-fuzzy-image-intelligence)** - Images
- **[AudioSummarizer](/blog/audiosummarizer-forensic-audio-characterization)** - Audio  profilage acoustique, diarrisation de l'émetteurM SK3 transcriptionMSC4
- **VideoSummarizer** (il s'agit de cet articleM SK1 - Vidéo (orchestre tous les trois, ajoute des shotsMSC5structure de la scèneMST6
- **[Système de gestion des données](/blog/datasummarizer-how-it-works)** - Données

Tous suivent la même chose **[Modèle RAG réduit](/blog/reduced-rag)**: extraire les signaux une fois , entreposer des preuves, synthétiser avec l’entrée LLM limitéeM SK3

---


Le traitement d’une image cinématographique de deux heures avec des embeddings CLIP prendrait des heures et coûterait des centaines de dollars à calculer.

**VideoSummarizer résout ce problème grâce à trois améliorations clés:**

1. **Déduplication perceptuelle du hash** - Passer à des cadres visuellement similaires avant de recourir à un ML coûteux 40% réduction
2. **Établissement d'un CLIP par lot** - Processus 8 images par passeur GPU au lieu d'une seule (3-5x speedup)
3. **Composition du pipeline** - Image de chaîneSummarizer pour les cadres-clés, AudioSummariser pour la paroleM SK2 NER  Reconnaissance d'entités désignéesMSC4 pour les entitésMska5 DocSum marizer pour l'analyse du texteMka6 GraphRAG pour les relations entre des entités

Le résultat: a M SK1 procédés de cinéma d'une heure en ~10-15 minutes, non en heuresCLIP est utilisé pour **fast, embeddings sémantiques locaux**; Les LLM de vision sont facultatifs et ne peuvent être invoqués que pour l'analyse élargieM SK1

Les mêmes principes d'architecture que [ImageSummarizer](/blog/constrained-fuzzy-image-intelligence) et [AudioSummarizer](/blog/audiosummarizer-forensic-audio-characterization), mais constitué d’une conduite d’analyse vidéo unifiéeM SK1

> **Perspectives de base:** Video is shots + audio + textM SK2 Tracer chaque domaine avec des outils spécialisés, fusionner les résultats en scènes cohérentesMSC4
> 
> - **Structure du processus en premier** (cutsM SK1 I-frames , segments audioMSC4
> - **Extraire une fois les signaux modaux croisés-** (embeddingsM SK1 transcriptions , entités)

**Terminologie:**

- **Récolte** - prise de la caméra entre les coupes (structuraleM SK2 à partir de la détection des scènes FFmpeg)
- **Scène** - groupe contigu de tirs formant une unité cohérente  sémantique
- **Evidence** - `(start_time, end_time)` + signaux + pointeurs + provenance

**Modèles ML clés utilisés:**

- **[CLIP](https://openai.com/research/clip)** - OpenAI's Contrastive Language -Image PreMSC3trainingM SK4génère des embeddings dimensionnels qui encodent la sémantique visuelle
- **[Les murmurs](https://openai.com/research/whisper)** - OpenAI 's speech recognition model; transcribe audio to text with timestamps
- **[BERT-NER](https://huggingface.co/dslim/bert-base-NER)** - Reconnaissance de l'entité désignée ; extrait des endroits de texte des personnes, organisationsM SK3
- **[Le temps de fonctionnement d'ONNX](https://onnxruntime.ai/)** - Cross-inférence MML de la plate-formeM SK2 exploite les modèles sur le CPU /GPU sans bouclage de cadreMSC4in

Cet article porte sur:

- Comment le VideoSummarizer orchestre cinq pipelines (ImageSommarizerM SK1 AudioSum marizer , NER, DocSummarzerMSSK4 GraphRAGMSC5
- Architecture des ondes: M SK1 vagues de la normalisation à la production d'éléments probants
- **Système de capacités**: Téléchargement de modèles lazy, Détection GPUM SK2 routage réactif
- Optimisation du CLIP par lots pour les embeddings de cadres clés (3-5x plus rapidesM SK1
- Déduplication perceptuelle du hash (40% réduction de cadresM SK1
- Multi-cluster des scènes de signalisation M SK1insertion + transcription + type de coupe S+ temporelle )
- Intégration NER pour l'extraction d'entités des transcriptions
- **Atomes éphémères** pour la limite de vitesse, estimation du tempsM SK1 et pression d'arrière
- Extrait: scènesM SK1 images, transcriptions , pistes de texte à titre d'éléments de preuve RAG

**Articles connexes**:

- **[RAG réduit](https://www.mostlylucid.net/blog/reduced-rag)** - Le modèle de base
- **[Modèle de fuzziness restreint](/blog/constrained-fuzziness-pattern)** - Le modèle fondamental
- **Réduction des mises en œuvre de RAG:**
  - [DocSummarizer](/blog/building-a-document-summarizer-with-rag) - Document RAG avec extraction d'entités
  - [ImageSummarizer](/blog/constrained-fuzzy-image-intelligence) - RAG d'image avec 22- intelligence visuelle des ondes
  - [AudioSummarizer](/blog/audiosummarizer-forensic-audio-characterization) - Caractérisation audio judiciaire
  - **VideoSummarizer (le présent articleM SK1** - Organisateur de l'intelligence vidéo

[TOC]

---


## Le problème: La vidéo est coûteuse

> **Indicateurs de référence**: Les chiffres ci-dessous mesurés sur AMD 9950X (16-coreM SK3 S/ NVIDIA A4000 | | (16GB | ) \ /\ | SSK9 | GB RAM | **ordre-deM SK1effets de grandeur**, aucune garantie de performance absolueM SK1

Un film typique contient:

- **~170,000 cadres** (2 heures à 24fpsM SK2
- **~2 heures d'audio** (discussionM SK1 musique , effets)
- **Les couches de texte multiples** (sous-titresM SK1crédits , sur le texte de l’écran

L’approche du saumon (aucune personne ne le faitM SK1 mais elle fixe l’échelle):

- Établissement de CLIP par cadre: M SK1ms × | | **9.4 heures**
- Vision LLM par cadre: M SK1s × | | 3 | 4 | **94 heures** (cloud Vision API ballpark

Même avec l'extraction des cadres clés (say, 500-1000 framesM SK3 queMSC4est encore 100-200 secondes d'inférence CLIP sérieuseMST6

**Approche traditionnelle**: "Extracter les cadres de clésM SK2 envoyer à Vision LLM, espérer le meilleurMSC4

**Problème**: Il brûle les calculs sur des cadres redundants ( beaucoup de cadres clés sont visuellement similaires ), les traite en série

**Solution**: MultiM SK1 Filtration par étapes , Traitement en lots, et composition des pipelinesMSC4

---


## L'architecture du VideoSummarizer

Mise en œuvre de VideoSummarizer **[RAG réduit](/blog/reduced-rag)** pour le vidéo avec une réduction de trois étapes-

```mermaid
flowchart TB
    subgraph Input["Video File (.mp4, .mkv, etc.)"]
        VF[Video File]
    end

    subgraph Stage1["Stage 1: Structural Analysis"]
        N[NormalizeWave<br/>FFprobe metadata]
        SD[ShotDetectionWave<br/>Scene cuts via FFmpeg]
        KE[KeyframeExtractionWave<br/>I-frame + dedup]
        AE[AudioExtractionWave<br/>FFmpeg audio extract]
    end

    subgraph Stage2["Stage 2: Content Extraction"]
        IS[ImageSummarizer<br/>CLIP, OCR, Vision]
        AS[AudioSummarizer<br/>Whisper, Diarization]
        NER[NER Service<br/>Entity extraction]
        DS[DocSummarizer<br/>Text analysis if long]
        GR[GraphRAG<br/>Entity relationships]
    end

    subgraph Stage3["Stage 3: Scene Assembly"]
        SC[SceneClusteringWave<br/>Multi-signal clustering]
        EV[EvidenceGenerationWave<br/>RAG chunks]
    end

    VF --> N
    N --> SD --> KE
    N --> AE
    KE --> IS
    AE --> AS
    IS --> NER
    AS --> NER
    NER --> DS
    NER --> GR
    IS --> SC
    DS --> SC
    GR --> EV
    SC --> EV

    style Stage1 stroke:#22c55e,stroke-width:2px
    style Stage2 stroke:#3b82f6,stroke-width:2px
    style Stage3 stroke:#8b5cf6,stroke-width:2px
```

### Autres éléments de preuve produits

Avant de s’engager dans la mise en oeuvre, ici ' c’est ce que vous obtiendrez M SK2le schéma de sortie):

| Artéfact | Champs clés CMS2 Source CMS3
|----------|------------|--------|
| **Scène** | `id`, `start_time`, `end_time`, `key_terms[]`, `speaker_ids[]`, `embedding[512]` | SceneClusteringWave  |
| **Récolte** | `id`, `start_time`, `end_time`, `cut_type`, `keyframe_path` | ShotDetectionWave  |
| **Déclaration** | `id`, `text`, `start_time`, `end_time`, `speaker_id`, `confidence` | TranscriptionWave  |
| **TextTrack** | `id`, `text`, `start_time`, `text_type` (titleMSC1credit/subtitle /ocrM SK4 MSC5 SubtitleExtractionWave SSK6
| **Cadre de clés** | `id`, `timestamp`, `frame_path`, `dhash`, `clip_embedding[512]` | KeyframeExtractionWave  |

Chaque artefact comprend **provenance**: onde source , échantillon de temps de traitementM SK2 cote de confiance. Ces artefacts sont les **unités de récupération primaire** pour les demandes de vidéo; les fichiers bruts ne sont jamais récupérés directement.

### Le pipeline de signalisation de la vague

VideoSummarizer utilise un **architecture des ondes basée sur le signal-** où chaque onde déclare explicitement ses contrats de signaux:

```csharp
public interface ISignalAwareVideoWave
{
    /// <summary>Signals this wave requires before it can run.</summary>

    IReadOnlyList<string> RequiredSignals { get; }

    /// <summary>Signals this wave can optionally use if available.</summary>

    IReadOnlyList<string> OptionalSignals { get; }

    /// <summary>Signals this wave emits on successful completion.</summary>

    IReadOnlyList<string> EmittedSignals { get; }

    /// <summary>Cache keys this wave produces for downstream waves.</summary>

    IReadOnlyList<string> CacheEmits { get; }

    /// <summary>Cache keys this wave consumes from upstream waves.</summary>

    IReadOnlyList<string> CacheUses { get; }
}
```

Cela permet **Coordination des ondes dynamiques**:

- Les ondes s'écoulent automatiquement si les signaux requis manquent
- Résolution des dépendances au moment du démarrage (pas d’ordre hardcoded)
- Les reruns partiels sont reproductibles (cache par clé de signalisation)
- Accroître la granularité de l’UI: chaque vague émet le progrès indépendamment

### Le pipeline 16-Wave

L’extraction des cadres-clés est mise en œuvre sous la forme de 7 ondes granulaires pour un meilleur parallèle et une plus grande efficacité du cache.

| Écoule MSC1 Priorité MSC2 Besoins SMC3 Emits SMC4 Temps ESC5
|------|----------|----------|-------|------|
| **Normaliser la vague** | 1000 | - | `video.duration`, `video.fps`, `video.normalized` | ~2s S|
| **FFmpegShotDetectionWave** | 900 | `video.normalized` | `shots.detected`, `shots.count` | ~5-10s S|
| **La vague de détection IFrame** | 850 | `video.normalized` | `keyframes.iframes_detected`, `keyframes.iframes_count` | ~3s S|
| **Voie de sélection des cadres-clés** | 840 | `shots.detected`, `keyframes.iframes_detected` | `keyframes.selected`, `keyframes.selected_count` | ~1s S|
| **La vague d'extraction des thumbnails** | 830 | `keyframes.selected` | `keyframes.thumbnails_extracted` | ~5s S|
| **KeyframeDeduplicationWave** | 820 | `keyframes.thumbnails_extracted` | `keyframes.deduplicated`, `keyframes.duplicates_skipped` | ~1s S|
| **KeyframeFullResExtractionWave** | 810 | `keyframes.deduplicated` | `keyframes.extracted`, `keyframes.count` | ~10s S|
| **ClipEmbeddingWave** | 800 | `keyframes.extracted` | `clip.embeddings_ready`, `clip.embeddings_count` | ~30s S|
| **ImageAnalysisWave** | 790 | `keyframes.deduplicated` | `keyframes.analyzed`, `ocr.extracted` | ~60s S|
| **TitleCreditsDetectionWave** | 750 | `shots.detected` | `title.detected`, `credits.detected` | ~5s S|
| **AudioExtractionWave** | 650 | `video.normalized` | `audio.extracted`, `audio.path` | ~30s S|
| **Voie de transcription** | 600 | `audio.extracted` | `transcription.complete`, `transcription.utterance_count` | ~120s S|
| **Sous-titreExtractionWave** | 550 | `video.normalized` | `subtitles.extracted` | ~2s S|
| **ChapitreExtractionWave** | 500 | `video.normalized` | `chapters.extracted` | ~1s S|
| **SceneClusteringWave** | 400 | `shots.detected` | `scenes.detected`, `scene.count` | ~5s S|
| **EvidenceGenerationWave** | 100 | `scenes.detected` | `evidence.generated` | ~2s S|

**Notes:**

- Utilisations de ImageAnalysisWave `keyframes.deduplicated` (non completM SK1res): Le OCR fonctionne sur les thumbnails ; la sous-titrage de vision utilise le pleinMSC4res lorsqu'il est disponible par routage des capacitésMST5
- Les vagues 3-7 sont les vagues granulaires de l’extraction du cadre-clé " sous

**Total pour 2-heure de film**: ~10-15 minutes (vsM SK3 heures sans optimisation)

### Bien-Keys de signalisation connues

Les signaux sont définis comme des constantes pour la cohérence:

```csharp
public static class VideoSignals
{
    // NormalizeWave signals
    public const string VideoDuration = "video.duration";
    public const string VideoFps = "video.fps";
    public const string VideoNormalized = "video.normalized";

    // Shot detection signals
    public const string ShotsDetected = "shots.detected";
    public const string ShotsCount = "shots.count";

    // Keyframe signals
    public const string IframesDetected = "keyframes.iframes_detected";
    public const string KeyframesSelected = "keyframes.selected";
    public const string KeyframesDeduplicated = "keyframes.deduplicated";
    public const string KeyframesExtracted = "keyframes.extracted";

    // CLIP embedding signals
    public const string ClipEmbeddingsReady = "clip.embeddings_ready";

    // Scene clustering signals
    public const string ScenesDetected = "scenes.detected";
    public const string SceneCount = "scene.count";

    // Transcription signals
    public const string TranscriptionComplete = "transcription.complete";
}
```

---


## Système de capacité: Modèles lazy & Routage

VideoSummarizer utilise un **capacité-architecture basée**: détecter GPU une fois au démarrage , télécharger les modèles lentement

### Manifeste de modèle (YAML + TypeM SK2Constants sûres)

Les modèles sont définis dans `models.yaml`. Aucune chaîne magique dans le code

```yaml
# models.yaml (excerpt)
models:
  clip-vit-b32:
    name: "CLIP ViT-B/32"
    download_url: "https://huggingface.co/openai/clip-vit-base-patch32/resolve/main/onnx/visual_model.onnx"
    preferred_providers: [CUDAExecutionProvider, DmlExecutionProvider, CPUExecutionProvider]

components:
  ClipEmbeddingWave:
    models: [clip-vit-b32]
    fallback_chain: [ImageAnalysisWave]
```

```csharp
// Type-safe constants (no raw strings)
await coordinator.EnsureModelAsync(ModelIds.ClipVitB32);
await coordinator.ActivateWaveAsync(ComponentIds.TranscriptionWave);

// Route with fallback
var route = await coordinator.RouteWorkAsync(new[]
{
    ComponentIds.ClipEmbeddingWave,    // Primary (GPU)
    ComponentIds.ImageAnalysisWave     // Fallback (CPU)
});
```

### Atomes d'efficacité du pipeline

Limitation des vitesses, estimation du temps , et pression d'arrière adaptative permettent à l'interface de rester réceptive tout en maximisant le débit

```csharp
// Time estimation from actual data
var estimator = CapabilityAtoms.CreateTimeEstimator();
using (estimator.Time("clip_embedding")) { await ProcessAsync(); }
var eta = estimator.GetEstimate("clip_embedding", remaining: 50);
// eta.Estimated, eta.Optimistic, eta.Pessimistic, eta.Confidence
```

> **Dossiers du système de capacité complète**: Voir `Mostlylucid.Summarizer.Core/Capabilities/` pour la détection de GPU, signal pub/subM SK2 contrôleurs d'arrière-pressure , et conception de topologie en filet

---


## Optimisation des clés 1: Déduplication perceptuelle de l'abréviation

Avant de lancer des embeddings CLIP coûteux, VideoSummarizer filtre les cadres visuellement similaires en utilisant **Différence hash (dHashM SK1**.

### Comment fonctionne dHash

```csharp
public class KeyframeDeduplicationService
{
    // dHash parameters: 9x8 grayscale = 64 bits
    private const int HashWidth = 9;
    private const int HashHeight = 8;
    private const int DefaultHammingThreshold = 10;

    public async Task<ulong> ComputeDHashAsync(string imagePath, CancellationToken ct)
    {
        using var image = Image.Load<Rgba32>(imagePath);

        // Resize to 9x8 (one extra column for gradient comparison)
        image.Mutate(x => x
            .Resize(HashWidth, HashHeight)
            .Grayscale());

        ulong hash = 0;
        int bit = 0;

        // Compare adjacent pixels horizontally
        for (int y = 0; y < HashHeight; y++)
        {
            for (int x = 0; x < HashWidth - 1; x++)
            {
                var left = image[x, y].R;
                var right = image[x + 1, y].R;

                // Set bit if left pixel is brighter than right
                if (left > right)
                {
                    hash |= (1UL << bit);
                }
                bit++;
            }
        }

        return hash;
    }

    public static int HammingDistance(ulong a, ulong b) =>
        BitOperations.PopCount(a ^ b);
}
```

**Exemple de sortie:**

```
Input: 50 keyframe candidates (from codec I-frames)

Deduplication (Hamming threshold 10):
  Frame 0: hash=0x8f3a2c1d → KEEP (first frame)
  Frame 1: hash=0x8f3a2c1e → SKIP (distance=1 from frame 0)
  Frame 2: hash=0x8f3a2c1f → SKIP (distance=2 from frame 0)
  Frame 3: hash=0xc7e1b4a2 → KEEP (distance=28 from frame 0)
  ...

Result: 50 → 30 frames (40% reduction)
Processing saved: ~8 seconds of CLIP inference
```

**Pourquoi cela importe:**

- **Réduction du cadre ~40%** sur le contenu typique
- **<1ms par cadre** pour le calcul du hash (vsM SK1 200ms pour CLIP)
- Filtre les cadres redundants **précédemment** Opérations GPU coûteuses

---


## Optimisation des clés 2: Embedding de CLIP en série

Au lieu de traiter une image à la fois, Batches vidéoSummarizer M SK1 images par passeur GPU.

### Architecture de traitement par lots

```csharp
public class BatchClipEmbeddingService
{
    private const int ClipImageSize = 224;
    private const int DefaultBatchSize = 8; // 8 images per GPU pass

    public async Task<Dictionary<int, float[]>> GenerateBatchEmbeddingsAsync(
        Dictionary<int, string> framePaths,
        IBackpressureController backpressure,
        ITimeEstimator estimator,
        int batchSize = DefaultBatchSize,
        CancellationToken ct = default)
    {
        var session = await GetOrLoadClipModelAsync(ct);
        var results = new ConcurrentDictionary<int, float[]>();

        // Pre-index batch for O(1) lookup
        var batches = framePaths
            .Select((kvp, idx) => (idx, kvp.Key, kvp.Value))
            .Chunk(batchSize)
            .ToList();

        foreach (var batch in batches)
        {
            // Acquire slot from backpressure controller (adaptive concurrency)
            using var slot = await backpressure.AcquireSlotAsync(ct);
            using var timer = estimator.Time("clip_batch");

            // Create batch tensor [batchSize, 3, 224, 224]
            var tensor = new DenseTensor<float>(new[] { batch.Length, 3, ClipImageSize, ClipImageSize });

            // Preprocess images into tensor slots
            for (int i = 0; i < batch.Length; i++)
            {
                var (_, frameIndex, path) = batch[i];
                PreprocessImageToTensor(path, tensor, slotIndex: i);
            }

            // Single GPU pass for entire batch
            var inputs = new List<NamedOnnxValue>
            {
                NamedOnnxValue.CreateFromTensor("input", tensor)
            };

            using var outputResults = session.Run(inputs);
            // Extract embeddings, record latency for backpressure adjustment
        }

        backpressure.RecordLatency(estimator.GetAverageDuration("clip_batch"));
        return new Dictionary<int, float[]>(results);
    }
}
```

**Comparaison des performances:**

```
Input: 30 keyframes (after deduplication)

Serial processing (1 frame at a time):
  30 × 200ms = 6,000ms (6.0 seconds)

Batch processing (8 frames per pass):
  4 batches × 350ms = 1,400ms (1.4 seconds)

Speedup: 4.3x
```

**Pourquoi le traitement en lots fonctionne:**

- Le parallèle GPU est sous-utilisé avec une seule inference d’image-
- Tensor par lots `[8, 3, 224, 224]` Utilise la même mémoire GPU que l'image unique (almost)
- ONNX Runtime optimise les opérations de lot internement

---


## Optimisation clé 3: Composition du pipeline

VideoSummarizer n’invente pas de nouveau ImageSommarizer ou AudioSum marizer. Il **• les chaînes** ils.

### Keyframe Sub-Pipeline:Intégration ImageSummarizer

L'extraction du cadre de clés est divisée en 7 vagues granulaires (voir la table des vagues ci-dessusM SK2 Voici le modèle de coordination montrant comment elles se chainent ensemble:

```csharp
// IFrameDetectionWave → KeyframeSelectionWave → ThumbnailExtractionWave
// → KeyframeDeduplicationWave → KeyframeFullResExtractionWave → ClipEmbeddingWave

// ClipEmbeddingWave coordinates with ImageSummarizer
public class ClipEmbeddingWave : IVideoWave, ISignalAwareVideoWave
{
    private readonly BatchClipEmbeddingService _batchClipService;
    private readonly IBackpressureController _backpressure;
    private readonly ITimeEstimator _estimator;

    public IReadOnlyList<string> RequiredSignals => [VideoSignals.KeyframesExtracted];
    public IReadOnlyList<string> EmittedSignals => [VideoSignals.ClipEmbeddingsReady];

    public async Task ProcessAsync(VideoContext context, CancellationToken ct)
    {
        var keyframes = context.GetCached<Dictionary<int, string>>("keyframes.paths");

        // Batch CLIP embedding with backpressure control
        var embeddings = await _batchClipService.GenerateBatchEmbeddingsAsync(
            keyframes, _backpressure, _estimator, batchSize: 8, ct);

        foreach (var (frameIndex, embedding) in embeddings)
            context.KeyframeEmbeddings[frameIndex] = embedding;
    }
}

// ImageAnalysisWave runs ImageSummarizer on deduplicated frames
public class ImageAnalysisWave : IVideoWave, ISignalAwareVideoWave
{
    public IReadOnlyList<string> RequiredSignals => [VideoSignals.KeyframesDeduplicated];

    public async Task ProcessAsync(VideoContext context, CancellationToken ct)
    {
        var keyframePaths = context.GetCached<List<string>>("keyframes.deduplicated_paths");

        foreach (var path in keyframePaths)
        {
            // Run ImageSummarizer for OCR, vision, captions
            var result = await _imageOrchestrator.AnalyzeAsync(path, ct);
            context.SetCached($"image_analysis.{Path.GetFileName(path)}", result);
        }
    }
}
```

### TranscriptionWave: AudioSummarizer Integration

L’extraction et la transcription sonores sont maintenant des signaux distincts-onde de sensibilisationM SK1

```csharp
// AudioExtractionWave runs first (extracts audio track from video)
public class AudioExtractionWave : IVideoWave, ISignalAwareVideoWave
{
    public IReadOnlyList<string> RequiredSignals => [VideoSignals.VideoNormalized];
    public IReadOnlyList<string> EmittedSignals => ["audio.extracted", "audio.path"];

    public async Task ProcessAsync(VideoContext context, CancellationToken ct)
    {
        var audioPath = await _ffmpegService.ExtractAudioAsync(
            context.VideoPath, context.WorkingDirectory, ct);
        context.SetCached("audio.path", audioPath);
    }
}

// TranscriptionWave depends on audio.extracted signal
public class TranscriptionWave : IVideoWave, ISignalAwareVideoWave
{
    public IReadOnlyList<string> RequiredSignals => ["audio.extracted"];
    public IReadOnlyList<string> EmittedSignals => [
        VideoSignals.TranscriptionComplete,
        "transcription.utterance_count"
    ];

    public async Task ProcessAsync(VideoContext context, CancellationToken ct)
    {
        var audioPath = context.GetCached<string>("audio.path");

        // Run AudioSummarizer pipeline (Whisper + diarization)
        var audioProfile = await _audioOrchestrator.AnalyzeAsync(audioPath, ct);

        // Extract utterances with speaker info
        var turns = audioProfile.GetValue<List<SpeakerTurn>>("speaker.turns");
        foreach (var turn in turns ?? [])
        {
            context.Utterances.Add(new Utterance
            {
                Id = Guid.NewGuid(),
                Text = turn.Text,
                StartTime = turn.StartSeconds,
                EndTime = turn.EndSeconds,
                SpeakerId = turn.SpeakerId,
                Confidence = turn.Confidence
            });
        }

        // Run NER on full transcript for entity extraction
        var transcript = audioProfile.GetValue<string>("transcription.full_text");
        if (!string.IsNullOrEmpty(transcript))
        {
            var entities = await _nerService.ExtractEntitiesAsync(transcript, ct);
            context.SetCached("transcript_entities", entities);

            // Emit entity signals by type (PER, ORG, LOC, MISC)
            foreach (var group in entities.GroupBy(e => e.Type))
            {
                context.AddSignal($"transcript.entities.{group.Key.ToLowerInvariant()}",
                    group.Select(e => e.Text).Distinct().ToList());
            }
        }
    }
}
```

---


## NER Integration: Reconnaissance des entités désignées

VideoSummarizer extrait des entités désignées de transcriptions en utilisant BERT-based NER M SK1ONNX).

### OnnxNerService

```csharp
public class OnnxNerService
{
    // Model: dslim/bert-base-NER (ONNX exported)
    // Entities: PER (Person), ORG (Organization), LOC (Location), MISC (Miscellaneous)

    public async Task<List<EntitySpan>> ExtractEntitiesAsync(string text, CancellationToken ct)
    {
        var entities = new List<EntitySpan>();

        // Chunk long text (BERT max 512 tokens)
        foreach (var chunk in ChunkText(text, maxTokens: 400, overlap: 50))
        {
            // Tokenize with WordPiece
            var tokens = _tokenizer.Tokenize(chunk);

            // Run ONNX inference
            var inputs = PrepareInputs(tokens);
            using var results = _session.Run(inputs);

            // Decode BIO tags
            var predictions = DecodePredictions(results);
            var chunkEntities = ExtractEntitySpans(tokens, predictions);

            entities.AddRange(chunkEntities);
        }

        // Deduplicate entities
        return entities
            .GroupBy(e => (e.Text.ToLowerInvariant(), e.Type))
            .Select(g => g.First())
            .ToList();
    }
}
```

**Exemple de sortie:**

```
Transcript: "Today we're speaking with John Smith from Microsoft about
their new AI lab in Seattle. The project, codenamed Phoenix, builds
on research from Stanford University."

Entities extracted:
  PER: John Smith
  ORG: Microsoft, Stanford University
  LOC: Seattle
  MISC: Phoenix

Signals emitted:
  transcript.entities.per = ["John Smith"]
  transcript.entities.org = ["Microsoft", "Stanford University"]
  transcript.entities.loc = ["Seattle"]
  transcript.entities.misc = ["Phoenix"]
```

**Pourquoi le NER est important pour la vidéo:**

- Enablez des questions comme "Trouver des vidéos mentionnant Microsoft"
- Liens vers le graphique de l'entité DocSummarizer
- Fournit des métadonnées structurées sans inference LLM

---


## Multi-Clustering des scènes de signalisation

L'utilisation d'embeddings CLIP seuls pour la détection des scènes n'est pas efficace't fonctionne bien. Les cadres clés sont peu nombreux par le design M SK2un changement par shot ), mais les prises de vue sont densesMSC4 Avec des embeddeds CLIP5 pour Scènes MCSK6, une couverture C(~2% un regroupement pure d'emplacements ne produit qu'une scène pour un film d'une durée de 2-heure M.

VideoSummarizer utilise un **approche de signal multi-** qui combine les signaux pondérés 4 pour une détection robuste des limites de la scène . Les limites sont choisies par **Évaluation pondérée déterministe**, fin non apprise-àM SK2finMSC3

### SceneClusteringWave: Multi-Architecture de signalisation

```csharp
public class SceneClusteringWave : IVideoWave, ISignalAwareVideoWave
{
    // Signal weights for boundary scoring
    private const double EmbeddingWeight = 0.4;   // CLIP embedding dissimilarity
    private const double TranscriptWeight = 0.3;  // Semantic shift in transcript
    private const double CutTypeWeight = 0.2;     // Fade/dissolve detection
    private const double TemporalWeight = 0.1;    // Time since last scene

    // Temporal constraints
    private const double MinSceneDuration = 15.0;   // Don't split scenes < 15s
    private const double MaxSceneDuration = 300.0;  // Force split at 5 minutes
    private const double TargetSceneDuration = 90.0; // Prefer ~90s scenes

    public IReadOnlyList<string> RequiredSignals => [VideoSignals.ShotsDetected];
    public IReadOnlyList<string> OptionalSignals => [
        VideoSignals.ClipEmbeddingsReady,
        VideoSignals.TranscriptionComplete,
        VideoSignals.KeyframesDeduplicated
    ];
    public IReadOnlyList<string> EmittedSignals => [
        VideoSignals.ScenesDetected,
        "scene.count",
        "scene.avg_duration",
        "scene.clustering_method"
    ];

    private List<(int shotIndex, double score)> ComputeBoundaryScores(VideoContext context)
    {
        var shots = context.Shots.OrderBy(s => s.StartTime).ToList();
        var scores = new List<(int, double)>();

        // Build embedding map with nearest-neighbor interpolation
        var shotEmbeddings = PropagateEmbeddingsToNearbyShots(context, shots);

        // Build transcript windows for semantic shift detection
        var transcriptWindows = BuildTranscriptWindows(context, shots, windowSeconds: 10);

        for (int i = 0; i < shots.Count - 1; i++)
        {
            double score = 0;
            var currentShot = shots[i];
            var nextShot = shots[i + 1];

            // 1. Embedding dissimilarity (40%)
            if (shotEmbeddings.TryGetValue(i, out var currentEmbed) &&
                shotEmbeddings.TryGetValue(i + 1, out var nextEmbed))
            {
                var similarity = CosineSimilarity(currentEmbed, nextEmbed);
                score += (1.0 - similarity) * EmbeddingWeight;
            }

            // 2. Transcript semantic shift (30%)
            if (transcriptWindows.TryGetValue(i, out var currentWords) &&
                transcriptWindows.TryGetValue(i + 1, out var nextWords))
            {
                var overlap = currentWords.Intersect(nextWords).Count();
                var union = currentWords.Union(nextWords).Count();
                var jaccard = union > 0 ? (double)overlap / union : 0;
                score += (1.0 - jaccard) * TranscriptWeight;
            }

            // 3. Cut type signal (20%) - fades/dissolves suggest scene boundaries
            if (currentShot.CutType is "fade" or "dissolve")
            {
                score += CutTypeWeight;
            }

            // 4. Temporal pressure (10%) - encourage splits near target duration
            var timeSinceLastScene = currentShot.EndTime - GetLastSceneBoundary();
            if (timeSinceLastScene > TargetSceneDuration)
            {
                var pressure = Math.Min(1.0, (timeSinceLastScene - TargetSceneDuration) / 60);
                score += pressure * TemporalWeight;
            }

            scores.Add((i, score));
        }

        return scores;
    }
}
```

### Principales innovations

1. **PrÃ©sence-Propagation de l'emplacement du voisin**: Seules les prises de ~2% ont un emplacement CLIP direct.

2. **Windows sémantiques de la traduction**: Construit des fenêtres de mots sur une seconde autour d'une image et détecte les déplacements sémantiques à l'aide d'un distance Jaccard ( un proxye de dérive sémantique bon marché mais robuste

3. **Connaissance du type de coupe**: FadeM SK1to-les transitions noires et de dissociation indiquent fortement les limites de la scène

4. **Limite d'adaptation**: Au lieu d'un seuil fixeM SK1 sélectionne les limites de la partie supérieure 25% des notes

5. **Contraintes temporelles**: Exécute le minimum des scènes et les limites de 15 à

**Exemple:**

```
Input: 1881 shots from a 2-hour movie
       39 keyframes with CLIP embeddings
       2302 utterances from transcript

Boundary scoring per shot:
  Shot 45-46: embedding=0.15, transcript=0.32, cut=0.0, temporal=0.0 → score=0.156
  Shot 46-47: embedding=0.08, transcript=0.12, cut=0.0, temporal=0.0 → score=0.068
  Shot 47-48: embedding=0.35, transcript=0.41, cut=0.2, temporal=0.05 → score=0.388 ← BOUNDARY
  ...

Adaptive threshold (top 25%): 0.25
Natural boundaries found: 45

Output: 47 scenes (avg 2.6 minutes per scene)
  - Min scene: 15.2s
  - Max scene: 298.4s
  - Total coverage: 100%

Signals:
  scenes.detected = true
  scene.count = 47
  scene.avg_duration = 156.3
  scene.clustering_method = "multi_signal_weighted"
```

---


## Le contrat de signalisation vidéo

VideoSummarizer prolonge le contrat de signalisation d’ImageSum marizer et AudioSommarizer:

```csharp
public record VideoSignal
{
    public required string Key { get; init; }      // "scene.count", "transcript.entities.per"
    public object? Value { get; init; }
    public double Confidence { get; init; } = 1.0;
    public required string Source { get; init; }   // "SceneClusteringWave"

    // Video-specific: time range
    public double? StartTime { get; init; }
    public double? EndTime { get; init; }

    public DateTime Timestamp { get; init; }
    public Dictionary<string, object>? Metadata { get; init; }
    public List<string>? Tags { get; init; }       // ["visual", "scene"]
}

public static class VideoSignalTags
{
    public const string Visual = "visual";
    public const string Audio = "audio";
    public const string Speech = "speech";
    public const string Ocr = "ocr";
    public const string Motion = "motion";
    public const string Scene = "scene";
    public const string Shot = "shot";
    public const string Metadata = "metadata";
}
```

**Signaux clés émis:**

| Système | Source | Description S|
|--------|--------|-------------|
| `video.duration` | Normalisation de la vague | Durée totale en secondes
| `video.resolution` | Normalisation de la vague | largeurM SK2 hauteur
| `video.fps` | Normalisation de la vague | Taux d'étalonnage |
| `shots.count` | ShotDetectionWave | Nombre de tirs détectés |
| `keyframes.count` | KeyframeExtractionWave
| `keyframes.duplicates_skipped` | KeyframeExtractionWave
| `scene.count` | SceneClusteringWave | Segments de scène cohérents |
| `transcript.entities.per` | TranscriptionWave | Nom des personnes provenant de l'NER
| `transcript.entities.org` | TranscriptionWave | Nom de l'organisation MSC2
| `transcript.word_count` | TranscriptionWave | Total des mots dans la transcription |

---


## VideoPipeline: Output RAG

L'article 5 de la Loi sur l'immigration et le statut des réfugiés `VideoPipeline` convert les signaux vidéo en `ContentChunk` pour l'indexation RAG:

```csharp
public class VideoPipeline : PipelineBase
{
    public override string PipelineId => "video";
    public override IReadOnlySet<string> SupportedExtensions => new HashSet<string>
    {
        ".mp4", ".mkv", ".avi", ".mov", ".wmv", ".webm", ".flv", ".m4v", ".mpeg", ".mpg"
    };

    private List<ContentChunk> BuildContentChunks(VideoContext context, string filePath)
    {
        var chunks = new List<ContentChunk>();

        // 1. Scene-based chunks (best for video retrieval)
        foreach (var scene in context.Scenes)
        {
            var sceneText = BuildSceneText(context, scene);
            var embedding = context.GetCached<float[]>($"scene_centroid.{scene.Id}");

            chunks.Add(new ContentChunk
            {
                Text = sceneText,
                ContentType = ContentType.Summary,
                Embedding = embedding,  // Proper vector column, not metadata
                Metadata = new Dictionary<string, object?>
                {
                    ["source"] = "video_scene",
                    ["scene_id"] = scene.Id,
                    ["key_terms"] = scene.KeyTerms,
                    ["speakers"] = scene.SpeakerIds,
                    ["start_time"] = scene.StartTime,
                    ["end_time"] = scene.EndTime
                }
            });
        }

        // 2. Transcript chunks (1-minute windows)
        var transcriptChunks = BuildTranscriptChunks(context, filePath);
        chunks.AddRange(transcriptChunks);

        // 3. Text track chunks (on-screen text/subtitles)
        foreach (var textTrack in context.TextTracks)
        {
            chunks.Add(new ContentChunk
            {
                Text = $"On-screen text: {textTrack.Text}",
                ContentType = ContentType.ImageOcr,
                Metadata = new Dictionary<string, object?>
                {
                    ["source"] = "video_ocr",
                    ["text_type"] = textTrack.TextType.ToString(),
                    ["start_time"] = textTrack.StartTime
                }
            });
        }

        return chunks;
    }

    private string BuildSceneText(VideoContext context, SceneSegment scene)
    {
        var parts = new List<string>();

        if (!string.IsNullOrEmpty(scene.Label))
            parts.Add($"Scene: {scene.Label}");

        parts.Add($"[{FormatTime(scene.StartTime)} - {FormatTime(scene.EndTime)}]");

        if (scene.KeyTerms.Count > 0)
            parts.Add($"Topics: {string.Join(", ", scene.KeyTerms)}");

        // Add utterances in this scene
        var sceneUtterances = context.Utterances
            .Where(u => u.StartTime >= scene.StartTime && u.EndTime <= scene.EndTime)
            .OrderBy(u => u.StartTime);

        if (sceneUtterances.Any())
            parts.Add($"Speech: {string.Join(" ", sceneUtterances.Select(u => u.Text))}");

        return string.Join("\n", parts);
    }
}
```

**Exemple de sortie d'un film:**

```json
{
  "chunks": [
    {
      "text": "Scene: Opening montage\n[0:00 - 2:34]\nTopics: city, night, traffic\nSpeech: The year is 2049. The world has changed.",
      "contentType": "Summary",
      "metadata": {
        "source": "video_scene",
        "scene_id": "abc123",
        "key_terms": ["city", "night", "traffic"],
        "start_time": 0.0,
        "end_time": 154.0
      }
    },
    {
      "text": "The detective arrived at the crime scene. Forensics had already processed the area.",
      "contentType": "Transcript",
      "metadata": {
        "source": "video_transcript",
        "time_window": "2:34 - 3:34",
        "utterance_count": 4
      }
    },
    {
      "text": "On-screen text: LOS ANGELES 2049",
      "contentType": "ImageOcr",
      "metadata": {
        "source": "video_ocr",
        "text_type": "Title"
      }
    }
  ]
}
```

---


## Caractéristiques du rendement

### Temps de traitement (2-film d'une heureM SK1 1080p)

| Étape MSC1 Temps MSC2 Notes ♫ MSC3 ♫
|-------|------|-------|
| FFprobe métadonnées
| Détection des coups | MSC2 s MSC3 Filtre de scène FFmpeg S|
| Extraction des cadres-clés | ~30s ≥| ≤500 I-frames |
| deduplication dHash | MSC2s MSC3 \MSC4 \ MSC5 \ msc6 \ cadres \ |
| Établissement d'un CLIP en série | CSK2s | \300 cadres, en série
| ImageSummarizer OCR
| Extraction de l'audio | MSC2s MSC3 FFmpeg SMC4
| Transcription de murmurer |
| Diarisation de l'émetteur |
| Extraction de l’NER | ~10s M| BERTM SK4NER sur la transcription P|
| Groupement des scènes | ~5s ≥| ≤|
| Production de données probantes
| **Total** | **~8-10 minutes** | |

### Sans optimisation

| Optimisation | Épargne
|--------------|---------|
| déduplication dHash | ~40% cadres filtrés CLIP conservé DS=
| CLIP en série | MSC2x plus rapide SMC3 | SMC4s conservés SMC5
| Composition du pipeline | Réutilise l'imageSummarizerM SK2Vêtes audioSommarizer |
| **Total des économies** | **~3-4 minutes** |

### Utilisation de la mémoire

| Composante | Mémoire |
|-----------|--------|
| CLIP ViTM SK1B/32 ONNX | ~350MB SSK5
| Base de whisper
| ECAPAM SK1TDNN | ~100MB S|
| BERTM SK1NER | ~500MB S|
| **Prêt** | **~1.5GB** |

---


## L'intégration avec lucidRAG

VideoSummarizer s'inscrit comme un `IPipeline` pour routage automatique:

```csharp
// In Program.cs
builder.Services.AddDocSummarizer(builder.Configuration.GetSection("DocSummarizer"));
builder.Services.AddDocSummarizerImages(builder.Configuration.GetSection("Images"));
builder.Services.AddVideoSummarizer();  // NEW
builder.Services.AddPipelineRegistry(); // Must be last

// Auto-routing by extension
var registry = services.GetRequiredService<IPipelineRegistry>();
var pipeline = registry.FindForFile("movie.mp4");  // Returns VideoPipeline
var result = await pipeline.ProcessAsync("movie.mp4");
```

**Extensions supportées:**

- `.mp4`, `.mkv`, `.avi`, `.mov`, `.wmv`, `.webm`, `.flv`, `.m4v`, `.mpeg`, `.mpg`

---


## Ce que vous obtiez

- **Échantillons RAG de niveau Scene-**: Segments cohérents avec les transcriptions , termes clés
- **Méthodes de preuve multimodales**: Visual (keyframe embeddings), audio (speaker diarizationM SK4texte MSC5OCRMSC6 sous-titresMST7
- **Entités désignées**: PersonnesM SK1 Organisations , emplacements à partir de la transcription NER
- **provenance vérifiable**: Tous les signaux ont une vague de source
- **Traitement efficace**: 10-15 minutes pour un film d’une durée de 2 heures.

## Quels sont les coûts?

- **Memory GPU ~1.5GB** pour tous les modèles ONNX
- **~8-10 traitement des minutes** par 2-heure de film
- **espace de disques** pour les fichiers intermédiaires ( nettoyé automatiquement)
- **Complexité**: L’orchestrement de cinq pipelines exige une compréhension des dépendances d’ondes

---


## Conclusion

VideoSummarizer démontre que **composition du pipeline** échelles:

1. **Réutilisation de pipelines spécialisés**: DonM SK1t reinventer ImageSummarizer ou AudioSommarizer. Chain them
2. **Filtrer avant les opérations coûteuses**: coûts de déduplication dHash <1msM SK2 permet d'éviter 40% des conclusions du CLIP
3. **Opérations GPU par lots**: 8 images par passe = ≥3-5x accélération
4. **Extraire la structure avant le contenu**: Shots → Scenes → Evidence (not raw frames SME4 LLMSMA5
5. **Gestion du modèle lazi**: Télécharger les modèles seulement si nécessaire, détecter GPU automatiquement
6. **Routage réactif**: Travail de route vers les composants disponibles , retour gracieux

Le résultat : est de transformer un film d’une heure en un livret de signalisation structuré comportant des scènes, des transcriptions et des entités, ainsi que des embeddings.

- "Trouver des scènes où John Smith discute de Microsoft
- "Pour voir des clips avec le texte de l’écran sur le projet Phoenix
- "Trouver des vidéos similaires à cette scène

**Le modèle RAG réduit pour le vidéo:**

```
Ingestion:  Video → 16 waves → Signals + Evidence (scenes, transcripts, entities)
Storage:    Signals (indexed) + Embeddings (CLIP, voice) + Evidence (chunks)
Query:      Filter (SQL) → Search (BM25 + vector) → Synthesize (LLM, ~5 results)
```

**Le système de capacité:**

```
Startup:    Detect GPU → Load ModelManifest (YAML) → Initialize SignalSink
Activation: Component requests model → Lazy download → Signal "ModelAvailable"
Routing:    Route to best provider → Fallback chain → Backpressure control
Atoms:      Rate limiting + Time estimation + Pipeline balancing
```

Il s'agit de **Fuzziness restreinte** à l’échelle

- **Components probabilistes proposent des signaux**: embeddings (CLIPM SK2 hypothèses OCR, prédictions de diarisation
- **La cotation déterministe assemble la structure**: cotes limites pondérées, sélection de seuilM SK2 contraintes temporelles
- **LLM (optionnel) synthétise à partir de données probantes**: contexte limité , provenance vérifiable

Le LLM fonctionne sur **pré-computed,éléments probants vérifiables**, jamais vidéo bruteM SK1

---


## Ressources

### lucidRAG Documentation

- **[VideoSummarizer Library](https://github.com/scottgal/lucidrag/tree/main/src/VideoSummarizer.Core)** - Code de source
- **[Documentation de l'NER](https://github.com/scottgal/lucidrag/blob/main/docs/NER_EXTRACTION_DEDUPLICATION.md)** - Architecture d'extraction des entités

### Bibliothèques connexes

- **[ImageSummarizer](https://github.com/scottgal/lucidrag/tree/main/src/ImageSummarizer.Core)** - Pipeline d'intelligence visuelle
- **[AudioSummarizer](https://github.com/scottgal/lucidrag/tree/main/src/AudioSummarizer.Core)** - Pipeline audio judiciaire
- **[DocSummarizer](https://github.com/scottgal/lucidrag/tree/main/src/Mostlylucid.DocSummarizer.Core)** - Pipeline de documents

### Modèles ONNX

- **[CLIP ViT-BM SK1](https://huggingface.co/openai/clip-vit-base-patch32)** - Emplacements visuels
- **[ECAPA-TDNN](https://huggingface.co/Wespeaker/wespeaker-ecapa-tdnn512-LM)** - Emplacements de haut-parleur
- **[BERT-NER](https://huggingface.co/dslim/bert-base-NER)** - Reconnaissance des entités désignées
- **[Les murmurs](https://github.com/openai/whisper)** - Transcription de la parole

### Articles connexes

**Patterns de base:**

- **[RAG réduit](https://www.mostlylucid.net/blog/reduced-rag)** - Le modèle de base
- **[Modèle de fuzziness restreint](/blog/constrained-fuzziness-pattern)** - Modèle fondamental

**Réduction des mises en œuvre de RAG:**

- **[DocSummarizer](/blog/building-a-document-summarizer-with-rag)** - Document RAG avec extraction d'entités
- **[ImageSummarizer](/blog/constrained-fuzzy-image-intelligence)** - RAG d'image avec 22- intelligence visuelle des ondes
  - **[Three-Tier OCR Pipeline](/blog/constrained-fuzzy-image-ocr-pipeline)** - escalation de l’OCR
- **[AudioSummarizer](/blog/audiosummarizer-forensic-audio-characterization)** - Caractérisation audio judiciaire
- **VideoSummarizer (le présent articleM SK1** - Organisateur de l'intelligence vidéo
- **[Système de gestion des données](/blog/datasummarizer-how-it-works)** - Profilage des données et détermination du schéma

---


## La série

| Partie | Modèle MSC2 Concentration MSC3
|------|---------|-------|
| 1 | [Fuzziness restreinte](/blog/constrained-fuzziness-pattern) Composante unique |
| 2 | [MoM Fuzzy restreint](/blog/constrained-mom-mixture-of-models) | Composantes multiples |
| 3 | [Traçage du contexte](/blog/constrained-fuzzy-context-dragging) | Temps / mémoire |
| 4 | [Intelligence de l'image](/blog/constrained-fuzzy-image-intelligence) | Architecture des vaguesM SK1 22 ondes |
| 4.1 | [Three-Tier OCR Pipeline](/blog/constrained-fuzzy-image-ocr-pipeline) | OCRM SK1 Modèles ONNX, bandes de films |
| 4.2 | [AudioSummarizer](/blog/audiosummarizer-forensic-audio-characterization) | Audio médico-légal, diarisation de l'émetteur |
| **4.3** | **VideoSummarizer (le présent articleM SK1** | **Orchestrement vidéo, lots CLIP, NER** |

**Suivant**: MultiM SK1 Graphique modal RAG avec lucidRAG. Composant les cinq résumés en un graphe de connaissance unifié à l'aide d'une liaison croisée

Toutes les parties suivent la même invariante: **Les composantes probabilistes proposent; les systèmes déterministes persistent**.